From b67d956e3951be1c9a43edc24ba106e7b0ee436a Mon Sep 17 00:00:00 2001 From: alexcrea <42614139+alexcrea@users.noreply.github.com> Date: Thu, 1 May 2025 19:01:16 +0200 Subject: [PATCH 1/4] fix inverted equal logic --- .../xyz/alexcrea/cuanvil/enchant/CAEnchantmentRegistry.java | 4 ++-- .../alexcrea/cuanvil/enchant/wrapped/CABukkitEnchantment.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantmentRegistry.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantmentRegistry.java index 5e2b1c1..4634a11 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantmentRegistry.java +++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantmentRegistry.java @@ -76,12 +76,12 @@ public class CAEnchantmentRegistry { */ public boolean register(@NotNull CAEnchantment enchantment) { if (byKeyMap.containsKey(enchantment.getKey())) { - if (!enchantment.equals(byKeyMap.get(enchantment.getKey()))) { + if (Objects.equals(enchantment, byKeyMap.get(enchantment.getKey()))) { // We are trying to register the exact same enchantment. so we just skip it. return false; } - if(ConfigHolder.DEFAULT_CONFIG.getConfig().getBoolean("caution_secret_do_not_log_duplicated_registered_key", false)){ + if (ConfigHolder.DEFAULT_CONFIG.getConfig().getBoolean("caution_secret_do_not_log_duplicated_registered_key", false)) { return false; } diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CABukkitEnchantment.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CABukkitEnchantment.java index db809ce..1a72458 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CABukkitEnchantment.java +++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CABukkitEnchantment.java @@ -18,6 +18,7 @@ import java.lang.reflect.Method; import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.logging.Level; /** @@ -163,7 +164,7 @@ public class CABukkitEnchantment extends CAEnchantmentBase { return false; } - return this.bukkit.equals(other.getEnchant()); + return Objects.equals(this.bukkit, other.getEnchant()); } } From f4ce267e445bceb351c26a848721e0673d7a0f3d Mon Sep 17 00:00:00 2001 From: alexcrea <42614139+alexcrea@users.noreply.github.com> Date: Thu, 1 May 2025 19:31:21 +0200 Subject: [PATCH 2/4] add immutable list --- .../io/delilaheve/util/ConfigOptions.kt | 16 +++ .../cuanvil/listener/PrepareAnvilListener.kt | 107 +++++++++++++----- 2 files changed, 93 insertions(+), 30 deletions(-) diff --git a/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt b/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt index d9d1f87..980999f 100644 --- a/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt +++ b/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt @@ -2,6 +2,7 @@ package io.delilaheve.util import io.delilaheve.CustomAnvil import io.delilaheve.util.EnchantmentUtil.enchantmentName +import org.bukkit.NamespacedKey import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.config.WorkPenaltyType import xyz.alexcrea.cuanvil.config.WorkPenaltyType.WorkPenaltyPart @@ -51,6 +52,8 @@ object ConfigOptions { const val DISABLE_MERGE_OVER_ROOT = "disable-merge-over" + const val IMMUTABLE_ENCHANTMENT_LIST = "immutable_enchantments" + // Keys for specific enchantment values private const val KEY_BOOK = "book" private const val KEY_ITEM = "item" @@ -478,4 +481,17 @@ object ConfigOptions { .takeIf { it in ENCHANT_LIMIT_RANGE } } + fun isImmutable(key: NamespacedKey): Boolean { + val immutables = ConfigHolder.DEFAULT_CONFIG.config.getStringList(IMMUTABLE_ENCHANTMENT_LIST) + + // We need to ignore case so can't just check "contain" + for (ench in immutables) { + if (ench.equals(key.toString(), ignoreCase = true) || + ench.equals(key.key, ignoreCase = true) + ) + return true + } + return false + } + } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt index 5708126..4cae038 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt @@ -19,6 +19,8 @@ import org.bukkit.event.Listener import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.meta.EnchantmentStorageMeta +import org.bukkit.inventory.meta.ItemMeta import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.util.* import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair @@ -52,13 +54,21 @@ class PrepareAnvilListener : Listener { val first = inventory.getItem(ANVIL_INPUT_LEFT) ?: return val second = inventory.getItem(ANVIL_INPUT_RIGHT) + if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return + if (isImmutable(first) || isImmutable(second)) { + CustomAnvil.verboseLog("Skipping anvil process as one of the two item is immutable") + + event.result = null + return + } + // Test custom recipe - if(testCustomRecipe(event, inventory, player, first, second)) return + if (testCustomRecipe(event, inventory, player, first, second)) return // Test rename lonely item - if(second == null) { + if (second == null) { doRenaming(event, inventory, player, first) return } @@ -70,23 +80,51 @@ class PrepareAnvilListener : Listener { } // Test for unit repair - if(testUnitRepair(event, inventory, player, first, second)) return + if (testUnitRepair(event, inventory, player, first, second)) return // Test for lore edit - if(testLoreEdit(event, inventory, player, first, second)) return + if (testLoreEdit(event, inventory, player, first, second)) return CustomAnvil.log("no anvil fuse type found") event.result = null } + private fun isImmutable(item: ItemStack?): Boolean { + if (item == null) return false + + val meta = item.itemMeta + return meta != null && + (hasImmutableEnchants(meta) || hasImmutableStoredEnchants(meta)) + } + + private fun hasImmutableEnchants(meta: ItemMeta): Boolean { + if (!meta.hasEnchants()) return false + + for (enchant in meta.enchants.keys) { + if (ConfigOptions.isImmutable(enchant.key)) return true + } + return false + } + + private fun hasImmutableStoredEnchants(meta: ItemMeta): Boolean { + if (meta !is EnchantmentStorageMeta || !meta.hasStoredEnchants()) return false + + for (enchant in meta.storedEnchants.keys) { + if (ConfigOptions.isImmutable(enchant.key)) return true + } + return false + } + // return true if a custom recipe exist with these ingredients - private fun testCustomRecipe(event: PrepareAnvilEvent, inventory: AnvilInventory, - player: HumanEntity, - first: ItemStack, second: ItemStack?): Boolean { + private fun testCustomRecipe( + event: PrepareAnvilEvent, inventory: AnvilInventory, + player: HumanEntity, + first: ItemStack, second: ItemStack? + ): Boolean { val recipe = CustomRecipeUtil.getCustomRecipe(first, second) CustomAnvil.verboseLog("custom recipe not null? ${recipe != null}") - if(recipe == null) return false + if (recipe == null) return false val amount = CustomRecipeUtil.getCustomRecipeAmount(recipe, first, second) @@ -94,7 +132,7 @@ class PrepareAnvilListener : Listener { resultItem.amount *= amount event.result = resultItem - if(DependencyManager.tryTreatAnvilResult(event, resultItem)) return true + if (DependencyManager.tryTreatAnvilResult(event, resultItem)) return true // Maybe add an option on custom craft to ignore/not ignore penalty ?? var xpCost = recipe.xpCostPerCraft * amount @@ -105,8 +143,10 @@ class PrepareAnvilListener : Listener { return true } - private fun doRenaming(event: PrepareAnvilEvent, inventory: AnvilInventory, - player: HumanEntity, first: ItemStack) { + private fun doRenaming( + event: PrepareAnvilEvent, inventory: AnvilInventory, + player: HumanEntity, first: ItemStack + ) { val resultItem = first.clone() var anvilCost = handleRename(resultItem, inventory, player) @@ -118,7 +158,7 @@ class PrepareAnvilListener : Listener { } event.result = resultItem - if(DependencyManager.tryTreatAnvilResult(event, resultItem)) return + if (DependencyManager.tryTreatAnvilResult(event, resultItem)) return anvilCost += AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.RENAME_ONLY) @@ -131,18 +171,20 @@ class PrepareAnvilListener : Listener { var sumCost = 0 var useColor = false - if(ConfigOptions.renameColorPossible && inventoryName != null){ + if (ConfigOptions.renameColorPossible && inventoryName != null) { val resultString = StringBuilder(inventoryName) - useColor = AnvilColorUtil.handleColor(resultString, player, + useColor = AnvilColorUtil.handleColor( + resultString, player, ConfigOptions.permissionNeededForColor, ConfigOptions.allowColorCode, ConfigOptions.allowHexadecimalColor, - AnvilColorUtil.ColorUseType.RENAME) + AnvilColorUtil.ColorUseType.RENAME + ) - if(useColor) { + if (useColor) { inventoryName = resultString.toString() - sumCost+= ConfigOptions.useOfColorCost + sumCost += ConfigOptions.useOfColorCost } } @@ -165,9 +207,11 @@ class PrepareAnvilListener : Listener { return 0 } - private fun doMerge(event: PrepareAnvilEvent, inventory: AnvilInventory, - player: HumanEntity, - first: ItemStack, second: ItemStack) { + private fun doMerge( + event: PrepareAnvilEvent, inventory: AnvilInventory, + player: HumanEntity, + first: ItemStack, second: ItemStack + ) { val newEnchants = first.findEnchantments() .combineWith(second.findEnchantments(), first, player) val resultItem = first.clone() @@ -195,14 +239,16 @@ class PrepareAnvilListener : Listener { // Finally, we set result event.result = resultItem - if(DependencyManager.tryTreatAnvilResult(event, resultItem)) return + if (DependencyManager.tryTreatAnvilResult(event, resultItem)) return AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, anvilCost) } // return true if there is a valid unit repair with these ingredients - private fun testUnitRepair(event: PrepareAnvilEvent, inventory: AnvilInventory, player: HumanEntity, - first: ItemStack, second: ItemStack): Boolean { + private fun testUnitRepair( + event: PrepareAnvilEvent, inventory: AnvilInventory, player: HumanEntity, + first: ItemStack, second: ItemStack + ): Boolean { val unitRepairAmount = first.getRepair(second) ?: return false val resultItem = first.clone() @@ -222,26 +268,27 @@ class PrepareAnvilListener : Listener { return true } event.result = resultItem - if(DependencyManager.tryTreatAnvilResult(event, resultItem)) return true + if (DependencyManager.tryTreatAnvilResult(event, resultItem)) return true AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, anvilCost) return true } - private fun testLoreEdit(event: PrepareAnvilEvent, inventory: AnvilInventory, player: HumanEntity, - first: ItemStack, second: ItemStack): Boolean { + private fun testLoreEdit( + event: PrepareAnvilEvent, inventory: AnvilInventory, player: HumanEntity, + first: ItemStack, second: ItemStack + ): Boolean { val type = second.type var result: ItemStack? = null val xpCost = AtomicInteger() - if(Material.WRITABLE_BOOK == type) { + if (Material.WRITABLE_BOOK == type) { result = AnvilLoreEditUtil.tryLoreEditByBook(player, first, second, xpCost) - } - else if(Material.PAPER == type) { + } else if (Material.PAPER == type) { result = AnvilLoreEditUtil.tryLoreEditByPaper(player, first, second, xpCost) } - if(result == null || first == result) { + if (result == null || first == result) { CustomAnvil.log("lore edit, But input is same as output") event.result = null return false From 8f408438dcc5321659128a1583ae374c163291ed Mon Sep 17 00:00:00 2001 From: alexcrea <42614139+alexcrea@users.noreply.github.com> Date: Thu, 1 May 2025 19:44:07 +0200 Subject: [PATCH 3/4] make immutability test before pre anvil bypass event --- .../alexcrea/cuanvil/listener/PrepareAnvilListener.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt index 4cae038..138546a 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt @@ -47,16 +47,10 @@ class PrepareAnvilListener : Listener { // Should find player val player: HumanEntity = InventoryViewUtil.getInstance().getPlayer(event.view) - // Test if the event should bypass custom anvil. - if (DependencyManager.tryEventPreAnvilBypass(event, player)) return - val inventory = event.inventory val first = inventory.getItem(ANVIL_INPUT_LEFT) ?: return val second = inventory.getItem(ANVIL_INPUT_RIGHT) - - if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return - if (isImmutable(first) || isImmutable(second)) { CustomAnvil.verboseLog("Skipping anvil process as one of the two item is immutable") @@ -64,6 +58,11 @@ class PrepareAnvilListener : Listener { return } + // Test if the event should bypass custom anvil. + if (DependencyManager.tryEventPreAnvilBypass(event, player)) return + + if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return + // Test custom recipe if (testCustomRecipe(event, inventory, player, first, second)) return From 368b4b2c841823c6aa0d6bf0148e2b477115f27d Mon Sep 17 00:00:00 2001 From: alexcrea <42614139+alexcrea@users.noreply.github.com> Date: Fri, 2 May 2025 12:46:19 +0200 Subject: [PATCH 4/4] made early bypass --- .../cuanvil/dependency/DependencyManager.kt | 33 ++++++++++++++++--- .../cuanvil/listener/PrepareAnvilListener.kt | 3 ++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt index cb612a4..71dce33 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt @@ -102,6 +102,35 @@ object DependencyManager { // Then handle plugin reload ecoEnchantCompatibility?.handleConfigReload() } + // Return true if should bypass (either by a dependency or error) + // called before immutability test + fun earlyTryEventPreAnvilBypass(event: PrepareAnvilEvent, player: HumanEntity): Boolean { + try { + return earlyUnsafeTryEventPreAnvilBypass(event, player) + } catch (e: Exception) { + CustomAnvil.instance.logger.log( + Level.SEVERE, + "Error while trying to handle custom anvil supported plugin: ", + e + ) + + // Just in case to avoid illegal items + event.inventory.setItem(ANVIL_OUTPUT_SLOT, null) + + // Finally, warn the player, maybe a lot of time but better warn than do nothing + event.view.player.sendMessage(ChatColor.RED.toString() + "Error while handling the anvil.") + return true + } + } + + private fun earlyUnsafeTryEventPreAnvilBypass(event: PrepareAnvilEvent, player: HumanEntity): Boolean { + var bypass = false + + // Test if the inventory is a gui(version specific) + if (externGuiTester?.testIfGui(event.view) == true) bypass = true + + return bypass + } // Return true if should bypass (either by a dependency or error) fun tryEventPreAnvilBypass(event: PrepareAnvilEvent, player: HumanEntity): Boolean { @@ -135,10 +164,6 @@ object DependencyManager { // Test excellent enchantments used prepare anvil if (!bypass && (excellentEnchantsCompatibility?.testPrepareAnvil(event) == true)) bypass = true - // Test if the inventory is a gui(version specific) - if (!bypass && (externGuiTester?.testIfGui(event.view) == true)) bypass = true - - return bypass } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt index 138546a..9d7245a 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt @@ -47,6 +47,9 @@ class PrepareAnvilListener : Listener { // Should find player val player: HumanEntity = InventoryViewUtil.getInstance().getPlayer(event.view) + // Test if custom anvil is bypassed before immutability test + if (DependencyManager.earlyTryEventPreAnvilBypass(event, player)) return + val inventory = event.inventory val first = inventory.getItem(ANVIL_INPUT_LEFT) ?: return val second = inventory.getItem(ANVIL_INPUT_RIGHT)