Skip to content

动作系统

动作系统用于在 YAML 中描述“发生某件事后要执行哪些效果”。它是 CoreLib 最常被业务模块调用的能力之一,常见于:

  • 锻造成功后发送消息、播放音效、发放奖励。
  • 强化失败后显示 Title、执行惩罚动作。
  • 技能释放时扣资源、执行命令、对准星上的目标造成伤害。
  • 烹饪完成后发放物品、广播提示、播放粒子。
  • GUI 按钮点击后打开界面、执行命令或触发业务流程。

一条管道就是一行文本

不同模块的字段名可能不同,例如 actions.successactionsdeny_actionsresult.success.actions。但核心思想一致:一个列表里的每一行都是一条独立的管道,按顺序执行。

一条管道由若干(stage)用 | 串起来,数据从左往右流动:

text
self | chance 25% | after 20t | send_message text="<green>触发成功</green>"

这一行读作:取自己作为目标,25% 概率通过,等 20 tick,然后发消息。段与段之间传递的是目标流(一份目标列表),所以“选谁”和“对他做什么”被分成了不同的段——这也是管道模型和早期单行动作最大的差别:以前 killentity radius=5 limit=3 type=zombie 把“找哪些实体”和“杀掉”混在一个动作里,现在前者是 nearby,后者是 kill_entity

yaml
actions:
  success:
    - 'self | send_message text="<green>锻造成功!</green>"'
    - 'self | play_sound sound=minecraft:entity.player.levelup volume=1 pitch=1'
    - 'run_command_as_console command="say %player_name% 完成了一次锻造"'

NOTE

段的参数在执行前会先解析占位符。CoreLib 自带 %player%%player_name%%player_uuid%%player_world%%player_x%%player_y%%player_z%,并会解析调用模块写入上下文的其他占位符,以及 PlaceholderAPI 提供的占位符。管道自己写入的变量读作 %var.<名字>%。各模块可用的业务变量请参阅对应模块文档。

第三方执行与结构化结果

第三方插件可通过 EmakiCoreLibApi.executeActionLineAsync(plugin, line, CoreActionExecutionContext) 编译并执行一整条管道。该方法任意线程可调用;返回的 future 可能在调用线程(例如立即校验失败)或最后一个 stage 的执行域完成,因此 continuation 访问 Bukkit 前必须通过 EmakiCoreLibApi.scheduling() 调度到合适的 owner thread。编译失败和业务执行失败都会以正常完成的结构化结果返回,不应只依赖异常处理。

结果类型为 CoreActionExecutionResult,包含 statusreasonKey/reasonArguments、编译 diagnostics、按顺序排列的 stages 摘要以及 keptTargetsstatus 包括 SUCCESSSKIPPEDPARTIALCOMPILE_FAILEDEXECUTION_FAILEDINVALID_REQUESTUNAVAILABLE;因此调用方可以分别处理“没有工作”“部分完成”“配置无法编译”和“运行失败”。

同一门面还提供只读注册表查询:actionStages() / actionStage(id)actionTriggers() / actionTrigger(id)。注册自定义 stage 时,CoreActionExecutionTarget.contextEntity() 表示实体上下文执行域;规划上下文使用 context.target()context.caster(),不要使用不存在的 subject()。返回的 CoreStageRegistrationregistration.close() 撤销。

最小的第三方执行示例:

java
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()));

语法

权威文法如下:

text
line     := node ('|' node)*
node     := branch | weighted | stage
branch   := 'if' condition '[' line ']' ('else' '[' line ']')?
weighted := 'weight' (weight '[' line ']')+
stage    := name arg*
arg      := key '=' value | bare_value

要点:

  • 一条管道永远是一行文本。不存在结构化 YAML 形式的管道,也没有第二套解析器。
  • 段名与参数之间用空格分隔,不是括号。写 send_message text="...",不要写 send_message(text="...")
  • 参数有两种形式:key=value,或者裸值(positional)。裸值按段声明的顺序对应到位置参数上,所以 chance 25% 等价于 chance chance=25%
  • 段名会被转成小写,Send_Messagesend_message 等价。参数名同样转小写。
  • 空行与以 # 开头的行会被忽略。
  • 单引号和双引号都可用于包裹参数值,被引号包裹时支持 \n\t\\\"\' 转义。引号内的 |[] 是普通字符,所以 text="a | b" 里的竖线不会被当成分段符。
  • 引号外的 |[] 即使没有空格也会切分 token,a|ba | b 完全一样。
  • || 是管道符的转义写法,也就是条件里的逻辑或;它永远不会被当成分段符。

三种角色

每个段都有固定的角色,决定它能出现在管道的什么位置:

角色作用例子
source产生目标流。通常是管道的第一段。selflooking_atnearby radius=5
gate过滤、排序、控制时序,或往上下文写入值。chance 25%where ...after 20t
action真正产生效果,作用在流里的每个目标上。send_messagedamagegive_money

角色是段自身的属性,不是位置决定的,所以 /emakicorelib action list 按角色分组列出,而不是给一个平铺清单——同一个 id 只在一种位置上合法。

省略 source 时默认为 self。这条默认值有个后果值得注意:想作用于调用方传进来的目标,必须显式写 inherited,否则会静默作用在施法者身上。

分支

条件不再是写在段前面的前缀,而是管道里的一个节点:

text
if %player_world%=="world" [ self | send_message text="你在主世界" ] else [ self | send_message text="你不在主世界" ]

else 分支可省略。分支体本身就是一条完整管道,可以有自己的 source 和多个段。条件支持 &&||!、括号,以及比较运算符 <<===!=>=>。字符串比较建议给右侧加引号,避免和数字表达式混淆。注意在分支条件里逻辑或要写成 ||

分支体也可以只放一个 gate,例如用 stop 提前结束:

text
looking_at | if %var.dead% [ stop ] else [ damage amount=5 ]

权重分支

if 按条件二选一,weight 按权重多选一:每个权重后面紧跟自己的分支体,运行时按权重占总和的比例抽中恰好一条执行。

text
self | weight 70 [ send_message text="<gray>普通</gray>" ] 25 [ send_message text="<blue>稀有</blue>" ] 5 [ broadcast_message text="<gold>史诗!</gold>" ]

这一行读作:70/25/5 的相对概率抽一条,抽中哪条就只执行那条的分支体。权重不要求总和为 100,写 7 2.5 0.5 概率完全一样——权重看的是相对关系,详见权重

权重紧贴各自的分支体,不是单独一个列表。这是刻意的:如果权重集中写成 [70, 25, 5] 再按顺序对应后面的动作,那么插入、删除或调整分支顺序都会让权重错位,而这种错位在加载期检查不出来,只会表现为"概率好像不对"。

要点:

  • 分支体和 if 的分支体一样,本身就是一条完整管道,可以有自己的 source 和多个段。
  • weight 恰好选中一条。想表达"每条独立判定、可能同时触发多条",那是多行 chance,不是 weight
  • 权重写 0 表示禁用该分支,它永远不会被抽中。但所有分支都是 0 会在加载期报错,因为那样这一段永远选不出任何分支。
  • 权重可以是算式或占位符,例如 weight %var.luck% [ ... ] 30 [ ... ]。占位符在执行前渲染,因此概率可以随玩家状态变化。
  • 权重必须是非负数。负权重在加载期就会被拒绝(字面量)或在运行期报错(占位符渲染结果),不会被静默忽略——否则一个手滑的负号会让整条分支凭空消失。
  • 权重是无名值,不写成 名称=值weight chance=70 [ ... ] 会报错。
  • weightif 共用 action.pipeline.max_branch_depth(默认 16)这一个嵌套深度上限,两者可以互相嵌套。

