Added 1.21.5 support, bumped version

This commit is contained in:
Will FP 2025-06-02 13:58:26 +01:00
parent dcc4309761
commit f2a547cb85
14 changed files with 381 additions and 7 deletions

View File

@ -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 {

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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<Enchantment>
get() =
(org.bukkit.Registry.ENCHANTMENT as DelayedRegistry<Enchantment, *>).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<NamespacedKey, net.minecraft.world.item.enchantment.Enchantment, Enchantment?> { 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<NamespacedKey, net.minecraft.world.item.enchantment.Enchantment, Enchantment>)
)
// Clear the enchantment cache
cache.set(bukkitRegistry, mutableMapOf<NamespacedKey, Enchantment>())
// Unfreeze NMS registry
frozenField.set(enchantmentRegistry, false)
unregisteredIntrusiveHoldersField.set(
enchantmentRegistry,
IdentityHashMap<net.minecraft.world.item.enchantment.Enchantment,
Holder.Reference<net.minecraft.world.item.enchantment.Enchantment>>()
)
/*
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<NamespacedKey, Enchantment>())
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.
*/
}
}

View File

@ -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
}
}

View File

@ -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<Enchantment>
) : 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)"
}
}

View File

@ -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<Enchantment>
) : 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
}
}

View File

@ -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))
}

View File

@ -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

Binary file not shown.

View File

@ -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

6
gradlew vendored
View File

@ -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.

4
gradlew.bat vendored
View File

@ -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

View File

@ -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")