Skip to content

Fix player-head update placed in the menu instead of the player inventory - #274

Merged
Maxlego08 merged 1 commit into
Maxlego08:developfrom
Teyir:fix/player-head-player-inventory
Oct 27, 2026
Merged

Maxlego08 merged 1 commit into
Maxlego08:developfrom
Teyir:fix/player-head-player-inventory

Conversation

@Teyir

@Teyir Teyir commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Problem

A button with both player-head and player-inventory: true shows the head without its skin in the player inventory. Once the skin has loaded, the textured head appears in the menu slot with the same number, on top of whatever button is there.

clear-inventory: true
items:
  head:
    slot: 2
    player-inventory: true
    item:
      player-head: "%player%"

Result: a default (Steve) head in hotbar slot 2, and the player's real head in slot 2 of the chest.

Cause

In InventoryDefault#displayFinalButton, the first render goes through addItem(button.isPlayerInventory(), slot, itemStack), which respects player-inventory. The asynchronous skull update, however, always writes to the menu:

this.getSpigotInventory().setItem(slot, updatedItemStack);

It also writes from the async thread, and it bypasses the anti-dupe and the inventory listeners, so with clear-inventory-type: PACKET_EVENT it could not have been routed to the player inventory either.

Fix

  • VInventory#updateItem(inPlayerInventory, slot, itemStack, enableAntiDupe) replaces the item of a button already placed. It keeps the existing ItemButton and its click handlers, applies the anti-dupe, calls the inventory listeners, then sets the item in the player inventory or in the menu. It does nothing if the inventory is closed or the button no longer exists.
  • The skull update now runs that method back on the player's thread (runAtEntity).
  • InventoryListener#updateItem (default false) lets a listener handle a late update. PacketEventPlayerInventoryManager uses it in PACKET_EVENT mode: it replaces the pending packet if the post-open flush has not run yet, and otherwise sends the packet instantly, so the real player inventory is never touched.
  • ItemButton#setDisplayItem stores the new item without placing it.

Tests

Tested in game on Paper with clear-inventory: true (DEFAULT mode) and a player-head: "%player%" button using player-inventory: true. The textured head now shows in the hotbar slot, nothing appears in the menu slot, the click still works, and the inventory is restored on close. PACKET_EVENT mode was not tested in game.

Note

updateItemMeta and handleUpdatedButton (refresh requirements and update: true) also write with getSpigotInventory().setItem(slot, …), so they probably have the same issue for player-inventory buttons. They are left out of this PR to keep it focused; updateItem could be reused there.

🤖 Generated with Claude Code

…ry buttons

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Maxlego08
Maxlego08 merged commit ae989bd into Maxlego08:develop Oct 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants