---
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<String> 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<ItemStack>` | 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 |