weightchance 解决的是不同问题:chance 25% 是单个闸门,没摇中就结束整条管道;weight 是多路选一,总会选中一条(除非全部权重为 0)。需要"25% 触发、否则什么都不做"用 chance;需要"必定发生,但发生哪种按概率分配"用 weight

text
self | weight 1 [ nearby radius=5 | damage amount=8 ] 1 [ send_message text="<gray>这次没打中</gray>" ]
self | if %var.is_boss% [ weight 80 [ run drop_common ] 20 [ run drop_rare ] ] else [ run drop_trash ]

命名序列与 run

一组反复用到的管道可以定义成命名序列,再用 run 调用:

yaml
action:
  templates:
    reward_common:
      - 'self | send_message text="<green>领取奖励成功</green>"'
      - 'self | play_sound sound=minecraft:entity.experience_orb.pickup volume=0.8 pitch=1.2'
    level_up_effect:
      - 'self | send_title title="<gold>等级提升!</gold>" subtitle="<gray>当前等级:%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'

调用时把参数直接写在 run 后面,序列体内读作 %var.<名字>%

text
run reward_common
run level_up_effect level=10
run reward_with_amount amount=100 reason=每日签到

序列定义可以放在 CoreLib 主配置的 action.templates,也可以放在各业务模块自己的数据目录;run 只认名字,不关心定义来自哪里。序列名不区分大小写。

序列在加载期就会被编译并做环检测,所以互相调用成环、调用不存在的名字、缺少必需参数都会在加载配置时报错,而不是等到运行时。

编译上限

以下上限在加载期检查,超限会拒绝该条配置并保留上一份有效配置,而不是悄悄截断——被截断的 times=100000 看起来像是生效了,这比直接报错更难排查。

配置项默认值含义
action.pipeline.max_repeat_times100every <间隔> times <次数> 的次数上限
action.pipeline.max_sequence_depth8run 的最大嵌套深度
action.pipeline.max_branch_depth16ifweight 的最大嵌套深度

循环任务限制

start_task 启动的长期循环由 action.loop.* 单独限制,与上面的编译上限互不影响。

配置项默认值含义
action.loop.enabledtrue是否允许启动循环任务。
action.loop.min_sync_interval5t同步域循环的最小间隔。
action.loop.min_async_interval100ms异步域循环的最小间隔。
action.loop.max_times7200单个循环任务的最大执行次数。
action.loop.max_active_loops_total5000全服同时活动的循环任务上限。
action.loop.max_active_loops_per_player16单个玩家同时活动的循环任务上限。
action.loop.max_active_loops_per_plugin1000单个插件同时活动的循环任务上限。
action.loop.cancel_player_loops_on_quittrue玩家退出时取消其循环任务。
action.loop.cancel_plugin_loops_on_disabletrue插件禁用时取消其循环任务。

参数类型说明

段参数有以下类型,在后续参数表中会标注。这些类型在加载期校验,写错的值不会等到运行时才暴露。

类型说明示例
STRING字符串。含空格或特殊字符时需加引号。text="<green>成功"
INTEGER整数。amount=3count=10
DOUBLE浮点数。volume=0.8amount=25.5
BOOLEAN布尔值。true/falsekeep=falseicon=true
DURATION时间。支持 t(tick)、s(秒)、ms(毫秒),纯数字视为 tick。20t1s500ms
TIME旧的 tick/秒写法,为兼容保留。20t1s
PERCENTAGE概率。支持百分比、小数与分数。50%0.51/3
ENTITY_TYPEBukkit EntityType 名。type=zombie
MATERIALBukkit Material 名。material=stone
SOUND声音键。Bukkit Sound 枚举名或 namespace:keysound=minecraft:block.anvil.use
EXPRESSION由 CoreLib 表达式引擎求值的算式。amount=%var.level%*4+18

从旧语法迁移

如果你手上还有旧配置,对照以下三条规则改写即可:

  • 段 id 加下划线分词。 sendmessage 写作 send_messagegivemoney 写作 give_moneyruncommandasconsole 写作 run_command_as_console,以此类推。
  • @ 控制前缀改为管道中的段。 @chance=25% 写作 chance 25%@delay=20t 写作 after 20t@if=<条件> 写作 if <条件> [ ... ] 分支。@ignore_failure 没有对应写法:管道的失败语义由段自身决定,需要“失败不阻断”的地方改用分支或把该段拆到单独一行。
  • 补上 source。 旧的动作行隐含作用于事件里的玩家;管道要显式写 source。作用于施法者写 self(省略时也是这个),作用于调用方传入的目标写 inherited

几个 id 变化不止加下划线:

旧写法现在
raysource looking_at
loopsync / loopasyncstart_task(同步/异步不再是配置项,见该段说明)
cancelloopstop_task
@template=名称 / usetemplaterun 名称
killentity radius=... type=...source nearby 负责搜索,kill_entity 只负责移除
createitem id=... senditem id=...create_item 写入唯一的管道物品,send_item 读它,不再需要 id
castmythicskillcast_mythic_skill,由 EmakiSkills 提供

cast_skill 有一处破坏性语义变更:它的 skill 参数现在是 EmakiSkills 技能 id,不再是 MythicMobs 技能 id。旧配置里 cast_skill skill=<Mythic 技能> 必须改成 cast_mythic_skill skill=<Mythic 技能>,否则会因找不到 EmakiSkills 技能而失败。

内置 source

source 产生目标流,通常写在管道最前面。省略 source 等价于 self

self

施法者自己。无参数。

text
self | send_message text="<green>操作成功!</green>"

inherited

调用方或上一个阶段交进来的目标流。无参数。

必须显式写出:省略 source 会退化为 self,所以一条本想作用于继承目标的管道如果漏写 source,会静默作用在施法者身上。

配合 keep 使用:前一个阶段写 looking_at | keep 记下目标,后一个阶段用 inherited 读回来。

text
inherited | damage amount=8

trigger

触发器指名的实体,用于主体与施法者不同的触发器。无参数。

上下文里存的是名字或 UUID 而不是实体引用——管道可能在运行前很久就编译好了,把活实体放进上下文键会妨碍回收。

text
trigger | send_message text="<yellow>你触发了机关</yellow>"

origin

管道的空间参考点,作为一个位置目标。无参数。

text
origin | spawn_particle particle=flame count=20

looking_at

施法者准星指向的实体。

参数类型必填默认值说明
rangeDOUBLE5射线长度。
widthDOUBLE0.5射线宽度。
text
looking_at range=12 | damage amount=6
looking_at | keep | send_message text="<red>你被锁定了</red>"

nearby

管道参考点周围的实体。

参数类型必填默认值说明
radiusDOUBLE1搜索半径。
limitINTEGER1最多返回的实体数。
typeENTITY_TYPE""实体类型过滤,空表示不过滤。
include_playersBOOLEANfalse是否把玩家算进候选。

结果按到参考点的距离排序后截断到 limit。找不到任何实体是“空”,会跳过后续段;写了一个不存在的实体类型是“无效”,会报错——旧实现把打错的类型当成跳过,服主根本看不到自己写错了。

text
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=5s

nearby_players

管道参考点周围的玩家。

参数类型必填默认值说明
radiusDOUBLE1搜索半径。
limitINTEGER0最多返回数,0 表示全部。

