Menu Input Slots
A menu normally owns every one of its slots: the click listener cancels anything it did not explicitly allow, so a player can never take an icon out or drop an item in. Input slots are the documented exception — a group of slots left to vanilla, where the player may deposit, take, swap and drag their own items. The blueprint only decides what may go in (requirements) and reacts to what changed (add-actions/remove-actions).
This is the building block for deposit boxes, repair/upgrade stations, trade-in counters, and any menu that has to receive an item rather than merely display one.
Declaring input slots
Add an input-slots: map to the menu blueprint. Each key is the input's id; the slots it lists become player-owned for the lifetime of the menu.
Field | Type | Description |
|---|---|---|
| int (placeholder) | A single input slot. Mutually exclusive with |
|
| Several input slots. Takes precedence over |
|
| Optional. An item the slots start out holding — see Starting item. |
|
| Optional. Evaluated before an item is allowed in. Absent or empty means the input takes anything. |
|
| Optional. Run after items were added to the slot. |
|
| Optional. Run after items were taken out of the slot. |
| boolean | Whether items left in the slots are handed back when the menu goes away. Default |
Slots are resolved from a placeholder wrapper, so slots: "%parameters_slots%" works the same way it does on a menu item.
Placeholders inside requirements and actions
Both the requirement list and the action lists run against the menu's own placeholder container, plus two extras scoped to the change being reported:
Placeholder | Resolves to |
|---|---|
| The |
| The raw slot index the change concerns. |
Note that %item% in an add-actions/remove-actions list is the difference, not the slot's whole content: dropping 16 more of a stack the slot already holds reports an addition of 16.
How changes are detected
Nothing about an input slot is driven per click type. An accepted click is simply left to vanilla and the result is read afterwards — one diff covers placing, taking, swapping, dropping and dragging:
The click (or drag) is vetted and, if allowed, applied by vanilla as usual.
A single reconcile pass is queued for the next tick — vanilla only applies the move after the event returns, so the outcome cannot be read any earlier.
Each input slot's current content is compared against the baseline the previous pass recorded, and the change is reported.
Reporting rules:
Change | Reported as |
|---|---|
Slot was empty, now holds an item |
|
Slot held an item, now empty |
|
Same item, larger amount |
|
Same item, smaller amount |
|
Item swapped for a different one |
|
"Same item" is a strict isSimilar comparison, so a genuinely different item is never mistaken for an amount change.
Baselines are updated before the actions run, and an action that throws is logged without costing the remaining slots their diff.
What is refused
Clicks the menu turns down are cancelled and the window is resent, so the client's optimistic prediction is corrected:
Double click and any
COLLECT_TO_CURSORaction — they reach past the clicked slot and would sweep the menu's own icons onto the cursor.Middle click and creative click — cloning.
Any click bringing in an item the input's
requirementsreject. The incoming item is the cursor, the hotbar item for a number-key swap, or the off-hand item for a swap-offhand; a shift click out of an input slot brings nothing in and is never vetted this way.A drag that touches any menu slot which is not an input slot, or where any input slot it reaches refuses the dragged item. A drag confined to the player's own inventory is left alone.
A requirement that throws refuses the item — the check fails closed, because letting the exception escape would leave the move to be applied by vanilla with nothing having vetted it.
Shift-clicking from the player inventory
A vanilla shift click would scatter the stack over every free slot of the menu, the gaps between icons included. The menu cancels it and performs the move itself, during the click, into the input slots only: matching stacks are topped up first, then empty input slots are filled, and whatever does not fit stays in the player's inventory. Slots whose requirements refuse the stack are skipped.
This does not apply when use-player-inventory is on — the bottom inventory is then a packet overlay and the stack the player sees is not the one they would be moving.
Starting item
item seeds the input's slots with an item when the menu is computed. It is a starting point, not a deposit: the add actions do not run for it, but taking it back out reports as a removal like any other.
The typical use is a repair or upgrade station that opens on an item the player already owns — seed it with copy-from so the slot shows the item the menu was opened for:
An input that merely mirrors something the player already holds wants return-on-close: false, otherwise closing the menu hands them a second copy. Seeding the same item into several slots with return-on-close: true logs a warning for the same reason: the player walks away with one copy per slot.
return-on-close
By default whatever the player left in the input slots is handed back when the menu goes away — those items are theirs and the inventory is about to be discarded. Items that do not fit are dropped at the player's feet, and a player who logged out in the meantime gets them dropped in the world.
Each returned item is reported through remove-actions, because giving it back undoes the deposit. Set return-on-close: false when the add actions already took ownership of what is dropped in — a deposit that credits the player would otherwise pay out and refund the item.
The close diff is settled synchronously rather than waiting for the queued reconcile: a click applied in the same tick the menu closes would otherwise never be reported.
Interaction with the rest of the menu
Nothing renders into an input slot. Items and layouts skip them entirely; a slot the player owns is never overwritten, cleared or refreshed by a render pass, and no
MenuItembecomes clickable there.A layout never wastes entries on them. Input slots are resolved before layouts, so an item carrying a layout taint that lands on an input slot does not contribute a slot to that layout.
use-player-inventoryand input slots do not mix. The player-inventory region is a packet overlay whose slots only exist client-side and can never hold a real item; declaring both logs a warning and the input stays confined to the menu inventory.
Warnings and validation
These are logged and skipped rather than failing the menu:
Condition | Behaviour |
|---|---|
Slot index below | The slot is ignored. |
Two inputs declaring the same slot | The first one to claim it keeps it. |
| Warning; the player inventory cannot take input. |
One | Warning; the player gets one copy back per slot. |
A failure to resolve the slots placeholder, or to build the starting item, aborts the compute and the menu does not open.