diff --git a/build.gradle.kts b/build.gradle.kts index 2682e5e0..9aa96566 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -37,6 +37,7 @@ dependencies { implementation(project(path = ":eco-core:core-nms:v1_21_3", configuration = "reobf")) implementation(project(path = ":eco-core:core-nms:v1_21_4", configuration = "reobf")) implementation(project(path = ":eco-core:core-nms:v1_21_5", configuration = "reobf")) + implementation(project(path = ":eco-core:core-nms:v1_21_6", configuration = "reobf")) } allprojects { diff --git a/eco-api/src/main/java/com/willfp/eco/core/proxy/ProxyConstants.java b/eco-api/src/main/java/com/willfp/eco/core/proxy/ProxyConstants.java index c9dcad7c..10c1a1d0 100644 --- a/eco-api/src/main/java/com/willfp/eco/core/proxy/ProxyConstants.java +++ b/eco-api/src/main/java/com/willfp/eco/core/proxy/ProxyConstants.java @@ -32,7 +32,8 @@ public final class ProxyConstants { "v1_21", "v1_21_3", "v1_21_4", - "v1_21_5" + "v1_21_5", + "v1_21_6" ); private ProxyConstants() { diff --git a/eco-core/core-nms/v1_21_6/build.gradle.kts b/eco-core/core-nms/v1_21_6/build.gradle.kts new file mode 100644 index 00000000..deb45610 --- /dev/null +++ b/eco-core/core-nms/v1_21_6/build.gradle.kts @@ -0,0 +1,52 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +plugins { + id("io.papermc.paperweight.userdev") +} + +group = "com.willfp" +version = rootProject.version + +dependencies { + implementation(project(":eco-core:core-nms:modern")) + implementation(project(":eco-core:core-nms:common")) + paperweight.paperDevBundle("1.21.6-R0.1-SNAPSHOT") + + implementation("net.kyori:adventure-text-minimessage:4.11.0") { + version { + strictly("4.11.0") + } + exclude(group = "net.kyori", module = "adventure-api") + } +} + +tasks { + build { + dependsOn(reobfJar) + } + + reobfJar { + mustRunAfter(shadowJar) + } + + shadowJar { + relocate( + "com.willfp.eco.internal.spigot.proxy.common", + "com.willfp.eco.internal.spigot.proxy.v1_21_6.common" + ) + relocate( + "net.kyori.adventure.text.minimessage", + "com.willfp.eco.internal.spigot.proxy.v1_21_6.minimessage" + ) + } + + compileJava { + options.release.set(21) + } + + compileKotlin { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_21) + } + } +} diff --git a/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/BukkitCommands.kt b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/BukkitCommands.kt new file mode 100644 index 00000000..0a3adeea --- /dev/null +++ b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/BukkitCommands.kt @@ -0,0 +1,35 @@ +package com.willfp.eco.internal.spigot.proxy.v1_21_6 + +import com.willfp.eco.core.command.PluginCommandBase +import com.willfp.eco.internal.spigot.proxy.BukkitCommandsProxy +import org.bukkit.Bukkit +import org.bukkit.command.Command +import org.bukkit.command.SimpleCommandMap +import org.bukkit.craftbukkit.CraftServer +import java.lang.reflect.Field + +class BukkitCommands : BukkitCommandsProxy { + private val knownCommandsField: Field by lazy { + SimpleCommandMap::class.java.getDeclaredField("knownCommands") + .apply { + isAccessible = true + } + } + + @Suppress("UNCHECKED_CAST") + private val knownCommands: MutableMap + get() = knownCommandsField.get(getCommandMap()) as MutableMap + + override fun getCommandMap(): SimpleCommandMap { + return (Bukkit.getServer() as CraftServer).commandMap + } + + override fun syncCommands() { + (Bukkit.getServer() as CraftServer).syncCommands() + } + + override fun unregisterCommand(command: PluginCommandBase) { + knownCommands.remove(command.name) + knownCommands.remove("${command.plugin.name.lowercase()}:${command.name}") + } +} diff --git a/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/CommonsInitializer.kt b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/CommonsInitializer.kt new file mode 100644 index 00000000..adaf4d87 --- /dev/null +++ b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/CommonsInitializer.kt @@ -0,0 +1,172 @@ +package com.willfp.eco.internal.spigot.proxy.v1_21_6 + +import ca.spottedleaf.dataconverter.minecraft.MCDataConverter +import com.willfp.eco.core.EcoPlugin +import com.willfp.eco.internal.spigot.proxy.CommonsInitializerProxy +import com.willfp.eco.internal.spigot.proxy.common.CommonsProvider +import com.willfp.eco.internal.spigot.proxy.common.packet.PacketInjectorListener +import com.willfp.eco.internal.spigot.proxy.common.toResourceLocation +import io.papermc.paper.adventure.PaperAdventure +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.serializer.json.JSONComponentSerializer +import net.minecraft.core.component.DataComponents +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.nbt.CompoundTag +import net.minecraft.nbt.Tag +import net.minecraft.network.chat.ComponentSerialization +import net.minecraft.resources.ResourceLocation +import net.minecraft.server.level.ServerPlayer +import net.minecraft.world.entity.PathfinderMob +import net.minecraft.world.item.Item +import org.bukkit.Bukkit +import org.bukkit.Material +import org.bukkit.NamespacedKey +import org.bukkit.craftbukkit.CraftServer +import org.bukkit.craftbukkit.entity.CraftEntity +import org.bukkit.craftbukkit.entity.CraftMob +import org.bukkit.craftbukkit.entity.CraftPlayer +import org.bukkit.craftbukkit.inventory.CraftItemStack +import org.bukkit.craftbukkit.inventory.CraftMetaArmor +import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer +import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry +import org.bukkit.craftbukkit.util.CraftMagicNumbers +import org.bukkit.craftbukkit.util.CraftNamespacedKey +import org.bukkit.entity.LivingEntity +import org.bukkit.entity.Mob +import org.bukkit.entity.Player +import org.bukkit.inventory.ItemStack +import org.bukkit.persistence.PersistentDataContainer +import java.lang.reflect.Field + +class CommonsInitializer : CommonsInitializerProxy { + override fun init(plugin: EcoPlugin) { + CommonsProvider.setIfNeeded(CommonsProviderImpl) + plugin.onEnable { + plugin.eventManager.registerListener(PacketInjectorListener) + } + } + + object CommonsProviderImpl : CommonsProvider { + private val cisHandle: Field = CraftItemStack::class.java.getDeclaredField("handle").apply { + isAccessible = true + } + + private val pdcRegsitry = CraftMetaArmor::class.java + .superclass // Access CraftMetaItem + .getDeclaredField("DATA_TYPE_REGISTRY") + .apply { isAccessible = true } + .get(null) as CraftPersistentDataTypeRegistry + + override val nbtTagString = 8 + + override fun toPathfinderMob(mob: Mob): PathfinderMob? { + val craft = mob as? CraftMob ?: return null + return craft.handle as? PathfinderMob + } + + override fun toResourceLocation(namespacedKey: NamespacedKey): ResourceLocation = + CraftNamespacedKey.toMinecraft(namespacedKey) + + override fun asNMSStack(itemStack: ItemStack): net.minecraft.world.item.ItemStack { + return if (itemStack !is CraftItemStack) { + CraftItemStack.asNMSCopy(itemStack) + } else { + cisHandle[itemStack] as net.minecraft.world.item.ItemStack? ?: CraftItemStack.asNMSCopy(itemStack) + } + } + + override fun asBukkitStack(itemStack: net.minecraft.world.item.ItemStack): ItemStack { + return CraftItemStack.asCraftMirror(itemStack) + } + + override fun mergeIfNeeded(itemStack: ItemStack, nmsStack: net.minecraft.world.item.ItemStack) { + if (itemStack !is CraftItemStack) { + itemStack.itemMeta = CraftItemStack.asCraftMirror(nmsStack).itemMeta + } + } + + override fun toBukkitEntity(entity: net.minecraft.world.entity.LivingEntity): LivingEntity? = + CraftEntity.getEntity(Bukkit.getServer() as CraftServer, entity) as? LivingEntity + + override fun makePdc(tag: CompoundTag, base: Boolean): PersistentDataContainer { + fun emptyPdc(): CraftPersistentDataContainer = CraftPersistentDataContainer(pdcRegsitry) + + fun CompoundTag?.toPdc(): PersistentDataContainer { + val pdc = emptyPdc() + this ?: return pdc + val keys = this.keySet() + for (key in keys) { + pdc.put(key, this[key]) + } + + return pdc + } + + return if (base) { + tag.toPdc() + } else { + if (tag.contains("PublicBukkitValues")) { + tag.getCompound("PublicBukkitValues").get().toPdc() + } else { + emptyPdc() + } + } + } + + override fun setPdc( + tag: CompoundTag, + pdc: PersistentDataContainer?, + item: net.minecraft.world.item.ItemStack? + ) { + fun CraftPersistentDataContainer.toTag(): CompoundTag { + val compound = CompoundTag() + val rawPublicMap: Map = this.raw + for ((key, value) in rawPublicMap) { + compound.put(key, value) + } + + return compound + } + + val container = when (pdc) { + is CraftPersistentDataContainer? -> pdc + else -> null + } + + if (item != null) { + if (container != null && !container.isEmpty) { + for (key in tag.keySet()) { + tag.remove(key) + } + + tag.merge(container.toTag()) + } else { + item.remove(DataComponents.CUSTOM_DATA) + } + } else { + if (container != null && !container.isEmpty) { + tag.put("PublicBukkitValues", container.toTag()) + } else { + tag.remove("PublicBukkitValues") + } + } + } + + override fun materialToItem(material: Material): Item = + BuiltInRegistries.ITEM.getOptional(material.key.toResourceLocation()) + .orElseThrow { IllegalArgumentException("Material is not item!") } + + override fun itemToMaterial(item: Item) = + Material.getMaterial(BuiltInRegistries.ITEM.getKey(item).path.uppercase()) + ?: throw IllegalArgumentException("Invalid material!") + + override fun toNMS(player: Player): ServerPlayer { + return (player as CraftPlayer).handle + } + + override fun toNMS(component: Component): net.minecraft.network.chat.Component { + // TODO: Don't have hard dependency on paper + return PaperAdventure.asVanilla(component) + } + } +} diff --git a/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/DisplayName.kt b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/DisplayName.kt new file mode 100644 index 00000000..5f903f5f --- /dev/null +++ b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/DisplayName.kt @@ -0,0 +1,57 @@ +package com.willfp.eco.internal.spigot.proxy.v1_21_6 + +import com.willfp.eco.core.packet.Packet +import com.willfp.eco.core.packet.sendPacket +import com.willfp.eco.internal.spigot.proxy.DisplayNameProxy +import com.willfp.eco.internal.spigot.proxy.common.toNMS +import net.kyori.adventure.text.Component +import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket +import net.minecraft.network.syncher.EntityDataAccessor +import net.minecraft.network.syncher.SynchedEntityData +import net.minecraft.world.entity.Entity +import org.bukkit.craftbukkit.entity.CraftLivingEntity +import org.bukkit.entity.LivingEntity +import org.bukkit.entity.Player +import java.util.Optional + +@Suppress("UNCHECKED_CAST") +class DisplayName : DisplayNameProxy { + private val displayNameAccessor = Entity::class.java + .declaredFields + .filter { it.type == EntityDataAccessor::class.java } + .toList()[2] + .apply { isAccessible = true } + .get(null) as EntityDataAccessor> + + private val customNameVisibleAccessor = Entity::class.java + .declaredFields + .filter { it.type == EntityDataAccessor::class.java } + .toList()[3] + .apply { isAccessible = true } + .get(null) as EntityDataAccessor + + override fun setClientsideDisplayName( + entity: LivingEntity, + player: Player, + displayName: Component, + visible: Boolean + ) { + if (entity !is CraftLivingEntity) { + return + } + + val nmsComponent = displayName.toNMS() + + val nmsEntity = entity.handle + + val packet = ClientboundSetEntityDataPacket( + nmsEntity.id, + listOf( + SynchedEntityData.DataValue.create(displayNameAccessor, Optional.of(nmsComponent)), + SynchedEntityData.DataValue.create(customNameVisibleAccessor, visible) + ) + ) + + player.sendPacket(Packet(packet)) + } +} diff --git a/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/DummyEntityFactory.kt b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/DummyEntityFactory.kt new file mode 100644 index 00000000..6c543c43 --- /dev/null +++ b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/DummyEntityFactory.kt @@ -0,0 +1,15 @@ +package com.willfp.eco.internal.spigot.proxy.v1_21_6 + +import com.willfp.eco.internal.entities.EcoDummyEntity +import com.willfp.eco.internal.spigot.proxy.DummyEntityFactoryProxy +import org.bukkit.Location +import org.bukkit.craftbukkit.CraftWorld +import org.bukkit.entity.Entity +import org.bukkit.entity.Zombie + +class DummyEntityFactory : DummyEntityFactoryProxy { + override fun createDummyEntity(location: Location): Entity { + val world = location.world as CraftWorld + return EcoDummyEntity(world.createEntity(location, Zombie::class.java)) + } +} diff --git a/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/EntityControllerFactory.kt b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/EntityControllerFactory.kt new file mode 100644 index 00000000..789eed2e --- /dev/null +++ b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/EntityControllerFactory.kt @@ -0,0 +1,12 @@ +package com.willfp.eco.internal.spigot.proxy.v1_21_6 + +import com.willfp.eco.core.entities.ai.EntityController +import com.willfp.eco.internal.spigot.proxy.EntityControllerFactoryProxy +import com.willfp.eco.internal.spigot.proxy.v1_21_6.entity.EcoEntityController +import org.bukkit.entity.Mob + +class EntityControllerFactory : EntityControllerFactoryProxy { + override fun createEntityController(entity: T): EntityController { + return EcoEntityController(entity) + } +} diff --git a/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/ExtendedPersistentDataContainerFactory.kt b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/ExtendedPersistentDataContainerFactory.kt new file mode 100644 index 00000000..bdf78d55 --- /dev/null +++ b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/ExtendedPersistentDataContainerFactory.kt @@ -0,0 +1,91 @@ +package com.willfp.eco.internal.spigot.proxy.v1_21_6 + +import com.willfp.eco.core.data.ExtendedPersistentDataContainer +import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy +import net.minecraft.nbt.Tag +import org.bukkit.Material +import org.bukkit.craftbukkit.inventory.CraftItemStack +import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer +import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry +import org.bukkit.inventory.ItemStack +import org.bukkit.persistence.PersistentDataContainer +import org.bukkit.persistence.PersistentDataType +import java.lang.reflect.Field + +class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFactoryProxy { + private val registry: CraftPersistentDataTypeRegistry + + init { + /* + Can't grab actual instance since it's in CraftMetaItem (which is package-private) + And getting it would mean more janky reflection + */ + val item = CraftItemStack.asCraftCopy(ItemStack(Material.STONE)) + val pdc = item.itemMeta!!.persistentDataContainer + + // Cross-version compatibility: + val registryField: Field = try { + CraftPersistentDataContainer::class.java.getDeclaredField("registry") + } catch (e: NoSuchFieldException) { + CraftPersistentDataContainer::class.java.superclass.getDeclaredField("registry") + } + + this.registry = registryField + .apply { isAccessible = true }.get(pdc) as CraftPersistentDataTypeRegistry + } + + override fun adapt(pdc: PersistentDataContainer): ExtendedPersistentDataContainer { + return when (pdc) { + is CraftPersistentDataContainer -> EcoPersistentDataContainer(pdc) + else -> throw IllegalArgumentException("Custom PDC instance ${pdc::class.java.name} is not supported!") + } + } + + override fun newPdc(): PersistentDataContainer { + return CraftPersistentDataContainer(registry) + } + + inner class EcoPersistentDataContainer( + private val handle: CraftPersistentDataContainer + ) : ExtendedPersistentDataContainer { + @Suppress("UNCHECKED_CAST") + private val customDataTags: MutableMap = + CraftPersistentDataContainer::class.java.getDeclaredField("customDataTags") + .apply { isAccessible = true }.get(handle) as MutableMap + + override fun set(key: String, dataType: PersistentDataType, value: Z) { + customDataTags[key] = + registry.wrap(dataType, dataType.toPrimitive(value, handle.adapterContext)) + } + + override fun has(key: String, dataType: PersistentDataType): Boolean { + val value = customDataTags[key] ?: return false + return registry.isInstanceOf(dataType, value) + } + + override fun get(key: String, dataType: PersistentDataType): Z? { + val value = customDataTags[key] ?: return null + return dataType.fromPrimitive(registry.extract(dataType, value), handle.adapterContext) + } + + override fun getOrDefault( + key: String, + dataType: PersistentDataType, + defaultValue: Z + ): Z { + return get(key, dataType) ?: defaultValue + } + + override fun remove(key: String) { + customDataTags.remove(key) + } + + override fun getAllKeys(): MutableSet { + return customDataTags.keys + } + + override fun getBase(): PersistentDataContainer { + return handle + } + } +} diff --git a/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/FastItemStackFactory.kt b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/FastItemStackFactory.kt new file mode 100644 index 00000000..2d22bb63 --- /dev/null +++ b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/FastItemStackFactory.kt @@ -0,0 +1,13 @@ +package com.willfp.eco.internal.spigot.proxy.v1_21_6 + +import com.willfp.eco.core.fast.FastItemStack +import com.willfp.eco.internal.spigot.proxy.FastItemStackFactoryProxy +import com.willfp.eco.internal.spigot.proxy.common.modern.NewEcoFastItemStack +import com.willfp.eco.internal.spigot.proxy.v1_21_6.item.NewerEcoFastItemStack +import org.bukkit.inventory.ItemStack + +class FastItemStackFactory : FastItemStackFactoryProxy { + override fun create(itemStack: ItemStack): FastItemStack { + return NewerEcoFastItemStack(itemStack) + } +} diff --git a/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/MiniMessageTranslator.kt b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/MiniMessageTranslator.kt new file mode 100644 index 00000000..f061f6d0 --- /dev/null +++ b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/MiniMessageTranslator.kt @@ -0,0 +1,33 @@ +package com.willfp.eco.internal.spigot.proxy.v1_21_6 + +import com.willfp.eco.core.display.Display +import com.willfp.eco.internal.spigot.proxy.MiniMessageTranslatorProxy +import com.willfp.eco.util.toLegacy +import net.kyori.adventure.text.minimessage.MiniMessage + +class MiniMessageTranslator : MiniMessageTranslatorProxy { + override fun format(message: String): String { + var mut = message + + val startsWithPrefix = mut.startsWith(Display.PREFIX) + if (startsWithPrefix) { + mut = mut.substring(2) + } + + mut = mut.replace('ยง', '&') + + val miniMessage = runCatching { + MiniMessage.miniMessage().deserialize( + mut + ).toLegacy() + }.getOrNull() ?: mut + + mut = if (startsWithPrefix) { + Display.PREFIX + miniMessage + } else { + miniMessage + } + + return mut + } +} diff --git a/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/PacketHandler.kt b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/PacketHandler.kt new file mode 100644 index 00000000..61d5b567 --- /dev/null +++ b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/PacketHandler.kt @@ -0,0 +1,46 @@ +package com.willfp.eco.internal.spigot.proxy.v1_21_6 + +import com.willfp.eco.core.EcoPlugin +import com.willfp.eco.core.packet.PacketListener +import com.willfp.eco.internal.spigot.proxy.PacketHandlerProxy +import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketHeldItemSlot +import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketSetSlot +import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.clearFrames +import com.willfp.eco.internal.spigot.proxy.v1_21_6.packet.NewItemsPacketOpenWindowMerchant +import com.willfp.eco.internal.spigot.proxy.v1_21_6.packet.NewItemsPacketSetCreativeSlot +import com.willfp.eco.internal.spigot.proxy.v1_21_6.packet.NewItemsPacketWindowItems +import net.minecraft.network.protocol.Packet +import org.bukkit.craftbukkit.entity.CraftPlayer +import org.bukkit.entity.Player + +class PacketHandler : PacketHandlerProxy { + override fun sendPacket(player: Player, packet: com.willfp.eco.core.packet.Packet) { + if (player !is CraftPlayer) { + return + } + + val handle = packet.handle + + if (handle !is Packet<*>) { + return + } + + player.handle.connection.send(handle) + } + + override fun clearDisplayFrames() { + clearFrames() + } + + override fun getPacketListeners(plugin: EcoPlugin): List { + // No PacketAutoRecipe for 1.21.3+ because recipes have been changed internally + + return listOf( + PacketHeldItemSlot, + NewItemsPacketOpenWindowMerchant, + NewItemsPacketSetCreativeSlot, + PacketSetSlot, + NewItemsPacketWindowItems(plugin) + ) + } +} diff --git a/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/SNBTConverter.kt b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/SNBTConverter.kt new file mode 100644 index 00000000..15e84477 --- /dev/null +++ b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/SNBTConverter.kt @@ -0,0 +1,95 @@ +package com.willfp.eco.internal.spigot.proxy.v1_21_6 + +import com.mojang.serialization.Dynamic +import com.willfp.eco.core.items.TestableItem +import com.willfp.eco.core.recipe.parts.EmptyTestableItem +import com.willfp.eco.internal.spigot.proxy.SNBTConverterProxy +import net.minecraft.nbt.CompoundTag +import net.minecraft.nbt.NbtOps +import net.minecraft.nbt.SnbtPrinterTagVisitor +import net.minecraft.nbt.TagParser +import net.minecraft.server.MinecraftServer +import net.minecraft.util.datafix.fixes.References +import org.bukkit.Bukkit +import org.bukkit.craftbukkit.CraftServer +import org.bukkit.craftbukkit.inventory.CraftItemStack +import org.bukkit.craftbukkit.util.CraftMagicNumbers +import org.bukkit.inventory.ItemStack + +private val registryAccess = (Bukkit.getServer() as CraftServer).server.registryAccess() + +class SNBTConverter : SNBTConverterProxy { + private fun parseItemSNBT(snbt: String): CompoundTag? { + val nbt = runCatching { TagParser.parseCompoundFully(snbt) }.getOrNull() ?: return null + val dataVersion = if (nbt.contains("DataVersion")) { + nbt.getInt("DataVersion").get() + } else null + + // If the data version is the same as the server's data version, we don't need to fix it + + if (dataVersion == CraftMagicNumbers.INSTANCE.dataVersion) { + return nbt + } + + return MinecraftServer.getServer().fixerUpper.update( + References.ITEM_STACK, + Dynamic(NbtOps.INSTANCE, nbt), + dataVersion ?: 3700, // 3700 is the 1.20.4 data version + CraftMagicNumbers.INSTANCE.dataVersion + ).value as CompoundTag + } + + + private fun nmsToNbt(nms: net.minecraft.world.item.ItemStack): CompoundTag { + return net.minecraft.world.item.ItemStack.CODEC + .encodeStart(registryAccess.createSerializationContext(NbtOps.INSTANCE), nms) + .result() + .get() as CompoundTag + } + + private fun nbtToNms(tag: CompoundTag): net.minecraft.world.item.ItemStack? { + return net.minecraft.world.item.ItemStack.CODEC + .parse(registryAccess.createSerializationContext(NbtOps.INSTANCE), tag) + .result() + .orElse(null) + } + + override fun fromSNBT(snbt: String): ItemStack? { + val tag = parseItemSNBT(snbt) ?: return null + val nms = nbtToNms(tag) ?: return null + return CraftItemStack.asBukkitCopy(nms) + } + + override fun toSNBT(itemStack: ItemStack): String { + val nms = CraftItemStack.asNMSCopy(itemStack) + val tag = nmsToNbt(nms) + tag.putInt("DataVersion", CraftMagicNumbers.INSTANCE.dataVersion) + return SnbtPrinterTagVisitor().visit(tag) + } + + override fun makeSNBTTestable(snbt: String): TestableItem { + val tag = parseItemSNBT(snbt) ?: return EmptyTestableItem() + val nms = nbtToNms(tag) + + tag.remove("Count") + return SNBTTestableItem(CraftItemStack.asBukkitCopy(nms), tag) + } + + inner class SNBTTestableItem( + private val item: ItemStack, + private val tag: CompoundTag + ) : TestableItem { + override fun matches(itemStack: ItemStack?): Boolean { + if (itemStack == null) { + return false + } + + val nms = CraftItemStack.asNMSCopy(itemStack) + val nmsTag = nmsToNbt(nms) + nmsTag.remove("Count") + return tag.copy().merge(nmsTag) == nmsTag && itemStack.type == item.type + } + + override fun getItem(): ItemStack = item + } +} diff --git a/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/Skull.kt b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/Skull.kt new file mode 100644 index 00000000..9b14227a --- /dev/null +++ b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/Skull.kt @@ -0,0 +1,18 @@ +package com.willfp.eco.internal.spigot.proxy.v1_21_6 + +import com.willfp.eco.internal.spigot.proxy.SkullProxy +import com.willfp.eco.internal.spigot.proxy.common.modern.texture +import org.bukkit.inventory.meta.SkullMeta + +class Skull : SkullProxy { + override fun setSkullTexture( + meta: SkullMeta, + base64: String + ) { + meta.texture = base64 + } + + override fun getSkullTexture( + meta: SkullMeta + ): String? = meta.texture +} diff --git a/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/TPS.kt b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/TPS.kt new file mode 100644 index 00000000..aed11f19 --- /dev/null +++ b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/TPS.kt @@ -0,0 +1,11 @@ +package com.willfp.eco.internal.spigot.proxy.v1_21_6 + +import com.willfp.eco.internal.spigot.proxy.TPSProxy +import org.bukkit.Bukkit +import org.bukkit.craftbukkit.CraftServer + +class TPS : TPSProxy { + override fun getTPS(): Double { + return (Bukkit.getServer() as CraftServer).handle.server.tps1.average + } +} diff --git a/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/entity/EcoEntityController.kt b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/entity/EcoEntityController.kt new file mode 100644 index 00000000..2d1e3a63 --- /dev/null +++ b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/entity/EcoEntityController.kt @@ -0,0 +1,95 @@ +package com.willfp.eco.internal.spigot.proxy.v1_21_6.entity + +import com.willfp.eco.core.entities.ai.CustomGoal +import com.willfp.eco.core.entities.ai.EntityController +import com.willfp.eco.core.entities.ai.EntityGoal +import com.willfp.eco.core.entities.ai.TargetGoal +import com.willfp.eco.internal.spigot.proxy.common.ai.CustomGoalFactory +import com.willfp.eco.internal.spigot.proxy.common.ai.getGoalFactory +import com.willfp.eco.internal.spigot.proxy.common.toPathfinderMob +import net.minecraft.world.entity.PathfinderMob +import net.minecraft.world.entity.ai.goal.Goal +import org.bukkit.entity.Mob + +class EcoEntityController( + private val handle: T +) : EntityController { + override fun addEntityGoal(priority: Int, goal: EntityGoal): EntityController { + val nms = getNms() ?: return this + + nms.goalSelector.addGoal( + priority, + goal.getGoalFactory()?.create(goal, nms) ?: return this + ) + + return this + } + + override fun removeEntityGoal(goal: EntityGoal): EntityController { + val nms = getNms() ?: return this + + val predicate: (Goal) -> Boolean = if (goal is CustomGoal<*>) { + { CustomGoalFactory.isGoalOfType(it, goal) } + } else { + { goal.getGoalFactory()?.isGoalOfType(it) == true } + } + + for (wrapped in nms.goalSelector.availableGoals.toSet()) { + if (predicate(wrapped.goal)) { + nms.goalSelector.removeGoal(wrapped.goal) + } + } + + return this + } + + override fun clearEntityGoals(): EntityController { + val nms = getNms() ?: return this + nms.goalSelector.availableGoals.clear() + return this + } + + override fun addTargetGoal(priority: Int, goal: TargetGoal): EntityController { + val nms = getNms() ?: return this + + nms.targetSelector.addGoal( + priority, goal.getGoalFactory()?.create(goal, nms) ?: return this + ) + + nms.targetSelector + + return this + } + + override fun removeTargetGoal(goal: TargetGoal): EntityController { + val nms = getNms() ?: return this + + val predicate: (Goal) -> Boolean = if (goal is CustomGoal<*>) { + { CustomGoalFactory.isGoalOfType(it, goal) } + } else { + { goal.getGoalFactory()?.isGoalOfType(it) == true } + } + + for (wrapped in nms.targetSelector.availableGoals.toSet()) { + if (predicate(wrapped.goal)) { + nms.targetSelector.removeGoal(wrapped.goal) + } + } + + return this + } + + override fun clearTargetGoals(): EntityController { + val nms = getNms() ?: return this + nms.targetSelector.availableGoals.clear() + return this + } + + private fun getNms(): PathfinderMob? { + return handle.toPathfinderMob() + } + + override fun getEntity(): T { + return handle + } +} \ No newline at end of file diff --git a/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/item/NewerEcoFastItemStack.kt b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/item/NewerEcoFastItemStack.kt new file mode 100644 index 00000000..429a6e71 --- /dev/null +++ b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/item/NewerEcoFastItemStack.kt @@ -0,0 +1,185 @@ +package com.willfp.eco.internal.spigot.proxy.v1_21_6.item + +import com.willfp.eco.internal.spigot.proxy.common.modern.NewEcoFastItemStack +import net.minecraft.core.component.DataComponents +import net.minecraft.world.item.component.TooltipDisplay +import org.bukkit.inventory.ItemFlag +import org.bukkit.inventory.ItemStack + + +open class NewerEcoFastItemStack( + bukkit: ItemStack, +) : NewEcoFastItemStack(bukkit) { + override fun addItemFlags(vararg hideFlags: ItemFlag) { + for (flag in hideFlags) { + when (flag) { + ItemFlag.HIDE_ENCHANTS -> { + handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip -> + tooltip.withHidden(DataComponents.ENCHANTMENTS, true) + } + } + + ItemFlag.HIDE_ATTRIBUTES -> { + handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip -> + tooltip.withHidden(DataComponents.ATTRIBUTE_MODIFIERS, true) + } + } + + ItemFlag.HIDE_UNBREAKABLE -> { + handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip -> + tooltip.withHidden(DataComponents.UNBREAKABLE, true) + } + } + + ItemFlag.HIDE_DESTROYS -> { + handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip -> + tooltip.withHidden(DataComponents.CAN_BREAK, true) + } + } + + ItemFlag.HIDE_PLACED_ON -> { + handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip -> + tooltip.withHidden(DataComponents.CAN_PLACE_ON, true) + } + } + + ItemFlag.HIDE_ADDITIONAL_TOOLTIP -> { + handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip -> + TooltipDisplay(true, tooltip.hiddenComponents) + } + } + + ItemFlag.HIDE_DYE -> { + handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip -> + tooltip.withHidden(DataComponents.DYED_COLOR, true) + } + } + + ItemFlag.HIDE_ARMOR_TRIM -> { + handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip -> + tooltip.withHidden(DataComponents.TRIM, true) + } + } + + ItemFlag.HIDE_STORED_ENCHANTS -> { + handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip -> + tooltip.withHidden(DataComponents.STORED_ENCHANTMENTS, true) + } + } + } + } + + apply() + } + + override fun removeItemFlags(vararg hideFlags: ItemFlag) { + for (flag in hideFlags) { + when (flag) { + ItemFlag.HIDE_ENCHANTS -> { + handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip -> + tooltip.withHidden(DataComponents.ENCHANTMENTS, false) + } + } + + ItemFlag.HIDE_ATTRIBUTES -> { + handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip -> + tooltip.withHidden(DataComponents.ATTRIBUTE_MODIFIERS, false) + } + } + + ItemFlag.HIDE_UNBREAKABLE -> { + handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip -> + tooltip.withHidden(DataComponents.UNBREAKABLE, false) + } + } + + ItemFlag.HIDE_DESTROYS -> { + handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip -> + tooltip.withHidden(DataComponents.CAN_BREAK, false) + } + } + + ItemFlag.HIDE_PLACED_ON -> { + handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip -> + tooltip.withHidden(DataComponents.CAN_PLACE_ON, false) + } + } + + ItemFlag.HIDE_ADDITIONAL_TOOLTIP -> { + handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip -> + TooltipDisplay(false, tooltip.hiddenComponents) + } + } + + ItemFlag.HIDE_DYE -> { + handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip -> + tooltip.withHidden(DataComponents.DYED_COLOR, false) + } + } + + ItemFlag.HIDE_ARMOR_TRIM -> { + handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip -> + tooltip.withHidden(DataComponents.TRIM, false) + } + } + + ItemFlag.HIDE_STORED_ENCHANTS -> { + handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip -> + tooltip.withHidden(DataComponents.STORED_ENCHANTMENTS, false) + } + } + } + } + + apply() + } + + override fun hasItemFlag(flag: ItemFlag): Boolean { + return when (flag) { + ItemFlag.HIDE_ENCHANTS -> { + val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false + !tooltip.shows(DataComponents.ENCHANTMENTS) + } + + ItemFlag.HIDE_ATTRIBUTES -> { + val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false + !tooltip.shows(DataComponents.ATTRIBUTE_MODIFIERS) + } + + ItemFlag.HIDE_UNBREAKABLE -> { + val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false + !tooltip.shows(DataComponents.UNBREAKABLE) + } + + ItemFlag.HIDE_DESTROYS -> { + val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false + !tooltip.shows(DataComponents.CAN_BREAK) + } + + ItemFlag.HIDE_PLACED_ON -> { + val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false + !tooltip.shows(DataComponents.CAN_PLACE_ON) + } + + ItemFlag.HIDE_ADDITIONAL_TOOLTIP -> { + val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false + tooltip.hideTooltip + } + + ItemFlag.HIDE_DYE -> { + val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false + !tooltip.shows(DataComponents.DYED_COLOR) + } + + ItemFlag.HIDE_ARMOR_TRIM -> { + val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false + !tooltip.shows(DataComponents.TRIM) + } + + ItemFlag.HIDE_STORED_ENCHANTS -> { + val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false + !tooltip.shows(DataComponents.STORED_ENCHANTMENTS) + } + } + } +} diff --git a/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/packet/NewItemsPacketOpenWindowMerchant.kt b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/packet/NewItemsPacketOpenWindowMerchant.kt new file mode 100644 index 00000000..30a2792b --- /dev/null +++ b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/packet/NewItemsPacketOpenWindowMerchant.kt @@ -0,0 +1,35 @@ +package com.willfp.eco.internal.spigot.proxy.v1_21_6.packet + +import com.willfp.eco.core.display.Display +import com.willfp.eco.core.packet.PacketEvent +import com.willfp.eco.core.packet.PacketListener +import com.willfp.eco.internal.spigot.proxy.common.asBukkitStack +import net.minecraft.network.protocol.game.ClientboundMerchantOffersPacket +import net.minecraft.world.item.trading.MerchantOffers + +object NewItemsPacketOpenWindowMerchant : PacketListener { + private val field = ClientboundMerchantOffersPacket::class.java + .declaredFields + .first { it.type == MerchantOffers::class.java } + .apply { isAccessible = true } + + override fun onSend(event: PacketEvent) { + val packet = event.packet.handle as? ClientboundMerchantOffersPacket ?: return + + val offers = MerchantOffers() + + for (offer in packet.offers) { + val new = offer.copy() + + Display.display(new.baseCostA.itemStack.asBukkitStack(), event.player) + if (new.costB.isPresent) { + Display.display(new.costB.get().itemStack.asBukkitStack(), event.player) + } + Display.display(new.result.asBukkitStack(), event.player) + + offers += new + } + + field.set(packet, offers) + } +} diff --git a/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/packet/NewItemsPacketSetCreativeSlot.kt b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/packet/NewItemsPacketSetCreativeSlot.kt new file mode 100644 index 00000000..5f359ca4 --- /dev/null +++ b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/packet/NewItemsPacketSetCreativeSlot.kt @@ -0,0 +1,19 @@ +package com.willfp.eco.internal.spigot.proxy.v1_21_6.packet + +import com.willfp.eco.core.display.Display +import com.willfp.eco.core.packet.PacketEvent +import com.willfp.eco.core.packet.PacketListener +import com.willfp.eco.internal.spigot.proxy.common.asBukkitStack +import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.DisplayFrame +import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.lastDisplayFrame +import net.minecraft.network.protocol.game.ServerboundSetCreativeModeSlotPacket + +object NewItemsPacketSetCreativeSlot : PacketListener { + override fun onReceive(event: PacketEvent) { + val packet = event.packet.handle as? ServerboundSetCreativeModeSlotPacket ?: return + + Display.revert(packet.itemStack.asBukkitStack()) + + event.player.lastDisplayFrame = DisplayFrame.EMPTY + } +} diff --git a/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/packet/NewItemsPacketWindowItems.kt b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/packet/NewItemsPacketWindowItems.kt new file mode 100644 index 00000000..132c8f1d --- /dev/null +++ b/eco-core/core-nms/v1_21_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_21_6/packet/NewItemsPacketWindowItems.kt @@ -0,0 +1,52 @@ +package com.willfp.eco.internal.spigot.proxy.v1_21_6.packet + +import com.willfp.eco.core.EcoPlugin +import com.willfp.eco.core.display.Display +import com.willfp.eco.core.packet.PacketEvent +import com.willfp.eco.internal.spigot.proxy.common.asBukkitStack +import com.willfp.eco.internal.spigot.proxy.common.asNMSStack +import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketWindowItems +import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.DisplayFrame +import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.lastDisplayFrame +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket +import java.util.UUID +import java.util.concurrent.ConcurrentHashMap + +class NewItemsPacketWindowItems( + plugin: EcoPlugin +) : PacketWindowItems(plugin) { + private val lastKnownWindowIDs = ConcurrentHashMap() + + override fun onSend(event: PacketEvent) { + val packet = event.packet.handle as? ClientboundContainerSetContentPacket ?: return + val player = event.player + + Display.display(packet.carriedItem.asBukkitStack(), player) + + val windowId = packet.containerId + + val lastKnownID = lastKnownWindowIDs[player.uniqueId] + lastKnownWindowIDs[player.uniqueId] = windowId + + // If there is any change in window ID at any point, + // Remove the last display frame to prevent any potential conflicts. + // If the window ID is not zero (not a player inventory), then remove too, + // as GUIs are not player inventories. + if (lastKnownID != windowId || windowId != 0) { + player.lastDisplayFrame = DisplayFrame.EMPTY + } + + val itemStacks = packet.items.map { it.asBukkitStack() } + + val newItems = modifyWindowItems(itemStacks.toMutableList(), windowId, player) + + val newPacket = ClientboundContainerSetContentPacket( + packet.containerId, + packet.stateId, + newItems.map { it.asNMSStack() }, + packet.carriedItem, + ) + + event.packet.handle = newPacket + } +} diff --git a/gradle.properties b/gradle.properties index 332fac7f..4b7740d3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ -version = 6.76.0 +version = 6.76.1 kotlin.daemon.jvmargs=-Xmx2g -XX:+UseG1GC -XX:MaxMetaspaceSize=512m org.gradle.parallel=true diff --git a/settings.gradle.kts b/settings.gradle.kts index efd67d23..7a4ece7f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -30,6 +30,7 @@ include(":eco-core:core-nms:v1_21") include(":eco-core:core-nms:v1_21_3") include(":eco-core:core-nms:v1_21_4") include(":eco-core:core-nms:v1_21_5") +include(":eco-core:core-nms:v1_21_6") include(":eco-core:core-proxy") include(":eco-core:core-plugin") include(":eco-core:core-backend")