nearby 共用同一套过滤逻辑,只是候选只取玩家。limit 默认为 0(全部)而不是 1,因为面向队伍或区域的效果通常要覆盖所有玩家,而不是最近的那一个。

text
nearby_players radius=10 | send_message text="<gold>队伍获得了增益</gold>"

offset

相对管道参考点偏移出的一个位置。

参数类型必填默认值说明
xDOUBLE0X 偏移。
yDOUBLE0Y 偏移。
zDOUBLE0Z 偏移。
relativeBOOLEANfalse是否沿参考点自身朝向偏移。

relative=false 时沿世界坐标轴偏移;relative=true 时沿参考点自身朝向,此时 z 是前方、x 是右方。

text
offset y=2 | spawn_particle particle=end_rod count=30
offset z=3 relative=true | explosion power=2 break_blocks=false

at

一个绝对坐标,或相对参考点的坐标。

参数类型必填默认值说明
worldSTRING""世界名。空则使用参考点所在世界。
xSTRING~X 坐标,支持 ~
ySTRING~Y 坐标,支持 ~
zSTRING~Z 坐标,支持 ~

坐标是 STRING 而不是 DOUBLE,这样 ~ 写法才能继续用:~ 表示“参考点在这一轴上的值”,~5 表示“比它多 5”,与原版命令坐标语法一致。

text
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_planks

player_by_name

按名字或 UUID 指定的在线玩家。

参数类型必填默认值说明
nameSTRING玩家名或 UUID。

name 是位置参数,可以省略 name=

text
player_by_name Notch | send_message text="<yellow>你被点名了</yellow>"
player_by_name %var.winner% | give_money amount=500

内置 gate

gate 负责过滤目标流、控制时序,或往管道上下文写值。它们本身不产生效果。

where

条件不成立时清空目标流。

参数类型必填默认值说明
conditionSTRING布尔条件。

condition 是位置参数。条件在调用前已经完成占位符替换,所以能用的变量就是占位符能提供的那些:管道自己写入的 %var.<名字>%、CoreLib 自带的玩家占位符、调用模块写入上下文的变量,以及 PlaceholderAPI 提供的占位符。

当前粒度是整条流而不是逐个目标:gate 每条流只被调用一次,参数是针对流里第一个目标渲染的,所以 where 的结果是保留整条流或清空整条流。单目标流(也就是最常见的 looking_at | where ... | damage 形态)两种语义没有区别。

text
self | set threshold=10 | looking_at | where %var.threshold%>5 | kill_entity
nearby radius=6 limit=10 | where %player_world%=="world" | damage amount=4

chance

概率没通过就结束管道。

参数类型必填默认值说明
chancePERCENTAGE概率,如 50%0.51/3

chance 是位置参数。没摇中是“停止”而不是“无效”——摇不中本来就是这个段的意义所在;而 chance abc 是打错了,服主需要看到报错。

需要“必定发生,但发生哪一种按概率分配”时用权重分支,不要用多行 chance 拼概率——那样每行独立判定,可能一条都不中,也可能同时中好几条。

text
self | chance 10% | broadcast_message text="<gold>%player_name% 触发了稀有奖励!</gold>"
self | chance 1/3 | give_money amount=100 provider=vault

limit

保留流里前 count 个目标,保持原有顺序。

参数类型必填默认值说明
countINTEGER保留几个目标。

count 是位置参数。

text
nearby radius=15 limit=50 | sort_by distance | limit 3 | damage amount=12

sort_by

按距离或生命值给目标流排序。

参数类型必填默认值说明
keySTRINGdistancehealth
orderSTRINGascascdesc

key 是位置参数。搭配 limit 就能表达“最近的 3 个”或“血最少的那个”。

text
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>远离战场</red>"

set

写入管道变量,之后读作 %var.<名字>%

这个段不声明任何参数,因为变量名由写管道的人决定——set damage=%var.level%*4+18 命名的变量 CoreLib 事先不可能知道。加载期校验会对这个段跳过“未知参数”检查。

能作为算式求值的值会被存成数字,所以 %var.damage% 读回来是 22 而不是 %var.level%*4+18;其余的原样保存。

text
self | set damage=%var.level%*4+18 | set label=暴击 | send_message text="<red>%var.label% %var.damage%</red>"

keep

把当前目标流标记为要带到下一阶段的那一份。无参数。

管道内部目标流本来就在段之间传递,所以这个 gate 原样放行。它不只是个空操作的原因在于:解释器会把它看到的流记录下来,这是把一个阶段的目标交给下一个阶段的方式。技能脚本在 cast 阶段写 looking_at | keep,在 hit 阶段用 inherited 读回来。

它记录的是运行到它时的流,而不是管道最终的流,所以这一行放在阶段里的什么位置不影响结果:后面的 gate 收窄了流,不会改变已记录的内容;第二个 keep 会覆盖第一个。

text
looking_at | keep | send_message text="<gray>已锁定目标</gray>"

stop

在此结束管道。无参数。

上报的是“停止”,所以整条管道的结果是跳过而不是失败——刻意停下来不是错误。主要用在分支里。

text
looking_at | if %var.dead% [ stop ] else [ damage amount=5 ]

create_item

构建一个物品,并作为管道的物品值发布出去。

参数类型必填默认值说明
item_sourceSTRING""物品来源。
amountINTEGER1物品数量。

它被登记为 gate 而不是 action,因为写入带类型的上下文是 gate 的职责:只有 gate 的通过结果才能把数据回灌到管道上下文。这也是它原样放行目标流的原因——这个段只是添了一个值,并不消耗目标。

管道里只有一个物品键,所以没有名字可起,旧的 id 参数不存在了;同一条管道里第二个 create_item 会替换掉前一个的值。

item_source 使用 CoreLib 物品来源短横线简写,详见 物品来源,例如原版 minecraft-diamond、自定义来源 emakiitem-flame_blade。原版来源不接受 minecraft:diamond 这种冒号写法。

text
self | create_item item_source=minecraft-golden_apple amount=2 | send_item
self | create_item item_source=emakiitem-flame_sword | send_item

after

延迟它之后的每一个段。

参数类型必填默认值说明
delayDURATION延迟,如 10t500ms2s

delay 是位置参数。这是个时序段而不是流变换:解释器认出 after,把管道余下的部分当成它的主体,然后带延迟调度这个主体。主体运行前会重新校验施法者、目标和归属插件,所以等待期间消失的目标会导致跳过而不是报错。

text
self | after 1s | send_message text="<gray>1 秒后发送的提示"
self | send_message text="<gold>准备</gold>" | after 20t | send_message text="<red>开始!</red>"

every

按间隔重复它之后的每一个段。

参数类型必填默认值说明
intervalDURATION1t间隔,如 20t1s
timesINTEGER0首次之后额外执行的次数。

写作 every <间隔> times <次数>times 计的是首次之外的额外次数,所以 times 0(默认值)表示主体只跑一次。次数受 action.pipeline.max_repeat_times(默认 100)限制,超限会拒绝该条配置。

text
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.5

every 适合一条管道内的短促重复。需要长时间运行、能按 key 取消的循环,用 start_task

内置 action

action 是真正产生效果的段。参数表里的“目标要求”说明这个段需要什么样的目标流;不满足时该段会被跳过,而不是让整条管道失败。

  • NONE:完全不需要目标。
  • OPTIONAL:有无目标都能跑,零目标时仍执行一次。
  • REQUIRED_ENTITY:至少要一个实体目标。
  • REQUIRED_LOCATION:至少要一个位置目标。
  • REQUIRED_ANY:至少要一个目标,实体或位置都可以。

