External inventory changes (e.g. by picking up items) are not registered on the client if you are in creative mode inventory and are not in the Survival Inventory tab, causing a desync.
Steps to reproduce:
Fill your hotbar with items.
Go into the creative mode inventory and go to any tab, but the inventory tab (e.g. the search tab).
While in the tab, take any other item and throw it onto the ground.
Wait until you pick it up and observe whether or not the item appears in the inventory.
Expected behavior:
The item is picked up and should show up in the inventory.
Actual behavior:
The item does not appear in the inventory. However, if you try to remove that particular item, e.g. using /clear @s <item>, the server will report that the item indeed was in the inventory.
Code analysis:
net.minecraft.client.multiplayer.ClientPacketListener
public void handleContainerSetSlot(ClientboundContainerSetSlotPacket packet) {
// ...
boolean conditional = false; // Line 1200
if (minecraft.screen instanceof CreativeModeInventoryScreen x) { // Line 1202
conditional = !x.isInventoryOpen(); // Line 1203
}
// 0 means player inventory
if (packet.getContainerId() == 0 && InventoryMenu.isHotbarSlot(slotId)) {
// ...
player.inventoryMenu.setItem(slotId, packet.getStateId(), item);
} else if (packet.getContainerId() == player.containerMenu.containerId && (packet.getContainerId() != 0 || !conditional)) {
player.containerMenu.setItem(slotId, packet.getStateId(), item);
}
}
This method is responsible for all of the single slot synchronization on the client side.
Because of it, inventory changes are almost completely ignored if they occur in a non-hotbar slot (first condition) and you are in the creative mode inventory (Line 1202), but not in the "Survival Inventory" tab (Line 1203). (why?)
Potential fix:
Just remove those weird conditions. Why do they even exist in the first place?
public void handleContainerSetSlot(ClientboundContainerSetSlotPacket packet) {
// ...
if (packet.getContainerId() == 0) {
// ...
player.inventoryMenu.setItem(slotId, packet.getStateId(), item);
} else if (packet.getContainerId() == player.containerMenu.containerId) {
player.containerMenu.setItem(slotId, packet.getStateId(), item);
}
}
Also, because this is a synchronization step, the client should be able to be confident about that particular remote slot, shouldn't it? So please also consider changing this:
net.minecraft.world.inventory.AbstractContainerMenu
public void setItem(int slotId, int stateId, ItemStack item) {
getSlot(slotId).set(item); // Line 593
setRemoteSlot(slotId, item); // New line
this.stateId = stateId; // Line 594
}
That is the setItem method from above, by the way.
Linked issues
relates to 2
Attachments
Comments 3
I just ran into this, too, on 1.21.1.
If instead of clearing, you switch to survival and click on the slot where the picked up item should be, it de-ghosts and you pick it up.
Can confirm in 1.21: (I dropped wool)
[media][media]