Skip to content

These docs were made completely by AI, so they might be right, or wrong, you'll need to test them yourself. This was made for a easier understanding of everything. So use at your own risk. If anything is wrong, please don't hurt to make a PR on the page you have a problem with. ON GITHUB

Container Menus

LCE uses a two-layer container system: Container objects hold the raw item data, and AbstractContainerMenu subclasses manage the GUI logic, slot layout, click handling, and sync between server and client.

Container is the pure-virtual interface that every item-holding object implements. It defines the basic storage contract.

Key constants and methods:

MethodPurpose
getContainerSize()Number of item slots
getItem(slot)Read an item from a slot
removeItem(slot, count)Split count items off a slot. Returns the removed items, or null if the slot is empty. All implementations include 4J’s duplication fix: if the resulting count is 0 or less, returns null.
removeItemNoUpdate(slot)Remove the entire stack without triggering listeners
setItem(slot, item)Write an item into a slot. Clamps count to getMaxStackSize() if it exceeds it.
getMaxStackSize()Default is LARGE_MAX_STACK_SIZE (64)
getName()Returns a localization string ID for the container name
setChanged()Mark the container as dirty for saving
stillValid(player)Whether the player can still interact (usually a 64-block distance check: distanceToSqr() > 8 * 8)
startOpen() / stopOpen()Lifecycle hooks (used by chests for animation, empty in most other containers)
ClassPurposeNotes
SimpleContainerGeneral-purpose fixed-size storageSupports ContainerListener callbacks via addListener() / removeListener(). Has a name (localization ID), size, items array, and a listeners vector.
CraftingContainer2D crafting gridWraps a width/height grid. Notifies its parent AbstractContainerMenu on changes via slotsChanged().
ResultContainerSingle-slot outputUsed for crafting and repair results.
CompoundContainerMerges two containersDelegates to c1 and c2 based on slot index. Used for double chests. If the slot index is less than c1’s size, reads from c1; otherwise reads from c2 with an offset.
MerchantContainerVillager trade slots3 slots: two payment, one result. Calls updateSellItem() on changes. Tracks activeRecipe and selectionHint.
EnchantmentContainerEnchanting table inputExtends SimpleContainer with a max stack size of 1 and menu callbacks.
RepairContainerAnvil input slotsExtends SimpleContainer. Triggers RepairMenu::createResult() on changes via enable_shared_from_this<RepairContainer>.
PlayerEnderChestContainerPer-player ender chestExtends SimpleContainer. Persists through NBT with setItemsByTag() / createTag(). Tracks the currently active EnderChestTileEntity.

AbstractContainerMenu is the base class for all inventory screens. It owns a list of Slot objects, tracks last-known slot contents for change detection, and manages a list of ContainerListener observers.

FieldTypePurpose
slotsvector<Slot*>*All slots in the menu
lastSlotsvector<shared_ptr<ItemInstance>>*Snapshot of each slot’s contents for change detection
containerIdintMenu ID for network sync
changeUidshortBackup/rollback version number (private)
m_bNeedsRenderedbool4J addition: whether the UI needs a render update
containerListenersvector<ContainerListener*>*Observers that get notified on changes
unSynchedPlayersunordered_set<shared_ptr<Player>>Players whose client state is out of date
ConstantValueMeaning
CONTAINER_ID_CARRIED-1The item on the cursor
CONTAINER_ID_INVENTORY0The player’s own inventory
CONTAINER_ID_CREATIVE-2Creative mode virtual inventory
CLICKED_OUTSIDE-999Click was outside the GUI window
ConstantValueBehavior
CLICK_PICKUP0Normal left/right click to pick up or place items
CLICK_QUICK_MOVE1Shift-click to transfer between sections
CLICK_SWAP2Number-key swap with hotbar
CLICK_CLONE3Middle-click clone (creative mode only)
MethodPurpose
addSlot(Slot*)Registers a slot, giving it a sequential index. Returns the slot pointer.
addSlotListener(ContainerListener*)Adds a listener and immediately sends it the current state of all slots
getItems()Returns the lastSlots vector
sendData(id, value)Sends a data value to all listeners via ContainerListener::dataChanged()
broadcastChanges()Compares each slot against lastSlots using ItemInstance::matches(). For any changed slot, fires ContainerListener::slotChanged() and updates the snapshot. Resets m_bNeedsRendered.
needsRendered()Returns m_bNeedsRendered (4J addition)
clickMenuButton(player, buttonId)Virtual. Used by enchanting and beacon menus for button-based interactions. Returns false by default.
getSlotFor(container, index)Finds a slot by its backing container and index using Slot::isAt()
getSlot(index)Direct slot access by menu index
quickMoveStack(player, slotIndex)Virtual. Subclasses override this to define shift-click transfer rules between slot regions.
clicked(slotIndex, buttonNum, clickType, player)The main click dispatcher. Handles pickup, quick-move, swap, and clone logic. Manages carried items through the player’s Inventory. Returns the item that was in the slot before the click.
mayCombine(slot, item)Hook for dyeable armor and damaged item combination (4J addition). Returns false by default.
loopClick(slotIndex, buttonNum, quickKeyHeld, player)Virtual. For iterating click behavior (protected).
removed(player)Drops any carried item when the menu closes
slotsChanged()Virtual. Called when a backing container changes. 4J simplified this to take no arguments (used to take a Container*).
setItem(slot, item)Sets a specific slot’s contents
setAll(items)Sets all slot contents from an array
setData(id, value)Virtual. Sets a data value and notifies listeners.
backup(inventory)Creates a MenuBackup snapshot and returns the changeUid
isSynched(player) / setSynched(player, bool)Manage the unSynchedPlayers set
stillValid(player)Pure virtual. Each subclass defines the distance/validity check.
moveItemStackTo(itemStack, start, end, backwards)Helper that moves items into a slot range, first trying to stack with existing items, then filling empty slots. The backwards flag reverses the scan order. Returns true if any items were moved.
isOverrideResultClick(slotNum, buttonNum)Virtual. Returns false by default.
getSize()4J addition: returns the slot count as unsigned int

