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 getInventory(), which returns a PlayerInventory & you read and mutate directly:

include/my_plugin.h
#include <endstone/endstone.hpp>

namespace es = endstone;

es::PlayerInventory &inventory = player.getInventory();
std::optional<es::ItemStack> held = inventory.getItemInMainHand();

PlayerInventory derives from 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

Construct an es::ItemStack from a type id and an amount, then hand it to the player with addItem:

es::ItemStack sword("minecraft:diamond_sword");  // amount defaults to 1
es::ItemStack apples("minecraft:golden_apple", 16);

auto leftover = player.getInventory().addItem(sword, apples);

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

auto leftover = player.getInventory().addItem(sword);
if (!leftover.empty()) {
    player.sendMessage("Your inventory is full!");
}

The ItemStack constructor is explicit and throws std::invalid_argument for an unknown type id, so build stacks from known-good identifiers. The constructor also takes a third data argument for the item's data value.

getType(), getAmount(), and getData() (with matching setters) read and change a stack after construction.

Work with specific slots

addItem 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 getItem and setItem:

auto &inventory = player.getInventory();

std::optional<es::ItemStack> first = inventory.getItem(0);  // empty if the slot is empty
inventory.setItem(0, es::ItemStack("minecraft:torch", 64));

getItem returns an empty std::optional for an empty slot, so check it before dereferencing. Passing std::nullopt to setItem empties the slot.

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

inventory.setItemInMainHand(es::ItemStack("minecraft:diamond_pickaxe"));
inventory.setItemInOffHand(es::ItemStack("minecraft:shield"));

inventory.setHelmet(es::ItemStack("minecraft:netherite_helmet"));
inventory.setChestplate(es::ItemStack("minecraft:elytra"));
inventory.setLeggings(es::ItemStack("minecraft:diamond_leggings"));
inventory.setBoots(es::ItemStack("minecraft:diamond_boots"));

inventory.setHeldItemSlot(4);  // which hotbar slot is selected, 0-8

Each setter has a matching getter - getHelmet(), getItemInMainHand(), and friends - that returns a std::optional<es::ItemStack>, so you can read back exactly what a player is wearing or holding.

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 getItemMeta(), change it, then write it back with setItemMeta:

es::ItemStack sword("minecraft:diamond_sword");

auto meta = sword.getItemMeta();           // std::unique_ptr<ItemMeta> - a copy
meta->setDisplayName("Excalibur");
meta->setLore({"Forged in legend", "Sharpness beyond measure"});
meta->setDamage(200);                      // durability used, not removed
meta->setUnbreakable(true);                // never loses durability

sword.setItemMeta(meta.get());

getItemMeta() returns a std::unique_ptr<ItemMeta> holding a copy. Editing it leaves the original stack untouched until you call setItemMeta, so build the meta fully, then apply it once.

The meta exposes hasDisplayName(), hasLore(), and hasDamage() to test before reading, and you can clear a field by passing std::nullopt to setDisplayName or setLore. Enchantments live here too - addEnchant(id, level, force), removeEnchant(id), hasEnchant(id), and getEnchantLevel(id):

auto meta = sword.getItemMeta();
meta->addEnchant("minecraft:sharpness", 5, true);  // force past the normal level cap
sword.setItemMeta(meta.get());

You can also ask getServer().getItemFactory() for a fresh meta for a given type with getItemMeta("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 containsAtLeast test for items by type or by exact stack; remove strips matching stacks; clear empties a slot or the whole inventory:

auto &inventory = player.getInventory();

if (inventory.contains("minecraft:diamond")) {              // any amount
    // ...
}
if (inventory.containsAtLeast("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), firstEmpty() gives the first open slot, and isEmpty() is a quick whole-inventory check. getSize() reports the slot count and getContents() returns every slot as a std::vector<std::optional<es::ItemStack>>.

A kit command

Tying it together: a command that hands the player a named, enchanted weapon and a stack of food. Each run builds fresh stacks, so every player gets their own:

include/my_plugin.h
#include <endstone/endstone.hpp>

namespace es = endstone;

class MyPlugin : public es::Plugin {
public:
    bool onCommand(es::CommandSender &sender, const es::Command &command,
                   const std::vector<std::string> &args) override
    {
        auto *player = sender.asPlayer();
        if (!player) {
            sender.sendErrorMessage("This command can only be run by a player.");
            return true;
        }

        es::ItemStack sword("minecraft:iron_sword");
        auto meta = sword.getItemMeta();
        meta->setDisplayName("Recruit's Blade");
        meta->setLore({"Standard issue"});
        meta->addEnchant("minecraft:unbreaking", 3, false);
        sword.setItemMeta(meta.get());

        es::ItemStack food("minecraft:cooked_beef", 32);

        auto leftover = player->getInventory().addItem(sword, food);
        if (!leftover.empty()) {
            player->sendMessage("Make room in your inventory first!");
        } else {
            player->sendMessage("Kit delivered. Good luck out there.");
        }
        return true;
    }
};

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

void onInteract(es::PlayerInteractEvent &event)
{
    auto held = event.getPlayer().getInventory().getItemInMainHand();
    if (held && held->getType() == "minecraft:stick") {
        event.getPlayer().sendMessage("You wave the stick.");
    }
}

ItemType 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