From 804c11a7f4a5978ad6fde38c0ad93ae6d34f931a Mon Sep 17 00:00:00 2001 From: alexcrea <42614139+alexcrea@users.noreply.github.com> Date: Wed, 21 Aug 2024 03:13:58 +0200 Subject: [PATCH] Do not continue anvil process if Disenchantment done something. --- .../io/delilaheve/AnvilEventListener.kt | 75 +++-------- .../cuanvil/dependency/DependencyManager.kt | 26 ++++ .../dependency/DisenchantmentDependency.kt | 121 ++++++++++++++++++ .../xyz/alexcrea/cuanvil/util/XpSetterUtil.kt | 67 ++++++++++ src/main/resources/plugin.yml | 1 + 5 files changed, 231 insertions(+), 59 deletions(-) create mode 100644 src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DisenchantmentDependency.kt create mode 100644 src/main/kotlin/xyz/alexcrea/cuanvil/util/XpSetterUtil.kt diff --git a/src/main/kotlin/io/delilaheve/AnvilEventListener.kt b/src/main/kotlin/io/delilaheve/AnvilEventListener.kt index 576963e..f5f3888 100644 --- a/src/main/kotlin/io/delilaheve/AnvilEventListener.kt +++ b/src/main/kotlin/io/delilaheve/AnvilEventListener.kt @@ -27,10 +27,12 @@ import org.bukkit.inventory.InventoryView.Property.REPAIR_COST import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.Repairable import xyz.alexcrea.cuanvil.config.ConfigHolder +import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.packet.PacketManager import xyz.alexcrea.cuanvil.group.ConflictType import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair +import xyz.alexcrea.cuanvil.util.XpSetterUtil.setAnvilInvXp import java.util.regex.Matcher import java.util.regex.Pattern import kotlin.math.min @@ -43,9 +45,9 @@ class AnvilEventListener(private val packetManager: PacketManager) : Listener { companion object { // Anvil's output slot - private const val ANVIL_INPUT_LEFT = 0 - private const val ANVIL_INPUT_RIGHT = 1 - private const val ANVIL_OUTPUT_SLOT = 2 + const val ANVIL_INPUT_LEFT = 0 + const val ANVIL_INPUT_RIGHT = 1 + const val ANVIL_OUTPUT_SLOT = 2 // static slot container private val NO_SLOT = SlotContainer(SlotType.NO_SLOT, 0) @@ -57,6 +59,9 @@ class AnvilEventListener(private val packetManager: PacketManager) : Listener { */ @EventHandler(priority = HIGHEST) fun anvilCombineCheck(event: PrepareAnvilEvent) { + // Test if the event should bypass custom anvil. + if(DependencyManager.tryEventPreAnvilBypass(event)) return + val inventory = event.inventory val first = inventory.getItem(ANVIL_INPUT_LEFT) ?: return val second = inventory.getItem(ANVIL_INPUT_RIGHT) @@ -75,7 +80,7 @@ class AnvilEventListener(private val packetManager: PacketManager) : Listener { resultItem.amount *= amount event.result = resultItem - handleAnvilXp(inventory, event, recipe.xpCostPerCraft * amount, true) + setAnvilInvXp(inventory, event.view, recipe.xpCostPerCraft * amount, true) return } @@ -96,7 +101,7 @@ class AnvilEventListener(private val packetManager: PacketManager) : Listener { anvilCost += calculatePenalty(first, null, resultItem) - handleAnvilXp(inventory, event, anvilCost) + setAnvilInvXp(inventory, event.view, anvilCost) return } @@ -130,7 +135,7 @@ class AnvilEventListener(private val packetManager: PacketManager) : Listener { // Finally, we set result event.result = resultItem - handleAnvilXp(inventory, event, anvilCost) + setAnvilInvXp(inventory, event.view, anvilCost) return } @@ -155,7 +160,7 @@ class AnvilEventListener(private val packetManager: PacketManager) : Listener { } event.result = resultItem - handleAnvilXp(inventory, event, anvilCost) + setAnvilInvXp(inventory, event.view, anvilCost) } else { CustomAnvil.log("no anvil fuse type found") event.result = null @@ -282,9 +287,13 @@ class AnvilEventListener(private val packetManager: PacketManager) : Listener { val player = event.whoClicked as? Player ?: return if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return val inventory = event.inventory as? AnvilInventory ?: return + if (event.rawSlot != ANVIL_OUTPUT_SLOT) { return } + // Test if the event should bypass custom anvil. + if(DependencyManager.tryClickAnvilResultBypass(event, inventory)) return + val output = inventory.getItem(ANVIL_OUTPUT_SLOT) ?: return val leftItem = inventory.getItem(ANVIL_INPUT_LEFT) ?: return val rightItem = inventory.getItem(ANVIL_INPUT_RIGHT) @@ -638,58 +647,6 @@ class AnvilEventListener(private val packetManager: PacketManager) : Listener { } - /** - * Display xp needed for the work on the anvil inventory - */ - private fun handleAnvilXp( - inventory: AnvilInventory, - event: PrepareAnvilEvent, - anvilCost: Int, - ignoreRules: Boolean = false - ) { - // Test repair cost limit - val finalAnvilCost = if ( - !ignoreRules && - !ConfigOptions.doRemoveCostLimit && - ConfigOptions.doCapCost) { - min(anvilCost, ConfigOptions.maxAnvilCost) - } else { - anvilCost - } - - /* Because Minecraft likes to have the final say in the repair cost displayed - * we need to wait for the event to end before overriding it, this ensures that - * we have the final say in the process. */ - CustomAnvil.instance - .server - .scheduler - .runTask(CustomAnvil.instance, Runnable { - inventory.maximumRepairCost = - if (ConfigOptions.doRemoveCostLimit || ignoreRules) - { Int.MAX_VALUE } - else - { ConfigOptions.maxAnvilCost + 1 } - - val player = event.view.player - - inventory.repairCost = finalAnvilCost - event.view.setProperty(REPAIR_COST, finalAnvilCost) - player.openInventory.setProperty(REPAIR_COST, finalAnvilCost) - - if(player is Player){ - if(player.gameMode != GameMode.CREATIVE ){ - val bypassToExpensive = (ConfigOptions.doReplaceTooExpensive) && - (finalAnvilCost >= 40) && - finalAnvilCost < inventory.maximumRepairCost - - packetManager.setInstantBuild(player, bypassToExpensive) - } - - player.updateInventory() - } - }) - } - @EventHandler fun onAnvilClose(event: InventoryCloseEvent){ val player = event.player diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt index 9fccff6..749f10b 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt @@ -1,6 +1,9 @@ package xyz.alexcrea.cuanvil.dependency import org.bukkit.Bukkit +import org.bukkit.event.inventory.InventoryClickEvent +import org.bukkit.event.inventory.PrepareAnvilEvent +import org.bukkit.inventory.AnvilInventory import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.dependency.packet.PacketManager import xyz.alexcrea.cuanvil.dependency.packet.PacketManagerSelector @@ -10,6 +13,7 @@ object DependencyManager { lateinit var packetManager: PacketManager var enchantmentSquaredCompatibility: EnchantmentSquaredDependency? = null var ecoEnchantCompatibility: EcoEnchantDependency? = null + var disenchantmentCompatibility: DisenchantmentDependency? = null fun loadDependency(){ val pluginManager = Bukkit.getPluginManager() @@ -30,6 +34,12 @@ object DependencyManager { ecoEnchantCompatibility!!.disableAnvilListener() } + // Disenchantment dependency + if(pluginManager.isPluginEnabled("Disenchantment")){ + disenchantmentCompatibility = DisenchantmentDependency() + disenchantmentCompatibility!!.redirectListeners() + } + } fun handleCompatibilityConfig() { @@ -52,4 +62,20 @@ object DependencyManager { } + fun tryEventPreAnvilBypass(event: PrepareAnvilEvent): Boolean { + var bypass = false + + if(disenchantmentCompatibility?.testPrepareAnvil(event) == true) bypass = true + + return bypass + } + + fun tryClickAnvilResultBypass(event: InventoryClickEvent, inventory: AnvilInventory): Boolean { + var bypass = false + + if(disenchantmentCompatibility?.testAnvilResult(event, inventory) == true) bypass = true + + return bypass + } + } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DisenchantmentDependency.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DisenchantmentDependency.kt new file mode 100644 index 0000000..302fc5a --- /dev/null +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DisenchantmentDependency.kt @@ -0,0 +1,121 @@ +package xyz.alexcrea.cuanvil.dependency + +import cz.kominekjan.disenchantment.events.ItemClickEvent +import cz.kominekjan.disenchantment.events.ItemEvent +import cz.kominekjan.disenchantment.events.SplitBookClickEvent +import cz.kominekjan.disenchantment.events.SplitBookEvent +import io.delilaheve.AnvilEventListener +import io.delilaheve.CustomAnvil +import org.bukkit.event.inventory.InventoryClickEvent +import org.bukkit.event.inventory.PrepareAnvilEvent +import org.bukkit.inventory.AnvilInventory +import org.bukkit.inventory.ItemStack +import org.bukkit.plugin.RegisteredListener +import xyz.alexcrea.cuanvil.util.XpSetterUtil + +class DisenchantmentDependency { + + init { + CustomAnvil.instance.logger.info("Disenchantment Detected !") + } + + private lateinit var splitEvent: SplitBookEvent + private lateinit var itemEvent: ItemEvent + + private lateinit var splitBookClickEvent: SplitBookClickEvent + private lateinit var itemClickEvent: ItemClickEvent + + fun redirectListeners() { + + val toUnregister = ArrayList() + // get required PrepareAnvilEvent listener + for (registeredListener in PrepareAnvilEvent.getHandlerList().registeredListeners) { + val listener = registeredListener.listener + + if(listener is SplitBookEvent){ + this.splitEvent = listener + toUnregister.add(registeredListener) + } + + if(listener is ItemEvent){ + itemEvent = listener + toUnregister.add(registeredListener) + } + + } + + for (listener in toUnregister) { + PrepareAnvilEvent.getHandlerList().unregister(listener) + } + toUnregister.clear() + + // get required InventoryClickEvent listener + for (registeredListener in InventoryClickEvent.getHandlerList().registeredListeners) { + val listener = registeredListener.listener + + if(listener is SplitBookClickEvent){ + splitBookClickEvent = listener + toUnregister.add(registeredListener) + } + + if(listener is ItemClickEvent){ + itemClickEvent = listener + toUnregister.add(registeredListener) + } + + } + + for (listener in toUnregister) { + InventoryClickEvent.getHandlerList().unregister(listener) + } + + } + + fun testPrepareAnvil(event: PrepareAnvilEvent): Boolean { + val previousResult = event.result + event.result = null + + // Test if event change the result + itemEvent.onDisenchantmentEvent(event) + + if(event.result != null) { + CustomAnvil.log("Detected pre anvil item extract bypass.") + return true + } + + splitEvent.onDisenchantmentEvent(event) + if(event.result != null) { + CustomAnvil.log("Detected pre anvil split enchant bypass.") + return true + } + + event.result = previousResult + return false + } + + fun testAnvilResult(event: InventoryClickEvent, inventory: AnvilInventory): Boolean { + val previousResultSlot = inventory.getItem(AnvilEventListener.ANVIL_OUTPUT_SLOT)?.clone() + + // Test event if change the result + itemClickEvent.onDisenchantmentClickEvent(event) + if(!testAnvilInventoryChange(inventory, previousResultSlot) || event.isCancelled) { + CustomAnvil.log("Detected anvil click item extract bypass.") + return true + } + + splitBookClickEvent.onDisenchantmentClickEvent(event) + if(!testAnvilInventoryChange(inventory, previousResultSlot) || event.isCancelled) { + CustomAnvil.log("Detected anvil click split enchant bypass.") + return true + } + + return false + } + + private fun testAnvilInventoryChange(inventory: AnvilInventory, previous: ItemStack?): Boolean { + val currentResult = inventory.getItem(AnvilEventListener.ANVIL_OUTPUT_SLOT) + + return currentResult == previous + } + +} diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/XpSetterUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/XpSetterUtil.kt new file mode 100644 index 0000000..d334099 --- /dev/null +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/XpSetterUtil.kt @@ -0,0 +1,67 @@ +package xyz.alexcrea.cuanvil.util + +import io.delilaheve.CustomAnvil +import io.delilaheve.util.ConfigOptions +import org.bukkit.GameMode +import org.bukkit.entity.Player +import org.bukkit.inventory.AnvilInventory +import org.bukkit.inventory.InventoryView +import org.bukkit.inventory.InventoryView.Property.REPAIR_COST +import xyz.alexcrea.cuanvil.dependency.DependencyManager +import kotlin.math.min + +object XpSetterUtil { + + /** + * Display xp needed for the work on the anvil inventory + */ + fun setAnvilInvXp( + inventory: AnvilInventory, + view: InventoryView, + anvilCost: Int, + ignoreRules: Boolean = false + ) { + // Test repair cost limit + val finalAnvilCost = if ( + !ignoreRules && + !ConfigOptions.doRemoveCostLimit && + ConfigOptions.doCapCost) { + min(anvilCost, ConfigOptions.maxAnvilCost) + } else { + anvilCost + } + + /* Because Minecraft likes to have the final say in the repair cost displayed + * we need to wait for the event to end before overriding it, this ensures that + * we have the final say in the process. */ + CustomAnvil.instance + .server + .scheduler + .runTask(CustomAnvil.instance, Runnable { + inventory.maximumRepairCost = + if (ConfigOptions.doRemoveCostLimit || ignoreRules) + { Int.MAX_VALUE } + else + { ConfigOptions.maxAnvilCost + 1 } + + val player = view.player + + inventory.repairCost = finalAnvilCost + view.setProperty(REPAIR_COST, finalAnvilCost) + player.openInventory.setProperty(REPAIR_COST, finalAnvilCost) + + if(player is Player){ + if(player.gameMode != GameMode.CREATIVE ){ + val bypassToExpensive = (ConfigOptions.doReplaceTooExpensive) && + (finalAnvilCost >= 40) && + finalAnvilCost < inventory.maximumRepairCost + + DependencyManager.packetManager.setInstantBuild(player, bypassToExpensive) + } + + player.updateInventory() + } + }) + } + +} \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 037c1cc..cfc2abc 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -53,6 +53,7 @@ permissions: softdepend: - UnsafeEnchantsPlus - ProtocolLib + - Disenchantment - EnchantsSquared - EcoEnchants - eco \ No newline at end of file