Astral Realms Documentation Help

Pipelines

A pipeline is a small, declarative event handler attached to a blueprint. It has three stages:

Bukkit event │ ▼ ┌─────────┐ captures event state into PipelineContext │ HEAD │ (drops, XP, location, block, entity, extra context) └────┬────┘ │ ▼ ┌─────────┐ │ LINKS │ transform context state (multiply drops, take durability, │ … │ execute actions, melt, hammer, …) └────┬────┘ │ ▼ ┌─────────┐ │ TAILS │ apply side effects (drop on ground, magnet to inventory, │ … │ debug log) └─────────┘

Every component is a WrappedPipelineComponent — a typed wrapper that carries an optional requirements block. Requirements are evaluated against the player and the current PipelineContext before the component runs.

How a pipeline gets wired up

  1. The blueprint is deserialised. The pipeline type strings are looked up in PipelineService's registry, which maps block-breakBlockBreakPipelineHead.class, hammerHammerPipelineLink.class, and so on. Each component's fields are deserialised as a @ConfigSerializable record / bean.

  2. PipelineService.initialize() walks every loaded blueprint and reads the @PipelineHeadMeta annotation off the head class. The annotation says which Bukkit event and at what priority the head wants to run.

  3. EventService registers one dummy listener per (EventPriority, Event) pair. The dummy listener routes incoming events into an EventNode and from there into matching EventEntry instances:

    • preChecks — heads whose @PipelineHeadMeta(checkItem = false) runs item-agnostically (currently none registered; reserved for global hooks).

    • postChecks — heads keyed by the blueprint Key they belong to.

  4. At runtime, when Bukkit fires the event, EventProcessor.process(event, node) runs:

    • Resolve every ItemStack the event carries via the registered EventContextAdapter for that event type.

    • Pull each ItemInstance out of the PDC and look up matching postChecks entries by blueprint key.

    • The adapter builds a PipelineContext — including any extra context the event needs (for example DragAndDropContext for InventoryClickEvent).

    • Check requirements; on failure cancel the event and skip the pipeline.

    • Invoke head.handle(event, context) — if it returns false, skip the pipeline.

    • Run every chain link in order, then every tail in order.

    • If context.dirty() was set by a link, persist the updated ItemInstance back to the stack at the end of the run. If context.replaceItemStack() is also set, swap the live ItemStack for the one in context.itemStack() (used by scalable item evolution).

PipelineContext

PipelineContext is the data bus that flows through a pipeline run. Heads populate it from the event, links transform it, tails consume it.

Immutable inputs (set when the context is built):

Field

Type

Notes

player

Player

Resolved via the registered EventContextAdapter.

event

Event

The triggering Bukkit event.

itemStack

ItemStack

The custom item that triggered the pipeline.

instance

ItemInstance

UUID + blueprint + version + item data, read from the item PDC.

pipeline

Pipeline

The pipeline currently executing.

extraContext

List<Object>

Adapter-supplied extras (e.g. DragAndDropContext).

Mutable state (populated by the head, mutated by links, consumed by tails):

Field

Type

Populated by

Used by

slot

EquipmentSlot

Most heads

Listeners deciding which inventory slot to write back to

location

Location

Most heads

drop, magnet

block

Block

block-break, harvest-block, shear-block, interact

hammer, infinite-bucket, build-wand, plant-crops, replant-crops, spade-grass

entity

Entity

entity-damage, entity-kill, interact-entity, bucket-entity, fish, shear-entity

store-entity, release-entity

drops

List<ItemStack>

block-break, harvest-block, shear-block, shear-entity, entity-kill, fish, hammer, fell-tree, break-leaves, replant-crops

multiply-drops, melt, drop, magnet, mailbox

experience

int

block-break, entity-kill, fell-tree, break-leaves, replant-crops

multiply-exp, drop, magnet

dirty

boolean

Links that mutated the ItemInstance data bag (scalable, set-stored-material, …)

EventProcessor persists the instance back to the stack if true.

replaceItemStack

boolean

scalable when an item evolves to its next form

EventProcessor swaps the live stack for context.itemStack() when true.

PipelineContext doubles as a placeholder source: it implements Function<String, Object> and exposes a PlaceholderContainer pre-populated with player_*, item_* (via ItemStackPlaceholder), the instance_* placeholder, location_* if a location was captured, and an extra-context_* namespace for every adapter-supplied extra (e.g. extra-context_drag-and-drop_cursor_material). See Placeholders for the full list.

Any PlaceholderWrapper<T> in a component config — amount, multiplier, chance — resolves through this container, so you can write:

links: durability: type: take-durability amount: "%instance_data_scalable_level% / 5"

Extra context

Some events carry data that doesn't fit the standard (player, item, drops, …) model. Event context adapters can attach typed payloads to context.extraContext() that components retrieve with context.extraContext(Class):

Event

Extra context payload

Used by

InventoryClickEvent

DragAndDropContext — cursor stack + its ItemInstance (if any)

drag-and-drop head, set-stored-material, repair, unbreakable links

Each payload implements ComplexPlaceholder so its fields are reachable from YAML through the extra-context_<namespace>_… chain (e.g. %extra-context_drag-and-drop_cursor_material%).

Authoring a pipeline

A pipeline is declared inside a blueprint's pipelines block. The map key is the pipeline's id (unique per blueprint), the value is the pipeline definition:

pipelines: area-mine: head: type: block-break whitelist: [STONE, COBBLESTONE, IRON_ORE] links: hammer: type: hammer radius: 1 depth: 0 durability: type: take-durability amount: 1 tails: magnet: type: magnet

The pipeline above:

  1. Captures BlockBreakEvent only for stone-family blocks (head whitelist).

  2. Adds neighbouring blocks' drops to the context via hammer.

  3. Takes 1 durability from the item via take-durability.

  4. Sends everything in drops straight to the player's inventory via magnet.

For the full catalogue of components see:

Type registry

PipelineService registers components under short string IDs. These are the strings you put in type:.

Kind

Type IDs

Heads

block-break, entity-damage, entity-kill, bucket-entity, fish, interact, interact-entity, equip-hand, drag-and-drop, harvest-block, shear-block, shear-entity, equip-armor, unequip-armor

Links — generic

take-durability, repair, unbreakable, execute-actions, execute-random-actions, multiply-drops, multiply-exp, remove-item, remove-cursor, set-item-model, debug-context

Links — data

scalable, set-stored-material, store-entity, release-entity

Links — ability

melt, hammer, infinite-bucket, break-leaves, build-wand, fell-tree, firework-boost, plant-crops, replant-crops, spade-grass

Tails

drop, magnet, mailbox, debug

The registry is populated in PipelineService's constructor. Custom components are not yet pluggable from outside the plugin.

Last modified: 25 July 2026