[SPIGOT-6716] Comparisons of enchanted book items sometimes fail due to the non-deterministic reordering of their stored enchantments Created: 19/Aug/21 Updated: 20/Aug/21 Resolved: 20/Aug/21 |
|
Status: | Resolved |
Project: | Spigot |
Component/s: | None |
Affects Version/s: | None |
Fix Version/s: | None |
Type: | Bug | Priority: | Minor |
Reporter: | blablubbabc | Assignee: | Unassigned |
Resolution: | Fixed | Votes: | 0 |
Labels: | comparison, enchantments, itemstack |
Version: | This server is running CraftBukkit version dev-Spigot-b166a49-18027d0 (MC: 1.17.1) (Implementing API version 1.17.1-R0.1-SNAPSHOT) |
Guidelines Read: | Yes |
Description |
There are cases in which CraftMetaEnchantedBook uses a non-ordered HashMap to store the enchantments of an enchanted book. Any NMS enchanted book ItemStack that has its stored enchantments in an order that does not match this non-deterministic order of the HashMap is prone to cause item comparison issues (CraftItemStack#equals, CraftItemStack#isSimilar) once the item stack is converted to a Bukkit ItemStack and then back to a NMS / Craft ItemStack. Example reproduction plugin: package de.blablubbabc.test2; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; import org.bukkit.plugin.Plugin;import net.minecraft.nbt.NBTTagCompound; public class TestItemStackSerialization2 implements Listener { private Plugin plugin; public TestItemStackSerialization2(Plugin plugin) { this.plugin = plugin; Bukkit.getPluginManager().registerEvents(this, plugin); } @EventHandler public void onInteract(PlayerInteractEvent event) { if (event.getAction() != Action.RIGHT_CLICK_AIR) return; if (event.getHand() != EquipmentSlot.HAND) return; ItemStack item = event.getItem(); if (item == null || item.getType() == Material.AIR) return; if (item.getType() != Material.ENCHANTED_BOOK) return; Player player = event.getPlayer(); PlayerInventory inventory = player.getInventory(); plugin.getLogger().info("Item in hand: " + getItemSNBT(item)); ItemStack bukkitCopy = new ItemStack(item.getType(), item.getAmount()); bukkitCopy.setItemMeta(item.getItemMeta()); plugin.getLogger().info("Bukkit copy: " + getItemSNBT(bukkitCopy)); inventory.setItemInOffHand(bukkitCopy); ItemStack offhand = inventory.getItemInOffHand(); plugin.getLogger().info("Bukkit copy in off hand: " + getItemSNBT(offhand)); plugin.getLogger().info("Similar to offhand item? " + item.isSimilar(offhand)); } private static String getItemSNBT(ItemStack itemStack) { if (itemStack == null) return null; net.minecraft.world.item.ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); NBTTagCompound itemNBT = nmsItem.save(new NBTTagCompound()); return itemNBT.toString(); } } Create an enchanted book item in-game with a particular enchantment order: give @s minecraft:enchanted_book{StoredEnchantments:[{lvl:4s,id:"minecraft:bane_of_arthropods"},{lvl:3s,id:"minecraft:piercing"},{lvl:3s,id:"minecraft:power"}]} 1 Hold the item in your hand and right-click. [18:12:56] [Server thread/INFO]: [@: Gave 1 [Enchanted Book] to blablubbabc] [18:12:58] [Server thread/INFO]: [TestPlugin] Item in hand: {Count:1b,id:"minecraft:enchanted_book",tag:{StoredEnchantments:[{id:"minecraft:bane_of_arthropods",lvl:4s},{id:"minecraft:piercing",lvl:3s},{id:"minecraft:power",lvl:3s}]}} [18:12:58] [Server thread/INFO]: [TestPlugin] Bukkit copy: {Count:1b,id:"minecraft:enchanted_book",tag:{StoredEnchantments:[{id:"minecraft:bane_of_arthropods",lvl:4s},{id:"minecraft:power",lvl:3s},{id:"minecraft:piercing",lvl:3s}]}} [18:12:58] [Server thread/INFO]: [TestPlugin] Bukkit copy in off hand: {Count:1b,id:"minecraft:enchanted_book",tag:{StoredEnchantments:[{id:"minecraft:bane_of_arthropods",lvl:4s},{id:"minecraft:power",lvl:3s},{id:"minecraft:piercing",lvl:3s}]}} [18:12:58] [Server thread/INFO]: [TestPlugin] Similar to offhand item? false This conversion between Craft and Bukkit ItemStack can also occur in other situations, implicitly. Once converted to a Bukkit ItemStack, the enchantments are reordered. When converted back to a Minecraft ItemStack, this new enchantment order is preserved. Once two CraftItemStacks with differently ordered stored enchantments are compared, they are considered non-equal. |