Pipelines
A pipeline is a small, declarative event handler attached to a blueprint. It has three stages:
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
The blueprint is deserialised. The pipeline
typestrings are looked up inPipelineService's registry, which mapsblock-break→BlockBreakPipelineHead.class,hammer→HammerPipelineLink.class, and so on. Each component's fields are deserialised as a@ConfigSerializablerecord / bean.PipelineService.initialize()walks every loaded blueprint and reads the@PipelineHeadMetaannotation off the head class. The annotation says which Bukkit event and at what priority the head wants to run.EventServiceregisters one dummy listener per(EventPriority, Event)pair. The dummy listener routes incoming events into anEventNodeand from there into matchingEventEntryinstances:preChecks— heads whose@PipelineHeadMeta(checkItem = false)runs item-agnostically (currently none registered; reserved for global hooks).postChecks— heads keyed by the blueprintKeythey belong to.
At runtime, when Bukkit fires the event,
EventProcessor.process(event, node)runs:Resolve every
ItemStackthe event carries via the registeredEventContextAdapterfor that event type.Pull each
ItemInstanceout of the PDC and look up matchingpostChecksentries by blueprint key.The adapter builds a
PipelineContext— including any extra context the event needs (for exampleDragAndDropContextforInventoryClickEvent).Check
requirements; on failure cancel the event and skip the pipeline.Invoke
head.handle(event, context)— if it returnsfalse, skip the pipeline.Run every chain link in order, then every tail in order.
If
context.dirty()was set by a link, persist the updatedItemInstanceback to the stack at the end of the run. Ifcontext.replaceItemStack()is also set, swap the live ItemStack for the one incontext.itemStack()(used byscalableitem 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 |
|---|---|---|
|
| Resolved via the registered |
|
| The triggering Bukkit event. |
|
| The custom item that triggered the pipeline. |
|
| UUID + blueprint + version + item data, read from the item PDC. |
|
| The pipeline currently executing. |
|
| Adapter-supplied extras (e.g. |
Mutable state (populated by the head, mutated by links, consumed by tails):
Field | Type | Populated by | Used by |
|---|---|---|---|
|
| Most heads | Listeners deciding which inventory slot to write back to |
|
| Most heads |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Links that mutated the | EventProcessor persists the instance back to the stack if |
|
|
| EventProcessor swaps the live stack for |
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:
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 |
|---|---|---|
|
|
|
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:
The pipeline above:
Captures
BlockBreakEventonly for stone-family blocks (head whitelist).Adds neighbouring blocks' drops to the context via
hammer.Takes 1 durability from the item via
take-durability.Sends everything in
dropsstraight to the player's inventory viamagnet.
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 |
|
Links — generic |
|
Links — data |
|
Links — ability |
|
Tails |
|
The registry is populated in PipelineService's constructor. Custom components are not yet pluggable from outside the plugin.