Item Data
Every ItemInstance carries a typed data bag — a Map<Key, Object> persisted alongside the item in the PDC. Built-in adapters cover XP/level progression and "remember a Material" use cases; plugins can register their own adapters via ItemDataRegistry.
How it works
A blueprint declares its
default-datablock — keys areKeystrings (e.g.astralitems:scalable), values are deserialised by the registered adapter.ItemFactory.make(blueprint)copies thedefault-dataentries into the newItemInstance's data map.When the stack is serialised,
ItemInstanceDataTypeiterates the data map and asks each adapter to write its entry into a nestedPersistentDataContainerkeyed by the adapter'sKey.When the stack is deserialised, the same key drives
ItemDataRegistry.findByKeyto recover the adapter and read the bytes back into a typed object.
Each piece of data is opaque to the framework — it is only the adapter that knows how to read or write it. New adapters can be added without touching the rest of the plugin.
ItemDataAdapter contract
Method | Purpose |
|---|---|
| The |
| Read a value from the binary stream. Called once per item when the stack is loaded. |
| Write a value to the binary stream. Called when the item is mutated and saved. |
| Runtime class for type-safe lookups via |
BinaryMessage is the same primitive stream used by AstralCore's packet protocol — see Infrastructure › Messaging for the helpers (writeInt, writeEnum, writeOptional, writeUtf8, …).
Registering an adapter
ItemDataRegistry is a static @UtilityClass — register early (during plugin enable, before any blueprint that references the key is loaded):
The registry uses a ConcurrentHashMap, so it is safe to register from any thread — but blueprints loaded before registration will not be able to resolve the key. Register synchronously on enable.
A minimal custom adapter
A "kill counter" that tracks how many entities a sword has killed:
Blueprint reference:
Mutating from a pipeline link (example sketch — registering a custom link is not yet pluggable, but the same pattern applies inside a Bukkit listener):
Making the data placeholder-accessible
To expose data through the placeholder system, the data class can implement ComplexPlaceholder (namespace() + get(PlaceholderContext)). The framework's built-in adapters do this — see ScalableItemData and StoredMaterialItemData. Implementations are then reachable from YAML as %instance_data_<key>_<subkey>%.
Built-in adapters
ScalableItemData
Key: astralitems:scalable
Holds a level + experience pair. Mutated by the scalable link.
Placeholders (namespace scalable):
Placeholder | Resolves to |
|---|---|
| Current level. |
| Accumulated XP since the last level-up. |
| The data object itself (for |
Default-data form:
StoredMaterialItemData
Key: astralitems:stored-material
Remembers one Material. Used by set-stored-material, build-wand, and similar tools that need to "lock" a block type onto the item.
Placeholders (namespace stored-material):
Placeholder | Resolves to |
|---|---|
| The stored |
| Same as above — explicit subkey form. |
| Translatable component for the material (e.g. |
| Adventure |
Default-data form:
StoredEntityItemData
Key: astralitems:entity
Stores a captured entity as serialised bytes. Written by store-entity and cleared by release-entity — the backing data for mob-catcher / pet-wand items.
Placeholders (namespace stored-entity):
Placeholder | Resolves to |
|---|---|
| The data object itself. |
|
|
Unlike the other built-in data types, StoredEntityItemData is written by pipeline links at runtime rather than declared in default-data — there is no meaningful literal for a serialised entity.
See also
Blueprints › default-data — YAML reference for declaring initial values.
Pipelines › PipelineContext — how the
dirty/replaceItemStackflags tell the framework to persist a mutated data bag back to the stack.Placeholders — full placeholder reference, including the
instance_data_*chain.