Astral Realms Documentation Help

Item System

The paper module exposes a small framework for building, serializing, and resolving item stacks. The same types are used everywhere an item is configured (menus, dialogs, NPC equipment, the /give command).

Overview

Type

Module / Package

Purpose

ItemStackWrapper

core.paper.model.itemstack

Placeholder-aware item template; lazily resolves to a Bukkit ItemStack.

ItemComponent<T>

core.paper.model.itemstack.component

Pluggable component that reads YAML and applies a piece of item meta.

ItemStackSupplier

core.paper.model.itemstack.supplier

Resolves an ItemStack for a (namespace, key) pair (vanilla, HDB, CraftEngine, …).

CommonRegistries

core.paper.registry

Holds the named registries for components and suppliers.

ItemStackUtils

core.paper.utils

Static helpers (areSimilar, areEquals, isAirOrNull).

ItemStackWrapper

ItemStackWrapper is an immutable record that implements PlaceholderWrapper<ItemStack>. Calling get() or get(parser) materializes the underlying Bukkit ItemStack — placeholders, math, and component values are re-evaluated each call.

ItemStackWrapper wrapper = node.get(ItemStackWrapper.class); // No context — uses RootPlaceholderContainer ItemStack stack = wrapper.get(); // With a per-player parser (PlaceholderContainer implements Function<String, Object>) PlaceholderContainer ctx = AstralPaperAPI.createPlaceholderContainer(player); ItemStack personalized = wrapper.get(ctx);

The wrapper holds:

  • material — the base Material (or a placeholder resolving to one).

  • copyFrom — optional ItemStack placeholder; when set, the wrapper clones it before applying overrides.

  • name, lore — Adventure ComponentWrapper/ComponentWrapperList for placeholder-aware text.

  • enchantments, itemFlags, amount — vanilla item meta values.

  • components — a map of registered ItemComponent<?> → its deserialized data object.

  • appendLore — when copyFrom is set, prepends the original lore to the wrapper's lore instead of replacing it.

Use it whenever you want config-defined items with placeholder substitution. For one-off items, build a plain Bukkit ItemStack directly.

ItemComponent

public interface ItemComponent<T> { T deserialize(ConfigurationNode node) throws SerializationException; void apply(ItemStack itemStack, ItemMeta meta, T data, @Nullable Function<String, Object> parser); }

Each component declares its own data type T. deserialize is called once at config load; apply is called every time the wrapper resolves. The parser argument is the placeholder context — pass it on to any PlaceholderWrapper fields you carry on T.

Registering a custom component

Components are stored in CommonRegistries.itemComponents(). Register early — before any YAML that references the new key is parsed:

public class CooldownItemComponent implements ItemComponent<Integer> { @Override public Integer deserialize(ConfigurationNode node) { return node.getInt(0); } @Override public void apply(ItemStack item, ItemMeta meta, Integer seconds, @Nullable Function<String, Object> parser) { meta.getPersistentDataContainer().set( new NamespacedKey("mymod", "cooldown"), PersistentDataType.INTEGER, seconds ); } } CommonRegistries.itemComponents().register("cooldown", new CooldownItemComponent());

The component is now usable in any item-stack block:

item-stack: material: DIAMOND_SWORD cooldown: 30

Built-in components

The 17 built-in components are documented from a user perspective in Menu Items › Item Components. The concrete implementations live in com.astralrealms.core.paper.model.itemstack.component.

ItemStackSupplier

A supplier produces real ItemStack instances for a (namespace, key) pair. Suppliers are how the system integrates with item plugins like HeadDatabase and CraftEngine.

public interface ItemStackSupplier { ItemStack get(String id); // by id (no player context) ItemStack get(Player player, Key key, int amount); // by adventure Key + amount (with player context) Collection<Key> completions(); // tab completion keys (used by /give) Key keyOf(ItemStack itemStack); // reverse-lookup: which supplier owns this stack? }

Built-in suppliers

Namespace

Class

Registered when

vanilla

VanillaItemStackSupplier

Always (built-in)

hdb

HeadDatabaseItemSupplier

HeadDatabaseAPIHook activates (HeadDatabase plugin present)

ce

CEItemStackSupplier

CraftEngineHook activates (CraftEngine plugin present)

Registering a custom supplier

public class MyItemSupplier implements ItemStackSupplier { @Override public ItemStack get(String id) { return MyRegistry.byId(id); } @Override public ItemStack get(Player p, Key k, int amount) { ItemStack s = get(k.value()); if (s != null) s.setAmount(amount); return s; } @Override public Collection<Key> completions() { return MyRegistry.allKeys(); } @Override public Key keyOf(ItemStack s) { return MyRegistry.idOf(s); } } AstralPaperAPI.registerItemStackSupplier("mymod", new MyItemSupplier());

After registration, the namespace is usable as a YAML material prefix:

item-stack: material: "mymod-magic_sword"

/give <player> mymod:magic_sword [amount] automatically gains a completion entry if the supplier returns the key from completions().

How the prefix is parsed

AstralPaperAPI.provideItemStack(String id) splits on the first -. The left side is looked up in the supplier registry; the right side is passed verbatim to supplier.get(key). A value with no - is treated as a plain vanilla Material name.

ItemStackUtils

Static helpers in com.astralrealms.core.paper.utils.ItemStackUtils:

Method

Description

isAirOrNull(ItemStack)

true if the stack is null or of an air material.

areSimilar(a, b)

Compare type, item meta, enchantments (or stored enchants for books), damage / max damage, and PersistentDataContainer bytes. Ignores amount.

areEquals(a, b)

areSimilar(a, b) && a.getAmount() == b.getAmount().

haveSimilarCustomModelData(a, b)

Deprecated. Compares item-model + custom-model-data only.

/give Command

The paper module ships a /give <player> <itemId> [amount] command (permission: astralrealms.command.give). It iterates all registered suppliers and uses the first one whose completions() contains the requested key.

When the MailboxService is available, the item goes through the mailbox; otherwise it falls back to inventory.addItem and drops the leftovers at the player's feet. A PlayerItemGiveEvent is fired on success.

# /give Steve hdb-12345 1 # /give Steve ce-mypack:magic_sword # /give Steve diamond_sword

Events

Event

When

Cancellable

PlayerItemGiveEvent

After /give successfully delivers an item to a player.

No

See Custom Events for the wider event API.

Last modified: 25 July 2026