Inventory and items

Read and change a player's inventory, build item stacks, and edit item metadata.

An item stack is a type plus an amount - 5 diamonds, one sword - and an inventory is the grid of slots that holds them. Every player carries a PlayerInventory: the main grid and hotbar, the four armour slots, and both hands. From a plugin you can read what a player is holding, hand them new items, and decorate those items with a custom name, lore, or damage.

Get a player's inventory

Every Player exposes an inventory property - a PlayerInventory you read and mutate directly:

src/endstone_my_plugin/my_plugin.py
inventory = player.inventory
held = inventory.item_in_main_hand  # ItemStack or None

PlayerInventory extends the base Inventory, so the slot, search, and clear methods below work on it as well as on chests and other containers.

Build an item stack and give it away

Create an ItemStack from a type id and an amount, then hand it to the player with add_item:

src/endstone_my_plugin/my_plugin.py
from endstone.inventory import ItemStack

sword = ItemStack("minecraft:diamond_sword")  # amount defaults to 1
apples = ItemStack("minecraft:golden_apple", 16)

leftover = player.inventory.add_item(sword, apples)

add_item fills existing stacks and empty slots as best it can. It returns a dict of whatever wouldn't fit, keyed by the index it tried to use - an empty dict means everything was stored. A full inventory is the usual reason items come back, so check the result before assuming the player got them:

leftover = player.inventory.add_item(sword)
if leftover:
    player.send_message("Your inventory is full!")

The ItemStack constructor also takes a third data argument for the item's data value. type, amount, and data are all readable and writable after construction.

Work with specific slots

add_item lets the inventory choose where things land. When you need a precise slot - the third hotbar position, an armour slot - address it by index with get_item and set_item:

inventory = player.inventory

first = inventory.get_item(0)              # ItemStack or None for an empty slot
inventory.set_item(0, ItemStack("minecraft:torch", 64))

get_item returns None for an empty slot, so guard before touching the result. Setting a slot to None empties it.

The held item and armour slots have named accessors so you don't have to memorise indices:

inventory.item_in_main_hand = ItemStack("minecraft:diamond_pickaxe")
inventory.item_in_off_hand = ItemStack("minecraft:shield")

inventory.helmet = ItemStack("minecraft:netherite_helmet")
inventory.chestplate = ItemStack("minecraft:elytra")
inventory.leggings = ItemStack("minecraft:diamond_leggings")
inventory.boots = ItemStack("minecraft:diamond_boots")

inventory.held_item_slot = 4  # which hotbar slot is selected, 0-8

Each of these reads back the current ItemStack (or None) too, so inventory.helmet tells you what a player is wearing.

Name items, add lore, and set damage

Beyond type and amount, an item carries an ItemMeta - its display name, lore lines, durability damage, and enchantments. Read a copy with the item_meta property, change it, then write it back with set_item_meta:

src/endstone_my_plugin/my_plugin.py
sword = ItemStack("minecraft:diamond_sword")

meta = sword.item_meta                     # a copy - edits don't apply until you set it back
meta.display_name = "Excalibur"
meta.lore = ["Forged in legend", "Sharpness beyond measure"]
meta.damage = 200                          # durability used, not removed
meta.is_unbreakable = True                 # never loses durability

sword.set_item_meta(meta)

item_meta returns a copy. Editing it leaves the original stack untouched until you call set_item_meta, so build the meta fully, then apply it once.

The meta exposes has_display_name, has_lore, and has_damage to test before reading, and you can clear a field by assigning None to display_name or lore. Enchantments live here too - add_enchant(id, level, force), remove_enchant(id), has_enchant(id), and get_enchant_level(id):

meta = sword.item_meta
meta.add_enchant("minecraft:sharpness", 5, True)  # force past the normal level cap
sword.set_item_meta(meta)

You can also ask self.server.item_factory for a fresh meta for a given type with get_item_meta("minecraft:diamond_sword") - useful when you want to prepare metadata before building a stack.

Check, remove, and clear

The base Inventory has a full set of query and removal helpers. contains and contains_at_least test for items by type or by exact stack; remove strips matching stacks; clear empties a slot or the whole inventory:

inventory = player.inventory

if inventory.contains("minecraft:diamond"):            # any amount
    ...
if inventory.contains_at_least("minecraft:diamond", 5):  # at least five
    ...

inventory.remove("minecraft:dirt")  # remove every matching stack
inventory.clear(0)                  # empty one slot
inventory.clear()                   # empty everything

first("minecraft:bread") returns the index of the first matching slot (or -1), first_empty gives the first open slot, and is_empty is a quick whole-inventory check. Because Inventory also supports the Python container protocol, len(inventory), inventory[0], and "minecraft:diamond" in inventory all work as you'd expect.

A kit command

Tying it together: a command that hands the player a named, enchanted weapon and a stack of food. The kit is built once in on_enable and copied on each use, so every player gets their own stacks:

src/endstone_my_plugin/my_plugin.py
from endstone import Player
from endstone.command import Command, CommandSender
from endstone.inventory import ItemStack
from endstone.plugin import Plugin

class MyPlugin(Plugin):
    api_version = "0.11"

    commands = {
        "kit": {
            "description": "Receive the starter kit.",
            "usages": ["/kit"],
            "permissions": ["my_plugin.command.kit"],
        }
    }

    permissions = {
        "my_plugin.command.kit": {"default": True},
    }

    def on_command(self, sender: CommandSender, command: Command, args: list[str]) -> bool:
        if not isinstance(sender, Player):
            sender.send_error_message("This command can only be run by a player.")
            return True

        sword = ItemStack("minecraft:iron_sword")
        meta = sword.item_meta
        meta.display_name = "Recruit's Blade"
        meta.lore = ["Standard issue"]
        meta.add_enchant("minecraft:unbreaking", 3, False)
        sword.set_item_meta(meta)

        food = ItemStack("minecraft:cooked_beef", 32)

        leftover = sender.inventory.add_item(sword, food)
        if leftover:
            sender.send_message("Make room in your inventory first!")
        else:
            sender.send_message("Kit delivered. Good luck out there.")
        return True

To inspect what a player is holding instead - a click-to-use wand, say - read item_in_main_hand inside an event handler and compare its type:

from endstone.event import event_handler, PlayerInteractEvent

    @event_handler
    def on_interact(self, event: PlayerInteractEvent) -> None:
        held = event.player.inventory.item_in_main_hand
        if held is not None and held.type == "minecraft:stick":
            event.player.send_message("You wave the stick.")

ItemStack.type compares directly against a type id string, so there's no enum to look up - check the held item's type and act on it.

On this page