Files
Documentation/content/world/entities/inventory/containers.en.md
2026-01-20 20:33:59 +01:00

22 KiB

title, type, weight
title type weight
Containers docs 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:

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

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

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

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

// 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:

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

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

// 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

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

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

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

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

// 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:

// 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:

// 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 >}}

// 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:

// 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

// 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

// 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:

// 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 >}}
// 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