diff --git a/build.gradle.kts b/build.gradle.kts index 0025c284..661e9fa8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -31,6 +31,7 @@ dependencies { implementation(project(":eco-core:core-nms:v1_21", configuration = "reobf")) implementation(project(":eco-core:core-nms:v1_21_3", configuration = "reobf")) implementation(project(":eco-core:core-nms:v1_21_4", configuration = "reobf")) + implementation(project(":eco-core:core-nms:v1_21_5", configuration = "reobf")) } allprojects { diff --git a/eco-core/core-nms/v1_21_5/build.gradle.kts b/eco-core/core-nms/v1_21_5/build.gradle.kts new file mode 100644 index 00000000..26f42504 --- /dev/null +++ b/eco-core/core-nms/v1_21_5/build.gradle.kts @@ -0,0 +1,28 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +plugins { + id("io.papermc.paperweight.userdev") +} + +group = "com.willfp" +version = rootProject.version + +dependencies { + paperweight.paperDevBundle("1.21.4-R0.1-SNAPSHOT") +} + +tasks { + build { + dependsOn(reobfJar) + } + + compileJava { + options.release = 21 + } + + compileKotlin { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_21) + } + } +} diff --git a/eco-core/core-nms/v1_21_5/src/main/kotlin/com/willfp/ecoenchants/proxy/v1_21_5/HideStoredEnchants.kt b/eco-core/core-nms/v1_21_5/src/main/kotlin/com/willfp/ecoenchants/proxy/v1_21_5/HideStoredEnchants.kt new file mode 100644 index 00000000..308def87 --- /dev/null +++ b/eco-core/core-nms/v1_21_5/src/main/kotlin/com/willfp/ecoenchants/proxy/v1_21_5/HideStoredEnchants.kt @@ -0,0 +1,33 @@ +package com.willfp.ecoenchants.proxy.v1_21_5 + +import com.willfp.eco.core.Prerequisite +import com.willfp.eco.core.fast.FastItemStack +import com.willfp.ecoenchants.display.HideStoredEnchantsProxy +import org.bukkit.inventory.ItemFlag +import javax.print.attribute.PrintRequestAttribute + +class HideStoredEnchants: HideStoredEnchantsProxy { + override fun hideStoredEnchants(fis: FastItemStack) { + if (Prerequisite.HAS_PAPER.isMet) { + fis.addItemFlags(ItemFlag.HIDE_STORED_ENCHANTS) + } else { + fis.addItemFlags(ItemFlag.HIDE_ADDITIONAL_TOOLTIP) + } + } + + override fun areStoredEnchantsHidden(fis: FastItemStack): Boolean { + return if (Prerequisite.HAS_PAPER.isMet) { + fis.hasItemFlag(ItemFlag.HIDE_STORED_ENCHANTS) + } else { + fis.hasItemFlag(ItemFlag.HIDE_ADDITIONAL_TOOLTIP) + } + } + + override fun showStoredEnchants(fis: FastItemStack) { + if (Prerequisite.HAS_PAPER.isMet) { + fis.removeItemFlags(ItemFlag.HIDE_STORED_ENCHANTS) + } else { + fis.removeItemFlags(ItemFlag.HIDE_ADDITIONAL_TOOLTIP) + } + } +} diff --git a/eco-core/core-nms/v1_21_5/src/main/kotlin/com/willfp/ecoenchants/proxy/v1_21_5/ModernEnchantmentRegisterer.kt b/eco-core/core-nms/v1_21_5/src/main/kotlin/com/willfp/ecoenchants/proxy/v1_21_5/ModernEnchantmentRegisterer.kt new file mode 100644 index 00000000..193cedf8 --- /dev/null +++ b/eco-core/core-nms/v1_21_5/src/main/kotlin/com/willfp/ecoenchants/proxy/v1_21_5/ModernEnchantmentRegisterer.kt @@ -0,0 +1,150 @@ +package com.willfp.ecoenchants.proxy.v1_21_5 + +import com.willfp.ecoenchants.enchant.EcoEnchant +import com.willfp.ecoenchants.enchant.EcoEnchants +import com.willfp.ecoenchants.enchant.impl.EcoEnchantBase +import com.willfp.ecoenchants.enchant.registration.modern.ModernEnchantmentRegistererProxy +import com.willfp.ecoenchants.proxy.v1_21_5.registration.EcoEnchantsCraftEnchantment +import com.willfp.ecoenchants.proxy.v1_21_5.registration.ModifiedVanillaCraftEnchantment +import com.willfp.ecoenchants.proxy.v1_21_5.registration.vanillaEcoEnchantsEnchantment +import io.papermc.paper.registry.entry.RegistryTypeMapper +import io.papermc.paper.registry.legacy.DelayedRegistry +import net.minecraft.core.Holder +import net.minecraft.core.MappedRegistry +import net.minecraft.core.Registry +import net.minecraft.core.registries.Registries +import net.minecraft.resources.ResourceLocation +import org.bukkit.Bukkit +import org.bukkit.NamespacedKey +import org.bukkit.craftbukkit.CraftRegistry +import org.bukkit.craftbukkit.CraftServer +import org.bukkit.craftbukkit.util.CraftNamespacedKey +import org.bukkit.enchantments.Enchantment +import java.lang.reflect.Modifier +import java.util.IdentityHashMap +import java.util.function.BiFunction +import javax.annotation.Nullable + +private val enchantmentRegistry = + (Bukkit.getServer() as CraftServer).server.registryAccess().lookupOrThrow(Registries.ENCHANTMENT) + +@Suppress("DEPRECATION") +private val bukkitRegistry: org.bukkit.Registry + get() = + (org.bukkit.Registry.ENCHANTMENT as DelayedRegistry).delegate() + +class ModernEnchantmentRegisterer : ModernEnchantmentRegistererProxy { + private val frozenField = MappedRegistry::class.java + .declaredFields + .filter { it.type.isPrimitive }[0] + .apply { isAccessible = true } + + private val allTags = MappedRegistry::class.java + .declaredFields + .filter { it.type.name.contains("TagSet") }[0] + .apply { isAccessible = true } + + private val unregisteredIntrusiveHoldersField = MappedRegistry::class.java + .declaredFields + .filter { it.type == Map::class.java } + .filter { it.isAnnotationPresent(Nullable::class.java) }[0] + .apply { isAccessible = true } + + // 1.21.4+ only has minecraftToBukkit in CraftRegistry, removing the duplicate in WritableCraftRegistry + private val minecraftToBukkit = CraftRegistry::class.java + .getDeclaredField("minecraftToBukkit") + .apply { isAccessible = true } + + private val cache = CraftRegistry::class.java + .getDeclaredField("cache") + .apply { isAccessible = true } + + override fun replaceRegistry() { + val newRegistryMTB = + BiFunction { key, registry -> + val eco = EcoEnchants.getByID(key.key) + val isRegistered = enchantmentRegistry.containsKey(CraftNamespacedKey.toMinecraft(key)) + + if (eco != null) { + eco as Enchantment + } else if (isRegistered) { + val holder = enchantmentRegistry.get(CraftNamespacedKey.toMinecraft(key)).get() + + ModifiedVanillaCraftEnchantment(key, registry, holder) + } else { + null + } + } + + // Update bukkit registry + // The nasty casting hack is because of some weird nullability changes, if I set the BiFunction to have a + // non-nullable bukkit enchantment type then it refuses to build, some sort of K2 compiler change. + @Suppress("UNCHECKED_CAST") + minecraftToBukkit.set( + bukkitRegistry, + RegistryTypeMapper(newRegistryMTB as BiFunction) + ) + + // Clear the enchantment cache + cache.set(bukkitRegistry, mutableMapOf()) + + // Unfreeze NMS registry + frozenField.set(enchantmentRegistry, false) + unregisteredIntrusiveHoldersField.set( + enchantmentRegistry, + IdentityHashMap>() + ) + + /* + Creating an unbound tag set requires using reflection because the inner class is + package-private, so we just find the method manually. + */ + + val unboundTagSet = MappedRegistry::class.java + .declaredClasses[0] + .declaredMethods + .filter { Modifier.isStatic(it.modifiers) } + .filter { it.parameterCount == 0 }[0] + .apply { isAccessible = true } + .invoke(null) + + allTags.set(enchantmentRegistry, unboundTagSet) + } + + override fun register(enchant: EcoEnchantBase): Enchantment { + // Clear the enchantment cache + cache.set(bukkitRegistry, mutableMapOf()) + + if (enchantmentRegistry.containsKey(CraftNamespacedKey.toMinecraft(enchant.enchantmentKey))) { + val nms = enchantmentRegistry[CraftNamespacedKey.toMinecraft(enchant.enchantmentKey)] + + if (nms.isPresent) { + return EcoEnchantsCraftEnchantment(enchant, nms.get()) + } else { + throw IllegalStateException("Enchantment ${enchant.id} wasn't registered") + } + } + + val vanillaEnchantment = vanillaEcoEnchantsEnchantment(enchant) + + enchantmentRegistry.createIntrusiveHolder(vanillaEnchantment) + + Registry.register( + enchantmentRegistry, + ResourceLocation.withDefaultNamespace(enchant.id), + vanillaEnchantment + ) + + return register(enchant) + } + + override fun unregister(enchant: EcoEnchant) { + /* + + You can't unregister from a minecraft registry, so we simply leave the stale reference there. + This shouldn't cause many issues in production as the bukkit registry is replaced on each reload. + + */ + } +} diff --git a/eco-core/core-nms/v1_21_5/src/main/kotlin/com/willfp/ecoenchants/proxy/v1_21_5/OpenInventory.kt b/eco-core/core-nms/v1_21_5/src/main/kotlin/com/willfp/ecoenchants/proxy/v1_21_5/OpenInventory.kt new file mode 100644 index 00000000..71a10df0 --- /dev/null +++ b/eco-core/core-nms/v1_21_5/src/main/kotlin/com/willfp/ecoenchants/proxy/v1_21_5/OpenInventory.kt @@ -0,0 +1,11 @@ +package com.willfp.ecoenchants.proxy.v1_21_5 + +import com.willfp.ecoenchants.mechanics.OpenInventoryProxy +import org.bukkit.craftbukkit.entity.CraftPlayer +import org.bukkit.entity.Player + +class OpenInventory : OpenInventoryProxy { + override fun getOpenInventory(player: Player): Any { + return (player as CraftPlayer).handle.containerMenu + } +} diff --git a/eco-core/core-nms/v1_21_5/src/main/kotlin/com/willfp/ecoenchants/proxy/v1_21_5/registration/EcoEnchantsCraftEnchantment.kt b/eco-core/core-nms/v1_21_5/src/main/kotlin/com/willfp/ecoenchants/proxy/v1_21_5/registration/EcoEnchantsCraftEnchantment.kt new file mode 100644 index 00000000..d65ba8da --- /dev/null +++ b/eco-core/core-nms/v1_21_5/src/main/kotlin/com/willfp/ecoenchants/proxy/v1_21_5/registration/EcoEnchantsCraftEnchantment.kt @@ -0,0 +1,104 @@ +package com.willfp.ecoenchants.proxy.v1_21_5.registration + +import com.willfp.eco.util.toComponent +import com.willfp.ecoenchants.display.getFormattedName +import com.willfp.ecoenchants.enchant.EcoEnchant +import com.willfp.ecoenchants.enchant.impl.EcoEnchantBase +import net.kyori.adventure.text.Component +import net.minecraft.core.Holder +import net.minecraft.world.item.enchantment.Enchantment +import org.bukkit.NamespacedKey +import org.bukkit.craftbukkit.enchantments.CraftEnchantment +import org.bukkit.enchantments.EnchantmentTarget +import org.bukkit.inventory.ItemStack + +class EcoEnchantsCraftEnchantment( + private val enchant: EcoEnchantBase, + holder: Holder +) : CraftEnchantment(holder), EcoEnchant by enchant { + init { + enchant.enchantment = this + } + + override fun onRegister() { + // Fix for hardcoded enchantments + if (plugin.isLoaded) { + enchant.onRegister() + } + } + + override fun onRemove() { + enchant.onRemove() + } + + override fun canEnchantItem(item: ItemStack): Boolean { + return enchant.canEnchantItem(item) + } + + override fun conflictsWith(other: org.bukkit.enchantments.Enchantment): Boolean { + return enchant.conflictsWith(other) + } + + @Deprecated( + message = "EcoEnchants enchantments are not translatable", + replaceWith = ReplaceWith("this.displayName(level)") + ) + override fun translationKey(): String { + return "ecoenchants:enchantment.$id" + } + + @Deprecated( + message = "getName is a legacy Spigot API", + replaceWith = ReplaceWith("this.displayName(level)") + ) + override fun getName(): String = this.id.uppercase() + override fun getMaxLevel(): Int = enchant.maximumLevel + + override fun getStartLevel(): Int = 1 + + @Deprecated( + message = "Treasure enchantments do not exist in EcoEnchants", + replaceWith = ReplaceWith("this.isEnchantable") + ) + override fun isTreasure(): Boolean = !enchant.isObtainableThroughEnchanting + + @Deprecated( + message = "Use EnchantmentType instead", + replaceWith = ReplaceWith("type.id") + ) + override fun isCursed(): Boolean { + return false + } + + override fun displayName(level: Int): Component { + return enchant.getFormattedName(level).toComponent() + } + + override fun isTradeable(): Boolean { + return enchant.isObtainableThroughTrading + } + + override fun isDiscoverable(): Boolean { + return enchant.isObtainableThroughDiscovery + } + + override fun getMinModifiedCost(level: Int): Int { + return Int.MAX_VALUE + } + + override fun getMaxModifiedCost(level: Int): Int { + return Int.MAX_VALUE + } + + override fun equals(other: Any?): Boolean { + return other is EcoEnchant && this.enchantmentKey == other.enchantmentKey + } + + override fun hashCode(): Int { + return this.enchantmentKey.hashCode() + } + + override fun toString(): String { + return "EcoEnchantsCraftEnchantment(key=$key)" + } +} diff --git a/eco-core/core-nms/v1_21_5/src/main/kotlin/com/willfp/ecoenchants/proxy/v1_21_5/registration/ModifiedVanillaCraftEnchantment.kt b/eco-core/core-nms/v1_21_5/src/main/kotlin/com/willfp/ecoenchants/proxy/v1_21_5/registration/ModifiedVanillaCraftEnchantment.kt new file mode 100644 index 00000000..6c66332e --- /dev/null +++ b/eco-core/core-nms/v1_21_5/src/main/kotlin/com/willfp/ecoenchants/proxy/v1_21_5/registration/ModifiedVanillaCraftEnchantment.kt @@ -0,0 +1,25 @@ +package com.willfp.ecoenchants.proxy.v1_21_5.registration + +import com.willfp.ecoenchants.enchant.vanillaEnchantmentData +import net.minecraft.core.Holder +import net.minecraft.world.item.enchantment.Enchantment +import org.bukkit.NamespacedKey +import org.bukkit.craftbukkit.enchantments.CraftEnchantment + +class ModifiedVanillaCraftEnchantment( + private val key: NamespacedKey, + target: Enchantment, + holder: Holder +) : CraftEnchantment(holder) { + override fun getMaxLevel(): Int = this.vanillaEnchantmentData?.maxLevel ?: super.getMaxLevel() + + override fun conflictsWith(other: org.bukkit.enchantments.Enchantment): Boolean { + val otherConflicts = when (other) { + is ModifiedVanillaCraftEnchantment -> other.vanillaEnchantmentData?.conflicts?.contains(this.key) == true + else -> other.conflictsWith(this) + } + + return this.vanillaEnchantmentData?.conflicts?.contains(other.key) ?: super.conflictsWith(other) + || otherConflicts + } +} diff --git a/eco-core/core-nms/v1_21_5/src/main/kotlin/com/willfp/ecoenchants/proxy/v1_21_5/registration/VanillaEcoEnchantsEnchantment.kt b/eco-core/core-nms/v1_21_5/src/main/kotlin/com/willfp/ecoenchants/proxy/v1_21_5/registration/VanillaEcoEnchantsEnchantment.kt new file mode 100644 index 00000000..80955485 --- /dev/null +++ b/eco-core/core-nms/v1_21_5/src/main/kotlin/com/willfp/ecoenchants/proxy/v1_21_5/registration/VanillaEcoEnchantsEnchantment.kt @@ -0,0 +1,21 @@ +package com.willfp.ecoenchants.proxy.v1_21_5.registration + +import com.willfp.ecoenchants.enchant.EcoEnchant +import net.minecraft.core.HolderSet +import net.minecraft.resources.ResourceLocation +import net.minecraft.world.item.enchantment.Enchantment + +fun vanillaEcoEnchantsEnchantment(enchant: EcoEnchant): Enchantment { + val enchantment = Enchantment.enchantment( + Enchantment.definition( + HolderSet.empty(), + 1, // 1.21 hardcodes a minimum weight of 1 + enchant.maximumLevel, + Enchantment.constantCost(1), + Enchantment.constantCost(1), + 0 + ) + ) + + return enchantment.build(ResourceLocation.withDefaultNamespace(enchant.id)) +} diff --git a/gradle.properties b/gradle.properties index ac169c44..86c450da 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,4 +4,4 @@ kotlin.code.style=official kotlin.daemon.jvmargs=-Xmx2g -XX\:+UseG1GC -XX\:MaxMetaspaceSize\=512m libreforge-version=4.75.0 org.gradle.parallel=true -version=12.22.0 +version=12.22.1 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a4b76b95..1b33c55b 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 cea7a793..002b867c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index f3b75f3b..23d15a93 100755 --- a/gradlew +++ b/gradlew @@ -114,7 +114,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -205,7 +205,7 @@ fi 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, +# * DEFAULT_JVM_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. @@ -213,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 9d21a218..db3a6ac2 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,11 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/settings.gradle.kts b/settings.gradle.kts index eea334a2..58b46647 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -28,3 +28,4 @@ include(":eco-core:core-nms:v1_20_R3") 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")