消息与反馈

send_message

发送 MiniMessage 格式聊天消息。目标要求 REQUIRED_ENTITY

参数类型必填默认值说明
textSTRINGMiniMessage 格式的消息文本。
yaml
actions:
  # 基础消息
  - 'self | send_message text="<green>操作成功!</green>"'
  # 带占位符的消息
  - 'self | send_message text="<yellow>欢迎回来,%player_name%!</yellow>"'
  # 复杂 MiniMessage 格式(渐变色 + 悬浮提示)
  - 'self | send_message text="<hover:show_text:''点击查看详情''><click:run_command:/menu><gradient:gold:yellow>打开菜单</gradient></click></hover>"'

send_action_bar

发送 ActionBar 消息。目标要求 REQUIRED_ENTITY

参数类型必填默认值说明
textSTRINGMiniMessage 格式的 ActionBar 文本。
yaml
actions:
  - 'self | send_action_bar text="<green>+50 经验</green>"'
  - 'self | send_action_bar text="<gray>烹饪进度:<green>████</green><dark_gray>██████</dark_gray> 40%</gray>"'

send_title

显示 Title(大标题 + 副标题)。目标要求 REQUIRED_ENTITY

参数类型必填默认值说明
titleSTRING主标题(MiniMessage 格式)。
subtitleSTRING""副标题(MiniMessage 格式)。
fade_inDURATION10t淡入时间。
stayDURATION40t停留时间。
fade_outDURATION10t淡出时间。
yaml
actions:
  # 基础 Title
  - 'self | send_title title="<gold>任务完成</gold>" subtitle="<gray>获得经验奖励</gray>"'
  # 自定义时间参数
  - 'self | send_title title="<red>警告</red>" subtitle="<yellow>你即将进入危险区域</yellow>" fade_in=5t stay=60t fade_out=20t'
  # 仅显示主标题(快速闪烁效果)
  - 'self | send_title title="<bold><gold>LEVEL UP!</gold></bold>" fade_in=0t stay=20t fade_out=5t'

broadcast_message

向全服广播 MiniMessage 消息。目标要求 NONE

参数类型必填默认值说明
textSTRINGMiniMessage 格式的广播文本。

不需要目标,所以可以不写 source:

yaml
actions:
  # 全服公告
  - 'broadcast_message text="<gold>[公告] <white>服务器将在 5 分钟后重启</white></gold>"'
  # 玩家成就广播
  - 'broadcast_message text="<light_purple>✦ %player_name% 完成了传说级锻造!✦</light_purple>"'

play_sound

给目标播放声音。目标要求 REQUIRED_ENTITY

参数类型必填默认值说明
soundSOUND声音键名。可写 Bukkit Sound 枚举名或 minecraft: 风格键。
volumeDOUBLE1音量(0 以上,大于 1 时增加可听距离)。
pitchDOUBLE1音调(0.5–2.0,1 为正常音调)。
yaml
actions:
  # 升级音效
  - 'self | play_sound sound=minecraft:entity.player.levelup volume=1 pitch=1'
  # 铁砧使用音效(低音调,大音量远距离可听)
  - 'self | play_sound sound=minecraft:block.anvil.use volume=2 pitch=0.8'
  # 给周围所有玩家播放
  - 'nearby_players radius=12 | play_sound sound=minecraft:entity.villager.no volume=1 pitch=1'

spawn_particle

在目标位置生成粒子。目标要求 REQUIRED_ANY

参数类型必填默认值说明
particleSTRING粒子键名(Bukkit Particle 枚举名)。
countINTEGER1粒子数量。
offset_xDOUBLE0X 轴偏移扩散范围。
offset_yDOUBLE0Y 轴偏移扩散范围。
offset_zDOUBLE0Z 轴偏移扩散范围。
extraDOUBLE0额外参数(速度或数据,依粒子类型而定)。

坐标不再是这个段的参数:位置由 source 决定,self 是玩家位置,atoffsetorigin 给出指定位置。

yaml
actions:
  # 玩家位置生成绿色粒子(成功反馈)
  - 'self | spawn_particle particle=happy_villager count=15 offset_x=0.3 offset_y=0.5 offset_z=0.3'
  # 玩家位置火焰粒子(技能释放)
  - 'self | spawn_particle particle=flame count=30 offset_x=0.5 offset_y=0.2 offset_z=0.5 extra=0.05'
  # 指定世界坐标生成粒子
  - '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'
  # 玩家头顶两格
  - 'offset y=2 | spawn_particle particle=explosion count=3 offset_x=0.1 offset_y=0.1 offset_z=0.1'

boss_bar_show

给目标显示一个 Boss 血条。目标要求 REQUIRED_ENTITY。同一 id 再次调用会更新已有血条。

参数类型必填默认值说明
idSTRINGBoss 血条 ID,用于后续更新或隐藏。
titleSTRING血条标题(MiniMessage 格式)。
progressDOUBLE1进度,取值 0 到 1。
colorSTRINGpurple血条颜色。
styleSTRINGsolid血条样式。
flagsSTRING""逗号分隔的血条标志。
yaml
actions:
  - 'self | boss_bar_show id=forge_progress title="<gold>锻造中</gold>" progress=0.5 color=yellow'
  - 'self | boss_bar_show id=forge_progress title="<green>即将完成</green>" progress=0.9'

boss_bar_hide

隐藏之前显示的 Boss 血条。目标要求 REQUIRED_ENTITY

参数类型必填默认值说明
idSTRINGBoss 血条 ID,或写 all 隐藏全部。
yaml
actions:
  - 'self | boss_bar_hide id=forge_progress'
  - 'self | boss_bar_hide id=all'

生命与状态

heal

恢复目标生命。目标要求 REQUIRED_ENTITY。恢复后不超过最大生命值。

IMPORTANT

此段直接设置生命值,不触发 Bukkit EntityRegainHealthEvent。不受任何治疗加成或减免影响。

参数类型必填默认值说明
amountDOUBLE治疗量(半心为单位,1 = 半颗心)。
yaml
actions:
  # 治疗 4 点(2 颗心)
  - 'self | heal amount=4'
  # 满血治疗(超出部分会被限制到最大生命)
  - 'self | heal amount=9999'
  # 少量治疗(配合概率使用)
  - 'self | chance 30% | heal amount=2'

damage

扣除目标生命。目标要求 REQUIRED_ENTITY。扣除后生命最低为 0。

IMPORTANT

此段直接设置生命值,不触发 Bukkit EntityDamageEvent。不受护甲、抗性、附魔等任何减免影响,也不会导致玩家死亡(最低为 0)。

参数类型必填默认值说明
amountDOUBLE伤害量。
yaml
actions:
  # 扣除 2 点生命(1 颗心)
  - 'self | damage amount=2'
  # 对准星上的目标造成伤害
  - 'looking_at range=10 | damage amount=8'
  # 配合条件使用
  - 'if %player_world%=="world_nether" [ self | damage amount=1 ]'

set_health

把目标生命设为指定值。目标要求 REQUIRED_ENTITY。自动限制在 0 到最大生命值之间。

参数类型必填默认值说明
amountDOUBLE目标生命值。

想让一个玩家死亡就用这个段设为 0,kill_entity 拒绝玩家目标。

yaml
actions:
  - 'self | set_health amount=20'
  - 'self | set_health amount=1'
  - 'looking_at | set_health amount=0'

feed

恢复目标饱食度。目标要求 REQUIRED_ENTITY

