diff --git a/.github/workflows/java-ci.yml b/.github/workflows/java-ci.yml index 011c7417..f0ab4d69 100644 --- a/.github/workflows/java-ci.yml +++ b/.github/workflows/java-ci.yml @@ -14,11 +14,11 @@ jobs: id: vars run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v2 with: distribution: 'temurin' - java-version: 17 + java-version: 21 - name: Setup build cache uses: actions/cache@v2.1.6 diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 10e3edb5..efa5a011 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -12,11 +12,11 @@ jobs: - name: Checkout latest code uses: actions/checkout@v2 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v2 with: distribution: 'temurin' - java-version: 17 + java-version: 21 - name: Setup build cache uses: actions/cache@v2.1.6 diff --git a/.github/workflows/test-publish.yml b/.github/workflows/test-publish.yml index 7664beb3..a2651311 100644 --- a/.github/workflows/test-publish.yml +++ b/.github/workflows/test-publish.yml @@ -14,11 +14,11 @@ jobs: id: vars run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v2 with: distribution: 'temurin' - java-version: 17 + java-version: 21 - name: Setup build cache uses: actions/cache@v2.1.6 diff --git a/build.gradle.kts b/build.gradle.kts index 83717d59..35d3d05b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,7 +10,7 @@ buildscript { plugins { id("java-library") - id("com.github.johnrengelman.shadow") version "8.1.1" + id("io.github.goooler.shadow") version "8.1.7" id("maven-publish") id("java") kotlin("jvm") version "1.9.21" @@ -30,13 +30,14 @@ dependencies { implementation(project(path = ":eco-core:core-nms:v1_20_R1", configuration = "reobf")) implementation(project(path = ":eco-core:core-nms:v1_20_R2", configuration = "reobf")) implementation(project(path = ":eco-core:core-nms:v1_20_R3", configuration = "reobf")) + implementation(project(path = ":eco-core:core-nms:v1_20_6", configuration = "reobf")) } allprojects { apply(plugin = "java") apply(plugin = "java-library") apply(plugin = "maven-publish") - apply(plugin = "com.github.johnrengelman.shadow") + apply(plugin = "io.github.goooler.shadow") apply(plugin = "kotlin") repositories { @@ -208,5 +209,13 @@ allprojects { } } + +// Root is Java 21 to support 1.20.6+, rest use Java 17 +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + withSourcesJar() +} + group = "com.willfp" version = findProperty("version")!! \ No newline at end of file diff --git a/eco-api/src/main/java/com/willfp/eco/core/extensions/ExtensionMetadata.java b/eco-api/src/main/java/com/willfp/eco/core/extensions/ExtensionMetadata.java index 9f679947..9c665106 100644 --- a/eco-api/src/main/java/com/willfp/eco/core/extensions/ExtensionMetadata.java +++ b/eco-api/src/main/java/com/willfp/eco/core/extensions/ExtensionMetadata.java @@ -13,6 +13,8 @@ import java.io.File; * @param version The extension version. * @param name The extension name. * @param author The extension's author. + * @param file The extension's file. + * @param minimumPluginVersion The minimum plugin version required for this extension. */ public record ExtensionMetadata(@NotNull String version, @NotNull String name, diff --git a/eco-api/src/main/java/com/willfp/eco/core/fast/FastItemStack.java b/eco-api/src/main/java/com/willfp/eco/core/fast/FastItemStack.java index 2131d94b..52096964 100644 --- a/eco-api/src/main/java/com/willfp/eco/core/fast/FastItemStack.java +++ b/eco-api/src/main/java/com/willfp/eco/core/fast/FastItemStack.java @@ -164,15 +164,23 @@ public interface FastItemStack extends PersistentDataHolder { * The returned PersistentDataContainer will not modify the item until the tag is set. * * @return The base NBT tag. + * @deprecated Items are now component-based. */ - PersistentDataContainer getBaseTag(); + @Deprecated(forRemoval = true, since = "6.70.0") + default PersistentDataContainer getBaseTag() { + throw new UnsupportedOperationException("Not supported in 1.20.5+"); + } /** * Set the base NBT tag (Not PublicBukkitValues, the base) from a PersistentDataContainer. * * @param container The PersistentDataContainer. + * @deprecated Items are now component-based. */ - void setBaseTag(@Nullable PersistentDataContainer container); + @Deprecated(forRemoval = true, since = "6.70.0") + default void setBaseTag(@Nullable PersistentDataContainer container) { + throw new UnsupportedOperationException("Not supported in 1.20.5+"); + } /** * Get the type of the item. diff --git a/eco-api/src/main/java/com/willfp/eco/core/gui/menu/MenuEventHandler.java b/eco-api/src/main/java/com/willfp/eco/core/gui/menu/MenuEventHandler.java index 37d29c86..8c52cf1f 100644 --- a/eco-api/src/main/java/com/willfp/eco/core/gui/menu/MenuEventHandler.java +++ b/eco-api/src/main/java/com/willfp/eco/core/gui/menu/MenuEventHandler.java @@ -5,6 +5,8 @@ import org.jetbrains.annotations.NotNull; /** * Handles menu events. + * + * @param The type of event to handle.x */ public abstract class MenuEventHandler { /** diff --git a/eco-api/src/main/java/com/willfp/eco/core/items/Items.java b/eco-api/src/main/java/com/willfp/eco/core/items/Items.java index b48aadbb..c56f8586 100644 --- a/eco-api/src/main/java/com/willfp/eco/core/items/Items.java +++ b/eco-api/src/main/java/com/willfp/eco/core/items/Items.java @@ -13,6 +13,7 @@ import com.willfp.eco.core.recipe.parts.TestableStack; import com.willfp.eco.core.recipe.parts.UnrestrictedMaterialTestableItem; import com.willfp.eco.util.NamespacedKeyUtils; import com.willfp.eco.util.NumberUtils; +import kotlin.Suppress; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.enchantments.Enchantment; @@ -515,8 +516,11 @@ public final class Items { * * @param itemStack The ItemStack. * @return The base NBT. + * @deprecated Items are now component-based. */ @NotNull + @Deprecated(since = "6.70.0", forRemoval = true) + @SuppressWarnings("removal") public static PersistentDataContainer getBaseNBT(@NotNull final ItemStack itemStack) { return FastItemStack.wrap(itemStack).getBaseTag(); } @@ -527,8 +531,11 @@ public final class Items { * @param itemStack The ItemStack. * @param container The base NBT tag. * @return The ItemStack, modified. Not required to use, as this modifies the instance.¬ + * @deprecated Items are now component-based. */ @NotNull + @Deprecated(since = "6.70.0", forRemoval = true) + @SuppressWarnings("removal") public static ItemStack setBaseNBT(@NotNull final ItemStack itemStack, @Nullable final PersistentDataContainer container) { FastItemStack fis = FastItemStack.wrap(itemStack); 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 a2f17cc1..72126856 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 @@ -27,7 +27,8 @@ public final class ProxyConstants { "v1_19_R3", "v1_20_R1", "v1_20_R2", - "v1_20_R3" + "v1_20_R3", + "v1_20_6" ); private ProxyConstants() { diff --git a/eco-api/src/main/kotlin/com/willfp/eco/core/items/Items.kt b/eco-api/src/main/kotlin/com/willfp/eco/core/items/Items.kt index 5d56d779..b433abed 100644 --- a/eco-api/src/main/kotlin/com/willfp/eco/core/items/Items.kt +++ b/eco-api/src/main/kotlin/com/willfp/eco/core/items/Items.kt @@ -22,6 +22,8 @@ fun ItemMeta.mergeFrom(other: ItemMeta): ItemMeta = * @see Items.getBaseNBT * @see Items.setBaseNBT */ +@Suppress("DEPRECATION") +@Deprecated("Not supported in 1.20.5+", level = DeprecationLevel.ERROR) var ItemStack.baseNBT: PersistentDataContainer get() = Items.getBaseNBT(this) set(value) { @@ -29,6 +31,8 @@ var ItemStack.baseNBT: PersistentDataContainer } /** @see Items.setBaseNBT */ +@Suppress("DEPRECATION", "DeprecatedCallableAddReplaceWith") +@Deprecated("Not supported in 1.20.5+", level = DeprecationLevel.ERROR) fun ItemStack.clearNBT() = Items.setBaseNBT(this, null) diff --git a/eco-core/core-nms/build.gradle.kts b/eco-core/core-nms/build.gradle.kts index ded32959..f6165f08 100644 --- a/eco-core/core-nms/build.gradle.kts +++ b/eco-core/core-nms/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("io.papermc.paperweight.userdev") version "1.5.3" apply false + id("io.papermc.paperweight.userdev") version "1.6.2" apply false } diff --git a/eco-core/core-nms/nms-common/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/common/NMSCommons.kt b/eco-core/core-nms/nms-common/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/common/NMSCommons.kt index 6a71e652..ad5744aa 100644 --- a/eco-core/core-nms/nms-common/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/common/NMSCommons.kt +++ b/eco-core/core-nms/nms-common/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/common/NMSCommons.kt @@ -7,6 +7,7 @@ import com.willfp.eco.internal.spigot.proxy.common.ai.EntityGoalFactory import com.willfp.eco.internal.spigot.proxy.common.ai.TargetGoalFactory import io.papermc.paper.adventure.PaperAdventure import net.kyori.adventure.text.Component +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer import net.minecraft.nbt.CompoundTag import net.minecraft.resources.ResourceLocation import net.minecraft.server.level.ServerPlayer @@ -74,6 +75,15 @@ fun Player.toNMS(): ServerPlayer = fun Component.toNMS(): net.minecraft.network.chat.Component = if (Prerequisite.HAS_PAPER.isMet) PaperAdventure.asVanilla(this) else impl.toNMS(this) +fun net.minecraft.network.chat.Component.toAdventure(): Component { + if (Prerequisite.HAS_PAPER.isMet) { + return PaperAdventure.asAdventure(this) + } + + val json = net.minecraft.network.chat.Component.Serializer.toJson(this) + return GsonComponentSerializer.gson().deserialize(json) +} + interface CommonsProvider { val nbtTagString: Int diff --git a/eco-core/core-nms/nms-common/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/common/item/EcoFastItemStack.kt b/eco-core/core-nms/nms-common/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/common/item/EcoFastItemStack.kt index 576a6dde..f4fb8624 100644 --- a/eco-core/core-nms/nms-common/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/common/item/EcoFastItemStack.kt +++ b/eco-core/core-nms/nms-common/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/common/item/EcoFastItemStack.kt @@ -29,10 +29,14 @@ import kotlin.experimental.and import kotlin.experimental.inv import kotlin.experimental.or +interface ImplementedFIS : FastItemStack { + fun apply() +} + @Suppress("UsePropertyAccessSyntax") class EcoFastItemStack( private val bukkit: org.bukkit.inventory.ItemStack -) : FastItemStack { +) : ImplementedFIS { private val handle = bukkit.asNMSStack() private val pdc = (if (handle.hasTag()) handle.getTag()!! else CompoundTag()).makePdc() @@ -184,9 +188,11 @@ class EcoFastItemStack( return this.flagBits and bitModifier == bitModifier } + @Deprecated("Not supported in 1.20.5+") override fun getBaseTag(): PersistentDataContainer = (if (handle.hasTag()) handle.getTag()!! else CompoundTag()).makePdc(base = true) + @Deprecated("Not supported in 1.20.5+") override fun setBaseTag(container: PersistentDataContainer?) { (if (handle.hasTag()) handle.getTag()!! else CompoundTag()).setPdc(container, item = handle) apply() @@ -259,7 +265,7 @@ class EcoFastItemStack( return handle.getTag()?.hashCode() ?: (0b00010101 * 31 + Item.getId(handle.getItem())) } - internal fun apply() { + override fun apply() { if (handle.hasTag()) { handle.getTag()?.setPdc(this.pdc) } @@ -276,9 +282,9 @@ class EcoFastItemStack( } } -private class ContinuallyAppliedPersistentDataContainer( +class ContinuallyAppliedPersistentDataContainer( val handle: PersistentDataContainer, - val fis: EcoFastItemStack + private val fis: ImplementedFIS ) : PersistentDataContainer by handle { override fun set(key: NamespacedKey, type: PersistentDataType, value: Z) { handle.set(key, type, value) diff --git a/eco-core/core-nms/v1_20_6/build.gradle.kts b/eco-core/core-nms/v1_20_6/build.gradle.kts new file mode 100644 index 00000000..4e5b4dd8 --- /dev/null +++ b/eco-core/core-nms/v1_20_6/build.gradle.kts @@ -0,0 +1,54 @@ +plugins { + id("io.papermc.paperweight.userdev") +} + +group = "com.willfp" +version = rootProject.version + +dependencies { + implementation(project(":eco-core:core-nms:nms-common")) + paperweight.paperDevBundle("1.20.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") + } + + // I know this is the wrong version, but org.bukkit.Material refused to work + compileOnly("io.papermc.paper:paper-api:1.20.6-R0.1-SNAPSHOT") +} + +tasks { + build { + dependsOn(reobfJar) + } + + reobfJar { + mustRunAfter(shadowJar) + } + + shadowJar { + relocate( + "com.willfp.eco.internal.spigot.proxy.common", + "com.willfp.eco.internal.spigot.proxy.v1_20_6.common" + ) + relocate( + "net.kyori.adventure.text.minimessage", + "com.willfp.eco.internal.spigot.proxy.v1_20_6.minimessage" + ) + } + + java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + withSourcesJar() + } + + compileKotlin { + kotlinOptions { + jvmTarget = "21" + } + } +} diff --git a/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/BukkitCommands.kt b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/BukkitCommands.kt new file mode 100644 index 00000000..953e8c04 --- /dev/null +++ b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/BukkitCommands.kt @@ -0,0 +1,35 @@ +package com.willfp.eco.internal.spigot.proxy.v1_20_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_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/CommonsInitializer.kt b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/CommonsInitializer.kt new file mode 100644 index 00000000..825e5c14 --- /dev/null +++ b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/CommonsInitializer.kt @@ -0,0 +1,169 @@ +package com.willfp.eco.internal.spigot.proxy.v1_20_6 + +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 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.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.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 = Class.forName("org.bukkit.craftbukkit.v1_20_R3.inventory.CraftMetaItem") + .getDeclaredField("DATA_TYPE_REGISTRY") + .apply { isAccessible = true } + .get(null) as CraftPersistentDataTypeRegistry + + override val nbtTagString = CraftMagicNumbers.NBT.TAG_STRING + + 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.allKeys + for (key in keys) { + pdc.put(key, this[key]) + } + + return pdc + } + + return if (base) { + tag.toPdc() + } else { + if (tag.contains("PublicBukkitValues")) { + tag.getCompound("PublicBukkitValues").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.allKeys.toSet()) { + 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 { + val json = JSONComponentSerializer.json().serialize(component) + val holderLookupProvider = (Bukkit.getServer() as CraftServer).server.registryAccess() + + return net.minecraft.network.chat.Component.Serializer.fromJson(json, holderLookupProvider)!! + } + } +} diff --git a/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/DisplayName.kt b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/DisplayName.kt new file mode 100644 index 00000000..110675b2 --- /dev/null +++ b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/DisplayName.kt @@ -0,0 +1,59 @@ +package com.willfp.eco.internal.spigot.proxy.v1_20_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 + nmsEntity.isCustomNameVisible + val entityData = SynchedEntityData.Builder(nmsEntity).build() + + entityData.set(displayNameAccessor, Optional.of(nmsComponent), true) + entityData.set(customNameVisibleAccessor, visible, true) + + val packet = ClientboundSetEntityDataPacket( + nmsEntity.id, + entityData.packDirty() ?: throw IllegalStateException("No packed entity data") + ) + + player.sendPacket(Packet(packet)) + } +} diff --git a/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/DummyEntityFactory.kt b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/DummyEntityFactory.kt new file mode 100644 index 00000000..0abb2fab --- /dev/null +++ b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/DummyEntityFactory.kt @@ -0,0 +1,15 @@ +package com.willfp.eco.internal.spigot.proxy.v1_20_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_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/EntityControllerFactory.kt b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/EntityControllerFactory.kt new file mode 100644 index 00000000..3e3bbef4 --- /dev/null +++ b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/EntityControllerFactory.kt @@ -0,0 +1,12 @@ +package com.willfp.eco.internal.spigot.proxy.v1_20_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_20_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_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/ExtendedPersistentDataContainerFactory.kt b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/ExtendedPersistentDataContainerFactory.kt new file mode 100644 index 00000000..bfea2a5b --- /dev/null +++ b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/ExtendedPersistentDataContainerFactory.kt @@ -0,0 +1,82 @@ +package com.willfp.eco.internal.spigot.proxy.v1_20_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 + +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 + this.registry = CraftPersistentDataContainer::class.java.getDeclaredField("registry") + .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 ims 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_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/FastItemStackFactory.kt b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/FastItemStackFactory.kt new file mode 100644 index 00000000..a52e113d --- /dev/null +++ b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/FastItemStackFactory.kt @@ -0,0 +1,376 @@ +package com.willfp.eco.internal.spigot.proxy.v1_20_6 + +import com.willfp.eco.core.fast.FastItemStack +import com.willfp.eco.internal.spigot.proxy.FastItemStackFactoryProxy +import com.willfp.eco.internal.spigot.proxy.common.asNMSStack +import com.willfp.eco.internal.spigot.proxy.common.item.ContinuallyAppliedPersistentDataContainer +import com.willfp.eco.internal.spigot.proxy.common.item.ImplementedFIS +import com.willfp.eco.internal.spigot.proxy.common.makePdc +import com.willfp.eco.internal.spigot.proxy.common.mergeIfNeeded +import com.willfp.eco.internal.spigot.proxy.common.setPdc +import com.willfp.eco.internal.spigot.proxy.common.toAdventure +import com.willfp.eco.internal.spigot.proxy.common.toItem +import com.willfp.eco.internal.spigot.proxy.common.toMaterial +import com.willfp.eco.internal.spigot.proxy.common.toNMS +import com.willfp.eco.util.StringUtils +import com.willfp.eco.util.toComponent +import com.willfp.eco.util.toLegacy +import net.kyori.adventure.text.Component +import net.minecraft.core.component.DataComponentType +import net.minecraft.core.component.DataComponents +import net.minecraft.nbt.CompoundTag +import net.minecraft.util.Unit +import net.minecraft.world.item.component.CustomData +import net.minecraft.world.item.component.CustomModelData +import net.minecraft.world.item.component.ItemLore +import org.bukkit.craftbukkit.CraftRegistry +import org.bukkit.craftbukkit.enchantments.CraftEnchantment +import org.bukkit.enchantments.Enchantment +import org.bukkit.inventory.ItemFlag +import org.bukkit.inventory.ItemStack +import org.bukkit.persistence.PersistentDataContainer +import kotlin.math.max + +class FastItemStackFactory : FastItemStackFactoryProxy { + override fun create(itemStack: ItemStack): FastItemStack { + return NewEcoFastItemStack(itemStack) + } + + @Suppress("UsePropertyAccessSyntax") + class NewEcoFastItemStack( + private val bukkit: ItemStack + ) : ImplementedFIS { + private val handle = bukkit.asNMSStack() as net.minecraft.world.item.ItemStack + private val pdc = if (handle.has(DataComponents.CUSTOM_DATA)) { + handle.get(DataComponents.CUSTOM_DATA)!!.copyTag().makePdc() + } else { + val tag = CompoundTag() + handle.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)) + tag.makePdc() + } + + override fun getEnchants(checkStored: Boolean): Map { + val enchantments = handle.get(DataComponents.ENCHANTMENTS) ?: return emptyMap() + + val map = mutableMapOf() + + for ((enchantment, level) in enchantments.entrySet()) { + val bukkit = CraftEnchantment.minecraftToBukkit(enchantment.value()) + + map[bukkit] = level + } + + return map + } + + override fun getEnchantmentLevel( + enchantment: Enchantment, + checkStored: Boolean + ): Int { + val minecraft = CraftRegistry + .bukkitToMinecraft(enchantment) + + val enchantments = handle.get(DataComponents.ENCHANTMENTS) ?: return 0 + var level = enchantments.getLevel(minecraft) + + if (checkStored) { + val storedEnchantments = handle.get(DataComponents.STORED_ENCHANTMENTS) ?: return 0 + level = max(level, storedEnchantments.getLevel(minecraft)) + } + + return level + } + + override fun setLore(lore: List?) = setLoreComponents(lore?.map { it.toComponent() }) + + override fun setLoreComponents(lore: List?) { + if (lore == null) { + handle.set(DataComponents.LORE, null) + } else { + handle.set(DataComponents.LORE, ItemLore(lore.map { it.toNMS() })) + } + + apply() + } + + override fun getLoreComponents(): List { + return handle.get(DataComponents.LORE)?.lines?.map { it.toAdventure() } ?: emptyList() + } + + override fun getLore(): List = + getLoreComponents().map { StringUtils.toLegacy(it) } + + override fun setDisplayName(name: Component?) { + if (name == null) { + handle.set(DataComponents.CUSTOM_NAME, null) + } else { + handle.set(DataComponents.CUSTOM_NAME, name.toNMS()) + } + + apply() + } + + override fun setDisplayName(name: String?) = setDisplayName(name?.toComponent()) + + override fun getDisplayNameComponent(): Component { + return handle.get(DataComponents.CUSTOM_NAME)?.toAdventure() + ?: Component.translatable(bukkit.type.toItem().getDescriptionId()) + } + + override fun getDisplayName(): String = displayNameComponent.toLegacy() + + private fun net.minecraft.world.item.ItemStack.modifyComponent( + component: DataComponentType, + modifier: (T) -> T + ) { + val current = handle.get(component) ?: return + this.set(component, modifier(current)) + } + + override fun addItemFlags(vararg hideFlags: ItemFlag) { + for (flag in hideFlags) { + when (flag) { + ItemFlag.HIDE_ENCHANTS -> { + handle.modifyComponent(DataComponents.ENCHANTMENTS) { enchantments -> + enchantments.withTooltip(false) + } + } + + ItemFlag.HIDE_ATTRIBUTES -> { + handle.modifyComponent(DataComponents.ATTRIBUTE_MODIFIERS) { attributes -> + attributes.withTooltip(false) + } + } + + ItemFlag.HIDE_UNBREAKABLE -> { + handle.modifyComponent(DataComponents.UNBREAKABLE) { unbreakable -> + unbreakable.withTooltip(false) + } + } + + ItemFlag.HIDE_DESTROYS -> { + handle.modifyComponent(DataComponents.CAN_BREAK) { destroys -> + destroys.withTooltip(false) + } + } + + ItemFlag.HIDE_PLACED_ON -> { + handle.modifyComponent(DataComponents.CAN_PLACE_ON) { placedOn -> + placedOn.withTooltip(false) + } + } + + ItemFlag.HIDE_ADDITIONAL_TOOLTIP -> { + handle.set(DataComponents.HIDE_ADDITIONAL_TOOLTIP, Unit.INSTANCE) + } + + ItemFlag.HIDE_DYE -> { + handle.modifyComponent(DataComponents.DYED_COLOR) { dyed -> + dyed.withTooltip(false) + } + } + + ItemFlag.HIDE_ARMOR_TRIM -> { + handle.modifyComponent(DataComponents.TRIM) { trim -> + trim.withTooltip(false) + } + } + + ItemFlag.HIDE_STORED_ENCHANTS -> { + handle.modifyComponent(DataComponents.STORED_ENCHANTMENTS) { storedEnchants -> + storedEnchants.withTooltip(false) + } + } + } + } + + apply() + } + + override fun removeItemFlags(vararg hideFlags: ItemFlag) { + for (flag in hideFlags) { + when (flag) { + ItemFlag.HIDE_ENCHANTS -> { + handle.modifyComponent(DataComponents.ENCHANTMENTS) { enchantments -> + enchantments.withTooltip(true) + } + } + + ItemFlag.HIDE_ATTRIBUTES -> { + handle.modifyComponent(DataComponents.ATTRIBUTE_MODIFIERS) { attributes -> + attributes.withTooltip(true) + } + } + + ItemFlag.HIDE_UNBREAKABLE -> { + handle.modifyComponent(DataComponents.UNBREAKABLE) { unbreakable -> + unbreakable.withTooltip(true) + } + } + + ItemFlag.HIDE_DESTROYS -> { + handle.modifyComponent(DataComponents.CAN_BREAK) { destroys -> + destroys.withTooltip(true) + } + } + + ItemFlag.HIDE_PLACED_ON -> { + handle.modifyComponent(DataComponents.CAN_PLACE_ON) { placedOn -> + placedOn.withTooltip(true) + } + } + + ItemFlag.HIDE_ADDITIONAL_TOOLTIP -> { + handle.remove(DataComponents.HIDE_ADDITIONAL_TOOLTIP) + } + + ItemFlag.HIDE_DYE -> { + handle.modifyComponent(DataComponents.DYED_COLOR) { dyed -> + dyed.withTooltip(true) + } + } + + ItemFlag.HIDE_ARMOR_TRIM -> { + handle.modifyComponent(DataComponents.TRIM) { trim -> + trim.withTooltip(true) + } + } + + ItemFlag.HIDE_STORED_ENCHANTS -> { + handle.modifyComponent(DataComponents.STORED_ENCHANTMENTS) { storedEnchants -> + storedEnchants.withTooltip(true) + } + } + } + } + + apply() + } + + override fun getItemFlags(): Set { + val currentFlags = mutableSetOf() + for (f in ItemFlag.values()) { + if (hasItemFlag(f)) { + currentFlags.add(f) + } + } + return currentFlags + } + + override fun hasItemFlag(flag: ItemFlag): Boolean { + return when (flag) { + ItemFlag.HIDE_ENCHANTS -> { + val enchantments = handle.get(DataComponents.ENCHANTMENTS) ?: return false + !enchantments.showInTooltip + } + + ItemFlag.HIDE_ATTRIBUTES -> { + val attributes = handle.get(DataComponents.ATTRIBUTE_MODIFIERS) ?: return false + !attributes.showInTooltip + } + + ItemFlag.HIDE_UNBREAKABLE -> { + val unbreakable = handle.get(DataComponents.UNBREAKABLE) ?: return false + !unbreakable.showInTooltip + } + + ItemFlag.HIDE_DESTROYS -> { + val destroys = handle.get(DataComponents.CAN_BREAK) ?: return false + !destroys.showInTooltip() + } + + ItemFlag.HIDE_PLACED_ON -> { + val placedOn = handle.get(DataComponents.CAN_PLACE_ON) ?: return false + !placedOn.showInTooltip() + } + + ItemFlag.HIDE_ADDITIONAL_TOOLTIP -> { + handle.get(DataComponents.HIDE_ADDITIONAL_TOOLTIP) != null + } + + ItemFlag.HIDE_DYE -> { + val dyed = handle.get(DataComponents.DYED_COLOR) ?: return false + !dyed.showInTooltip() + } + + ItemFlag.HIDE_ARMOR_TRIM -> { + val armorTrim = handle.get(DataComponents.TRIM) ?: return false + !armorTrim.showInTooltip + } + + ItemFlag.HIDE_STORED_ENCHANTS -> { + val storedEnchants = handle.get(DataComponents.STORED_ENCHANTMENTS) ?: return false + !storedEnchants.showInTooltip + } + } + } + + override fun getRepairCost(): Int { + return handle.get(DataComponents.REPAIR_COST) ?: 0 + } + + override fun setRepairCost(cost: Int) { + handle.set(DataComponents.REPAIR_COST, cost) + + apply() + } + + override fun getPersistentDataContainer(): PersistentDataContainer { + return ContinuallyAppliedPersistentDataContainer(this.pdc, this) + } + + override fun getAmount(): Int = handle.getCount() + + override fun setAmount(amount: Int) { + handle.setCount(amount) + } + + override fun setType(material: org.bukkit.Material) { + @Suppress("DEPRECATION") + handle.setItem(material.toItem()) + apply() + } + + override fun getType(): org.bukkit.Material = handle.getItem().toMaterial() + + override fun getCustomModelData(): Int? = + handle.get(DataComponents.CUSTOM_MODEL_DATA)?.value + + override fun setCustomModelData(data: Int?) { + if (data == null) { + handle.remove(DataComponents.CUSTOM_MODEL_DATA) + } else { + handle.set(DataComponents.CUSTOM_MODEL_DATA, CustomModelData(data)) + } + + apply() + } + + override fun equals(other: Any?): Boolean { + if (other !is NewEcoFastItemStack) { + return false + } + + return other.hashCode() == this.hashCode() + } + + override fun hashCode(): Int { + return net.minecraft.world.item.ItemStack.hashItemAndComponents(handle) + } + + override fun apply() { + handle.update(DataComponents.CUSTOM_DATA, CustomData.of(CompoundTag())) { + it.apply { + @Suppress("DEPRECATION") + unsafe.setPdc(pdc) + } + } + + bukkit.mergeIfNeeded(handle) + } + + override fun unwrap(): ItemStack { + return bukkit + } + } +} diff --git a/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/MiniMessageTranslator.kt b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/MiniMessageTranslator.kt new file mode 100644 index 00000000..7aceb007 --- /dev/null +++ b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/MiniMessageTranslator.kt @@ -0,0 +1,33 @@ +package com.willfp.eco.internal.spigot.proxy.v1_20_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_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/PacketHandler.kt b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/PacketHandler.kt new file mode 100644 index 00000000..1fa3bae9 --- /dev/null +++ b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/PacketHandler.kt @@ -0,0 +1,46 @@ +package com.willfp.eco.internal.spigot.proxy.v1_20_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.PacketAutoRecipe +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.PacketWindowItems +import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.clearFrames +import com.willfp.eco.internal.spigot.proxy.v1_20_6.packet.NewItemsPacketOpenWindowMerchant +import com.willfp.eco.internal.spigot.proxy.v1_20_6.packet.NewItemsPacketSetCreativeSlot +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 { + return listOf( + PacketAutoRecipe(plugin), + PacketHeldItemSlot, + NewItemsPacketOpenWindowMerchant, + NewItemsPacketSetCreativeSlot, + PacketSetSlot, + PacketWindowItems(plugin) + ) + } +} diff --git a/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/SNBTConverter.kt b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/SNBTConverter.kt new file mode 100644 index 00000000..51c4b2cb --- /dev/null +++ b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/SNBTConverter.kt @@ -0,0 +1,54 @@ +package com.willfp.eco.internal.spigot.proxy.v1_20_6 + +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.SnbtPrinterTagVisitor +import net.minecraft.nbt.TagParser +import org.bukkit.Bukkit +import org.bukkit.craftbukkit.CraftServer +import org.bukkit.craftbukkit.inventory.CraftItemStack +import org.bukkit.inventory.ItemStack + +private val holderLookupProvider = (Bukkit.getServer() as CraftServer).server.registryAccess() + +class SNBTConverter : SNBTConverterProxy { + override fun fromSNBT(snbt: String): ItemStack? { + val nbt = runCatching { TagParser.parseTag(snbt) }.getOrNull() ?: return null + val nms = net.minecraft.world.item.ItemStack.parse(holderLookupProvider, nbt).orElse(null) ?: return null + return CraftItemStack.asBukkitCopy(nms) + } + + override fun toSNBT(itemStack: ItemStack): String { + val nms = CraftItemStack.asNMSCopy(itemStack) + return SnbtPrinterTagVisitor().visit(nms.save(holderLookupProvider)) + } + + override fun makeSNBTTestable(snbt: String): TestableItem { + val nbt = runCatching { TagParser.parseTag(snbt) }.getOrNull() ?: return EmptyTestableItem() + val nms = net.minecraft.world.item.ItemStack.parse(holderLookupProvider, nbt).orElse(null) + ?: return EmptyTestableItem() + + nbt.remove("Count") + return SNBTTestableItem(CraftItemStack.asBukkitCopy(nms), nbt) + } + + 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 = nms.save(holderLookupProvider) as CompoundTag + 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_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/Skull.kt b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/Skull.kt new file mode 100644 index 00000000..6809a343 --- /dev/null +++ b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/Skull.kt @@ -0,0 +1,18 @@ +package com.willfp.eco.internal.spigot.proxy.v1_20_6 + +import com.willfp.eco.internal.spigot.proxy.SkullProxy +import com.willfp.eco.internal.spigot.proxy.common.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_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/TPS.kt b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/TPS.kt new file mode 100644 index 00000000..609235e1 --- /dev/null +++ b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/TPS.kt @@ -0,0 +1,11 @@ +package com.willfp.eco.internal.spigot.proxy.v1_20_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_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/entity/EcoEntityController.kt b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/entity/EcoEntityController.kt new file mode 100644 index 00000000..589405f1 --- /dev/null +++ b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/entity/EcoEntityController.kt @@ -0,0 +1,95 @@ +package com.willfp.eco.internal.spigot.proxy.v1_20_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_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/packet/NewItemsPacketOpenWindowMerchant.kt b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/packet/NewItemsPacketOpenWindowMerchant.kt new file mode 100644 index 00000000..e131b278 --- /dev/null +++ b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/packet/NewItemsPacketOpenWindowMerchant.kt @@ -0,0 +1,35 @@ +package com.willfp.eco.internal.spigot.proxy.v1_20_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_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/packet/NewItemsPacketSetCreativeSlot.kt b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/packet/NewItemsPacketSetCreativeSlot.kt new file mode 100644 index 00000000..db4d2b8b --- /dev/null +++ b/eco-core/core-nms/v1_20_6/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_6/packet/NewItemsPacketSetCreativeSlot.kt @@ -0,0 +1,19 @@ +package com.willfp.eco.internal.spigot.proxy.v1_20_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 + } +} \ No newline at end of file diff --git a/eco-core/core-nms/v1_20_R3/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_R3/TPS.kt b/eco-core/core-nms/v1_20_R3/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_R3/TPS.kt index 0e6f5c78..eab7d6a2 100644 --- a/eco-core/core-nms/v1_20_R3/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_R3/TPS.kt +++ b/eco-core/core-nms/v1_20_R3/src/main/kotlin/com/willfp/eco/internal/spigot/proxy/v1_20_R3/TPS.kt @@ -6,6 +6,6 @@ import org.bukkit.craftbukkit.v1_20_R3.CraftServer class TPS : TPSProxy { override fun getTPS(): Double { - return (Bukkit.getServer() as CraftServer).handle.server.recentTps[0] + return (Bukkit.getServer() as CraftServer).handle.server.tps1.average } -} \ No newline at end of file +} diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/integrations/antigrief/AntigriefIridiumSkyblock.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/integrations/antigrief/AntigriefIridiumSkyblock.kt index 1634ead4..ff42864e 100644 --- a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/integrations/antigrief/AntigriefIridiumSkyblock.kt +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/integrations/antigrief/AntigriefIridiumSkyblock.kt @@ -13,7 +13,7 @@ class AntigriefIridiumSkyblock : AntigriefIntegration { player: Player, block: Block ): Boolean { - val api = IridiumSkyblockAPI.getInstance() + val api = IridiumSkyblockAPI.getInstance() ?: return true return api.getIslandPermission(api.getIslandViaLocation(block.location).orElse(null) ?: return true, api.getUser(player), PermissionType.BLOCK_BREAK) } @@ -22,7 +22,7 @@ class AntigriefIridiumSkyblock : AntigriefIntegration { player: Player, location: Location ): Boolean { - val api = IridiumSkyblockAPI.getInstance() + val api = IridiumSkyblockAPI.getInstance() ?: return true return api.getIslandPermission(api.getIslandViaLocation(location).orElse(null) ?: return true, api.getUser(player), PermissionType.BLOCK_BREAK) } @@ -31,7 +31,7 @@ class AntigriefIridiumSkyblock : AntigriefIntegration { player: Player, block: Block ): Boolean { - val api = IridiumSkyblockAPI.getInstance() + val api = IridiumSkyblockAPI.getInstance() ?: return true return api.getIslandPermission(api.getIslandViaLocation(block.location).orElse(null) ?: return true, api.getUser(player), PermissionType.BLOCK_PLACE) } @@ -40,7 +40,7 @@ class AntigriefIridiumSkyblock : AntigriefIntegration { player: Player, victim: LivingEntity ): Boolean { - val api = IridiumSkyblockAPI.getInstance() + val api = IridiumSkyblockAPI.getInstance() ?: return true return when (victim) { is Player -> api.getIslandViaLocation(victim.location).orElse(null) != null diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/integrations/bstats/Metrics.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/integrations/bstats/Metrics.kt index aff9cc96..11374365 100644 --- a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/integrations/bstats/Metrics.kt +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/integrations/bstats/Metrics.kt @@ -8,6 +8,7 @@ import java.io.ByteArrayOutputStream import java.io.DataOutputStream import java.io.File import java.io.InputStreamReader +import java.net.URI import java.net.URL import java.nio.charset.StandardCharsets import java.util.UUID @@ -109,7 +110,7 @@ class Metrics(private val plugin: EcoPlugin) { infoLogger.accept("Sent bStats metrics data: $data") } val url = String.format(REPORT_URL, platform) - val connection = URL(url).openConnection() as HttpsURLConnection + val connection = URI(url).toURL().openConnection() as HttpsURLConnection // Compress the data to save bandwidth val compressedData = compress(data.toString()) connection.requestMethod = "POST" diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 249e5832..7f93135c 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 15de9024..b82aa23a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index a69d9cb6..1aa94a42 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +80,11 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,22 +131,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,11 +198,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/gradlew.bat b/gradlew.bat index f127cfd4..93e3f59f 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% diff --git a/settings.gradle.kts b/settings.gradle.kts index 134968d0..44f39991 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,6 +21,7 @@ include(":eco-core:core-nms:v1_19_R3") include(":eco-core:core-nms:v1_20_R1") include(":eco-core:core-nms:v1_20_R2") include(":eco-core:core-nms:v1_20_R3") +include(":eco-core:core-nms:v1_20_6") include(":eco-core:core-proxy") include(":eco-core:core-plugin") include(":eco-core:core-backend") \ No newline at end of file