-
Bug
-
Resolution: Fixed
-
Minor
-
None
-
None
-
None
-
This server is running CraftBukkit version git-Spigot-e5b1b5d-1ec1b05 (MC: 1.14.4) (Implementing API version 1.14.4-R0.1-SNAPSHOT)
-
Yes
Attribute modifiers in items are stored as a list inside the item's nbt tag. As such, the order of the attribute modifiers influences whether two items are considered equal (see CraftItemStack#isSimilar, which compares the nbt tags of the involved items).
Bukkit represents those attribute modifiers as a HashMultimap inside CraftMetaItem, which does not persist this order / produces an arbitrary order. After a nms ItemStack gets converted to a bukkit ItemStack and back to an nms ItemStack (such as when dragging an itemstack around inside an inventory, or during de/serialization), the order of the attribute modifiers can end up different, resulting in the items no longer being considered as equal/similar when their corresponding CraftItemStacks are compared.
This only affects items created via minecraft's commands (are there other sources in vanilla minecraft for items with attributes?).
Example command:
/give @p minecraft:diamond_sword{AttributeModifiers:[{UUIDMost: 2872L, UUIDLeast: 894654L, Amount: 20.0d, Slot: "mainhand", AttributeName: "generic.attackDamage", Operation: 0, Name: "generic.attackDamage"}, {UUIDMost: 2L, UUIDLeast: 3L, Amount: 10.0d, Slot: "mainhand", AttributeName: "generic.attackSpeed", Operation: 0, Name: "generic.attackSpeed2"}, {UUIDMost: 1L, UUIDLeast: 2L, Amount: 20.0d, Slot: "mainhand", AttributeName: "generic.attackSpeed", Operation: 0, Name: "generic.attackSpeed"}]} 1
Minecraft will store the attribute modifiers in the order they are given in the command. On my system the HashMultimap used inside CraftMetaItem will reorder these attribute modifiers, as can be seen in this output: http://prntscr.com/ostxz4
The plugin code for creating this output can be found here: https://pastebin.com/yPkWK4CD
I basically created two items via a command block with this command, and then dragged one of the items around in the inventory, causing it to get converted to its bukkit representation and back, resulting in its attribute modifiers getting reordered. Both items are then placed inside a chest and then compared via the above plugin code.
Similarly to how enchantments use a LinkedHashMap to persist the enchantment order, a solution might be to use a LinkedListMultimap instead of the HashMultimap.
A LinkedHashMultimap might work as well, but has some subtle differences regarding the ordering of the map's keys (though, only the ordering of the map's entries might be relevant, since those represent the attribute modifiers). However, I don't know the individual performance/memory characteristics of these two implementations to prefer/recommend one over the other. LinkedListMultimap seems to produce a more consistent ordering between entries and keys.
One related sidenote: Minecraft itself seems to use a HashMultimap for representing the item attribute modifiers under some circumstances. If the item's attribute modifiers would get reordered due to this under certain circumstances, this could be considered a vanilla bug. But so far I can only see this to affect the order in which these attribute modifiers get applied to entities. But entities seem to store their attributes in an arbitrary order anyways (see AttributeMapServer/Base). This on the other hand might affect the order in which the attributes are saved inside the entity data. I am not sure right now if this affects anything related to the bukkit API though.