Invsee & Ender Chest
/invsee and /ecview both open an InventoryMirror — a Bukkit inventory that is purely a display: it is rendered from the target's live inventory and never treated as authoritative. Every viewer interaction with it is cancelled by default and re-applied by hand directly to the live source. Because a second, independently-grabbable copy of an item is never created, item duplication or deletion through the mirror is structurally impossible rather than merely guarded against.
Two implementations share the InventoryMirror interface:
Implementation | Backs | Size | Insertion slots |
|---|---|---|---|
|
| 54-slot display window; only the first 41 slots (hotbar + storage + armor + off-hand) are usable/mapped to the source. | Slots 0–35 (storage + hotbar) only — armor and off-hand are excluded from shift-click insertion. |
|
| Matches the target's actual ender chest size (27 slots unless upgraded via the vanilla ender chest expansion mechanic). | Every slot. |
Session lifecycle
InventoryService tracks one active mirror session per viewer and a target→viewers multimap. Opening a second mirror on the same viewer replaces their previous session. Closing the mirror inventory (InventoryCloseEvent) or the viewer quitting ends their session; the target quitting force-closes every viewer currently looking at their inventory.
Whenever the target's live inventory changes outside the mirror — normal gameplay (PlayerInventorySlotChangeEvent) or their own ender chest interaction — every open mirror on that target is scheduled to re-render on the next tick (coalesced: multiple changes within the same tick produce one refresh). This is purely cosmetic; because every click reads the live source at interaction time, a stale display can never itself cause a dupe.
Click semantics (top inventory = the mirror)
Every click landing outside usableSize() (i.e. filler/padding slots) is inert. For a click inside the usable range:
Click | Behavior |
|---|---|
| Standard vanilla-style pickup/place/stack/swap against the cursor, applied to the source slot. |
| Standard vanilla-style half-stack take / single-item place / single-item add, applied to the source slot. |
| Moves the source item into the viewer's own inventory ( |
| Swaps the source slot's item with the viewer's hotbar slot at the pressed number. |
| Drops one item from the source slot at the target's location (not the viewer's). |
| Drops the entire source stack at the target's location. |
| Always denied — vanilla's collect-to-cursor would scan both inventories uncontrollably. |
Anything else (middle-click clone, | Denied as a no-op. |
After any handled top-inventory click, the mirror is re-rendered and viewer.updateInventory() is called to keep the client's view consistent with the (now-changed) source.
Click semantics (bottom inventory = the viewer's own)
Only shift-clicks reach into the source; every other click stays inside the viewer's own inventory and is left to vanilla. A shift-click first tries to merge the clicked item onto existing similar stacks across the mirror's insertion slots, then fills any empty insertion slot, and writes back whatever didn't fit to the viewer's clicked slot (or clears it if everything was absorbed).
Drags
Any drag (InventoryDragEvent) that touches even one raw slot inside the mirror's top inventory is denied outright — the whole drag is cancelled, not partially applied. Drags confined entirely to the viewer's own (bottom) inventory are left to vanilla.
Self-view guard
/invsee and /ecview both refuse (cannot-view-self) if the sender targets themselves before a session is even started — the reason given in source is that a viewer editing their own live inventory through the mirror would alias the mirror's source with the viewer's own inventory, breaking the item-conservation guarantee the whole design relies on.