From 63f2f16b9fc6bf2d6a7ef309a6ec922527b0bfb0 Mon Sep 17 00:00:00 2001 From: alexcrea Date: Mon, 2 Mar 2026 19:03:33 +0100 Subject: [PATCH] add anvil simulation test for diag --- .../cuanvil/command/CustomAnvilCmd.kt | 7 +- .../cuanvil/command/DiagnosticExecutor.kt | 159 ++++++++++++++---- .../cuanvil/listener/PrepareAnvilListener.kt | 8 + src/main/resources/plugin.yml | 2 + 4 files changed, 142 insertions(+), 34 deletions(-) diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/command/CustomAnvilCmd.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/command/CustomAnvilCmd.kt index 421ec7b..2a46fa8 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/command/CustomAnvilCmd.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/command/CustomAnvilCmd.kt @@ -7,7 +7,6 @@ import org.bukkit.command.CommandExecutor import org.bukkit.command.CommandSender import org.bukkit.command.TabCompleter import java.util.ArrayList -import java.util.Arrays class CustomAnvilCmd(plugin: CustomAnvil) : CommandExecutor, TabCompleter { @@ -61,12 +60,12 @@ class CustomAnvilCmd(plugin: CustomAnvil) : CommandExecutor, TabCompleter { args: Array ): MutableList { val result = ArrayList() - if(args.size < 3) { - for (cmd in commands) { + if(args.size < 2) { + for (cmd in commands) { result.add(cmd.key) } } else { - val subcmd = commands[args[1].lowercase()] + val subcmd = commands[args[0].lowercase()] if(subcmd != null) { val newArgs = args.copyOfRange(1, args.size) diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/command/DiagnosticExecutor.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/command/DiagnosticExecutor.kt index 23ab4b9..e30b6fc 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/command/DiagnosticExecutor.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/command/DiagnosticExecutor.kt @@ -1,5 +1,6 @@ package xyz.alexcrea.cuanvil.command +import com.github.stefvanschie.inventoryframework.inventoryview.interface_.InventoryViewUtil import io.delilaheve.CustomAnvil import net.md_5.bungee.api.chat.ClickEvent import net.md_5.bungee.api.chat.HoverEvent @@ -7,16 +8,27 @@ import net.md_5.bungee.api.chat.TextComponent import net.md_5.bungee.api.chat.hover.content.Text import org.bukkit.Bukkit import org.bukkit.ChatColor +import org.bukkit.Material import org.bukkit.command.Command import org.bukkit.command.CommandSender +import org.bukkit.enchantments.Enchantment import org.bukkit.entity.HumanEntity +import org.bukkit.entity.Player +import org.bukkit.event.inventory.InventoryType import org.bukkit.event.inventory.PrepareAnvilEvent +import org.bukkit.inventory.AnvilInventory +import org.bukkit.inventory.InventoryView +import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.meta.Damageable +import org.bukkit.inventory.meta.EnchantmentStorageMeta import org.bukkit.plugin.Plugin +import org.bukkit.plugin.PluginManager import org.bukkit.plugin.RegisteredListener import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.packet.NoPacketManager import xyz.alexcrea.cuanvil.dependency.packet.ProtocoLibWrapper import xyz.alexcrea.cuanvil.dependency.packet.versions.PaperPacketManager +import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener import java.util.* import java.util.stream.Collectors @@ -30,7 +42,8 @@ class DiagnosticExecutor: CASubCommand() { enum class DiagParams(val value: String) { OS_PRIVACY("os_privacy"), PLUGIN_PRIVACY("plugin_privacy"), - //NO_TEST("no_anvil_test"), + NO_MERGE_TEST("no_merge_test"); + // TODO enchant list } private fun fetchParameters(args: Array): EnumSet { @@ -76,17 +89,22 @@ class DiagnosticExecutor: CASubCommand() { val stb = StringBuilder("```\n") val params = fetchParameters(args) + var hasError = false try { - diagnostic(stb, params) - } catch(e: Exception){ + diagnostic(sender, stb, params) + } catch(e: Throwable){ stb.append("\n\nError happened trying to get diagnostic data:\n") .append(e.message).append("\n") .append(e.stackTrace.joinToString("\n")) + hasError = true + e.printStackTrace() } stb.append("\n```") if (sender is HumanEntity) { + if(hasError) + sender.spigot().sendMessage(TextComponent(ChatColor.RED.toString() + "There was an error running the diagnostic")) val message = TextComponent(ChatColor.GREEN.toString() + "Click to copy diagnostic data") message.clickEvent = ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, stb.toString()) @@ -104,42 +122,71 @@ class DiagnosticExecutor: CASubCommand() { return sender.hasPermission(CustomAnvil.diagnosticPermission) } - fun diagnostic(stb: StringBuilder, params: Set){ + fun diagnostic(sender: CommandSender, stb: StringBuilder, params: Set){ stb.append("Server Info\n") - stb.append("Plugin Version: ").append(CustomAnvil.instance.description.version).append("\n") - stb.append("Latest Update: ").append(CustomAnvil.latestVer).append('\n') - stb.append("Server Version: ").append(Bukkit.getVersion()).append(" (").append(Bukkit.getName()).append(')').append("\n") - stb.append("Plugin Enabled: ").append(if(CustomAnvil.instance.isEnabled) "Yes" else "No").append("\n") - stb.append("NMS type: ").append(fetchNMSType()) + stb.append("\nPlugin Version: ").append(CustomAnvil.instance.description.version) + stb.append("\nLatest Update: ").append(CustomAnvil.latestVer) + stb.append("\nServer Version: ").append(Bukkit.getVersion()).append(" (").append(Bukkit.getName()).append(')') + stb.append("\nPlugin Enabled: ").append(if(CustomAnvil.instance.isEnabled) "Yes" else "No") + stb.append("\nNMS type: ").append(fetchNMSType()) if(!params.contains(DiagParams.OS_PRIVACY)) { - stb.append("Java Version: ").append(System.getProperty("java.version")).append("\n") - stb.append("OS: ").append(System.getProperty("os.name")).append(" ") + stb.append("\nJava Version: ").append(System.getProperty("java.version")) + stb.append("\nOS: ").append(System.getProperty("os.name")).append(" ") .append(System.getProperty("os.version")) .append(System.getProperty("os.arch")) - .append("\n\n") } if(!params.contains(DiagParams.PLUGIN_PRIVACY)) { - pluginListDiag(stb) + pluginListDiag(sender, stb) } prepareAnvilListeners(stb) - stb.append("\n\n") + if(!params.contains(DiagParams.NO_MERGE_TEST)){ + if(sender is Player) { + testMerge(sender, stb) + } + } + } + + private fun testMerge(player: Player, stb: StringBuilder) { + val sword = ItemStack(Material.DIAMOND_SWORD) + val damagedSword = sword.clone() + val enchantedSword = sword.clone() + val enchantedBook = ItemStack(Material.ENCHANTED_BOOK) + val unitForRepair = ItemStack(Material.DIAMOND) + + var meta = damagedSword.itemMeta + (meta as Damageable).damage = 5 + damagedSword.itemMeta = meta + + meta = enchantedSword.itemMeta + meta!!.addEnchant(Enchantment.DAMAGE_ALL, 1, true) + enchantedSword.itemMeta = meta + + meta = enchantedBook.itemMeta + (meta as EnchantmentStorageMeta).addStoredEnchant(Enchantment.DAMAGE_ALL, 1, true) + enchantedBook.itemMeta = meta + + stb.append("\n\nItem to Item repair:") + simulateAnvil(player, stb, damagedSword, damagedSword, sword) + + stb.append("\n\nUnit repair:") + simulateAnvil(player, stb, damagedSword, unitForRepair, sword) + + stb.append("\n\nEnchanting an item:") + simulateAnvil(player, stb, sword, enchantedBook, enchantedSword) } private fun fetchNMSType(): String { val packetManager = DependencyManager.packetManager val packetManagerClass = packetManager.javaClass - val result: String - if(packetManagerClass == PaperPacketManager::class.java) { - result = "Paper NMS" - } else if(packetManagerClass == ProtocoLibWrapper::class.java) { - result = "Protocolib" - } else if(packetManagerClass == NoPacketManager::class.java) { - result = "None" - } else { - result = "Version Specific" + val result = when (packetManagerClass) { + PaperPacketManager::class.java -> "Paper NMS" + ProtocoLibWrapper::class.java -> "Protocolib" + NoPacketManager::class.java -> "None" + else -> "Version Specific" + } return "$result ${if(packetManager.canSetInstantBuild) '✅' else '❌'}" @@ -150,7 +197,7 @@ class DiagnosticExecutor: CASubCommand() { return this.name + " v" + this.description.version } - private fun pluginListDiag(stb: StringBuilder) { + private fun pluginListDiag(sender: CommandSender, stb: StringBuilder) { val enabledPlugins: MutableList = ArrayList() val disabledPlugins: MutableList = ArrayList() for (plugin in Bukkit.getPluginManager().plugins) { @@ -161,17 +208,17 @@ class DiagnosticExecutor: CASubCommand() { } } - stb.append("Enabled Plugins: ").append( + stb.append("\nEnabled Plugins: ").append( enabledPlugins.stream() .map { plugin -> plugin!!.pluginNameDisplay } .reduce { a: String?, b: String? -> "$a, $b" }.orElse("None") - ).append("\n") + ) - stb.append("Disabled Plugins: ").append( + stb.append("\nDisabled Plugins: ").append( disabledPlugins.stream() .map { plugin -> plugin!!.pluginNameDisplay } .reduce { a: String?, b: String? -> "$a, $b" }.orElse("None") - ).append("\n") + ) } fun prepareAnvilListeners(stb: StringBuilder) { @@ -185,11 +232,63 @@ class DiagnosticExecutor: CASubCommand() { .collect(Collectors.toSet()) eventListeners.remove(CustomAnvil.instance) - stb.append("Prepare Anvil Listeners: ").append( + stb.append("\nPrepare Anvil Listeners: ").append( if (eventListeners.isEmpty()) "None" else eventListeners.stream() .map { plugin -> plugin!!.pluginNameDisplay } .reduce { a: String?, b: String? -> "$a, $b" }.orElse("None") - ).append("\n\n") + ) + } + + fun simulateAnvil(player: Player, stb: StringBuilder, left: ItemStack?, right: ItemStack?, result: ItemStack?) { + var invView: InventoryView + var event: PrepareAnvilEvent + try { + val fakeInv = Bukkit.createInventory(player, InventoryType.ANVIL) + invView = player.openInventory(fakeInv)!! + event = PrepareAnvilEvent(invView, result) + } catch (e: Throwable) { + // Help + val menuTypeClazz = Class.forName("org.bukkit.inventory.MenuType") + val anvilTypeField = menuTypeClazz.getField("ANVIL") + val anvilType = anvilTypeField.get(null) + val createMethod = anvilType.javaClass.getMethod("create", HumanEntity::class.java) + invView = createMethod.invoke(anvilType, player) as InventoryView + + player.openInventory(invView) + + val anvilViewClass = Class.forName("org.bukkit.inventory.view.AnvilView") + val constructor = PrepareAnvilEvent::class.java.getConstructor(anvilViewClass, ItemStack::class.java) + event = constructor.newInstance(invView, result) + } + + val fakeInv = InventoryViewUtil.getInstance().getTopInventory(invView) as AnvilInventory + fakeInv.setItem(0, left) + fakeInv.setItem(1, right) + + val xp = fakeInv.repairCost + val maxXp = fakeInv.maximumRepairCost + val mergeResult = fakeInv.getItem(2) + stb.append("\n${if(result == mergeResult) "E" else "Une"}xpected Result") + + PrepareAnvilListener().anvilCombineCheck(event) + // Now we check if item and xp same + stb.append("\nXP/Max XP: ") + .append(if(fakeInv.repairCost == xp) "Correct" else "Incorrect") + .append("/") + .append(if(fakeInv.maximumRepairCost == maxXp) "Correct" else "Incorrect") + .append(" (${fakeInv.repairCost} $xp|${fakeInv.maximumRepairCost} $maxXp)") + .append("\nMerge result: ") + .append(if(fakeInv.getItem(2) == mergeResult) "Correct" else "Incorrect") + + PrepareAnvilListener.IS_EMPTY_TEST = true + Bukkit.getPluginManager().callEvent(event) + stb.append("\nNull result test: ") + .append(if(event.result == null) "Correct" else "Incorrect") + + fakeInv.setItem(0, null) + fakeInv.setItem(1, null) + fakeInv.setItem(2, null) + player.closeInventory() } } \ No newline at end of file diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt index ebf16ed..8d55f3c 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt @@ -38,6 +38,8 @@ class PrepareAnvilListener : Listener { const val ANVIL_INPUT_LEFT = 0 const val ANVIL_INPUT_RIGHT = 1 const val ANVIL_OUTPUT_SLOT = 2 + + var IS_EMPTY_TEST = false } private fun ItemStack?.isAir(): Boolean { @@ -63,6 +65,12 @@ class PrepareAnvilListener : Listener { val first = inventory.getItem(ANVIL_INPUT_LEFT) ?: return val second = inventory.getItem(ANVIL_INPUT_RIGHT) + if(IS_EMPTY_TEST) { + event.result = null + IS_EMPTY_TEST = false + return + } + if (isImmutable(first) || isImmutable(second)) { CustomAnvil.verboseLog("Skipping anvil process as one of the two item is immutable") diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 03ed5aa..5807f8d 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -12,6 +12,8 @@ libraries: [${libraries}] commands: customanvil: description: Generic command for custom anvil + aliases: + - ca anvilconfigreload: description: Reload every config of this plugin permission: ca.command.reload