mojira.dev
MC-299373

Server-client desync in tick order execution

It is possible to create a server-client desynced item via advancement reward- & tick function execution due to a difference in tick execution order between server and client.
Recreating this takes 4 things:

  • Any consumable item.

  • An advancement triggered via minecraft:consume_item

  • A reward function “storing“ the consumed item

  • A tick function “restoring“ the stored item to its original slot

The item can be generated using /give @s minecraft:stick[minecraft:use_remainder={"id":"minecraft:feather"},minecraft:consumable={"consume_seconds":0.5}]. While the use_remainder is not necessary, it does make the specifics of the bug more visible and easier to verify.

The advancement is simply:

{
  "criteria": {
    "use_item": {
      "trigger": "minecraft:consume_item"
    }
  },
  "rewards": {
    "function": "namespace:store_item"
  }
}

with its reward function putting a copy of the item into the players offhand:

item replace entity @s weapon.offhand from entity @s weapon.mainhand

Lastly a tick function “restoring” the item for all players with the advancement and revoking it after so we don’t get stuck in a loop:

execute as @a[advancements={namespace:consumed_item=true}] run item replace entity @s weapon.mainhand from entity @s weapon.offhand
execute as @a[advancements={namespace:consumed_item=true}] run advancement revoke @s only namespace:consumed_item

With all this in place, consuming the item in the mainhand now puts a desynced item back into the players hand. It appears as the original items use remainder but querying it’s data using /data get entity @s SelectedItem reveals that it’s actually the original item itself. In this example the client thinks the player has a feather in their hand while the server knows that it’s actually a stick. This discrepancy is resolved the moment the player moves the item or drops it out of their inventory.

Interestingly this desync does not occur when the original items consume_seconds are 0.0. In such a case both the client and server know that the held item afterwards is a stick.

The expectation of how this would work after a fix is that both the server and client agree on the held item after all functions have run their course, specifically agree that the now “restored“ item in this example is a stick, the way it currently works when `{"consume_seconds":0.0}`.

Linked issues

Comments 0

No comments.

Syrenyx

(Unassigned)

Plausible

Platform

Normal

Inventory, Networking

1.21.7

Retrieved