The menu tracks unSynchedPlayers, a set of players whose client state is out of date. isSynched() and setSynched() control this flag. Change detection uses ItemInstance::matches() to compare current slot contents against lastSlots snapshots.

MenuBackup provides transactional rollback support, storing snapshots keyed by changeUid. It supports save(), rollback(), and deleteBackup().

Slot connects a Container position to a screen coordinate. Each slot holds a reference to its parent container, a slot index, and an (x, y) position for rendering.

Fields:

FieldTypePurpose
containershared_ptr<Container>The backing container
slotintThe slot index within the container (private)
indexintThe slot’s position in the menu’s slot list
x, yintScreen position for rendering

Key methods:

MethodPurpose
mayPlace(item)Whether the item can go here (returns true by default, overridden for type-restricted slots)
mayPickup(player)Whether the player can take the item out (returns true by default)
getMaxStackSize()Stack limit for this slot (delegates to container by default)
set(item) / getItem()Read/write the slot contents through the backing container
hasItem()Whether the slot is non-empty
remove(count)Split items off the slot via container->removeItem()
onTake(player, item)Callback after a player takes items (used for achievements, XP drops)
onQuickCraft(picked, original)Called during quick-craft to track amounts
checkTakeAchievements(picked)Protected. Fires achievement checks after taking items.
swap(other)Swap contents with another slot
mayCombine(item)4J addition for item combination support (returns false by default)
combine(item)4J addition that does the combination (returns the item unchanged by default)
isAt(container, index)Identity check: returns true if this slot’s backing container and slot index match
setChanged()Calls container->setChanged()
getNoItemIcon()Returns the icon to show in an empty slot (null by default)

Specialized slot subclasses exist within menus. For example, BrewingStandMenu::PotionSlot only allows bottles and caps stack size at 1, while BrewingStandMenu::IngredientsSlot only accepts valid brewing ingredients.

The player’s own inventory screen. Has a 2x2 crafting grid, 4 armor slots, 27 main inventory slots, and 9 hotbar slots.

RegionConstantRange
ResultRESULT_SLOT0
Crafting gridCRAFT_SLOT_START .. CRAFT_SLOT_END1-4
ArmorARMOR_SLOT_START .. ARMOR_SLOT_END5-8
Main inventoryINV_SLOT_START .. INV_SLOT_END9-35
HotbarUSE_ROW_SLOT_START .. USE_ROW_SLOT_END36-44

Overrides slotsChanged() to re-check crafting recipes. Supports mayCombine for dyeable armor.

The 3x3 crafting table screen. Tied to a world position for the stillValid() distance check. Drops crafting grid contents on close through removed().

Wraps a FurnaceTileEntity. Three data values are tracked and broadcast to listeners:

Data IDMeaning
0tickCount (cook progress)
1litDuration (total fuel time)
2litTime (remaining fuel time)