参数类型必填默认值说明
amountINTEGER20恢复的饱食度点数。
saturationDOUBLE0恢复的饱和度。
yaml
actions:
  - 'self | feed'
  - 'self | feed amount=6 saturation=3'

ignite

点燃目标。目标要求 REQUIRED_ENTITY

参数类型必填默认值说明
durationDURATION5s燃烧持续时间。
yaml
actions:
  - 'looking_at | ignite duration=8s'
  - 'nearby radius=5 limit=10 | ignite'

extinguish

熄灭目标身上的火。目标要求 REQUIRED_ENTITY。无参数。

yaml
actions:
  - 'self | extinguish'

kill_entity

移除目标实体。目标要求 REQUIRED_ENTITY。无参数。

这是旧的 killentity 去掉搜索之后剩下的部分:radius / limit / type / include_players 全部搬到了 source nearby。选择哪些实体从来不是这个段的事,现在它只是把流交给它的东西移除掉。

玩家目标会被拒绝。对玩家调用 Entity#remove 不是受支持的操作;要让玩家死亡请用 set_health amount=0

yaml
actions:
  # 清掉周围 8 格内最多 5 只僵尸
  - 'nearby radius=8 limit=5 type=zombie | kill_entity'
  # 清掉准星上的实体
  - 'looking_at range=20 | kill_entity'

projectile

从施法者发射一枚自驱动的抛射物。目标要求 OPTIONAL

参数类型必填默认值说明
speedDOUBLE1.5每 tick 飞行的方块数。
gravityDOUBLE0.05每 tick 的下坠量。
lifetimeINTEGER60最长存活 tick 数。
hit_radiusDOUBLE0.5命中判定半径。
pierceINTEGER0额外穿透的实体数。
homingBOOLEANfalse是否追踪当前目标。
homing_strengthDOUBLE0.1追踪转向强度。
particleSTRINGflame尾迹粒子键名。
damageDOUBLE0命中时造成的伤害,0 表示不造成伤害。
directionSTRINGlook初始方向:look(视线)或 target(目标)。
yaml
actions:
  # 直线火球
  - 'self | projectile speed=2 damage=8 particle=flame'
  # 追踪弹,需要先有目标
  - 'looking_at range=25 | projectile homing=true homing_strength=0.2 damage=12 direction=target'
  # 穿透弹
  - 'self | projectile speed=2.5 pierce=3 damage=5 gravity=0'

药水效果

give_potion_effect

给目标添加药水效果。目标要求 REQUIRED_ENTITY

参数类型必填默认值说明
typeSTRING药水效果类型。可写 speedminecraft:strength 等。
levelINTEGER效果等级,从 1 起算。1 对应 Bukkit amplifier 0(游戏内 I 级)。
durationDURATION持续时间。如 100t5s10000ms
ambientBOOLEANfalse是否为环境效果(粒子更稀疏、更透明)。
particlesBOOLEANtrue是否显示粒子。
iconBOOLEANtrue是否在 HUD 显示效果图标。
yaml
actions:
  # 速度 II 持续 30 秒
  - 'self | give_potion_effect type=speed level=2 duration=30s particles=true icon=true'
  # 力量 I 持续 1 分钟(无粒子,更隐蔽)
  - 'self | give_potion_effect type=strength level=1 duration=60s particles=false icon=true'
  # 夜视(环境效果,粒子稀疏)
  - 'self | give_potion_effect type=night_vision level=1 duration=5s ambient=true'
  # 使用 minecraft: 前缀写法
  - 'self | give_potion_effect type=minecraft:regeneration level=3 duration=10s'
  # 给周围敌人上减速
  - 'nearby radius=6 limit=10 | give_potion_effect type=slowness level=2 duration=5s'

remove_potion_effect

移除目标的指定药水效果。目标要求 REQUIRED_ENTITY

参数类型必填默认值说明
typeSTRING药水效果类型。
yaml
actions:
  - 'self | remove_potion_effect type=slowness'
  - 'self | remove_potion_effect type=minecraft:weakness'

clear_potion_effects

清除目标当前所有药水效果。目标要求 REQUIRED_ENTITY。无参数。

yaml
actions:
  # 清除所有药水效果(净化)
  - 'self | clear_potion_effects'
  # 延迟 2 秒后清除
  - 'self | after 2s | clear_potion_effects'
  # 仅在特定世界清除
  - 'if %player_world%=="world_arena" [ self | clear_potion_effects ]'

物品

多个物品段用同一个 item_source 参数,取值是 CoreLib 物品来源短横线简写,详见 物品来源

槽位参数 slot 支持:

  • mainhandmain_handhand
  • offhandoff_hand
  • helmetchestplate/chestleggings/legsboots
  • 背包索引 035,也可写 slot_0hotbar_0

send_item

把管道物品交给目标。目标要求 REQUIRED_ENTITY。无参数。

这个段声明了自己需要管道物品,所以没有前置 create_itemsend_item加载期错误而不是运行时空值:校验器会拿这个声明和触发阶段承诺提供的上下文比对。

旧的 idkeep 参数都没了。管道里只有一个物品键而不是一张按名字索引的表,而读取上下文的值并不会消耗它,所以“保留在临时仓库里”已经无从谈起。

yaml
actions:
  - 'self | create_item item_source=minecraft-golden_apple amount=2 | send_item'

give_item

把物品来源对应的物品发放到目标背包。目标要求 REQUIRED_ENTITY

参数类型必填默认值说明
item_sourceSTRING""物品来源。
amountINTEGER1数量。

create_item + send_item 的区别在于:这个段直接按来源发放,不经过管道物品,所以不需要两段配合,但也无法在发放前对物品做别的处理。

yaml
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

把目标指定槽位的物品设为给定来源的物品。目标要求 REQUIRED_ENTITY

参数类型必填默认值说明
slotSTRINGmainhand背包槽位。
item_sourceSTRING""物品来源。
amountINTEGER1数量。
yaml
actions:
  - 'self | set_item slot=mainhand item_source=emakiitem-flame_sword'
  - 'self | set_item slot=helmet item_source=minecraft-diamond_helmet'

clear_item

清空目标指定槽位的物品。目标要求 REQUIRED_ENTITY

参数类型必填默认值说明
slotSTRING背包槽位。
item_sourceSTRING""期望的物品来源。指定后只有匹配时才清空,不匹配则跳过。
yaml
actions:
  # 清空主手物品(无条件)
  - 'self | clear_item slot=mainhand'
  # 仅当主手是木棍时才清空
  - 'self | clear_item slot=mainhand item_source=minecraft-stick'
  # 清空头盔槽位(仅匹配自定义物品时)
  - 'self | clear_item slot=helmet item_source=emakiitem-cursed_helmet'
  # 清空指定背包格子
  - 'self | clear_item slot=0'

take_item

从目标背包扣除匹配的物品。目标要求 REQUIRED_ENTITY

参数类型必填默认值说明
item_sourceSTRING""期望扣除的物品来源。
amountINTEGER1扣除数量。
yaml
actions:
  - 'self | take_item item_source=minecraft-emerald amount=3'
  - 'self | take_item item_source=emakiitem-forge_token amount=1'

drop_item

在目标位置掉落物品。目标要求 REQUIRED_ANY

参数类型必填默认值说明
item_sourceSTRING""物品来源。
amountINTEGER1物品数量。

坐标由 source 提供,不再是这个段的参数。

