Skip to content
51 changes: 48 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,11 +81,14 @@ 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

@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 @@ -84,6 +97,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 @@ -352,4 +370,31 @@ object NmsAccessorImpl : NmsAccessor {
}
return nmsStack.componentsPatch.entrySet().map { PaperDataComponentType.minecraftToBukkit(it.key) }
}

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)
.withOptionalParameter(LootContextParams.ENCHANTMENT_LEVEL, lootContext.enchantmentLevel)
.withOptionalParameter(LootContextParams.ENCHANTMENT_ACTIVE, lootContext.enchantmentActive)
.withOptionalParameter(LootContextParams.ADDITIONAL_COST_COMPONENT_ALLOWED, lootContext.additionalCostComponentAllowed?.let { NmsUnit.INSTANCE })
.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
14 changes: 14 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 @@ -30,11 +30,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 +141,18 @@ open class RebarBlock private constructor(val block: Block) : WailaSupplier, Key
*/
open fun postInitialise() {}

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

@JvmOverloads
fun <D : BlockData> editBlockData(dataType: Class<D>, editor: Consumer<D>, applyPhysics: Boolean = true) {
val blockData = block.blockData
editor.accept(dataType.cast(blockData))
block.setBlockData(blockData, 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 @@ -123,7 +123,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 @@ -15,6 +15,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>, DeathRebarEntityHandler,

@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>, DeathRebarEntityHandl
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
18 changes: 18 additions & 0 deletions rebar/src/main/kotlin/io/github/pylonmc/rebar/item/RebarItem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,24 @@ open class RebarItem(val stack: ItemStack) : Keyed {
return schema.key == key
}

/**
* Checks if [stack] is a Rebar item but not castable to [clazz].
*/
@JvmStatic
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
fun isRebarItemAndIsNot(stack: ItemStack?, key: NamespacedKey): Boolean {
val schema = RebarItemSchema.fromStack(stack) ?: return false
return schema.key != key
}

/**
* Suppresses warnings about missing/incorrect translation keys for the item name and lore
* for the given item key
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package io.github.pylonmc.rebar.item.builder

import io.papermc.paper.datacomponent.DataComponentTypes
import io.papermc.paper.datacomponent.item.CustomModelData
import org.bukkit.Color
import org.bukkit.inventory.ItemStack

@Suppress("UnstableApiUsage")
class CustomModelDataBuilder {
Comment thread
JustAHuman-xD marked this conversation as resolved.
val strings = mutableListOf<String>()
val floats = mutableListOf<Float>()
val flags = mutableListOf<Boolean>()
val colors = mutableListOf<Color>()

fun addString(string: String) = apply {
this.strings.add(string)
}

fun addStrings(strings: List<String>) = apply {
this.strings.addAll(strings)
}

fun addStrings(vararg strings: String) = apply {
strings.forEach { addString(it) }
}

fun addFloat(float: Float) = apply {
this.floats.add(float)
}

fun addFloats(floats: List<Float>) = apply {
this.floats.addAll(floats)
}

fun addFloats(vararg floats: Float) = apply {
floats.forEach { addFloat(it) }
}

fun addFlag(flag: Boolean) = apply {
this.flags.add(flag)
}

fun addFlags(flags: List<Boolean>) = apply {
this.flags.addAll(flags)
}

fun addFlags(vararg flags: Boolean) = apply {
flags.forEach { addFlag(it) }
}

fun addColor(color: Color) = apply {
this.colors.add(color)
}

fun addColors(colors: List<Color>) = apply {
this.colors.addAll(colors)
}

fun addColors(vararg colors: Color) = apply {
colors.forEach { addColor(it) }
}

fun build(): CustomModelData = CustomModelData.customModelData()
.addStrings(this.strings)
.addFloats(this.floats)
.addFlags(this.flags)
.addColors(this.colors)
.build()

companion object {
fun of(customModelData: CustomModelData?): CustomModelDataBuilder {
val builder = CustomModelDataBuilder()
if (customModelData != null) {
builder.addStrings(customModelData.strings())
builder.addFloats(customModelData.floats())
builder.addFlags(customModelData.flags())
builder.addColors(customModelData.colors())
}
return builder
}

fun of(itemStack: ItemStack) = of(itemStack.getData(DataComponentTypes.CUSTOM_MODEL_DATA))

fun of(builder: ItemStackBuilder) = of(builder.stack)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -151,20 +151,10 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro
fun defaultTranslatableLore(key: NamespacedKey) =
lore(Component.translatable(loreKey(key), ""))

fun editCustomModelData(editFunction: Consumer<CustomModelData.Builder>) = apply {
val customModelData = stack.getData(DataComponentTypes.CUSTOM_MODEL_DATA)
val newCustomModelData = CustomModelData.customModelData()

if (customModelData != null) {
newCustomModelData.addFlags(customModelData.flags())
newCustomModelData.addStrings(customModelData.strings())
newCustomModelData.addFloats(customModelData.floats())
newCustomModelData.addColors(customModelData.colors())
}

fun editCustomModelData(editFunction: Consumer<CustomModelDataBuilder>) = apply {
val newCustomModelData = CustomModelDataBuilder.of(this)
editFunction.accept(newCustomModelData)

stack.setData(DataComponentTypes.CUSTOM_MODEL_DATA, newCustomModelData)
stack.setData(DataComponentTypes.CUSTOM_MODEL_DATA, newCustomModelData.build())
}

/**
Expand Down
Loading
Loading