Shift-click logic routes fuel items to the fuel slot, smeltable items to the ingredient slot, and results to the player inventory.

Wraps a BrewingStandTileEntity. Uses custom slot classes:

  • PotionSlot: Only takes potion bottles, max stack 1. Fires an achievement on take.
  • IngredientsSlot: Only takes valid brewing ingredients, max stack 1.

Broadcasts brewTime as data ID 0.

Single input slot (INGREDIENT_SLOT = 0, max stack 1). Followed by player inventory slots starting at INV_SLOT_START (1) through hotbar ending at USE_ROW_SLOT_END.

Generates three random enchantment costs[] based on nearby bookshelves when slotsChanged() is called. The m_costsChanged flag (4J addition) tracks whether costs need to be re-broadcast.

clickMenuButton() applies the chosen enchantment at index i (0-2), spending player XP. Uses a Random seeded by nameSeed.

Three slots: two payment inputs (PAYMENT1_SLOT = 0, PAYMENT2_SLOT = 1) and one result output (RESULT_SLOT = 2). Wraps a Merchant (villager) and a MerchantContainer.

Screen coordinates for the trade slots:

  • SELLSLOT1_X = 36, SELLSLOT2_X = 62, BUYSLOT_X = 120
  • ROW1_Y = 24, ROW2_Y = 53

The setSelectionHint() method syncs the selected trade recipe from the trade list GUI. getMerchant() is a 4J addition.

Two input slots (INPUT_SLOT = 0, ADDITIONAL_SLOT = 1) and one result slot (RESULT_SLOT = 2).

Computes cost (XP levels) through createResult(). Supports item renaming with setItemName(). The DATA_TOTAL_COST (0) data ID broadcasts the repair cost to listeners. Tracks repairItemCountCost for the number of items consumed in the repair.

slotsChanged() is overloaded: one version takes a Container* and triggers createResult(), the other uses the base class’s no-arg version.

Simple 9-slot grid wrapping a DispenserTileEntity. No special slot logic beyond standard quick-move between dispenser and inventory.

Used for single and double chests. Calculates containerRows from the container size and creates the right slot grid. Calls startOpen() / stopOpen() for chest animations.

MC adds several new container menus that LCEMP doesn’t have:

MenuPurposeNotes
BeaconMenuBeacon block UIHas a PaymentSlot that only accepts emeralds, diamonds, gold ingots, and iron ingots with max stack size 1. Tracks levels, primaryPower, and secondaryPower as copied values for client-side display.
HopperMenuHopper inventory5-slot container starting at CONTENTS_SLOT_START (0). Wraps a Container (the hopper).
HorseInventoryMenuHorse/donkey inventoryManages a HorseSaddleSlot (only accepts saddles), a HorseArmorSlot (only accepts horse armor, with an isActive() check), and optional chest inventory for horses.
FireworksMenuFireworks crafting1 result slot + 9 crafting slots + player inventory. Tracks m_canMakeFireworks, m_canMakeCharge, and m_canMakeFade to validate ingredient placement via isValidIngredient(). Has canTakeItemForPickAll() to control pick-all behavior.
AnvilMenuAnvil (separate file)In LCEMP, the anvil logic lives inside RepairMenu. MC extracts it into its own AnvilMenu class file.
  • AnimalChest: Extends SimpleContainer. Used by horses and donkeys. Takes a name and size in the constructor. Has a second constructor with an explicit iTitle param and hasCustomName flag (4J addition).

  • WorldlyContainer: An interface extending Container that adds sided inventory access. Defines three pure virtual methods:

    • getSlotsForFace(face): returns which slot indices are accessible from a given block face
    • canPlaceItemThroughFace(slot, item, face): whether an item can be inserted from that face
    • canTakeItemThroughFace(slot, item, face): whether an item can be extracted from that face

    Used by hoppers and droppers for sided inventory access. LCEMP doesn’t have this concept.

  • Hopper (interface): Extends Container (with virtual inheritance). Defines getLevel(), getLevelX/Y/Z() for position. HopperTileEntity and MinecartHopper both implement this.

The TrapMenu in LCEMP only handles dispensers. In MC, it also covers droppers through the new DropperTileEntity. The menu logic is the same since droppers use the same 9-slot layout.

In MC, FurnaceTileEntity and BrewingStandTileEntity change from inheriting Container to inheriting WorldlyContainer, adding face-based slot access for hopper integration.

The core AbstractContainerMenu, Container, Slot, and synchronization systems are the same across both codebases.