yaml
actions:
  # 在玩家头顶掉落物品
  - 'offset y=2 | drop_item item_source=minecraft-diamond amount=1'
  # 在固定坐标掉落奖励
  - 'at world=world x=100 y=65 z=-50 | drop_item item_source=minecraft-gold_ingot amount=5'
  # 在玩家前方三格掉落
  - 'offset z=3 relative=true | drop_item item_source=minecraft-emerald amount=2'

repair_item

修复目标槽位中物品的耐久。目标要求 REQUIRED_ENTITY

参数类型必填默认值说明
slotSTRINGmainhand背包槽位。
amountINTEGER0修复的耐久点数;小于等于 0 表示完全修复。
yaml
actions:
  - 'self | repair_item'
  - 'self | repair_item slot=chestplate amount=50'

damage_item

增加目标槽位中物品的损耗。目标要求 REQUIRED_ENTITY

参数类型必填默认值说明
slotSTRINGmainhand背包槽位。
amountINTEGER1增加的损耗点数。
delete_itemBOOLEANfalse耐久耗尽时是否移除该物品。
yaml
actions:
  - 'self | damage_item amount=10'
  - 'self | damage_item slot=mainhand amount=100 delete_item=true'

方块与世界

place_block

在目标位置放置物品来源对应的方块。目标要求 REQUIRED_ANY

参数类型必填默认值说明
item_sourceSTRING""方块物品来源。

支持原版方块以及 CraftEngine、ItemsAdder、Nexo、Oraxen 自定义方块。若来源不是可放置方块,该段会跳过而不是中断整条管道。有玩家上下文时会触发 Bukkit 放置事件,保护插件取消时不会放置。

yaml
actions:
  # 放置原版方块(注意用连字符分隔命名空间和 ID)
  - 'at world=world x=100 y=64 z=-20 | place_block item_source=minecraft-stone'
  # 在玩家脚下放置方块
  - 'at x=~ y=~-1 z=~ | place_block item_source=minecraft-oak_planks'
  # 放置 CraftEngine 自定义方块
  - 'at x=~1 y=~ z=~ | place_block item_source=ce-cutting_board'
  # 放置 ItemsAdder 自定义方块
  - 'offset y=2 | place_block item_source=itemsadder-decorative_lamp'

set_block

把目标位置设为某种方块。目标要求 REQUIRED_ANY

参数类型必填默认值说明
materialSTRING""方块材质名。
block_dataSTRING""Bukkit BlockData 字符串。
apply_physicsBOOLEANtrue是否触发方块物理更新。

place_block 的区别:这个段直接写材质,不走物品来源,也不触发 Bukkit 放置事件,所以能设置那些没有对应物品的方块状态。

yaml
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

破坏或清除目标位置的方块。目标要求 REQUIRED_ANY

参数类型必填默认值说明
drop_itemsBOOLEANfalse是否掉落方块物品。
apply_physicsBOOLEANtrue清除方块时是否触发物理更新。
yaml
actions:
  - 'at x=~ y=~-1 z=~ | break_block drop_items=true'
  - 'at world=world x=10 y=64 z=10 | break_block'

explosion

在目标位置产生爆炸。目标要求 REQUIRED_ANY

参数类型必填默认值说明
powerDOUBLE0爆炸威力。
fireBOOLEANfalse是否引燃。
break_blocksBOOLEANfalse是否破坏方块。

默认威力为 0、不引燃、不破坏方块,也就是只有视听效果,需要真实破坏时要显式打开。

yaml
actions:
  # 纯特效爆炸
  - 'self | explosion power=2'
  # 会破坏方块的爆炸
  - 'looking_at range=15 | explosion power=4 fire=true break_blocks=true'

spawn_entity

在目标位置生成实体。目标要求 REQUIRED_ANY

参数类型必填默认值说明
typeENTITY_TYPE实体类型。
countINTEGER1生成数量。
yaml
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

把目标传送到指定坐标。目标要求 REQUIRED_ENTITY

参数类型必填默认值说明
worldSTRING""目标世界名。空则使用目标当前世界。
xSTRING~X 坐标。支持 ~ 相对当前坐标(如 ~5 表示 X+5)。
ySTRING~Y 坐标。支持 ~ 相对坐标。
zSTRING~Z 坐标。支持 ~ 相对坐标。
yawDOUBLE目标当前朝向偏航角(水平朝向,0–360)。缺省时保留当前朝向。
pitchDOUBLE目标当前朝向俯仰角(垂直朝向,-90–90)。缺省时保留当前朝向。

坐标是这个段自己的参数而不是靠 source 提供:source 决定的是“传送谁”,坐标决定的是“传到哪”,两者都是必要信息。

yaml
actions:
  # 传送到固定坐标
  - 'self | teleport world=world x=0 y=100 z=0 yaw=180 pitch=0'
  # 相对传送(向上 10 格)
  - 'self | teleport y=~10'
  # 跨世界传送
  - 'self | teleport world=world_nether x=50 y=64 z=-100 yaw=90 pitch=0'
  # 把附近玩家都拉到出生点
  - 'nearby_players radius=30 | teleport world=world x=0 y=65 z=0'

经济

三个经济段共用同一组参数。

参数类型必填默认值说明
amountDOUBLE金额。
providerSTRINGauto经济提供者。可选:autovaultexcellenteconomy
currencySTRING""货币类型。ExcellentEconomy 多货币时必须指定。

give_money

给目标增加余额。目标要求 REQUIRED_ENTITY

yaml
actions:
  # 基础给钱(自动选择经济插件)
  - 'self | give_money amount=100'
  # 指定 Vault 经济
  - 'self | give_money amount=500 provider=vault'
  # ExcellentEconomy 多货币
  - 'self | give_money amount=50 provider=excellenteconomy currency=gems'

take_money

扣除目标余额。目标要求 REQUIRED_ENTITY。余额不足时该段失败。

yaml
actions:
  - 'self | take_money amount=25.5 provider=vault'
  - 'self | take_money amount=10 provider=excellenteconomy currency=coins'

set_money

把目标余额设为指定值。目标要求 REQUIRED_ENTITY

yaml
actions:
  - 'self | set_money amount=0 provider=vault'
  - 'self | set_money amount=1000 provider=auto'

经验

三个经验段共用同一组参数。目标要求都是 REQUIRED_ENTITY

参数类型必填默认值说明
amountINTEGER经验点数或等级数。
modeSTRINGpoints模式:points(经验点数)或 levels(等级)。

give_exp

给目标增加经验。

yaml
actions:
  # 给予 100 经验点
  - 'self | give_exp amount=100 mode=points'
  # 给予 3 个等级
  - 'self | give_exp amount=3 mode=levels'
  # 默认模式为 points,可省略
  - 'self | give_exp amount=50'

take_exp

扣除目标经验。扣除后最低为 0。

yaml
actions:
  - 'self | take_exp amount=200 mode=points'
  - 'self | take_exp amount=1 mode=levels'

set_exp

把目标总经验或等级设为指定值。

yaml
actions:
  - 'self | set_exp amount=30 mode=levels'
  - 'self | set_exp amount=0 mode=points'

命令

三个命令段共用同一个参数。命令值会自动去掉开头的 /,但为了可读性建议配置里不要写。

参数类型必填默认值说明
commandSTRING要执行的命令(不含开头 /)。

run_command_as_player

以目标玩家身份执行命令。目标要求 REQUIRED_ENTITY。受玩家权限限制。

yaml
actions:
  - 'self | run_command_as_player command="spawn"'
  - 'self | run_command_as_player command="menu open main"'

run_command_as_op

临时给目标玩家 OP 执行命令,执行后恢复原 OP 状态。目标要求 REQUIRED_ENTITY

