mojira.dev
MC-268431

External inventory changes except for the hotbar are not registered in a creative mode item selection screen

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:

  1. Fill your hotbar with items.

  2. Go into the creative mode inventory and go to any tab, but the inventory tab (e.g. the search tab).

  3. While in the tab, take any other item and throw it onto the ground.

  4. 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

Attachments

Comments 3

Can confirm in 1.21: (I dropped wool)

[media][media]

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.

Rob23

(Unassigned)

Confirmed

Platform

Normal

Inventory, Networking

1.20.4, 24w06a, 1.21, 24w33a

24w38a

Retrieved