-
Type:
Bug
-
Resolution: Fixed
-
Priority:
Minor
-
None
-
Affects Version/s: None
-
This server is running CraftBukkit version dev-Spigot-b166a49-18027d0 (MC: 1.17.1) (Implementing API version 1.17.1-R0.1-SNAPSHOT)
-
Yes
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.
Output:
[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.