Action System
The action system describes, in YAML, what should happen after a business event. It is one of the most frequently used CoreLib features, and shows up across Forge, Strengthen, Gem, Cooking, Skills, Item, Attribute, and Level.
A pipeline is one line of text
Field names differ between modules — actions.success, actions, deny_actions, result.success.actions — but the model is the same: every line in the list is an independent pipeline, and the lines run in order.
A pipeline chains stages with |, and data flows left to right:
self | chance 25% | after 20t | send_message text="<green>Triggered</green>"Read that as: take the caster as the target, pass a 25% roll, wait 20 ticks, then send the message. What travels between stages is a target flow — a list of targets. That split is the point of the model: picking targets and acting on them are different stages. The old killentity radius=5 limit=3 type=zombie bundled the search into the effect; now the search is nearby and the effect is kill_entity.
actions:
success:
- 'self | send_message text="<green>Forge success!</green>"'
- 'self | play_sound sound=minecraft:entity.player.levelup volume=1 pitch=1'
- 'run_command_as_console command="say %player_name% completed a forge"'NOTE
Stage arguments are placeholder-resolved before execution. CoreLib supplies %player%, %player_name%, %player_uuid%, %player_world%, %player_x%, %player_y%, %player_z%, resolves whatever the calling module wrote into the context, and resolves PlaceholderAPI placeholders. Variables the pipeline sets itself read back as %var.<name>%. For module-specific variables, see that module's page.
Third-party execution and structured results
Third-party plugins can compile and execute a complete pipeline with EmakiCoreLibApi.executeActionLineAsync(plugin, line, CoreActionExecutionContext). It is callable from any thread. The future may complete on the calling thread (for example, an immediate validation result) or in the final stage's execution domain, so schedule through EmakiCoreLibApi.scheduling() before a continuation touches Bukkit. Compilation and business failures complete normally as structured results; do not rely on exceptional completion alone.
CoreActionExecutionResult contains status, reasonKey/reasonArguments, compile diagnostics, ordered stages summaries, and keptTargets. Statuses are SUCCESS, SKIPPED, PARTIAL, COMPILE_FAILED, EXECUTION_FAILED, INVALID_REQUEST, and UNAVAILABLE, allowing callers to distinguish no work, partial completion, invalid configuration, and runtime failure.
The facade also exposes read-only registry queries: actionStages() / actionStage(id) and actionTriggers() / actionTrigger(id). For a custom stage, CoreActionExecutionTarget.contextEntity() selects the entity-context execution domain; planning contexts use context.target() or context.caster() (not subject()). Revoke the returned CoreStageRegistration with registration.close().
The compatibility callback onStageRegistryRebuilt(owner, callback) replaces the previous callback for the same owner. Use addStageRegistryRebuildListener(owner, callback) when one plugin needs multiple independent callbacks; each is appended independently and returns a closeable handle.
Minimal third-party execution example:
CoreActionExecutionContext context = CoreActionExecutionContext.builder()
.caster(player)
.build();
EmakiCoreLibApi.executeActionLineAsync(this,
"self | send_message text='<green>Hello</green>'", context)
.thenAccept(result -> getLogger().info(result.status().name()));Syntax
The authoritative grammar:
line := node ('|' node)*
node := branch | weighted | stage
branch := 'if' condition '[' line ']' ('else' '[' line ']')?
weighted := 'weight' (weight '[' line ']')+
stage := name arg*
arg := key '=' value | bare_valueKey points:
- A pipeline is always one line of text. There is no structured YAML form and no second parser.
- Stage name and arguments are separated by spaces, not parentheses. Write
send_message text="...", notsend_message(text="..."). - Arguments come in two forms:
key=value, or a bare positional value. Bare values map onto positional parameters in declaration order, sochance 25%is the same aschance chance=25%. - Stage names are lower-cased, so
Send_Messageandsend_messageare equivalent. Argument names are lower-cased too. - Blank lines and lines starting with
#are ignored. - Single and double quotes both work. Inside quotes,
\n,\t,\\,\", and\'escape. Quoted|,[, and]are ordinary characters, so the bar intext="a | b"is not a separator. - Outside quotes,
|,[, and]split tokens even without surrounding spaces:a|banda | bare identical. ||is the escaped bar, which is also logical OR in conditions. It is never treated as a separator.
Three roles
Every stage has a fixed role, and that role decides where it may appear in a pipeline:
| Role | Purpose | Examples |
|---|---|---|
| source | Produces the target flow. Usually the first stage. | self, looking_at, nearby radius=5 |
| gate | Filters, sorts, controls timing, or writes context values. | chance 25%, where ..., after 20t |
| action | Produces the actual effect, applied to every target in flow. | send_message, damage, give_money |
The role belongs to the stage, not to the position, which is why /emakicorelib action list groups by role instead of printing one flat list: a given id is only legal in one kind of slot.
Omitting the source defaults to self. That default has a consequence worth remembering: to act on the targets your caller passed in, you must write inherited explicitly, or the pipeline silently acts on the caster.
Branches
Conditions are no longer a prefix in front of a stage. They are a node in the pipeline:
if %player_world%=="world" [ self | send_message text="In the overworld" ] else [ self | send_message text="Somewhere else" ]The else branch is optional. A branch body is a complete pipeline in its own right, with its own source and as many stages as you like. Conditions support &&, ||, !, parentheses, and the comparisons <, <=, ==, !=, >=, >. Quote the right-hand side of string comparisons so they are not mistaken for arithmetic. Remember that logical OR is || inside a branch condition.
A branch body can also hold a single gate, for example stop to end early:
looking_at | if %var.dead% [ stop ] else [ damage amount=5 ]Weighted branches
Where if picks one of two by condition, weight picks one of many by weight: each weight is followed by its own branch body, and at runtime exactly one body is chosen in proportion to its share of the total.
self | weight 70 [ send_message text="<gray>Common</gray>" ] 25 [ send_message text="<blue>Rare</blue>" ] 5 [ broadcast_message text="<gold>Epic!</gold>" ]Read this as: roll once at relative odds 70/25/5, then run only the body that was drawn. Weights need not total 100 — writing 7 2.5 0.5 gives identical probabilities, because a weight is a relative share. See Weights.
Each weight sits directly in front of its own body rather than in one separate list. That is deliberate: had the weights been collected as [70, 25, 5] and matched positionally against the bodies that follow, inserting, deleting, or reordering a branch would silently misalign them, and that misalignment is invisible at load time — it only shows up as "the odds feel wrong".
Key points:
- A branch body is a complete pipeline, exactly like an
ifbody, with its own source and as many stages as you like. weightselects exactly one body. If you want each outcome judged independently so several can fire at once, that is multiplechancelines, notweight.- A weight of
0disables that branch; it can never be drawn. All weights being0is a load-time error, because the stage could then never select anything. - Weights may be expressions or placeholders, for example
weight %var.luck% [ ... ] 30 [ ... ]. Placeholders are rendered before execution, so the odds can follow player state. - Weights must be non-negative. A negative weight is rejected at load time (literals) or reported at runtime (rendered placeholders) rather than ignored — otherwise one stray minus sign would make a branch vanish without a word.
- Weights are unnamed values, never
name=value.weight chance=70 [ ... ]is an error. weightandifshare the single nesting limitaction.pipeline.max_branch_depth(default 16), and may be nested inside each other.
weight and chance answer different questions. chance 25% is a single gate: miss the roll and the whole pipeline ends. weight is a multi-way choice that always selects something (unless every weight is 0). Use chance for "fires 25% of the time, otherwise nothing happens"; use weight for "always happens, but which outcome is decided by odds".
self | weight 1 [ nearby radius=5 | damage amount=8 ] 1 [ send_message text="<gray>Missed this time</gray>" ]
self | if %var.is_boss% [ weight 80 [ run drop_common ] 20 [ run drop_rare ] ] else [ run drop_trash ]Named sequences and run
A group of pipelines you reuse can be defined as a named sequence and called with run:
action:
templates:
reward_common:
- 'self | send_message text="<green>Reward claimed</green>"'
- 'self | play_sound sound=minecraft:entity.experience_orb.pickup volume=0.8 pitch=1.2'
level_up_effect:
- 'self | send_title title="<gold>Level up!</gold>" subtitle="<gray>Now level %var.level%</gray>"'
- 'self | play_sound sound=minecraft:entity.player.levelup volume=1 pitch=1'
- 'self | spawn_particle particle=totem_of_undying count=30 offset_x=0.5 offset_y=1 offset_z=0.5'Arguments go straight after run, and the sequence body reads them as %var.<name>%:
run reward_common
run level_up_effect level=10
run reward_with_amount amount=100 reason=daily_loginSequences can live under action.templates in the CoreLib main config or in a business module's own data directory. run only cares about the name, not where it was defined. Names are case-insensitive.
Sequences are compiled and cycle-checked at load time, so mutual recursion, calls to names that do not exist, and missing required arguments are reported when the config loads rather than at runtime.
Compile limits
These limits are checked at load time. Exceeding one rejects that config entry and keeps the last valid config instead of silently truncating — a truncated times=100000 looks like it worked, which is harder to diagnose than an error.
| Config key | Default | Meaning |
|---|---|---|
action.pipeline.max_repeat_times | 100 | Upper bound for every <interval> times <n> |
action.pipeline.max_sequence_depth | 8 | Maximum run nesting depth |
action.pipeline.max_branch_depth | 16 | Maximum if and weight nesting depth |
Loop task limits
Long-running loops started by start_task are governed separately by action.loop.*, independently of the compile limits above.
| Config key | Default | Meaning |
|---|---|---|
action.loop.enabled | true | Whether loop tasks may start at all. |
action.loop.min_sync_interval | 5t | Minimum interval for sync-domain loops. |
action.loop.min_async_interval | 100ms | Minimum interval for async-domain loops. |
action.loop.max_times | 7200 | Maximum executions for a single loop task. |
action.loop.max_active_loops_total | 5000 | Server-wide cap on active loop tasks. |
action.loop.max_active_loops_per_player | 16 | Per-player cap on active loop tasks. |
action.loop.max_active_loops_per_plugin | 1000 | Per-plugin cap on active loop tasks. |
action.loop.cancel_player_loops_on_quit | true | Cancel a player's loop tasks when they quit. |
action.loop.cancel_plugin_loops_on_disable | true | Cancel a plugin's loop tasks when it is disabled. |
Parameter types
Stage parameters carry the types listed below, and the parameter tables in this page name them. Types are validated at load time, so a malformed value does not wait until runtime to surface.
| Type | Meaning | Example |
|---|---|---|
| STRING | Text. Quote it when it contains spaces or special characters. | text="<green>Success" |
| INTEGER | Whole number. | amount=3, count=10 |
| DOUBLE | Decimal number. | volume=0.8, amount=25.5 |
| BOOLEAN | true or false. | delete_item=false, icon=true |
| DURATION | Time. Accepts t (ticks), s, ms; a bare number means ticks. | 20t, 1s, 500ms |
| TIME | The older tick/second notation, kept for compatibility. | 20t, 1s |
| PERCENTAGE | Probability. Accepts percentages, decimals, and fractions. | 50%, 0.5, 1/3 |
| ENTITY_TYPE | A Bukkit EntityType name. | type=zombie |
| MATERIAL | A Bukkit Material name. | material=stone |
| SOUND | A sound key: a Bukkit Sound enum name or namespace:key. | sound=minecraft:block.anvil.use |
| EXPRESSION | Arithmetic evaluated by the CoreLib expression engine. | amount=%var.level%*4+18 |
Migrating from the old syntax
If you still have old config, three rules cover most of the rewrite:
- Stage ids are underscore-separated.
sendmessagebecomessend_message,givemoneybecomesgive_money,runcommandasconsolebecomesrun_command_as_console, and so on for every id. @control prefixes became stages in the pipeline.@chance=25%becomeschance 25%,@delay=20tbecomesafter 20t, and@if=<condition>becomes anif <condition> [ ... ]branch.@ignore_failurehas no replacement: failure semantics now belong to each stage, so where you need "failure must not block", use a branch or move that stage onto its own line.- Write the source. Old action lines implicitly acted on the player from the event. A pipeline states its source:
selffor the caster (also the default when omitted),inheritedfor the targets your caller passed in.
A few ids changed by more than an underscore:
| Old | Now |
|---|---|
ray | source looking_at |
loopsync / loopasync | start_task (sync/async is no longer a config option; see that stage) |
cancelloop | stop_task |
@template=name / usetemplate | run name |
killentity radius=... type=... | source nearby does the search, kill_entity only removes |
createitem id=... senditem id=... | create_item publishes the single pipeline item, send_item reads it; no id |
castmythicskill | cast_mythic_skill, provided by EmakiSkills |
cast_skill carries one breaking semantic change: its skill argument is now an EmakiSkills skill id, no longer a MythicMobs skill id. Old config written as cast_skill skill=<mythic skill> must become cast_mythic_skill skill=<mythic skill>, otherwise it fails looking for an EmakiSkills skill that does not exist.
Built-in sources
A source produces the target flow and usually opens the pipeline. Omitting the source is equivalent to self.
self
The caster. No parameters.
self | send_message text="<green>Done</green>"inherited
The target flow handed in by the caller or by the previous phase. No parameters.
You have to write this one out: omitting the source falls back to self, so a pipeline that meant to act on inherited targets silently acts on the caster instead.
Pairs with keep: an earlier phase writes looking_at | keep to record the flow, and a later phase reads it back with inherited.
inherited | damage amount=8trigger
The entity a trigger named, for triggers whose subject differs from the caster. No parameters.
The context holds a name or UUID rather than an entity reference. Pipelines may be compiled long before they run, and parking live entities in context keys gets in the way of collection.
trigger | send_message text="<yellow>You set off the trap</yellow>"origin
The pipeline's spatial reference point, as a location target. No parameters.
origin | spawn_particle particle=flame count=20looking_at
The entity under the caster's crosshair.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
range | DOUBLE | No | 5 | Ray length. |
width | DOUBLE | No | 0.5 | Ray width. |
looking_at range=12 | damage amount=6
looking_at | keep | send_message text="<red>You are locked on</red>"nearby
Entities around the pipeline reference point.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
radius | DOUBLE | No | 1 | Search radius. |
limit | INTEGER | No | 1 | Maximum entities returned. |
type | ENTITY_TYPE | No | "" | Entity type filter; empty means no filter. |
include_players | BOOLEAN | No | false | Whether players count as candidates. |
Results are sorted by distance from the reference point, then truncated to limit. Finding no entity is "empty" and skips the following stages; naming an entity type that does not exist is "invalid" and raises an error. The old implementation treated a typo'd type as a skip, which meant server owners never saw their own mistake.
nearby radius=8 limit=5 type=zombie | damage amount=10
nearby radius=4 limit=20 include_players=true | give_potion_effect type=slowness level=1 duration=5snearby_players
Players around the pipeline reference point.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
radius | DOUBLE | No | 1 | Search radius. |
limit | INTEGER | No | 0 | Maximum returned; 0 means all. |
Same filtering logic as nearby, restricted to players. limit defaults to 0 (all) rather than 1, because party-wide and area-wide effects normally want every player, not the closest one.
nearby_players radius=10 | send_message text="<gold>Your party gained a buff</gold>"offset
A location offset from the pipeline reference point.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
x | DOUBLE | No | 0 | X offset. |
y | DOUBLE | No | 0 | Y offset. |
z | DOUBLE | No | 0 | Z offset. |
relative | BOOLEAN | No | false | Offset along the reference point's own facing. |
With relative=false the offset follows world axes. With relative=true it follows the reference point's facing, where z is forward and x is right.
offset y=2 | spawn_particle particle=end_rod count=30
offset z=3 relative=true | explosion power=2 break_blocks=falseat
An absolute coordinate, or one relative to the reference point.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
world | STRING | No | "" | World name; empty uses the reference point's world. |
x | STRING | No | ~ | X coordinate, supports ~. |
y | STRING | No | ~ | Y coordinate, supports ~. |
z | STRING | No | ~ | Z coordinate, supports ~. |
The coordinates are STRING rather than DOUBLE so that ~ notation keeps working: ~ is "the reference point's value on this axis" and ~5 is "five more than that", matching vanilla command coordinate syntax.
at world=world x=100 y=65 z=-30 | spawn_particle particle=end_rod count=50
at x=~ y=~-1 z=~ | set_block material=oak_planksplayer_by_name
An online player identified by name or UUID.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
name | STRING | Yes | — | Player name or UUID. |
name is positional, so name= can be dropped.
player_by_name Notch | send_message text="<yellow>You were called out</yellow>"
player_by_name %var.winner% | give_money amount=500Built-in gates
A gate filters the target flow, controls timing, or writes values into the pipeline context. Gates produce no effects of their own.
where
Empties the target flow when the condition is false.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
condition | STRING | Yes | — | Boolean condition. |
condition is positional. Placeholders are already substituted before the gate runs, so expressions like %target.health%<10 work directly.
The current granularity is the whole flow, not per target: a gate is invoked once per flow, with arguments rendered against the first target, so where either keeps the entire flow or clears it. For single-target flows — the common looking_at | where ... | damage shape — the two semantics are indistinguishable.
looking_at | where %target.health%<10 | kill_entity
nearby radius=6 limit=10 | where %player_world%=="world" | damage amount=4chance
Ends the pipeline when the roll fails.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
chance | PERCENTAGE | Yes | — | Probability, such as 50%, 0.5, 1/3. |
chance is positional. A failed roll reports "stopped", not "invalid" — losing the roll is the whole point of the stage. chance abc, on the other hand, is a typo the server owner needs to see as an error.
When something should always happen but which outcome is decided by odds, use weighted branches instead of stacking chance lines — separate lines are judged independently, so none may fire, or several may fire at once.
self | chance 10% | broadcast_message text="<gold>%player_name% hit a rare reward!</gold>"
self | chance 1/3 | give_money amount=100 provider=vaultlimit
Keeps the first count targets in the flow, preserving order.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
count | INTEGER | Yes | — | How many targets to keep. |
count is positional.
nearby radius=15 limit=50 | sort_by distance | limit 3 | damage amount=12sort_by
Sorts the target flow by distance or health.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
key | STRING | Yes | — | distance or health. |
order | STRING | No | asc | asc or desc. |
key is positional. Combined with limit this expresses "the three closest" or "the one with the least health".
nearby radius=20 limit=30 | sort_by health order=asc | limit 1 | kill_entity
nearby_players radius=25 | sort_by distance order=desc | limit 5 | send_title title="<red>Too far from the fight</red>"set
Writes pipeline variables, read back as %var.<name>%.
This stage declares no parameters, because the writer of the pipeline chooses the names — CoreLib cannot know in advance about a variable named by set damage=%var.level%*4+18. Load-time validation skips the unknown-parameter check for this stage.
Values that evaluate as arithmetic are stored as numbers, so %var.damage% reads back as 22 rather than %var.level%*4+18. Everything else is stored verbatim.
self | set damage=%var.level%*4+18 | set label=Critical | send_message text="<red>%var.label% %var.damage%</red>"keep
Marks the current target flow as the one to carry into the next phase. No parameters.
The flow already passes between stages inside a pipeline, so this gate passes it through untouched. What makes it more than a no-op is that the interpreter records the flow it sees, and that recording is how one phase hands its targets to the next. A skill script writes looking_at | keep in its cast phase and reads it back with inherited in its hit phase.
It records the flow as of the moment it runs, not the pipeline's final flow, so where the line sits within the phase does not change the result: later gates narrowing the flow do not alter what was recorded, and a second keep overwrites the first.
looking_at | keep | send_message text="<gray>Target locked</gray>"stop
Ends the pipeline here. No parameters.
It reports "stopped", so the pipeline result is a skip rather than a failure — deliberately stopping is not an error. Mostly used inside branches.
looking_at | if %var.dead% [ stop ] else [ damage amount=5 ]create_item
Builds an item and publishes it as the pipeline's item value.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
item_source | STRING | No | "" | Item source. |
amount | INTEGER | No | 1 | Item amount. |
It is registered as a gate rather than an action because writing typed context is a gate's job: only a gate's passed result is written back into the pipeline context. That is also why it passes the target flow through unchanged — the stage adds a value, it does not consume targets.
The pipeline holds a single item key, so there is nothing to name and the old id parameter is gone. A second create_item in the same pipeline replaces the first one's value.
item_source uses the CoreLib hyphenated item source shorthand — see Item Sources — for example vanilla minecraft-diamond or custom emakiitem-flame_blade. Vanilla sources do not accept colon notation such as minecraft:diamond.
self | create_item item_source=minecraft-golden_apple amount=2 | send_item
self | create_item item_source=emakiitem-flame_sword | send_itemafter
Delays every stage after it.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
delay | DURATION | Yes | — | Delay, such as 10t, 500ms, 2s. |
delay is positional. This is a timing stage rather than a flow transform: the interpreter recognizes after, treats the rest of the pipeline as its body, and schedules that body with the delay. The body re-validates caster, targets, and owning plugin before it runs, so a target that disappeared during the wait produces a skip rather than an error.
self | after 1s | send_message text="<gray>Sent one second later"
self | send_message text="<gold>Ready</gold>" | after 20t | send_message text="<red>Go!</red>"every
Repeats every stage after it on an interval.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
interval | DURATION | No | 1t | Interval, such as 20t or 1s. |
times | INTEGER | No | 0 | Extra runs after the first. |
Written as every <interval> times <n>. times counts runs beyond the first, so times 0 (the default) means the body runs once. The count is bounded by action.pipeline.max_repeat_times (default 100), and exceeding it rejects the config entry.
self | every 10t times 5 | spawn_particle particle=flame count=10
origin | every 1s times 3 | play_sound sound=minecraft:block.note_block.bell volume=1 pitch=1.5every suits short bursts inside one pipeline. For a long-running loop you can cancel by key, use start_task.
Built-in actions
Actions are the stages that produce effects. The "target requirement" noted for each stage says what kind of target flow it needs; when the requirement is not met the stage is skipped rather than failing the whole pipeline.
NONE: needs no target at all.OPTIONAL: runs either way, and still runs once with zero targets.REQUIRED_ENTITY: needs at least one entity target.REQUIRED_LOCATION: needs at least one location target.REQUIRED_ANY: needs at least one target, entity or location.
Messages and feedback
send_message
Sends a MiniMessage chat message. Target requirement REQUIRED_ENTITY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
text | STRING | Yes | — | Message text in MiniMessage. |
actions:
# Plain message
- 'self | send_message text="<green>Done!</green>"'
# With a placeholder
- 'self | send_message text="<yellow>Welcome back, %player_name%!</yellow>"'
# Richer MiniMessage: gradient plus hover and click
- 'self | send_message text="<hover:show_text:''Click for details''><click:run_command:/menu><gradient:gold:yellow>Open menu</gradient></click></hover>"'send_action_bar
Sends an action bar message. Target requirement REQUIRED_ENTITY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
text | STRING | Yes | — | Action bar text in MiniMessage. |
actions:
- 'self | send_action_bar text="<green>+50 XP</green>"'
- 'self | send_action_bar text="<gray>Cooking: <green>████</green><dark_gray>██████</dark_gray> 40%</gray>"'send_title
Shows a title and subtitle. Target requirement REQUIRED_ENTITY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
title | STRING | Yes | — | Main title in MiniMessage. |
subtitle | STRING | No | "" | Subtitle in MiniMessage. |
fade_in | DURATION | No | 10t | Fade-in time. |
stay | DURATION | No | 40t | Hold time. |
fade_out | DURATION | No | 10t | Fade-out time. |
actions:
# Plain title
- 'self | send_title title="<gold>Quest complete</gold>" subtitle="<gray>Experience awarded</gray>"'
# Custom timings
- 'self | send_title title="<red>Warning</red>" subtitle="<yellow>Entering a danger zone</yellow>" fade_in=5t stay=60t fade_out=20t'
# Title only, quick flash
- 'self | send_title title="<bold><gold>LEVEL UP!</gold></bold>" fade_in=0t stay=20t fade_out=5t'broadcast_message
Broadcasts a MiniMessage message to the server. Target requirement NONE.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
text | STRING | Yes | — | Broadcast text in MiniMessage. |
No target is needed, so the source can be left out:
actions:
# Server announcement
- 'broadcast_message text="<gold>[Notice] <white>Restarting in 5 minutes</white></gold>"'
# Achievement broadcast
- 'broadcast_message text="<light_purple>✦ %player_name% completed a legendary forge! ✦</light_purple>"'play_sound
Plays a sound for the target. Target requirement REQUIRED_ENTITY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
sound | SOUND | Yes | — | Sound key: a Bukkit Sound enum name or a minecraft: style key. |
volume | DOUBLE | No | 1 | Volume; above 1 increases audible range. |
pitch | DOUBLE | No | 1 | Pitch, 0.5–2.0, where 1 is normal. |
actions:
# Level-up chime
- 'self | play_sound sound=minecraft:entity.player.levelup volume=1 pitch=1'
# Anvil, lower pitch and audible from further away
- 'self | play_sound sound=minecraft:block.anvil.use volume=2 pitch=0.8'
# Play for everyone nearby
- 'nearby_players radius=12 | play_sound sound=minecraft:entity.villager.no volume=1 pitch=1'spawn_particle
Spawns particles at the target location. Target requirement REQUIRED_ANY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
particle | STRING | Yes | — | Particle key (Bukkit Particle enum name). |
count | INTEGER | No | 1 | Particle count. |
offset_x | DOUBLE | No | 0 | Spread along X. |
offset_y | DOUBLE | No | 0 | Spread along Y. |
offset_z | DOUBLE | No | 0 | Spread along Z. |
extra | DOUBLE | No | 0 | Extra data (speed or data, depending on particle type). |
Coordinates are no longer this stage's parameters: the source decides the location. self is the player's position, and at, offset, origin give an explicit one.
actions:
# Green particles on the player for success feedback
- 'self | spawn_particle particle=happy_villager count=15 offset_x=0.3 offset_y=0.5 offset_z=0.3'
# Flames on the player for a skill cast
- 'self | spawn_particle particle=flame count=30 offset_x=0.5 offset_y=0.2 offset_z=0.5 extra=0.05'
# Fixed world coordinates
- 'at world=world x=100 y=65 z=-30 | spawn_particle particle=end_rod count=50 offset_x=1 offset_y=2 offset_z=1 extra=0.02'
# Two blocks above the player
- 'offset y=2 | spawn_particle particle=explosion count=3 offset_x=0.1 offset_y=0.1 offset_z=0.1'boss_bar_show
Shows a boss bar to the target. Target requirement REQUIRED_ENTITY. Calling it again with the same id updates the existing bar.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
id | STRING | Yes | — | Boss bar id, used to update or hide it later. |
title | STRING | Yes | — | Bar title in MiniMessage. |
progress | DOUBLE | No | 1 | Progress, from 0 to 1. |
color | STRING | No | purple | Bar color. |
style | STRING | No | solid | Bar style. |
flags | STRING | No | "" | Comma-separated bar flags. |
actions:
- 'self | boss_bar_show id=forge_progress title="<gold>Forging</gold>" progress=0.5 color=yellow'
- 'self | boss_bar_show id=forge_progress title="<green>Almost done</green>" progress=0.9'boss_bar_hide
Hides a boss bar shown earlier. Target requirement REQUIRED_ENTITY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
id | STRING | Yes | — | Boss bar id, or all to hide every bar. |
actions:
- 'self | boss_bar_hide id=forge_progress'
- 'self | boss_bar_hide id=all'Health and state
heal
Restores the target's health. Target requirement REQUIRED_ENTITY. Never exceeds max health.
IMPORTANT
This stage sets health directly and does not fire Bukkit's EntityRegainHealthEvent. No healing bonus or reduction applies.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
amount | DOUBLE | Yes | — | Health restored, in half-hearts (1 = half a heart). |
actions:
# Restore 4 points (two hearts)
- 'self | heal amount=4'
# Full heal; the excess is clamped to max health
- 'self | heal amount=9999'
# Small heal behind a roll
- 'self | chance 30% | heal amount=2'damage
Removes health from the target. Target requirement REQUIRED_ENTITY. Health floors at 0.
IMPORTANT
This stage sets health directly and does not fire Bukkit's EntityDamageEvent. Armor, resistance, and enchantments do not reduce it, and it cannot kill a player because the floor is 0.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
amount | DOUBLE | Yes | — | Damage amount. |
actions:
# Remove 2 health (one heart)
- 'self | damage amount=2'
# Damage whatever is under the crosshair
- 'looking_at range=10 | damage amount=8'
# Behind a condition
- 'if %player_world%=="world_nether" [ self | damage amount=1 ]'set_health
Sets the target's health to a value. Target requirement REQUIRED_ENTITY. Clamped between 0 and max health.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
amount | DOUBLE | Yes | — | Target health. |
Use this stage with 0 to kill a player; kill_entity rejects player targets.
actions:
- 'self | set_health amount=20'
- 'self | set_health amount=1'
- 'looking_at | set_health amount=0'feed
Restores the target's food level. Target requirement REQUIRED_ENTITY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
amount | INTEGER | No | 20 | Food points restored. |
saturation | DOUBLE | No | 0 | Saturation restored. |
actions:
- 'self | feed'
- 'self | feed amount=6 saturation=3'ignite
Sets the target on fire. Target requirement REQUIRED_ENTITY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
duration | DURATION | No | 5s | Burn duration. |
actions:
- 'looking_at | ignite duration=8s'
- 'nearby radius=5 limit=10 | ignite'extinguish
Puts out the fire on the target. Target requirement REQUIRED_ENTITY. No parameters.
actions:
- 'self | extinguish'kill_entity
Removes the target entity. Target requirement REQUIRED_ENTITY. No parameters.
This is what remains of the old killentity once the search was taken out: radius, limit, type, and include_players all moved to the nearby source. Choosing which entities to act on was never this stage's job; now it simply removes whatever the flow hands it.
Player targets are rejected. Calling Entity#remove on a player is not a supported operation — to kill a player, use set_health amount=0.
actions:
# Clear up to five zombies within eight blocks
- 'nearby radius=8 limit=5 type=zombie | kill_entity'
# Clear whatever is under the crosshair
- 'looking_at range=20 | kill_entity'projectile
Launches a self-driven projectile from the caster. Target requirement OPTIONAL.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
speed | DOUBLE | No | 1.5 | Blocks travelled per tick. |
gravity | DOUBLE | No | 0.05 | Downward pull per tick. |
lifetime | INTEGER | No | 60 | Maximum lifetime in ticks. |
hit_radius | DOUBLE | No | 0.5 | Hit detection radius. |
pierce | INTEGER | No | 0 | How many extra entities it passes through. |
homing | BOOLEAN | No | false | Steer toward the current target. |
homing_strength | DOUBLE | No | 0.1 | Homing turn strength. |
particle | STRING | No | flame | Trail particle key. |
damage | DOUBLE | No | 0 | Damage dealt on hit; 0 means none. |
direction | STRING | No | look | Initial direction: look (line of sight) or target. |
actions:
# Straight fireball
- 'self | projectile speed=2 damage=8 particle=flame'
# Homing shot, which needs a target first
- 'looking_at range=25 | projectile homing=true homing_strength=0.2 damage=12 direction=target'
# Piercing shot
- 'self | projectile speed=2.5 pierce=3 damage=5 gravity=0'Potion effects
give_potion_effect
Adds a potion effect to the target. Target requirement REQUIRED_ENTITY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
type | STRING | Yes | — | Effect type, written as speed, minecraft:strength, and so on. |
level | INTEGER | Yes | — | Effect level, one-based. 1 maps to Bukkit amplifier 0 (in-game I). |
duration | DURATION | Yes | — | Duration, such as 100t, 5s, 10000ms. |
ambient | BOOLEAN | No | false | Ambient effect (sparser, more transparent particles). |
particles | BOOLEAN | No | true | Show particles. |
icon | BOOLEAN | No | true | Show the effect icon in the HUD. |
actions:
# Speed II for 30 seconds
- 'self | give_potion_effect type=speed level=2 duration=30s particles=true icon=true'
# Strength I for a minute, no particles for a subtler look
- 'self | give_potion_effect type=strength level=1 duration=60s particles=false icon=true'
# Night vision as an ambient effect
- 'self | give_potion_effect type=night_vision level=1 duration=5s ambient=true'
# Namespaced form
- 'self | give_potion_effect type=minecraft:regeneration level=3 duration=10s'
# Slow everything nearby
- 'nearby radius=6 limit=10 | give_potion_effect type=slowness level=2 duration=5s'remove_potion_effect
Removes one potion effect from the target. Target requirement REQUIRED_ENTITY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
type | STRING | Yes | — | Effect type. |
actions:
- 'self | remove_potion_effect type=slowness'
- 'self | remove_potion_effect type=minecraft:weakness'clear_potion_effects
Clears every active potion effect on the target. Target requirement REQUIRED_ENTITY. No parameters.
actions:
# Cleanse
- 'self | clear_potion_effects'
# Cleanse two seconds later
- 'self | after 2s | clear_potion_effects'
# Only in a specific world
- 'if %player_world%=="world_arena" [ self | clear_potion_effects ]'Items
Several item stages share the same item_source parameter, whose value is the CoreLib hyphenated item source shorthand. See Item Sources.
The slot parameter accepts:
mainhand,main_hand,handoffhand,off_handhelmet,chestplate/chest,leggings/legs,boots- inventory indexes
0to35, also writable asslot_0orhotbar_0
send_item
Hands the pipeline item to the target. Target requirement REQUIRED_ENTITY. No parameters.
This stage declares that it needs the pipeline item, which makes a send_item without a preceding create_item a load-time error rather than a runtime null: the validator compares that declaration against the context the trigger phase promises to provide.
The old id and keep parameters are both gone. The pipeline holds a single item key rather than a table indexed by name, and reading a context value does not consume it, so there is no longer anything to "keep in temporary storage".
actions:
- 'self | create_item item_source=minecraft-golden_apple amount=2 | send_item'give_item
Gives the item for an item source into the target's inventory. Target requirement REQUIRED_ENTITY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
item_source | STRING | No | "" | Item source. |
amount | INTEGER | No | 1 | Amount. |
The difference from create_item plus send_item: this stage gives straight from the source without going through the pipeline item, so it needs only one stage, but it also gives you no chance to do anything to the item first.
actions:
- 'self | give_item item_source=minecraft-diamond amount=5'
- 'nearby_players radius=10 | give_item item_source=emakiitem-strengthen_stone amount=1'set_item
Sets the item in one of the target's slots from an item source. Target requirement REQUIRED_ENTITY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
slot | STRING | No | mainhand | Inventory slot. |
item_source | STRING | No | "" | Item source. |
amount | INTEGER | No | 1 | Amount. |
actions:
- 'self | set_item slot=mainhand item_source=emakiitem-flame_sword'
- 'self | set_item slot=helmet item_source=minecraft-diamond_helmet'clear_item
Clears the item in one of the target's slots. Target requirement REQUIRED_ENTITY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
slot | STRING | Yes | — | Inventory slot. |
item_source | STRING | No | "" | Expected item source. When set, the slot is only cleared on a match, otherwise skipped. |
actions:
# Clear the main hand unconditionally
- 'self | clear_item slot=mainhand'
# Only clear it if it holds a stick
- 'self | clear_item slot=mainhand item_source=minecraft-stick'
# Clear the helmet slot only for a matching custom item
- 'self | clear_item slot=helmet item_source=emakiitem-cursed_helmet'
# Clear a specific inventory index
- 'self | clear_item slot=0'take_item
Removes matching items from the target's inventory. Target requirement REQUIRED_ENTITY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
item_source | STRING | No | "" | Item source to remove. |
amount | INTEGER | No | 1 | Amount to remove. |
actions:
- 'self | take_item item_source=minecraft-emerald amount=3'
- 'self | take_item item_source=emakiitem-forge_token amount=1'drop_item
Drops an item at the target location. Target requirement REQUIRED_ANY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
item_source | STRING | No | "" | Item source. |
amount | INTEGER | No | 1 | Item amount. |
The coordinates come from the source and are no longer this stage's parameters.
actions:
# Drop above the player
- 'offset y=2 | drop_item item_source=minecraft-diamond amount=1'
# Drop a reward at fixed coordinates
- 'at world=world x=100 y=65 z=-50 | drop_item item_source=minecraft-gold_ingot amount=5'
# Drop three blocks in front of the player
- 'offset z=3 relative=true | drop_item item_source=minecraft-emerald amount=2'repair_item
Repairs the durability of the item in a slot. Target requirement REQUIRED_ENTITY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
slot | STRING | No | mainhand | Inventory slot. |
amount | INTEGER | No | 0 | Durability points repaired; 0 or less repairs fully. |
actions:
- 'self | repair_item'
- 'self | repair_item slot=chestplate amount=50'damage_item
Adds durability damage to the item in a slot. Target requirement REQUIRED_ENTITY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
slot | STRING | No | mainhand | Inventory slot. |
amount | INTEGER | No | 1 | Durability damage added. |
delete_item | BOOLEAN | No | false | Remove the item when durability runs out. |
actions:
- 'self | damage_item amount=10'
- 'self | damage_item slot=mainhand amount=100 delete_item=true'Blocks and world
place_block
Places the block for an item source at the target location. Target requirement REQUIRED_ANY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
item_source | STRING | No | "" | Block item source. |
Vanilla blocks work, as do CraftEngine, ItemsAdder, Nexo, and Oraxen custom blocks. If the source is not a placeable block, the stage skips rather than breaking the pipeline. With a player context it fires the Bukkit place event, so a protection plugin cancelling it means nothing is placed.
actions:
# Vanilla block; note the hyphen between namespace and id
- 'at world=world x=100 y=64 z=-20 | place_block item_source=minecraft-stone'
# Under the player's feet
- 'at x=~ y=~-1 z=~ | place_block item_source=minecraft-oak_planks'
# CraftEngine custom block
- 'at x=~1 y=~ z=~ | place_block item_source=ce-cutting_board'
# ItemsAdder custom block
- 'offset y=2 | place_block item_source=itemsadder-decorative_lamp'set_block
Sets the block at the target location. Target requirement REQUIRED_ANY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
material | STRING | No | "" | Block material name. |
block_data | STRING | No | "" | Bukkit BlockData string. |
apply_physics | BOOLEAN | No | true | Trigger a block physics update. |
The difference from place_block: this stage takes a material directly instead of an item source and does not fire the Bukkit place event, so it can set block states that have no corresponding item.
actions:
- 'at world=world x=50 y=70 z=100 | set_block material=chest'
- 'at x=~ y=~-1 z=~ | set_block material=oak_stairs block_data="[facing=north,half=top]"'
- 'origin | set_block material=air apply_physics=false'break_block
Breaks or clears the block at the target location. Target requirement REQUIRED_ANY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
drop_items | BOOLEAN | No | false | Drop the block's items. |
apply_physics | BOOLEAN | No | true | Trigger a physics update when clearing the block. |
actions:
- 'at x=~ y=~-1 z=~ | break_block drop_items=true'
- 'at world=world x=10 y=64 z=10 | break_block'explosion
Creates an explosion at the target location. Target requirement REQUIRED_ANY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
power | DOUBLE | No | 0 | Explosion power. |
fire | BOOLEAN | No | false | Set fire. |
break_blocks | BOOLEAN | No | false | Break blocks. |
The defaults — power 0, no fire, no block damage — give you sound and visuals only. Real destruction has to be turned on explicitly.
actions:
# Cosmetic explosion
- 'self | explosion power=2'
# Explosion that actually breaks blocks
- 'looking_at range=15 | explosion power=4 fire=true break_blocks=true'spawn_entity
Spawns entities at the target location. Target requirement REQUIRED_ANY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
type | ENTITY_TYPE | Yes | — | Entity type. |
count | INTEGER | No | 1 | How many to spawn. |
actions:
- 'at world=world x=100 y=65 z=-30 | spawn_entity type=zombie count=3'
- 'offset z=4 relative=true | spawn_entity type=armor_stand'Teleport
teleport
Teleports the target to a coordinate. Target requirement REQUIRED_ENTITY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
world | STRING | No | "" | Destination world; empty uses the target's current world. |
x | STRING | No | ~ | X coordinate. Supports ~ relative notation (~5 means X+5). |
y | STRING | No | ~ | Y coordinate. Supports ~. |
z | STRING | No | ~ | Z coordinate. Supports ~. |
yaw | DOUBLE | No | target's current | Yaw (horizontal facing, 0–360). Omitted keeps the current facing. |
pitch | DOUBLE | No | target's current | Pitch (vertical facing, -90–90). Omitted keeps the current facing. |
The coordinates are this stage's own parameters rather than something the source provides: the source decides who gets teleported, the coordinates decide where to, and both are necessary.
actions:
# Fixed coordinates
- 'self | teleport world=world x=0 y=100 z=0 yaw=180 pitch=0'
# Relative, ten blocks up
- 'self | teleport y=~10'
# Across worlds
- 'self | teleport world=world_nether x=50 y=64 z=-100 yaw=90 pitch=0'
# Pull every nearby player to spawn
- 'nearby_players radius=30 | teleport world=world x=0 y=65 z=0'Economy
The three economy stages share one parameter set.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
amount | DOUBLE | Yes | — | Amount. |
provider | STRING | No | auto | Economy provider: auto, vault, or excellenteconomy. |
currency | STRING | No | "" | Currency id. Required when ExcellentEconomy runs multiple currencies. |
give_money
Adds to the target's balance. Target requirement REQUIRED_ENTITY.
actions:
# Basic, letting CoreLib pick the economy plugin
- 'self | give_money amount=100'
# Explicitly Vault
- 'self | give_money amount=500 provider=vault'
# ExcellentEconomy with multiple currencies
- 'self | give_money amount=50 provider=excellenteconomy currency=gems'take_money
Takes from the target's balance. Target requirement REQUIRED_ENTITY. The stage fails when the balance is insufficient.
actions:
- 'self | take_money amount=25.5 provider=vault'
- 'self | take_money amount=10 provider=excellenteconomy currency=coins'set_money
Sets the target's balance to a value. Target requirement REQUIRED_ENTITY.
actions:
- 'self | set_money amount=0 provider=vault'
- 'self | set_money amount=1000 provider=auto'Experience
The three experience stages share one parameter set, and all require REQUIRED_ENTITY.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
amount | INTEGER | Yes | — | Experience points or levels. |
mode | STRING | No | points | Mode: points (experience points) or levels. |
give_exp
Adds experience to the target.
actions:
# 100 experience points
- 'self | give_exp amount=100 mode=points'
# Three levels
- 'self | give_exp amount=3 mode=levels'
# points is the default and can be omitted
- 'self | give_exp amount=50'take_exp
Removes experience from the target, flooring at 0.
actions:
- 'self | take_exp amount=200 mode=points'
- 'self | take_exp amount=1 mode=levels'set_exp
Sets the target's total experience or level.
actions:
- 'self | set_exp amount=30 mode=levels'
- 'self | set_exp amount=0 mode=points'Commands
The three command stages share one parameter. A leading / is stripped automatically, but leaving it out of config reads better.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
command | STRING | Yes | — | The command to run, without a leading /. |
run_command_as_player
Runs a command as the target player. Target requirement REQUIRED_ENTITY. Subject to the player's own permissions.
actions:
- 'self | run_command_as_player command="spawn"'
- 'self | run_command_as_player command="menu open main"'run_command_as_op
Temporarily grants the target player OP, runs the command, then restores the original OP state. Target requirement REQUIRED_ENTITY.
CAUTION
This stage grants OP temporarily, which carries real risk: for the duration of the command the player holds full OP. If any part of the command comes from a placeholder the player can influence, you have handed out a privilege escalation path. Prefer run_command_as_console, or grant a temporary permission through your permissions plugin.
actions:
- 'self | run_command_as_op command="lp user %player_name% permission set example.vip true"'run_command_as_console
Runs a command as the console. Target requirement NONE (placeholders are still resolved).
No target is needed, so the source can be left out:
actions:
# Server announcement
- 'run_command_as_console command="say %player_name% completed a forge"'
# Grant a permission
- 'run_command_as_console command="lp user %player_name% permission set forge.master true"'
# Call another plugin
- 'run_command_as_console command="crate give %player_name% legendary 1"'Long-running tasks
every covers short bursts inside a single pipeline. For a loop that runs for a long time, can be cancelled by key, and stops on its own when the player logs off or dies, use the two stages below.
start_task
Starts a named sequence that repeats on an interval. Target requirement OPTIONAL.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
sequence | STRING | Yes | — | Name of the sequence to repeat. |
times | INTEGER | No | 1 | How many times to run. |
interval | DURATION | No | 20t | Interval between runs. |
initial_delay | DURATION | No | 0t | Delay before the first run. |
key | STRING | No | "" | Task key, used to cancel it later. |
on_conflict | STRING | No | replace | Key conflict policy: replace, ignore, allow_duplicate. |
stop_when_offline | BOOLEAN | No | true | Stop when the player logs off. |
stop_when_dead | BOOLEAN | No | false | Stop when the player dies. |
stop_when | STRING | No | "" | Stop once this condition holds. |
stop_on_failure | BOOLEAN | No | false | Stop when the sequence fails. |
This one stage replaces the old loopsync and loopasync. In the old implementation the async marker only picked a different minimum interval and ran one extra pre-check; the scheduling was identical either way. Real thread affinity now comes from the execution domain each stage declares for itself, so there is nothing left for a config-level sync/async switch to express.
Any parameter not in the table above is passed down as a sequence argument and read inside the sequence as %var.<name>%. This replaces the old with. prefix, which only existed because the old parameter model could not tell declared parameters from extra ones. It can now.
action:
templates:
burn_tick:
- 'inherited | damage amount=1'
- 'inherited | spawn_particle particle=flame count=5'
buff_pulse:
- 'self | send_action_bar text="<gold>Blessing: %var.label%</gold>"'
actions:
# Once a second, ten times
- 'looking_at | keep | start_task sequence=burn_tick times=10 interval=20t key=burn_%player_name%'
# With a sequence argument, stopping on death
- 'self | start_task sequence=buff_pulse times=30 interval=1s key=buff stop_when_dead=true label=Holy_Blessing'
# Stop as soon as the condition no longer holds
- 'self | start_task sequence=buff_pulse times=100 interval=20t stop_when=%player_world%!="world_arena"'stop_task
Cancels running tasks by key. Target requirement NONE.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
key | STRING | Yes | — | Task key to cancel. |
match | STRING | No | exact | Match mode: exact or prefix. |
Cancelling nothing counts as a skip, not a failure. Config routinely cancels a task that may not be running — cleaning up a buff loop on an event that fires in both cases, for instance — and treating that as an error would fill the log with correct configuration.
actions:
- 'stop_task key=burn_%player_name%'
- 'stop_task key=buff_ match=prefix'Scripts
The three js_* stages run GraalJS scripts. They share the same parameters and differ only in target requirement and execution domain, so picking the wrong one means the script touches Bukkit API from the wrong thread.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
code | STRING | Yes | — | JavaScript source. |
timeout | INTEGER | No | 5000 | Timeout in milliseconds. |
A timeout of zero or less falls back to the default. Empty code, a timeout, and an interruption all count as skipped rather than failed; only a thrown script error fails the stage.
The script's evaluated value is written to a single key, script_result, in that stage's own outcome data. It does not become a pipeline variable, so later stages cannot read %var.script_result%.
js_compute
Pure computation with no Bukkit state access. Target requirement NONE, execution domain ASYNC_COMPUTE. Binds context, plus player when the current target is a player.
Because it runs on an async thread, the script must not touch Bukkit state.
Inside the script, context.getVariable(name) and context.hasVariable(name) read pipeline variables.
actions:
- 'self | set level=%player_level% | js_compute code="context.getVariable(''level'') * 10"'js_entity
Scripts that act on a player or entity. Target requirement REQUIRED_ENTITY, execution domain CONTEXT_ENTITY. Binds player and context; skipped when the target is not a player.
js_location
Scripts that act on a block or location. Target requirement REQUIRED_LOCATION, execution domain LOCATION_REGION. Binds location and context, plus a read-only player when the current target is a player.
MythicMobs and skills
cast_mythic_skill and cast_skill come from the EmakiSkills module and are not CoreLib built-ins. For their parameters and behaviour, see Skills CoreLib Actions.
Listing and running pipelines from commands
Administrators can inspect the current registry and run a pipeline by hand from inside the server:
/corelib action list
/corelib action run <pipeline text>The same subcommand is available through /emakicorelib action, /emakicore action, and the plural alias actions. It reuses the emakicorelib.admin permission. Console execution has no player context, so stages needing an entity target skip through the normal target-requirement path. The run keyword is optional: /corelib action self | heal amount=4 is equivalent to spelling it out.
action list groups ids by role (source / gate / action) and shows the owning plugin. Grouping rather than a flat list is deliberate: an id is only legal in one position within a pipeline, and a flat list cannot tell an operator where it may be used.
action run compiles before it executes, so a syntax error or unknown stage name arrives with its own diagnostic instead of a vague execution failure. Only the first diagnostic is shown — the compiler reports every problem on a line, and the rest are usually consequences of the first. When execution finishes it reports the pipeline's overall result, naming the failing stage on failure, because a hand-typed pipeline is normally being debugged and the failing stage is the answer.
Where to find sub-plugin stages
CoreLib owns the stage registry and the pipeline engine. Business plugins may append their own sources, gates, and actions to that same registry when they enable. To keep this page from becoming a stale catalogue of every plugin's stage parameters, it documents only CoreLib built-ins.
Stages registered by sub-plugins live on those plugins' own action pages:
| Source module | Documentation |
|---|---|
| EmakiAttribute | Attribute CoreLib Actions |
| EmakiForge | Forge CoreLib Actions |
| EmakiStrengthen | Strengthen CoreLib Actions |
| EmakiCooking | Cooking CoreLib Actions |
| EmakiGem | Gem CoreLib Actions |
| EmakiLevel | Level CoreLib Actions |
| EmakiSkills | Skills CoreLib Actions |
| EmakiItem | Item CoreLib Actions |
| EmakiCodex | Codex CoreLib Actions |
When a stage comes from a business plugin, its parameters, context variables, and execution timing are defined by that plugin's documentation.
Registering stages from third-party plugins
External developers can depend on emaki-corelib-api alone and register custom stages into CoreLib's shared registry through the method matching the role:
EmakiCoreLibApi.registerActionStage(plugin, stage)— register an actionEmakiCoreLibApi.registerActionSource(plugin, source)— register a sourceEmakiCoreLibApi.registerActionGate(plugin, gate)— register a gate
CoreLib automatically revokes stages and rebuild callbacks when the owner plugin is disabled; callers should still close returned handles during normal lifecycle cleanup.
An action implements CoreActionStage and declares id, category, description, parameters, targetRequirement, requiredContext, and executionTarget. CoreLib reuses the same lexer, grammar, parameter validation, placeholder rendering, scheduling, and debug output.
executionTarget has no default implementation, and that is intentional: a stage has to state which thread it runs on rather than inheriting a vague default. requiredContext is the mechanism that turns "missing an upstream stage" into a load-time error — send_item declares that it needs the pipeline item, so the validator catches a missing create_item while the config is loading.
Minimal example:
CoreStageRegistration registration = EmakiCoreLibApi.registerActionStage(plugin, new CoreActionStage() {
@Override
public String id() {
return "my_custom_stage";
}
@Override
public String category() {
return "myplugin";
}
@Override
public String description() {
return "Runs my plugin's custom effect.";
}
@Override
public List<CoreStageParameter> parameters() {
return List.of(CoreStageParameter.required("value", CoreStageParameterType.STRING, "Custom value"));
}
@Override
public CoreTargetRequirement targetRequirement() {
return CoreTargetRequirement.REQUIRED_ENTITY;
}
@Override
public CoreActionExecutionTarget executionTarget(CoreStagePlanningContext context) {
// Pick the entity's own thread when you need main-thread Bukkit APIs.
return CoreActionExecutionTarget.contextEntity();
}
@Override
public CoreActionOutcome execute(CoreStageContext context, CoreResolvedArguments arguments) {
// Your plugin logic goes here.
return CoreActionOutcome.success();
}
});
// On plugin disable:
registration.close();Stage ID reference
source
| id | Purpose |
|---|---|
self | The caster (the default when omitted) |
inherited | The target flow passed in by the caller |
trigger | The entity named by the trigger |
origin | The pipeline's reference point location |
looking_at | The entity under the crosshair |
nearby | Entities in range |
nearby_players | Players in range |
offset | A location offset from the reference point |
at | An absolute or relative coordinate |
player_by_name | A player named by name or UUID |
gate
| id | Purpose |
|---|---|
where | Clears the flow when the condition is false |
chance | Ends the pipeline when the roll fails |
limit | Keeps the first N targets |
sort_by | Sorts by distance or health |
set | Writes %var.*% pipeline variables |
keep | Records the flow for the next phase to inherit |
stop | Ends the pipeline here |
create_item | Builds and publishes the pipeline item |
after | Delays every following stage |
every | Repeats every following stage on an interval |
action
| id | Category | Target requirement | Purpose |
|---|---|---|---|
send_message | message | REQUIRED_ENTITY | Send a chat message |
send_action_bar | message | REQUIRED_ENTITY | Send an action bar |
send_title | message | REQUIRED_ENTITY | Show a title |
broadcast_message | message | NONE | Broadcast server-wide |
play_sound | feedback | REQUIRED_ENTITY | Play a sound |
spawn_particle | feedback | REQUIRED_ANY | Spawn particles |
boss_bar_show | feedback | REQUIRED_ENTITY | Show a boss bar |
boss_bar_hide | feedback | REQUIRED_ENTITY | Hide a boss bar |
heal | entity | REQUIRED_ENTITY | Restore health |
damage | entity | REQUIRED_ENTITY | Remove health |
set_health | entity | REQUIRED_ENTITY | Set health |
feed | entity | REQUIRED_ENTITY | Restore food |
ignite | entity | REQUIRED_ENTITY | Set on fire |
extinguish | entity | REQUIRED_ENTITY | Put out fire |
kill_entity | entity | REQUIRED_ENTITY | Remove an entity |
projectile | combat | OPTIONAL | Launch a self-driven projectile |
give_potion_effect | entity | REQUIRED_ENTITY | Add a potion effect |
remove_potion_effect | entity | REQUIRED_ENTITY | Remove a potion effect |
clear_potion_effects | entity | REQUIRED_ENTITY | Clear all potion effects |
send_item | item | REQUIRED_ENTITY | Send the pipeline item |
give_item | item | REQUIRED_ENTITY | Give an item by source |
set_item | item | REQUIRED_ENTITY | Set a slot's item |
clear_item | item | REQUIRED_ENTITY | Clear a slot's item |
take_item | item | REQUIRED_ENTITY | Remove items from inventory |
drop_item | item | REQUIRED_ANY | Drop an item at the target |
repair_item | item | REQUIRED_ENTITY | Repair durability |
damage_item | item | REQUIRED_ENTITY | Add durability damage |
place_block | world | REQUIRED_ANY | Place a block from an item source |
set_block | world | REQUIRED_ANY | Set a block by material |
break_block | world | REQUIRED_ANY | Break a block |
explosion | world | REQUIRED_ANY | Create an explosion |
spawn_entity | entity | REQUIRED_ANY | Spawn entities |
teleport | entity | REQUIRED_ENTITY | Teleport |
give_money | economy | REQUIRED_ENTITY | Add money |
take_money | economy | REQUIRED_ENTITY | Take money |
set_money | economy | REQUIRED_ENTITY | Set balance |
give_exp | player | REQUIRED_ENTITY | Add experience |
take_exp | player | REQUIRED_ENTITY | Remove experience |
set_exp | player | REQUIRED_ENTITY | Set experience |
run_command_as_player | command | REQUIRED_ENTITY | Run as the player |
run_command_as_op | command | REQUIRED_ENTITY | Run as temporary OP |
run_command_as_console | command | NONE | Run as the console |
start_task | task | OPTIONAL | Start a repeating sequence |
stop_task | task | NONE | Cancel tasks by key |
js_compute | script | NONE | Pure-computation JS script |
js_entity | script | REQUIRED_ENTITY | JS script acting on an entity |
js_location | script | REQUIRED_LOCATION | JS script acting on a location |
Troubleshooting
Unknown stage name at load time. Check that the id is underscore-separated: it is send_message, not sendmessage. Confirm the id exists with /corelib action list, and confirm it appears in the right position — a source cannot sit where an action belongs.
Right stage name, but "unknown parameter". A few parameter names changed too. Item sources are uniformly item_source (the source and item aliases are gone), set_block has no block alias, and extra start_task arguments are written directly without a with. prefix.
The pipeline was skipped without an error. An unmet target requirement skips rather than fails. The usual cause is a missing source that fell back to self, where self is empty without a player context — or a nearby that found nothing.
|| is not working in a branch condition. Logical OR must be written ||. A single | is read as a stage separator.
send_item errors at load time. The stage needs the pipeline item, so the same pipeline must have a create_item ahead of it.
times is reported as out of range. every ... times N is bounded by action.pipeline.max_repeat_times (default 100). For more repetitions, use start_task, whose count quota is governed separately by the task service.
Spaces in a value got split apart. Values containing spaces, MiniMessage tags, or commands must be quoted. Wrapping the whole pipeline line in single quotes in YAML is also worth doing, so : and # are not interpreted by YAML.
A complete example
A full post-forge success config, showing several stage types working together:
action:
success:
# Core feedback
- 'self | send_title title="<gold>Forge complete</gold>" subtitle="<gray>Quality: %forge_quality%</gray>" fade_in=5t stay=40t fade_out=10t'
- 'self | play_sound sound=minecraft:block.anvil.use volume=1 pitch=1.2'
- 'self | spawn_particle particle=happy_villager count=20 offset_x=0.5 offset_y=0.5 offset_z=0.5'
# Economy reward: a 10% chance to refund part of the cost
- 'self | chance 10% | give_money amount=50 provider=vault | send_message text="<yellow>Lucky! 50 coins refunded.</yellow>"'
# Experience reward
- 'self | give_exp amount=30 mode=points'
# Extra reward for high quality
- 'if %forge_quality%=="epic" [ self | send_message text="<light_purple>Epic quality! Bonus strengthen stone.</light_purple>" | create_item item_source=emakiitem-strengthen_stone amount=1 | send_item ]'
# Server broadcast, legendary only
- 'if %forge_quality%=="legendary" [ broadcast_message text="<gold>%player_name% forged legendary gear!</gold>" ]'
# Delayed reminder
- 'self | after 2s | send_message text="<gray>Your gear is updated; check your inventory.</gray>"'Note the second reward line: once chance passes, both give_money and send_message further along the same pipeline run. The old syntax needed the chance written on both lines, rolling twice independently, which could pay out the money without showing the message. One pipeline rolls once, and every stage after it shares that result.