CAUTION

这个段会临时赋予 OP 权限,存在安全风险:命令执行期间玩家拥有完整 OP 权限,如果命令内容来自可被玩家影响的占位符,等于把提权入口交了出去。建议优先用 run_command_as_console,或者用权限插件做临时授权。

yaml
actions:
  - 'self | run_command_as_op command="lp user %player_name% permission set example.vip true"'

run_command_as_console

以控制台身份执行命令。目标要求 NONE(占位符仍会解析)。

不需要目标,所以可以不写 source:

yaml
actions:
  # 全服公告
  - 'run_command_as_console command="say %player_name% 完成了一次锻造"'
  # 给予权限
  - 'run_command_as_console command="lp user %player_name% permission set forge.master true"'
  # 执行其他插件命令
  - 'run_command_as_console command="crate give %player_name% legendary 1"'

长时间任务

every 适合一条管道内的短促重复。要启动一个能长时间运行、能按 key 取消、玩家离线或死亡时自动停止的循环,用下面两个段。

start_task

启动一个按间隔重复执行的命名序列。目标要求 OPTIONAL

参数类型必填默认值说明
sequenceSTRING要重复执行的序列名。
timesINTEGER1执行次数。
intervalDURATION20t执行间隔。
initial_delayDURATION0t首次执行前的延迟。
keySTRING""任务 key,用于后续取消。
on_conflictSTRINGreplacekey 冲突策略:replaceignoreallow_duplicate
stop_when_offlineBOOLEANtrue玩家离线时停止。
stop_when_deadBOOLEANfalse玩家死亡时停止。
stop_whenSTRING""条件成立时停止。
stop_on_failureBOOLEANfalse序列执行失败时停止。

这一个段取代了旧的 loopsyncloopasync。旧实现里 async 标记只是选了个不同的最小间隔并多跑一次前置检查,调度方式两者完全一样;真正的线程归属来自每个段自己声明的执行域,所以配置层面的同步/异步开关已经无从表达。

不在上表里的参数会被当成序列体的参数传下去,在序列里读作 %var.<名字>%。这取代了旧的 with. 前缀——那个前缀存在的唯一原因是旧参数模型分不清“声明过的参数”和“额外的参数”,现在能分清了。

yaml
action:
  templates:
    burn_tick:
      - 'inherited | damage amount=1'
      - 'inherited | spawn_particle particle=flame count=5'
    buff_pulse:
      - 'self | send_action_bar text="<gold>祝福剩余 %var.label%</gold>"'

actions:
  # 每秒一次,共 10 次
  - 'looking_at | keep | start_task sequence=burn_tick times=10 interval=20t key=burn_%player_name%'
  # 带序列参数,玩家死亡时停止
  - 'self | start_task sequence=buff_pulse times=30 interval=1s key=buff stop_when_dead=true label=神圣祝福'
  # 条件不成立就停
  - 'self | start_task sequence=buff_pulse times=100 interval=20t stop_when=%player_world%!="world_arena"'

stop_task

按 key 取消正在运行的任务。目标要求 NONE

参数类型必填默认值说明
keySTRING要取消的任务 key。
matchSTRINGexact匹配方式:exactprefix

什么都没取消到会被当成跳过而不是失败:配置里经常要取消一个可能并没在跑的任务,比如在一个两种情况都会触发的事件上清理增益循环,把这种情况当错误会让日志被正确的配置刷满。

yaml
actions:
  - 'stop_task key=burn_%player_name%'
  - 'stop_task key=buff_ match=prefix'

脚本

三个 js_* 段执行 GraalJS 脚本。它们共用同一组参数,区别只在目标要求与线程域:选错会导致脚本在错误的线程上碰 Bukkit API。

参数类型必填默认值说明
codeSTRINGJavaScript 代码。
timeoutINTEGER5000超时时间,单位毫秒。

timeout 写 0 或负数时按默认值处理。代码为空、超时或被中断都算跳过而不是失败;脚本抛错才算失败。

脚本的求值结果只写入该段执行结果里的 script_result 一个键,不会自动变成管道变量,所以后续段读不到 %var.script_result%

js_compute

纯计算脚本,不操作 Bukkit 状态。目标要求 NONE,执行域 ASYNC_COMPUTE。绑定 context;当前目标是玩家时额外绑定 player

因为跑在异步线程,脚本里不得触碰 Bukkit 状态。

脚本里可以用 context.getVariable(名字)context.hasVariable(名字) 读取管道变量。

yaml
actions:
  - 'self | set level=%player_level% | js_compute code="context.getVariable(''level'') * 10"'

js_entity

操作玩家/实体的脚本。目标要求 REQUIRED_ENTITY,执行域 CONTEXT_ENTITY。绑定 playercontext;目标不是玩家时跳过。

js_location

操作方块/位置的脚本。目标要求 REQUIRED_LOCATION,执行域 LOCATION_REGION。绑定 locationcontext;当前目标是玩家时额外绑定只读的 player

MythicMobs 与技能

cast_mythic_skillcast_skill 由 EmakiSkills 模块提供,不是 CoreLib 内置段,参数与行为见 Skills CoreLib 动作

在命令中查看与执行管道

管理员可在服务端内直接查看当前注册表,并手动执行一条管道:

text
/corelib action list
/corelib action run <管道文本>

同样可通过 /emakicorelib action/emakicore action 或复数别名 actions 调用。该命令复用 emakicorelib.admin 权限;控制台执行时没有玩家上下文,需要实体目标的段会按既有目标要求跳过。run 关键字可以省略,/corelib action self | heal amount=4 与带 run 等价。

action list 按角色(source / gate / action)分组列出 id 与所属插件。之所以分组而不给平铺清单:一个 id 只在管道的某一种位置上合法,平铺列表无法告诉运维这个 id 能用在哪。

action run 会先编译再执行,所以语法错误或未知段名会带着自己的诊断信息报出来,而不是变成一个笼统的执行失败。只显示第一条诊断——编译器会报出一行上的所有问题,其余通常都是第一个问题的连带结果。执行结束后会报告整条管道的状态;失败时会指出是哪个段失败的,因为手打的管道通常是在调试,失败的段就是答案本身。

子插件段在哪里看

CoreLib 提供段注册表和统一的管道引擎;业务插件可以在启用时向同一个注册表追加自己的 source、gate 和 action。为了避免 CoreLib 页面集中维护所有插件的专属段参数,本页只列 CoreLib 内置段。

子插件注册的段已拆分到对应插件自己的动作页:

来源模块文档
EmakiAttributeAttribute CoreLib 动作
EmakiForgeForge CoreLib 动作
EmakiStrengthenStrengthen CoreLib 动作
EmakiCookingCooking CoreLib 动作
EmakiGemGem CoreLib 动作
EmakiLevelLevel CoreLib 动作
EmakiSkillsSkills CoreLib 动作
EmakiItemItem CoreLib 动作
EmakiCodexCodex CoreLib 动作

如果一个段来自某个业务插件,它的参数、上下文变量和执行时机以该插件文档为准。

第三方插件注册段

外部插件开发者可以只依赖 emaki-corelib-api,按角色调用对应的注册方法把自定义段注册进 CoreLib 的共享注册表:

  • EmakiCoreLibApi.registerActionStage(plugin, stage) — 注册 action
  • EmakiCoreLibApi.registerActionSource(plugin, source) — 注册 source
  • EmakiCoreLibApi.registerActionGate(plugin, gate) — 注册 gate

