mojira.dev
MC-312234

Item component tooltip_display is incompatible with EncoderCache and HashGenerator

The tooltip_display item component stores the hidden components in a ReferenceLinkedOpenHashSetwhich provides value equality for all collections which contain the same set of items regardless of their order. However, the HashGeneratorprespects the order of items and computes unique hashes for them.

For example, a EncoderCachetreats the [unbreakable, lore] and [lore, unbreakable] as the exact same TypedDataComponentand serves the same encoder output. However, the HashGeneratorgives the [unbreakable, lore] a hash of 94075786 while the [lore, unbreakable] gets a hash of -50756321. This means that the HashedPatchMapcan incorrectly refuse a ItemStack given the mismatched sate.

The EncoderCache holds a limited set of values in a collection of SoftReference’s and the output can vary depending on multiple factors. On the other end, the server holds a maximum of 256 items in the ContainerSynchronizerwhich is per player. When one of these goes out of sync, the server consistently refuses matching ItemStack’s.

This can be reproduced in the singleplayer fairly easily with the help of debugger:

DO NOT DISCONNECT BETWEEN THE STEPS GIVEN!

Give yourself two unique set of axes:

/give @p diamond_axe[tooltip_display={hidden_components:[unbreakable,lore]}]

/give @p golden_axe[tooltip_display={hidden_components:[lore,unbreakable]}]

Set a breakpoint in HashedPatchMap on the line if (!actualHash.equals(expectedHash))inside the matcheswith condition of typedDataComponent.type() == DataComponents.TOOLTIP_DISPLAYand log output of actualHash + " | " + expectedHash.

Click both of the items, you should see consistent output of 94075786 | 94075786 when the diamond axe was given first. Otherwise you would see -50756321 | -50756321.

Clear the EncoderCache manually by executing DataComponents.ENCODER_CACHE.cache.invalidateAll(). The client can do this at any moment by itself, we are just forcing it here for demonstration purposes!

Now click on the golden axe first, if you did not, you need to clear the cache again. Afterwards you can click on either of the items freely which should now give consistent output of 94075786 | -50756321, meaning the hashes don’t match and the ItemStackis refused. This won’t get fixed on resynchronization and the server will keep refusing the ItemStackuntil both the client’s and the server’s cache match again.

Comments 1

Going to add that this will also poison all other item components that hold said items. You can also reproduce this with shulker boxes too.

Give yourself two identical shulker boxes that only differ in the tool_display component:
/give @p minecraft:shulker_box[container=[{slot:0,item:{id:"minecraft:diamond_axe",components:{"minecraft:tooltip_display":{"hidden_components":[unbreakable,lore]}}}}]]


/give @p minecraft:shulker_box[container=[{slot:0,item:{id:"minecraft:diamond_axe",components:{"minecraft:tooltip_display":{"hidden_components":[lore,unbreakable]}}}}]]

Now perform the same steps mentioned in OP.
You will notice that the first shulker box's hash is -1241602278 and the second one is 989085267.

isokissa3

(Unassigned)

Plausible

26.3, 26.4 Snapshot 2

Retrieved