Skip to content
48 changes: 45 additions & 3 deletions nms/src/main/kotlin/io/github/pylonmc/rebar/nms/NmsAccessorImpl.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.pylonmc.rebar.nms

import com.destroystokyo.paper.event.player.PlayerRecipeBookClickEvent
import com.google.common.collect.BiMap
import com.mojang.brigadier.StringReader
import com.mojang.brigadier.exceptions.CommandSyntaxException
import io.github.pylonmc.rebar.Rebar
Expand All @@ -10,6 +11,7 @@ import io.github.pylonmc.rebar.entity.packet.BlockTextureEntity
import io.github.pylonmc.rebar.i18n.PlayerTranslationHandler
import io.github.pylonmc.rebar.item.ItemTypeWrapper
import io.github.pylonmc.rebar.item.RebarItemSchema
import io.github.pylonmc.rebar.item.loot.LootTableResultBuilder
import io.github.pylonmc.rebar.nms.entity.BlockTextureEntityImpl
import io.github.pylonmc.rebar.nms.inventory.KeyedContainerListener
import io.github.pylonmc.rebar.nms.packet.PlayerPacketHandler
Expand All @@ -23,34 +25,41 @@ import io.papermc.paper.datacomponent.PaperDataComponentType
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import net.kyori.adventure.text.Component
import net.minecraft.advancements.AdvancementRewards.Builder.recipe
import net.minecraft.commands.arguments.item.ItemParser
import net.minecraft.core.BlockPos
import net.minecraft.core.registries.Registries
import net.minecraft.nbt.TextComponentTagVisitor
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket
import net.minecraft.network.protocol.game.ClientboundPlaceGhostRecipePacket
import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket
import net.minecraft.resources.Identifier
import net.minecraft.resources.ResourceKey
import net.minecraft.server.MinecraftServer
import net.minecraft.util.context.ContextKeySet
import net.minecraft.world.inventory.AbstractCraftingMenu
import net.minecraft.world.inventory.RecipeBookMenu.PostPlaceAction
import net.minecraft.world.item.Item
import net.minecraft.world.item.crafting.RecipeManager
import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity
import net.minecraft.world.level.block.state.properties.Property
import net.minecraft.world.level.storage.loot.LootParams
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets
import net.minecraft.world.level.storage.loot.parameters.LootContextParams
import net.minecraft.world.phys.BlockHitResult
import org.bukkit.Bukkit
import net.minecraft.world.phys.Vec3
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.World
import org.bukkit.block.Block
import org.bukkit.block.BlockFace
import org.bukkit.craftbukkit.CraftEquipmentSlot
import org.bukkit.craftbukkit.CraftLootTable
import org.bukkit.craftbukkit.CraftRegistry
import org.bukkit.craftbukkit.CraftServer
import org.bukkit.craftbukkit.CraftWorld
import org.bukkit.craftbukkit.block.CraftBlock
import org.bukkit.craftbukkit.block.CraftBlockEntityState
import org.bukkit.craftbukkit.block.data.CraftBlockData
import org.bukkit.craftbukkit.damage.CraftDamageSource
import org.bukkit.craftbukkit.entity.CraftEntity
import org.bukkit.craftbukkit.entity.CraftLivingEntity
import org.bukkit.craftbukkit.entity.CraftPlayer
Expand All @@ -61,6 +70,7 @@ import org.bukkit.entity.Entity
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Player
import org.bukkit.inventory.*
import org.bukkit.loot.LootTable
import org.bukkit.persistence.PersistentDataContainer
import java.lang.invoke.MethodHandle
import java.lang.invoke.MethodHandles
Expand All @@ -71,6 +81,7 @@ import kotlin.coroutines.EmptyCoroutineContext
import kotlin.math.max
import kotlin.math.min
import com.mojang.datafixers.util.Pair as NmsPair
import net.minecraft.util.Unit as NmsUnit
import net.minecraft.world.entity.EquipmentSlot as NmsEquipmentSlot
import net.minecraft.world.item.ItemStack as NmsItemStack
import net.minecraft.core.component.DataComponentType as NmsDataComponentType
Expand All @@ -83,6 +94,8 @@ import net.minecraft.core.component.DataComponentType as NmsDataComponentType
@Suppress("unused")
object NmsAccessorImpl : NmsAccessor {

private val CONTEXT_KEY_SET_REGISTRY: BiMap<Identifier, ContextKeySet>

// We use both the field and the handle because the handle will have significantly better performance
// getting the field value but cannot be used for setting so we still need the raw field.
// (even if we used a VarHandle, because the field is normally final, setting will not work)
Expand All @@ -91,6 +104,11 @@ object NmsAccessorImpl : NmsAccessor {

init {
try {
val contextKeySetRegistryField = LootContextParamSets::class.java.getDeclaredField("REGISTRY")
contextKeySetRegistryField.isAccessible = true
@Suppress("UNCHECKED_CAST")
CONTEXT_KEY_SET_REGISTRY = contextKeySetRegistryField.get(null) as BiMap<Identifier, ContextKeySet>

furnaceQuickCheckField = AbstractFurnaceBlockEntity::class.java.getDeclaredField("quickCheck")
furnaceQuickCheckField.isAccessible = true

Expand Down Expand Up @@ -473,4 +491,28 @@ object NmsAccessorImpl : NmsAccessor {
val nmsStack = (itemStack as CraftItemStack).handle
return componentsMatch(itemStack, components) && nmsStack.componentsPatch.size() == components.size
}

override fun getRandomItems(world: World, contextSet: NamespacedKey, lootTable: LootTable, optionalRandomLootSeed: Long?, lootContext: LootTableResultBuilder): Collection<ItemStack> {
val contextParamSet = CONTEXT_KEY_SET_REGISTRY[CraftNamespacedKey.toMinecraft(contextSet)] ?: throw IllegalArgumentException("Invalid context set $contextSet")
val nmsTable = (lootTable as CraftLootTable).handle
val lootParams = LootParams.Builder((world as CraftWorld).handle)
.withOptionalParameter(LootContextParams.THIS_ENTITY, lootContext.thisEntity?.let { (it as CraftEntity).handle })
.withOptionalParameter(LootContextParams.INTERACTING_ENTITY, lootContext.interactingEntity?.let { (it as CraftEntity).handle })
.withOptionalParameter(LootContextParams.TARGET_ENTITY, lootContext.targetEntity?.let { (it as CraftEntity).handle })
.withOptionalParameter(LootContextParams.LAST_DAMAGE_PLAYER, lootContext.lastDamagePlayer?.let { (it as CraftPlayer).handle })
.withOptionalParameter(LootContextParams.DAMAGE_SOURCE, lootContext.damageSource?.let { (it as CraftDamageSource).handle })
.withOptionalParameter(LootContextParams.ATTACKING_ENTITY, lootContext.attackingEntity?.let { (it as CraftEntity).handle })
.withOptionalParameter(LootContextParams.DIRECT_ATTACKING_ENTITY, lootContext.directAttackingEntity?.let { (it as CraftEntity).handle })
.withOptionalParameter(LootContextParams.ORIGIN, lootContext.origin?.let { Vec3(it.x, it.y, it.z) })
.withOptionalParameter(LootContextParams.BLOCK_STATE, lootContext.blockData?.let { (it as CraftBlockData).state })
.withOptionalParameter(LootContextParams.BLOCK_ENTITY, lootContext.blockState?.let { (it as? CraftBlockEntityState<*>)?.blockEntity })
.withOptionalParameter(LootContextParams.TOOL, lootContext.tool?.let { (it as CraftItemStack).handle })
.withOptionalParameter(LootContextParams.EXPLOSION_RADIUS, lootContext.explosionRadius)
.create(contextParamSet)
return if (optionalRandomLootSeed != null) {
nmsTable.getRandomItems(lootParams, optionalRandomLootSeed)
} else {
nmsTable.getRandomItems(lootParams)
}.map { it.asBukkitMirror() }
}
}
1 change: 1 addition & 0 deletions rebar/src/main/kotlin/io/github/pylonmc/rebar/Rebar.kt
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ object Rebar : JavaPlugin(), RebarAddon {
SignRebarBlockHandler.register(this, pm)
SneakRebarBlockHandler.register(this, pm)
SpongeRebarBlockHandler.register(this, pm)
StructureGrowRebarBlockHandler.register(this, pm)
TargetRebarBlockHandler.register(this, pm)
TNTRebarBlockHandler.register(this, pm)
UnloadRebarBlockHandler.register(this, pm)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,9 @@ internal object BlockListener : MultiListener {

@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
private fun disallowStructureGrow(event: StructureGrowEvent) {
// The block at event.location is handled in StructureGrowRebarBlockHandler
for (state in event.blocks) {
if (BlockStorage.isRebarBlock(state.block)) {
if (event.location != state.location && BlockStorage.isRebarBlock(state.block)) {
event.isCancelled = true
return
}
Expand Down
13 changes: 13 additions & 0 deletions rebar/src/main/kotlin/io/github/pylonmc/rebar/block/RebarBlock.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import io.github.pylonmc.rebar.item.builder.ItemStackBuilder
import io.github.pylonmc.rebar.nms.NmsAccessor
import io.github.pylonmc.rebar.registry.RebarRegistry
import io.github.pylonmc.rebar.util.IMMEDIATE_FACES
import io.github.pylonmc.rebar.util.editBlockData
import io.github.pylonmc.rebar.util.isChunkLoaded
import io.github.pylonmc.rebar.util.position.BlockPosition
import io.github.pylonmc.rebar.util.position.position
Expand All @@ -30,11 +31,13 @@ import io.papermc.paper.datacomponent.DataComponentTypes
import net.kyori.adventure.key.Key
import org.bukkit.*
import org.bukkit.block.Block
import org.bukkit.block.data.BlockData
import org.bukkit.entity.ItemDisplay
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataAdapterContext
import org.bukkit.persistence.PersistentDataContainer
import java.util.function.Consumer

/**
* Represents a Rebar block in the world.
Expand Down Expand Up @@ -139,6 +142,16 @@ open class RebarBlock private constructor(val block: Block) : WailaSupplier, Key
*/
open fun postInitialise() {}

@JvmOverloads
fun editBlockData(editor: Consumer<BlockData>, applyPhysics: Boolean = true) {
block.editBlockData(editor, applyPhysics)
}

@JvmOverloads
fun <D : BlockData> editBlockData(dataType: Class<D>, editor: Consumer<D>, applyPhysics: Boolean = true) {
block.editBlockData(dataType, editor, applyPhysics)
}

Comment thread
JustAHuman-xD marked this conversation as resolved.
/**
* Used to initialize [blockTextureEntity].
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ interface CargoRebarBlock : LogisticRebarBlock, EntityHolderRebarBlock {
.build()
)
.itemStack(
ItemStackBuilder.Companion.of(Material.GRAY_CONCRETE)
ItemStackBuilder.of(Material.GRAY_CONCRETE)
.addCustomModelDataString("pylon:cargo_duct:line"))
.build(fromLocation)
addEntity("cargo:direct-connection:${face}", display)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package io.github.pylonmc.rebar.block.interfaces

import io.github.pylonmc.rebar.block.BlockListener
import io.github.pylonmc.rebar.block.BlockListener.logEventHandleErr
import io.github.pylonmc.rebar.block.BlockStorage
import io.github.pylonmc.rebar.event.api.MultiListener
import io.github.pylonmc.rebar.event.api.annotation.MultiHandlers
import io.github.pylonmc.rebar.event.api.annotation.UniversalHandler
import org.bukkit.event.EventPriority
import org.bukkit.event.world.StructureGrowEvent
import org.jetbrains.annotations.ApiStatus

interface StructureGrowRebarBlockHandler {
fun onStructureGrow(event: StructureGrowEvent, priority: EventPriority) {}

@ApiStatus.Internal
companion object : MultiListener {
@UniversalHandler
private fun onStructureGrow(event: StructureGrowEvent, priority: EventPriority) {
val rebarBlock = BlockStorage.get(event.location)
if (rebarBlock is StructureGrowRebarBlockHandler) {
try {
MultiHandlers.handleEvent(rebarBlock, "onStructureGrow", event, priority)
} catch (e: Exception) {
BlockListener.logEventHandleErr(event, e, rebarBlock)
}
} else if (priority == EventPriority.LOWEST) {
event.isCancelled = true
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import io.github.pylonmc.rebar.util.rebarKey
import io.github.pylonmc.rebar.util.setNullable
import io.papermc.paper.datacomponent.DataComponentTypes
import io.papermc.paper.datacomponent.item.CustomModelData
import jdk.jfr.internal.StringPool.addString
import org.bukkit.block.Block
import org.bukkit.block.BlockFace
import org.bukkit.entity.ItemDisplay
Expand Down Expand Up @@ -79,11 +80,10 @@ class FluidEndpointDisplay : RebarEntity<ItemDisplay>, RemoveRebarEntityHandler,

@Suppress("UnstableApiUsage")
fun updateItemDisplay() {
val modelData = CustomModelData.customModelData()
modelData.addString("fluid_point_${point.type.name.lowercase()}:${pipeDisplay?.pipe?.key ?: "none"}")
modelData.addString("face=${face.oppositeFace.name.lowercase()}")
this.entity.setItemStack(this.entity.itemStack.apply {
setData(DataComponentTypes.CUSTOM_MODEL_DATA, modelData)
setData(DataComponentTypes.CUSTOM_MODEL_DATA, CustomModelData.customModelData()
.addString("fluid_point_${point.type.name.lowercase()}:${pipeDisplay?.pipe?.key ?: "none"}")
.addString("face=${face.oppositeFace.name.lowercase()}"))
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@ class FluidIntersectionDisplay : RebarEntity<ItemDisplay>, RemoveRebarEntityHand
if (connectedPipeDisplays.isEmpty()) return

val marker = BlockStorage.getAs(FluidIntersectionMarker::class.java, entity.location.block) ?: return
val modelData = CustomModelData.customModelData()
modelData.addString("fluid_point_intersection:${marker.pipe.key}")
val modelData = CustomModelData.customModelData().addString("fluid_point_intersection:${marker.pipe.key}")

val from = this.entity.location
for (face in IMMEDIATE_FACES) {
Expand Down
34 changes: 32 additions & 2 deletions rebar/src/main/kotlin/io/github/pylonmc/rebar/item/RebarItem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -212,15 +212,45 @@ open class RebarItem(val stack: ItemStack) : Keyed {
}

/**
* Checks if [stack] is a Rebar item with schema [schema].
*/
* Checks if [stack] is a Rebar item with schema [schema].
*/
@JvmStatic
@Contract("null -> false")
fun isRebarItem(stack: ItemStack?, schema: RebarItemSchema): Boolean {
val otherSchema = RebarItemSchema.fromStack(stack) ?: return false
return otherSchema === schema
}

/**
* Checks if [stack] is a Rebar item but not castable to [clazz].
*/
@JvmStatic
@Contract("null -> false")
fun isRebarItemAndIsNot(stack: ItemStack?, clazz: Class<*>): Boolean {
val schema = RebarItemSchema.fromStack(stack) ?: return false
return !schema.isType(clazz)
}

/**
* Checks if [stack] is a Rebar item but not with the id [key].
*/
@JvmStatic
@Contract("null -> false")
fun isRebarItemAndIsNot(stack: ItemStack?, key: NamespacedKey): Boolean {
val schema = RebarItemSchema.fromStack(stack) ?: return false
return schema.key != key
}

/**
* Checks if [stack] is a Rebar item but not with the schema [schema].
*/
@JvmStatic
@Contract("null -> false")
fun isRebarItemAndIsNot(stack: ItemStack?, schema: RebarItemSchema): Boolean {
val otherSchema = RebarItemSchema.fromStack(stack) ?: return false
return otherSchema !== schema
}

/**
* Suppresses warnings about missing/incorrect translation keys for the item name and lore
* for the given item key
Expand Down
Loading
Loading