可用 actionStages() / actionStage(id)actionTriggers() / actionTrigger(id) 查询当前只读注册表。CoreLib 重建注册表后,onStageRegistryRebuilt(plugin, callback) 兼容地按 owner 替换旧回调;同一插件需要多个独立回调时用 addStageRegistryRebuildListener(plugin, callback),它们会独立追加并返回可关闭句柄。插件禁用时 CoreLib 会自动撤销该 owner 的段与回调;调用方仍应在生命周期结束时显式关闭返回句柄。

action 实现 CoreActionStage,声明 idcategorydescriptionparameterstargetRequirementrequiredContextexecutionTarget。CoreLib 会复用同一套词法、语法、参数校验、占位符渲染、调度与 debug 输出。

executionTarget 没有默认实现,这是有意的:一个段必须自己说清楚它要在哪个线程上跑,不能靠继承一个默认值含糊过去。requiredContext 则是让“缺少前置段”变成加载期错误的机制——send_item 声明自己需要管道物品,校验器就能在加载配置时发现少了 create_item

最小示例:

java
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) {
        // 需要 Bukkit 主线程 API 时选实体所属线程。
        return CoreActionExecutionTarget.contextEntity();
    }

    @Override
    public CoreActionOutcome execute(CoreStageContext context, CoreResolvedArguments arguments) {
        // 这里执行你的插件逻辑。
        return CoreActionOutcome.success();
    }
});

// 插件禁用时:
registration.close();

段 ID 速查表

source

id用途
self施法者自己(省略时的默认)
inherited调用方传入的目标流
trigger触发器指名的实体
origin管道参考点位置
looking_at准星指向的实体
nearby周围实体
nearby_players周围玩家
offset相对参考点偏移的位置
at绝对或相对坐标
player_by_name按名字或 UUID 指定的玩家

gate

id用途
where条件不成立时清空目标流
chance概率没通过就结束管道
limit保留前 N 个目标
sort_by按距离或生命值排序
set写入 %var.*% 管道变量
keep记录目标流供下一阶段继承
stop在此结束管道
create_item构建并发布管道物品
after延迟后续所有段
every按间隔重复后续所有段

action

id分类目标要求用途
send_message消息REQUIRED_ENTITY发送聊天消息
send_action_bar消息REQUIRED_ENTITY发送 ActionBar
send_title消息REQUIRED_ENTITY显示 Title
broadcast_message消息NONE全服广播
play_sound反馈REQUIRED_ENTITY播放音效
spawn_particle反馈REQUIRED_ANY生成粒子
boss_bar_show反馈REQUIRED_ENTITY显示 Boss 血条
boss_bar_hide反馈REQUIRED_ENTITY隐藏 Boss 血条
heal实体REQUIRED_ENTITY恢复生命
damage实体REQUIRED_ENTITY扣除生命
set_health实体REQUIRED_ENTITY设置生命
feed实体REQUIRED_ENTITY恢复饱食度
ignite实体REQUIRED_ENTITY点燃
extinguish实体REQUIRED_ENTITY熄灭
kill_entity实体REQUIRED_ENTITY移除实体
projectile战斗OPTIONAL发射自驱动抛射物
give_potion_effect实体REQUIRED_ENTITY添加药水效果
remove_potion_effect实体REQUIRED_ENTITY移除药水效果
clear_potion_effects实体REQUIRED_ENTITY清除所有药水效果
send_item物品REQUIRED_ENTITY发送管道物品
give_item物品REQUIRED_ENTITY按来源发放物品
set_item物品REQUIRED_ENTITY设置槽位物品
clear_item物品REQUIRED_ENTITY清除槽位物品
take_item物品REQUIRED_ENTITY扣除背包物品
drop_item物品REQUIRED_ANY在目标位置掉落物品
repair_item物品REQUIRED_ENTITY修复物品耐久
damage_item物品REQUIRED_ENTITY增加物品损耗
place_block世界REQUIRED_ANY按物品来源放置方块
set_block世界REQUIRED_ANY按材质设置方块
break_block世界REQUIRED_ANY破坏方块
explosion世界REQUIRED_ANY产生爆炸
spawn_entity实体REQUIRED_ANY生成实体
teleport实体REQUIRED_ENTITY传送
give_money经济REQUIRED_ENTITY给予金钱
take_money经济REQUIRED_ENTITY扣除金钱
set_money经济REQUIRED_ENTITY设置金钱
give_exp玩家REQUIRED_ENTITY给予经验
take_exp玩家REQUIRED_ENTITY扣除经验
set_exp玩家REQUIRED_ENTITY设置经验
run_command_as_player命令REQUIRED_ENTITY以玩家身份执行
run_command_as_op命令REQUIRED_ENTITY以临时 OP 执行
run_command_as_console命令NONE以控制台执行
start_task任务OPTIONAL启动重复执行的序列
stop_task任务NONE按 key 取消任务
js_compute脚本NONE纯计算 JS 脚本
js_entity脚本REQUIRED_ENTITY操作实体的 JS 脚本
js_location脚本REQUIRED_LOCATION操作位置的 JS 脚本

故障排查

加载时报未知段名。 检查 id 是否用下划线分词:是 send_message 而不是 sendmessage。用 /corelib action list 确认这个 id 存在,并确认它出现在管道的正确位置——source 不能写在 action 的位置上。

段名对但报“未知参数”。 参数名也变了几个:物品来源统一叫 item_source(不再有 source / item 别名),set_block 没有 block 别名,start_task 的额外参数直接写而不带 with. 前缀。

管道跳过了但没报错。 目标要求不满足会跳过而不是失败。最常见的原因是漏写 source 导致退化成 self,而 self 在没有玩家上下文时是空的;或者 nearby 没搜到实体。

分支条件里的 || 不生效。 逻辑或必须写成 ||,单个 | 会被当成分段符。

send_item 在加载时报错。 这个段需要管道物品,同一条管道里必须先有 create_item

times 报超限。 every ... times N 的上限是 action.pipeline.max_repeat_times(默认 100)。需要更多次数请用 start_task,它的次数配额由任务服务单独控制。

参数值里的空格被拆开了。 含空格、MiniMessage 标签或命令的值必须加引号。YAML 里整行管道本身也建议用单引号包起来,避免 :# 被 YAML 解析。

完整管道示例

以下是一个锻造成功后的完整配置,展示多种段的组合使用:

yaml
action:
  success:
    # 基础反馈
    - 'self | send_title title="<gold>锻造成功</gold>" subtitle="<gray>品质:%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'

    # 经济奖励(10% 概率返还部分费用)
    - 'self | chance 10% | give_money amount=50 provider=vault | send_message text="<yellow>幸运!返还了 50 金币。</yellow>"'

    # 经验奖励
    - 'self | give_exp amount=30 mode=points'

    # 高品质额外奖励
    - 'if %forge_quality%=="epic" [ self | send_message text="<light_purple>史诗品质!额外获得强化石。</light_purple>" | create_item item_source=emakiitem-strengthen_stone amount=1 | send_item ]'

    # 全服广播(仅传说品质)
    - 'if %forge_quality%=="legendary" [ broadcast_message text="<gold>%player_name% 锻造出了传说品质装备!</gold>" ]'

    # 延迟提示
    - 'self | after 2s | send_message text="<gray>装备已更新,请查看背包。</gray>"'

注意第二条:chance 通过后,同一条管道里后面的 give_moneysend_message 都会执行。旧写法要在两行上各写一次概率,两次独立摇点,结果可能出现给了钱却没提示;现在一条管道摇一次点,后续段共享这次结果。