--- title: Containers type: docs weight: 2 --- Containers hold and manage collections of ItemStacks in Hytale. The inventory system uses `ItemContainer` as the base abstraction with `Inventory` as the player's specialized multi-section container. ## Inventory Structure Player inventories are composed of multiple `ItemContainer` sections: ```java Inventory inv = player.getInventory(); // Get individual sections ItemContainer storage = inv.getStorage(); // Main storage (36 slots default) ItemContainer hotbar = inv.getHotbar(); // Hotbar (9 slots default) ItemContainer armor = inv.getArmor(); // Armor slots ItemContainer utility = inv.getUtility(); // Utility items (4 slots default) ItemContainer backpack = inv.getBackpack(); // Backpack (expandable) // Combined containers for operations across multiple sections CombinedItemContainer hotbarFirst = inv.getCombinedHotbarFirst(); CombinedItemContainer storageFirst = inv.getCombinedStorageFirst(); ``` ### Section IDs Each section has a constant ID for operations: | Section | ID | Constant | |---------|----|----| | Hotbar | -1 | `Inventory.HOTBAR_SECTION_ID` | | Storage | -2 | `Inventory.STORAGE_SECTION_ID` | | Armor | -3 | `Inventory.ARMOR_SECTION_ID` | | Utility | -5 | `Inventory.UTILITY_SECTION_ID` | | Tools | -8 | `Inventory.TOOLS_SECTION_ID` (deprecated) | | Backpack | -9 | `Inventory.BACKPACK_SECTION_ID` | ## ItemContainer Basics ### Reading Items ```java ItemContainer container = inv.getStorage(); // Get capacity (NOT getSize!) short capacity = container.getCapacity(); // Get item at slot (slot is SHORT type) ItemStack item = container.getItemStack((short) 0); // Check if empty boolean empty = container.isEmpty(); // Safe item access ItemStack stack = container.getItemStack((short) slot); if (!ItemStack.isEmpty(stack)) { String itemId = stack.getItemId(); int quantity = stack.getQuantity(); } ``` ### Setting Items ```java ItemContainer container = inv.getHotbar(); // Set item at slot - returns a Transaction ItemStackSlotTransaction transaction = container.setItemStackForSlot( (short) 0, new ItemStack("iron_sword", 1) ); // Check if operation succeeded if (transaction.succeeded()) { // Item was set successfully } // Clear a slot container.setItemStackForSlot((short) 0, null); ``` ### Adding Items ```java ItemContainer container = inv.getStorage(); ItemStack toAdd = new ItemStack("wood_plank", 64); // Add to first available slot (stacks with existing items first) ItemStackTransaction transaction = container.addItemStack(toAdd); // Check for remaining items that couldn't fit ItemStack remainder = transaction.getRemainder(); if (!ItemStack.isEmpty(remainder)) { // Some items couldn't fit int leftover = remainder.getQuantity(); } // Add to specific slot ItemStackSlotTransaction slotTransaction = container.addItemStackToSlot( (short) 5, toAdd ); // Check if container can fit items before adding if (container.canAddItemStack(toAdd)) { container.addItemStack(toAdd); } ``` ### Removing Items ```java // Remove from specific slot SlotTransaction transaction = container.removeItemStackFromSlot((short) 0); // Remove specific quantity from slot ItemStackSlotTransaction removeTransaction = container.removeItemStackFromSlot( (short) 0, 10 // quantity to remove ); // Remove specific item type from anywhere in container ItemStack toRemove = new ItemStack("iron_ore", 5); ItemStackTransaction itemTransaction = container.removeItemStack(toRemove); // Check if items can be removed before removing if (container.canRemoveItemStack(toRemove)) { container.removeItemStack(toRemove); } ``` ## Active Slots Players have active slots for hotbar and utility items: ```java Inventory inv = player.getInventory(); // Get active hotbar slot (0-8) byte activeHotbar = inv.getActiveHotbarSlot(); // Set active hotbar slot inv.setActiveHotbarSlot((byte) 3); // Get item currently in hand ItemStack handItem = inv.getItemInHand(); // Get/set active utility slot byte activeUtility = inv.getActiveUtilitySlot(); inv.setActiveUtilitySlot((byte) 1); // Get utility item ItemStack utilityItem = inv.getUtilityItem(); ``` ## Moving Items ### Between Slots ```java Inventory inv = player.getInventory(); ItemContainer storage = inv.getStorage(); ItemContainer hotbar = inv.getHotbar(); // Move item from one slot to another in same container MoveTransaction transaction = storage.moveItemStackFromSlotToSlot( (short) 0, // from slot 64, // quantity storage, // to container (short) 5 // to slot ); // Move between different containers storage.moveItemStackFromSlotToSlot( (short) 0, 32, hotbar, (short) 0 ); // Use Inventory's moveItem for section-based moves inv.moveItem( Inventory.STORAGE_SECTION_ID, // from section 5, // from slot 10, // quantity Inventory.HOTBAR_SECTION_ID, // to section 0 // to slot ); ``` ### Smart Moving ```java // Smart move considers item type for optimal placement inv.smartMoveItem( Inventory.STORAGE_SECTION_ID, 0, // slot 64, // quantity SmartMoveType.EquipOrMergeStack // tries to equip armor or merge stacks ); ``` ## Iterating Containers ```java ItemContainer container = inv.getStorage(); // Iterate all non-empty slots container.forEach((slot, itemStack) -> { getLogger().at(Level.INFO).log("Slot " + slot + ": " + itemStack.getItemId() + " x" + itemStack.getQuantity()); }); // Count items matching a condition int swordCount = container.countItemStacks( stack -> stack.getItemId().contains("sword") ); // Check if container has stackable items ItemStack testStack = new ItemStack("stone", 1); boolean hasStackable = container.containsItemStacksStackableWith(testStack); ``` ## Checking Contents ```java ItemContainer container = inv.getStorage(); // Check if inventory contains specific item type and quantity public boolean hasItems(ItemContainer container, String itemId, int amount) { int count = container.countItemStacks( stack -> stack.getItemId().equals(itemId) ); return count >= amount; } // Find first slot with specific item public short findSlotWithItem(ItemContainer container, String itemId) { for (short i = 0; i < container.getCapacity(); i++) { ItemStack stack = container.getItemStack(i); if (!ItemStack.isEmpty(stack) && stack.getItemId().equals(itemId)) { return i; } } return -1; // Not found } // Count empty slots public int countEmptySlots(ItemContainer container) { int empty = 0; for (short i = 0; i < container.getCapacity(); i++) { if (ItemStack.isEmpty(container.getItemStack(i))) { empty++; } } return empty; } ``` ## Clearing Containers ```java ItemContainer container = inv.getStorage(); // Clear entire container ClearTransaction transaction = container.clear(); // Drop all items (returns list of dropped items) List droppedItems = container.dropAllItemStacks(); // Remove all items (returns list) List removedItems = container.removeAllItemStacks(); // Clear entire player inventory inv.clear(); // Drop all from player inventory List allDropped = inv.dropAllItemStacks(); ``` ## Sorting Available `SortType` values: `NAME`, `TYPE`, `RARITY` ```java ItemContainer container = inv.getStorage(); // Sort items container.sortItems(SortType.NAME); // Sort via Inventory (also saves sort preference) inv.sortStorage(SortType.NAME); inv.setSortType(SortType.RARITY); // Or TYPE ``` ## Container Events ```java // Register for container change events container.registerChangeEvent(event -> { ItemContainer changedContainer = event.container(); Transaction transaction = event.transaction(); getLogger().at(Level.INFO).log("Container changed!"); }); // With priority container.registerChangeEvent(EventPriority.EARLY, event -> { // Handle early }); ``` ## Transaction System All modification operations return Transaction objects: ```java // Transactions track success and changes ItemStackTransaction transaction = container.addItemStack(itemStack); if (transaction.succeeded()) { ItemStack remainder = transaction.getRemainder(); // ... } // Slot transactions include slot info ItemStackSlotTransaction slotTransaction = container.setItemStackForSlot( (short) 0, itemStack ); if (slotTransaction.succeeded()) { short slot = slotTransaction.getSlot(); ItemStack before = slotTransaction.getSlotBefore(); ItemStack after = slotTransaction.getSlotAfter(); } ``` ## Operation Parameters Most container operations support optional parameters: ```java // Default values DEFAULT_ADD_ALL_OR_NOTHING = false; // Partial adds allowed DEFAULT_REMOVE_ALL_OR_NOTHING = true; // Only full removals DEFAULT_FULL_STACKS = false; // Can split stacks DEFAULT_EXACT_AMOUNT = true; // Exact quantities only DEFAULT_FILTER = true; // Apply slot filters // Add with parameters container.addItemStack(itemStack, allOrNothing, fullStacks, filter); // allOrNothing: if true, fails entirely if not all items fit // fullStacks: if true, only fills empty slots (no stacking) // filter: if true, respects slot filters ``` ## Material and Resource Removal Containers support removing items by material type or resource type (used for crafting): {{< callout type="info" >}} **MaterialQuantity constructor:** `MaterialQuantity(itemId, resourceTypeId, tag, quantity, metadata)` At least one of `itemId`, `resourceTypeId`, or `tag` must be non-null. {{< /callout >}} ```java // Remove by material using itemId MaterialQuantity materialByItem = new MaterialQuantity("iron_ingot", null, null, 5, null); if (container.canRemoveMaterial(materialByItem)) { MaterialTransaction transaction = container.removeMaterial(materialByItem); } // Remove by material using resourceTypeId MaterialQuantity materialByResource = new MaterialQuantity(null, "iron", null, 5, null); if (container.canRemoveMaterial(materialByResource)) { MaterialTransaction transaction = container.removeMaterial(materialByResource); } // Remove by resource type (simpler constructor) ResourceQuantity resource = new ResourceQuantity("wood", 10); if (container.canRemoveResource(resource)) { ResourceTransaction transaction = container.removeResource(resource); } // Remove by tag index if (container.canRemoveTag(tagIndex, quantity)) { TagTransaction transaction = container.removeTag(tagIndex, quantity); } // Bulk removal List materials = List.of( new MaterialQuantity("iron_ingot", null, null, 2, null), new MaterialQuantity(null, null, "Wood", 5, null) // Using tag ); if (container.canRemoveMaterials(materials)) { container.removeMaterials(materials); } ``` ## Bulk Operations Add or remove multiple items at once: ```java // Add multiple items List items = List.of( new ItemStack("iron_ore", 10), new ItemStack("gold_ore", 5) ); if (container.canAddItemStacks(items)) { ListTransaction transaction = container.addItemStacks(items); } // Remove multiple items if (container.canRemoveItemStacks(items)) { container.removeItemStacks(items); } // Add items in order (preserves slot positions) container.addItemStacksOrdered(items); container.addItemStacksOrdered((short) 5, items); // Starting at slot 5 ``` ## Advanced Move Operations ```java // Move all items to another container ListTransaction> result = storage.moveAllItemStacksTo(hotbar, backpack); // Move all items matching a condition storage.moveAllItemStacksTo( item -> item.getItemId().contains("ore"), hotbar ); // Quick stack: only moves items that can stack with existing items storage.quickStackTo(hotbar); // Combine small stacks into one slot container.combineItemStacksIntoSlot(targetContainer, (short) 0); // Swap items between containers storage.swapItems( (short) 0, // source position hotbar, // target container (short) 0, // destination position (short) 5 // number of slots to swap ); ``` ## Replace Operations ```java // Replace item in slot if it matches expected ItemStackSlotTransaction transaction = container.replaceItemStackInSlot( (short) 0, expectedItem, // must match current item to proceed newItem ); // Replace all items using a function container.replaceAll((slot, existing) -> { if (existing.getItemId().equals("old_item")) { return new ItemStack("new_item", existing.getQuantity()); } return existing; }); ``` ## Slot Filters Control which items can go in which slots: ```java // Set global filter for the container container.setGlobalFilter(FilterType.WHITELIST); // Set filter for specific slot container.setSlotFilter( FilterActionType.ADD, // Filter on add operations (short) 0, // Slot slotFilter // Filter implementation ); ``` ## Best Practices {{< callout type="info" >}} **Container Tips:** - Slot indices are `short`, not `int` - cast appropriately - Always check `ItemStack.isEmpty(stack)` - handles both null and empty - Use `getQuantity()` not `getCount()` - Check transaction success with `succeeded()` - Use combined containers for cross-section operations - Player inventory changes trigger `LivingEntityInventoryChangeEvent` {{< /callout >}} ```java // Safe pattern for working with containers public void safeAddItem(ItemContainer container, ItemStack item) { if (ItemStack.isEmpty(item)) { return; } if (!container.canAddItemStack(item)) { // Handle full container return; } ItemStackTransaction transaction = container.addItemStack(item); if (!transaction.succeeded()) { // Handle failure } } ``` ## ItemContainer API Reference ### Core Methods | Method | Returns | Description | |--------|---------|-------------| | `getCapacity()` | `short` | Total number of slots | | `getItemStack(short)` | `ItemStack?` | Get item at slot | | `isEmpty()` | `boolean` | True if no items | | `clone()` | `ItemContainer` | Clone the container | | `clear()` | `ClearTransaction` | Remove all items | ### Add Operations | Method | Returns | Description | |--------|---------|-------------| | `canAddItemStack(ItemStack)` | `boolean` | Check if can add | | `canAddItemStack(ItemStack, fullStacks, filter)` | `boolean` | Check with options | | `addItemStack(ItemStack)` | `ItemStackTransaction` | Add to first available | | `addItemStack(ItemStack, allOrNothing, fullStacks, filter)` | `ItemStackTransaction` | Add with options | | `canAddItemStackToSlot(short, ItemStack, allOrNothing, filter)` | `boolean` | Check if can add to slot | | `addItemStackToSlot(short, ItemStack)` | `ItemStackSlotTransaction` | Add to specific slot | | `addItemStackToSlot(short, ItemStack, allOrNothing, filter)` | `ItemStackSlotTransaction` | Add to slot with options | | `canAddItemStacks(List)` | `boolean` | Check if can add multiple | | `addItemStacks(List)` | `ListTransaction` | Add multiple items | | `addItemStacksOrdered(List)` | `ListTransaction` | Add in order | | `addItemStacksOrdered(short offset, List)` | `ListTransaction` | Add in order from offset | ### Set Operations | Method | Returns | Description | |--------|---------|-------------| | `setItemStackForSlot(short, ItemStack)` | `ItemStackSlotTransaction` | Set item at slot | | `setItemStackForSlot(short, ItemStack, filter)` | `ItemStackSlotTransaction` | Set with filter option | | `replaceItemStackInSlot(short, ItemStack expected, ItemStack new)` | `ItemStackSlotTransaction` | Replace if matches | | `replaceAll(SlotReplacementFunction)` | `ListTransaction` | Replace all items | ### Remove Operations | Method | Returns | Description | |--------|---------|-------------| | `removeItemStackFromSlot(short)` | `SlotTransaction` | Remove entire slot | | `removeItemStackFromSlot(short, filter)` | `SlotTransaction` | Remove with filter | | `removeItemStackFromSlot(short, quantity)` | `ItemStackSlotTransaction` | Remove quantity | | `removeItemStackFromSlot(short, ItemStack, quantity)` | `ItemStackSlotTransaction` | Remove matching item | | `canRemoveItemStack(ItemStack)` | `boolean` | Check if can remove | | `removeItemStack(ItemStack)` | `ItemStackTransaction` | Remove item type | | `canRemoveItemStacks(List)` | `boolean` | Check if can remove multiple | | `removeItemStacks(List)` | `ListTransaction` | Remove multiple items | | `removeAllItemStacks()` | `List` | Remove and return all | | `dropAllItemStacks()` | `List` | Drop all (respects cantDrop) | | `dropAllItemStacks(filter)` | `List` | Drop with filter option | ### Material/Resource/Tag Removal | Method | Returns | Description | |--------|---------|-------------| | `canRemoveMaterial(MaterialQuantity)` | `boolean` | Check material removal | | `removeMaterial(MaterialQuantity)` | `MaterialTransaction` | Remove by material | | `removeMaterialFromSlot(short, MaterialQuantity)` | `MaterialSlotTransaction` | Remove material from slot | | `canRemoveMaterials(List)` | `boolean` | Check multiple materials | | `removeMaterials(List)` | `ListTransaction` | Remove multiple materials | | `canRemoveResource(ResourceQuantity)` | `boolean` | Check resource removal | | `removeResource(ResourceQuantity)` | `ResourceTransaction` | Remove by resource | | `removeResourceFromSlot(short, ResourceQuantity)` | `ResourceSlotTransaction` | Remove resource from slot | | `canRemoveResources(List)` | `boolean` | Check multiple resources | | `removeResources(List)` | `ListTransaction` | Remove multiple resources | | `canRemoveTag(tagIndex, quantity)` | `boolean` | Check tag removal | | `removeTag(tagIndex, quantity)` | `TagTransaction` | Remove by tag | | `removeTagFromSlot(short, tagIndex, quantity)` | `TagSlotTransaction` | Remove tag from slot | ### Move Operations | Method | Returns | Description | |--------|---------|-------------| | `moveItemStackFromSlot(short, ItemContainer)` | `MoveTransaction` | Move slot to container | | `moveItemStackFromSlot(short, quantity, ItemContainer)` | `MoveTransaction` | Move quantity | | `moveItemStackFromSlot(short, ItemContainer...)` | `ListTransaction` | Move to multiple containers | | `moveItemStackFromSlotToSlot(short, quantity, ItemContainer, short)` | `MoveTransaction` | Move to specific slot | | `moveAllItemStacksTo(ItemContainer...)` | `ListTransaction` | Move all items | | `moveAllItemStacksTo(Predicate, ItemContainer...)` | `ListTransaction` | Move matching items | | `quickStackTo(ItemContainer...)` | `ListTransaction` | Move stackable items only | | `combineItemStacksIntoSlot(ItemContainer, short)` | `ListTransaction` | Combine stacks into slot | | `swapItems(short srcPos, ItemContainer, short destPos, short length)` | `ListTransaction` | Swap item ranges | ### Utility Methods | Method | Returns | Description | |--------|---------|-------------| | `forEach(ShortObjectConsumer)` | `void` | Iterate non-empty slots | | `forEachWithMeta(consumer, meta)` | `void` | Iterate with metadata | | `countItemStacks(Predicate)` | `int` | Count matching items (total quantity) | | `containsItemStacksStackableWith(ItemStack)` | `boolean` | Check for stackable items | | `sortItems(SortType)` | `ListTransaction` | Sort container | | `registerChangeEvent(Consumer)` | `EventRegistration` | Listen for changes | | `registerChangeEvent(EventPriority, Consumer)` | `EventRegistration` | Listen with priority | | `setGlobalFilter(FilterType)` | `void` | Set container filter | | `setSlotFilter(FilterActionType, short, SlotFilter)` | `void` | Set slot filter | | `containsContainer(ItemContainer)` | `boolean` | Check if contains container | ### Static Methods | Method | Returns | Description | |--------|---------|-------------| | `copy(from, to, remainder)` | `T` | Copy items between containers | | `ensureContainerCapacity(container, capacity, supplier, remainder)` | `T` | Ensure container has capacity | | `getNewContainer(capacity, supplier)` | `ItemContainer` | Create or get empty | | `getMatchingResourceType(Item, resourceId)` | `ItemResourceType?` | Find resource type for item | | `validateQuantity(int)` | `void` | Throws if < 0 | | `validateSlotIndex(short, capacity)` | `void` | Throws if out of bounds | ### Static Constants | Field | Type | Description | |-------|------|-------------| | `CODEC` | `CodecMapCodec` | Serialization codec | | `DEFAULT_ADD_ALL_OR_NOTHING` | `boolean` | false | | `DEFAULT_REMOVE_ALL_OR_NOTHING` | `boolean` | true | | `DEFAULT_FULL_STACKS` | `boolean` | false | | `DEFAULT_EXACT_AMOUNT` | `boolean` | true | | `DEFAULT_FILTER` | `boolean` | true | ### Nested Classes | Class | Description | |-------|-------------| | `ItemContainerChangeEvent` | Event record with `container()` and `transaction()` | ## Inventory API Reference | Method | Returns | Description | |--------|---------|-------------| | `getStorage()` | `ItemContainer` | Main storage section | | `getHotbar()` | `ItemContainer` | Hotbar section | | `getArmor()` | `ItemContainer` | Armor section | | `getUtility()` | `ItemContainer` | Utility section | | `getBackpack()` | `ItemContainer` | Backpack section | | `getSectionById(int)` | `ItemContainer?` | Get section by ID | | `getItemInHand()` | `ItemStack?` | Currently held item | | `getActiveHotbarSlot()` | `byte` | Active hotbar slot | | `setActiveHotbarSlot(byte)` | `void` | Set active hotbar | | `getCombinedHotbarFirst()` | `CombinedItemContainer` | Hotbar+Storage combined | | `moveItem(...)` | `void` | Move between sections | | `clear()` | `void` | Clear all sections |