--- title: ItemStacks type: docs weight: 1 --- ItemStack represents a stack of items in Hytale with quantity, durability, and metadata. ## Creating ItemStacks ItemStacks are created using constructors with an item ID: ```java // Create by item ID ItemStack sword = new ItemStack("iron_sword"); // Create with quantity ItemStack materials = new ItemStack("wood_plank", 64); // Create with quantity and metadata BsonDocument metadata = new BsonDocument(); ItemStack customItem = new ItemStack("iron_sword", 1, metadata); // Create with full parameters (durability) ItemStack damagedSword = new ItemStack("iron_sword", 1, 50.0, 100.0, null); // Parameters: itemId, quantity, durability, maxDurability, metadata ``` ## ItemStack Properties ```java ItemStack stack = new ItemStack("iron_sword", 1); // Get the item ID String itemId = stack.getItemId(); // "iron_sword" // Get the Item asset Item item = stack.getItem(); // Get quantity (NOT getCount!) int quantity = stack.getQuantity(); // Check if empty boolean empty = stack.isEmpty(); // Check validity boolean valid = stack.isValid(); // Durability double durability = stack.getDurability(); double maxDurability = stack.getMaxDurability(); boolean unbreakable = stack.isUnbreakable(); // true if maxDurability <= 0 boolean broken = stack.isBroken(); // true if durability == 0 ``` ## Modifying ItemStacks ItemStack uses a `with*` pattern that returns NEW instances: ```java ItemStack stack = new ItemStack("iron_sword", 1); // Change quantity - returns NEW ItemStack or null if quantity is 0 ItemStack moreItems = stack.withQuantity(32); // Change durability ItemStack damaged = stack.withDurability(50.0); // Increase durability ItemStack repaired = stack.withIncreasedDurability(25.0); // Restore full durability ItemStack fullyRepaired = stack.withRestoredDurability(100.0); // Change max durability ItemStack stronger = stack.withMaxDurability(200.0); // Change state (for items with states) ItemStack newState = stack.withState("activated"); // Add/modify metadata ItemStack withMeta = stack.withMetadata(metadataDocument); // Add specific metadata value ItemStack tagged = stack.withMetadata("CustomKey", Codec.STRING, "CustomValue"); ``` {{< callout type="warning" >}} **Important:** `withQuantity(0)` returns `null`, not an empty ItemStack. Always check for null when decreasing quantity! {{< /callout >}} ## The EMPTY Constant Use `ItemStack.EMPTY` for empty stacks: ```java // Static empty instance (singleton) ItemStack empty = ItemStack.EMPTY; // Check for empty if (stack.isEmpty()) { // Stack is empty } // Static helper method if (ItemStack.isEmpty(stack)) { // Handles null and empty stacks } ``` ## Comparing ItemStacks ```java ItemStack a = new ItemStack("iron_sword", 1); ItemStack b = new ItemStack("iron_sword", 5); ItemStack c = new ItemStack("diamond_sword", 1); // Check if stackable (same itemId, durability, maxDurability, AND metadata) // Note: Different quantities can stack, but durability values must match exactly boolean canStack = a.isStackableWith(b); // Check equivalent type (same itemId and metadata, ignores durability values) boolean sameType = a.isEquivalentType(b); // Check same item type only (just itemId comparison) boolean sameItem = ItemStack.isSameItemType(a, c); // false // Static helpers (handle nulls safely) ItemStack.isStackableWith(a, b); ItemStack.isEquivalentType(a, b); ``` ## Working with Metadata ItemStack supports BSON metadata for custom data: ```java // Create metadata codec KeyedCodec OWNER_KEY = new KeyedCodec<>("Owner", Codec.STRING); // Add metadata ItemStack withOwner = stack.withMetadata(OWNER_KEY, "PlayerName"); // Read metadata String owner = stack.getFromMetadataOrNull(OWNER_KEY); // Read with key and codec Integer level = stack.getFromMetadataOrNull("Level", Codec.INTEGER); // Read with default from BuilderCodec MyData data = stack.getFromMetadataOrDefault("Data", MyData.CODEC); ``` ## Block Items Check if an item can be placed as a block: ```java ItemStack stack = new ItemStack("stone", 1); // Get associated block key (null if not a block item) String blockKey = stack.getBlockKey(); if (blockKey != null) { // This item can be placed as a block } // Check via Item asset Item item = stack.getItem(); if (item.hasBlockType()) { String blockId = item.getBlockId(); } ``` ## Common Patterns ### Consuming Items ```java public ItemStack consumeOne(ItemStack stack) { if (stack == null || stack.isEmpty()) { return null; } int newQuantity = stack.getQuantity() - 1; // withQuantity returns null if quantity is 0 return stack.withQuantity(newQuantity); } // Usage with container public void useItem(ItemContainer container, short slot) { ItemStack current = container.getItemStack(slot); ItemStack remaining = consumeOne(current); // remaining may be null if stack was depleted container.setItemStackForSlot(slot, remaining); } ``` ### Checking Item Type ```java public boolean isHoldingSword(ItemStack hand) { if (hand == null || hand.isEmpty()) { return false; } // Check item type by ID return hand.getItemId().contains("sword"); // Or check via Item asset // return hand.getItem().getCategory().equals("weapon"); } ``` ### Splitting Stacks ```java public ItemStack[] splitStack(ItemStack stack, int splitAmount) { if (stack == null || stack.isEmpty()) { return null; } int currentQuantity = stack.getQuantity(); if (splitAmount >= currentQuantity) { return new ItemStack[] { stack, null }; } // Create two stacks ItemStack remaining = stack.withQuantity(currentQuantity - splitAmount); ItemStack split = stack.withQuantity(splitAmount); return new ItemStack[] { remaining, split }; } ``` ### Merging Stacks ```java public ItemStack[] mergeStacks(ItemStack target, ItemStack source) { if (!target.isStackableWith(source)) { return new ItemStack[] { target, source }; // Can't merge } int maxStack = target.getItem().getMaxStack(); int totalQuantity = target.getQuantity() + source.getQuantity(); if (totalQuantity <= maxStack) { // Full merge return new ItemStack[] { target.withQuantity(totalQuantity), null }; } // Partial merge return new ItemStack[] { target.withQuantity(maxStack), source.withQuantity(totalQuantity - maxStack) }; } ``` ## Best Practices {{< callout type="info" >}} **Remember:** - Use `getQuantity()` not `getCount()` - Hytale uses "quantity" - `withQuantity(0)` returns `null` - check for this! - Use `ItemStack.isEmpty(stack)` to handle both null and empty - ItemStacks are mostly immutable - `with*` methods return new instances - Use `isStackableWith()` before attempting to merge stacks - `ResourceType.Id` must not be null when creating items via codecs {{< /callout >}} ```java // Good: Handle null from withQuantity ItemStack result = stack.withQuantity(newQuantity); if (result == null) { // Stack depleted, handle appropriately } // Good: Safe empty check if (ItemStack.isEmpty(stack)) { // Handles both null and empty ItemStack.EMPTY } // Bad: Ignoring the returned value stack.withQuantity(10); // Returns new stack, original unchanged! ``` ## ItemStack API Reference ### Instance Methods | Method | Returns | Description | |--------|---------|-------------| | `getItemId()` | `String` | The item type identifier | | `getItem()` | `Item` | The Item asset (returns `Item.UNKNOWN` if not found) | | `getQuantity()` | `int` | Stack size | | `getDurability()` | `double` | Current durability | | `getMaxDurability()` | `double` | Maximum durability | | `isEmpty()` | `boolean` | True if itemId equals "Empty" | | `isUnbreakable()` | `boolean` | True if maxDurability <= 0 | | `isBroken()` | `boolean` | True if NOT unbreakable AND durability == 0 | | `isValid()` | `boolean` | True if empty OR item asset exists | | `isStackableWith(ItemStack)` | `boolean` | Same itemId, durability, maxDurability, metadata | | `isEquivalentType(ItemStack)` | `boolean` | Same itemId and metadata (ignores durability) | | `getBlockKey()` | `String?` | Block ID if item is placeable, null otherwise | | `getOverrideDroppedItemAnimation()` | `boolean` | Animation override flag | | `getMetadata()` | `BsonDocument?` | **Deprecated** - Returns cloned metadata | | `getFromMetadataOrNull(KeyedCodec)` | `T?` | Get typed metadata value | | `getFromMetadataOrNull(String, Codec)` | `T?` | Get typed metadata by key | | `getFromMetadataOrDefault(String, BuilderCodec)` | `T` | Get metadata with default | ### Modifier Methods (return new ItemStack) | Method | Returns | Description | |--------|---------|-------------| | `withQuantity(int)` | `ItemStack?` | **Returns null if quantity is 0** | | `withDurability(double)` | `ItemStack` | Clamped to [0, maxDurability] | | `withMaxDurability(double)` | `ItemStack` | Also clamps current durability | | `withIncreasedDurability(double)` | `ItemStack` | Add to current durability | | `withRestoredDurability(double)` | `ItemStack` | Set both durability and max | | `withState(String)` | `ItemStack` | Change item state | | `withMetadata(BsonDocument)` | `ItemStack` | Replace all metadata | | `withMetadata(KeyedCodec, T)` | `ItemStack` | Set typed metadata | | `withMetadata(String, Codec, T)` | `ItemStack` | Set metadata by key | | `withMetadata(String, BsonValue)` | `ItemStack` | Set raw BSON value | | `setOverrideDroppedItemAnimation(boolean)` | `void` | **Mutates in place** | ### Static Fields | Field | Type | Description | |-------|------|-------------| | `EMPTY` | `ItemStack` | Singleton empty stack (itemId = "Empty") | | `EMPTY_ARRAY` | `ItemStack[]` | Empty array constant | | `CODEC` | `BuilderCodec` | Serialization codec | ### Static Methods | Method | Returns | Description | |--------|---------|-------------| | `isEmpty(ItemStack)` | `boolean` | Null-safe empty check | | `isStackableWith(ItemStack, ItemStack)` | `boolean` | Null-safe stackable check | | `isEquivalentType(ItemStack, ItemStack)` | `boolean` | Null-safe type check | | `isSameItemType(ItemStack, ItemStack)` | `boolean` | Compare itemId only | | `fromPacket(ItemQuantity)` | `ItemStack?` | Create from network packet | ### Nested Classes | Class | Description | |-------|-------------| | `ItemStack.Metadata` | Contains `BLOCK_STATE` constant for block state metadata key |