Astral Realms Documentation Help

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

PlayerInventoryMirror

/invsee

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.

EnderChestMirror

/ecview

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

LEFT

Standard vanilla-style pickup/place/stack/swap against the cursor, applied to the source slot.

RIGHT

Standard vanilla-style half-stack take / single-item place / single-item add, applied to the source slot.

SHIFT_LEFT/SHIFT_RIGHT

Moves the source item into the viewer's own inventory (addItem); any leftover that didn't fit is written back to the source slot.

NUMBER_KEY

Swaps the source slot's item with the viewer's hotbar slot at the pressed number.

DROP

Drops one item from the source slot at the target's location (not the viewer's).

CONTROL_DROP

Drops the entire source stack at the target's location.

DOUBLE_CLICK

Always denied — vanilla's collect-to-cursor would scan both inventories uncontrollably.

Anything else (middle-click clone, SWAP_OFFHAND, window-border clicks, …)

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.

See also

Last modified: 25 July 2026