---
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<SlotTransaction> 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<ItemStack> droppedItems = container.dropAllItemStacks();

// Remove all items (returns list)
List<ItemStack> removedItems = container.removeAllItemStacks();

// Clear entire player inventory
inv.clear();

// Drop all from player inventory
List<ItemStack> 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<MaterialQuantity> 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<ItemStack> items = List.of(
    new ItemStack("iron_ore", 10),
    new ItemStack("gold_ore", 5)
);

if (container.canAddItemStacks(items)) {
    ListTransaction<ItemStackTransaction> 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<MoveTransaction<ItemStackTransaction>> 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<ItemStack>)` | `boolean` | Check if can add multiple |
| `addItemStacks(List<ItemStack>)` | `ListTransaction` | Add multiple items |
| `addItemStacksOrdered(List<ItemStack>)` | `ListTransaction` | Add in order |
| `addItemStacksOrdered(short offset, List<ItemStack>)` | `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<ItemStack>)` | `boolean` | Check if can remove multiple |
| `removeItemStacks(List<ItemStack>)` | `ListTransaction` | Remove multiple items |
| `removeAllItemStacks()` | `List<ItemStack>` | Remove and return all |
| `dropAllItemStacks()` | `List<ItemStack>` | Drop all (respects cantDrop) |
| `dropAllItemStacks(filter)` | `List<ItemStack>` | 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<MaterialQuantity>)` | `boolean` | Check multiple materials |
| `removeMaterials(List<MaterialQuantity>)` | `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<ResourceQuantity>)` | `boolean` | Check multiple resources |
| `removeResources(List<ResourceQuantity>)` | `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<ItemContainer>` | 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 |
