动作系统
动作系统用于在 YAML 中描述“发生某件事后要执行哪些效果”。它是 CoreLib 最常被业务模块调用的能力之一,常见于:
- 锻造成功后发送消息、播放音效、发放奖励。
- 强化失败后显示 Title、执行惩罚动作。
- 技能释放时扣资源、执行命令、对准星上的目标造成伤害。
- 烹饪完成后发放物品、广播提示、播放粒子。
- GUI 按钮点击后打开界面、执行命令或触发业务流程。
一条管道就是一行文本
不同模块的字段名可能不同,例如 actions.success、actions、deny_actions、result.success.actions。但核心思想一致:一个列表里的每一行都是一条独立的管道,按顺序执行。
一条管道由若干段(stage)用 | 串起来,数据从左往右流动:
self | chance 25% | after 20t | send_message text="<green>触发成功</green>"这一行读作:取自己作为目标,25% 概率通过,等 20 tick,然后发消息。段与段之间传递的是目标流(一份目标列表),所以“选谁”和“对他做什么”被分成了不同的段——这也是管道模型和早期单行动作最大的差别:以前 killentity radius=5 limit=3 type=zombie 把“找哪些实体”和“杀掉”混在一个动作里,现在前者是 nearby,后者是 kill_entity。
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,包含 status、reasonKey/reasonArguments、编译 diagnostics、按顺序排列的 stages 摘要以及 keptTargets。status 包括 SUCCESS、SKIPPED、PARTIAL、COMPILE_FAILED、EXECUTION_FAILED、INVALID_REQUEST、UNAVAILABLE;因此调用方可以分别处理“没有工作”“部分完成”“配置无法编译”和“运行失败”。
同一门面还提供只读注册表查询:actionStages() / actionStage(id) 与 actionTriggers() / actionTrigger(id)。注册自定义 stage 时,CoreActionExecutionTarget.contextEntity() 表示实体上下文执行域;规划上下文使用 context.target() 或 context.caster(),不要使用不存在的 subject()。返回的 CoreStageRegistration 用 registration.close() 撤销。
最小的第三方执行示例:
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()));语法
权威文法如下:
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_Message与send_message等价。参数名同样转小写。 - 空行与以
#开头的行会被忽略。 - 单引号和双引号都可用于包裹参数值,被引号包裹时支持
\n、\t、\\、\"、\'转义。引号内的|、[、]是普通字符,所以text="a | b"里的竖线不会被当成分段符。 - 引号外的
|、[、]即使没有空格也会切分 token,a|b与a | b完全一样。 ||是管道符的转义写法,也就是条件里的逻辑或;它永远不会被当成分段符。
三种角色
每个段都有固定的角色,决定它能出现在管道的什么位置:
| 角色 | 作用 | 例子 |
|---|---|---|
| source | 产生目标流。通常是管道的第一段。 | self、looking_at、nearby radius=5 |
| gate | 过滤、排序、控制时序,或往上下文写入值。 | chance 25%、where ...、after 20t |
| action | 真正产生效果,作用在流里的每个目标上。 | send_message、damage、give_money |
角色是段自身的属性,不是位置决定的,所以 /emakicorelib action list 按角色分组列出,而不是给一个平铺清单——同一个 id 只在一种位置上合法。
省略 source 时默认为 self。这条默认值有个后果值得注意:想作用于调用方传进来的目标,必须显式写 inherited,否则会静默作用在施法者身上。
分支
条件不再是写在段前面的前缀,而是管道里的一个节点:
if %player_world%=="world" [ self | send_message text="你在主世界" ] else [ self | send_message text="你不在主世界" ]else 分支可省略。分支体本身就是一条完整管道,可以有自己的 source 和多个段。条件支持 &&、||、!、括号,以及比较运算符 <、<=、==、!=、>=、>。字符串比较建议给右侧加引号,避免和数字表达式混淆。注意在分支条件里逻辑或要写成 ||。
分支体也可以只放一个 gate,例如用 stop 提前结束:
looking_at | if %var.dead% [ stop ] else [ damage amount=5 ]权重分支
if 按条件二选一,weight 按权重多选一:每个权重后面紧跟自己的分支体,运行时按权重占总和的比例抽中恰好一条执行。
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 [ ... ]会报错。 weight与if共用action.pipeline.max_branch_depth(默认 16)这一个嵌套深度上限,两者可以互相嵌套。
weight 与 chance 解决的是不同问题:chance 25% 是单个闸门,没摇中就结束整条管道;weight 是多路选一,总会选中一条(除非全部权重为 0)。需要"25% 触发、否则什么都不做"用 chance;需要"必定发生,但发生哪种按概率分配"用 weight。
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 调用:
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.<名字>%:
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_times | 100 | every <间隔> times <次数> 的次数上限 |
action.pipeline.max_sequence_depth | 8 | run 的最大嵌套深度 |
action.pipeline.max_branch_depth | 16 | if 与 weight 的最大嵌套深度 |
循环任务限制
start_task 启动的长期循环由 action.loop.* 单独限制,与上面的编译上限互不影响。
| 配置项 | 默认值 | 含义 |
|---|---|---|
action.loop.enabled | true | 是否允许启动循环任务。 |
action.loop.min_sync_interval | 5t | 同步域循环的最小间隔。 |
action.loop.min_async_interval | 100ms | 异步域循环的最小间隔。 |
action.loop.max_times | 7200 | 单个循环任务的最大执行次数。 |
action.loop.max_active_loops_total | 5000 | 全服同时活动的循环任务上限。 |
action.loop.max_active_loops_per_player | 16 | 单个玩家同时活动的循环任务上限。 |
action.loop.max_active_loops_per_plugin | 1000 | 单个插件同时活动的循环任务上限。 |
action.loop.cancel_player_loops_on_quit | true | 玩家退出时取消其循环任务。 |
action.loop.cancel_plugin_loops_on_disable | true | 插件禁用时取消其循环任务。 |
参数类型说明
段参数有以下类型,在后续参数表中会标注。这些类型在加载期校验,写错的值不会等到运行时才暴露。
| 类型 | 说明 | 示例 |
|---|---|---|
| STRING | 字符串。含空格或特殊字符时需加引号。 | text="<green>成功" |
| INTEGER | 整数。 | amount=3、count=10 |
| DOUBLE | 浮点数。 | volume=0.8、amount=25.5 |
| BOOLEAN | 布尔值。true/false。 | keep=false、icon=true |
| DURATION | 时间。支持 t(tick)、s(秒)、ms(毫秒),纯数字视为 tick。 | 20t、1s、500ms |
| TIME | 旧的 tick/秒写法,为兼容保留。 | 20t、1s |
| PERCENTAGE | 概率。支持百分比、小数与分数。 | 50%、0.5、1/3 |
| ENTITY_TYPE | Bukkit EntityType 名。 | type=zombie |
| MATERIAL | Bukkit Material 名。 | material=stone |
| SOUND | 声音键。Bukkit Sound 枚举名或 namespace:key。 | sound=minecraft:block.anvil.use |
| EXPRESSION | 由 CoreLib 表达式引擎求值的算式。 | amount=%var.level%*4+18 |
从旧语法迁移
如果你手上还有旧配置,对照以下三条规则改写即可:
- 段 id 加下划线分词。
sendmessage写作send_message,givemoney写作give_money,runcommandasconsole写作run_command_as_console,以此类推。 @控制前缀改为管道中的段。@chance=25%写作chance 25%,@delay=20t写作after 20t,@if=<条件>写作if <条件> [ ... ]分支。@ignore_failure没有对应写法:管道的失败语义由段自身决定,需要“失败不阻断”的地方改用分支或把该段拆到单独一行。- 补上 source。 旧的动作行隐含作用于事件里的玩家;管道要显式写 source。作用于施法者写
self(省略时也是这个),作用于调用方传入的目标写inherited。
几个 id 变化不止加下划线:
| 旧写法 | 现在 |
|---|---|
ray | source looking_at |
loopsync / loopasync | start_task(同步/异步不再是配置项,见该段说明) |
cancelloop | stop_task |
@template=名称 / usetemplate | run 名称 |
killentity radius=... type=... | source nearby 负责搜索,kill_entity 只负责移除 |
createitem id=... senditem id=... | create_item 写入唯一的管道物品,send_item 读它,不再需要 id |
castmythicskill | cast_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
施法者自己。无参数。
self | send_message text="<green>操作成功!</green>"inherited
调用方或上一个阶段交进来的目标流。无参数。
必须显式写出:省略 source 会退化为 self,所以一条本想作用于继承目标的管道如果漏写 source,会静默作用在施法者身上。
配合 keep 使用:前一个阶段写 looking_at | keep 记下目标,后一个阶段用 inherited 读回来。
inherited | damage amount=8trigger
触发器指名的实体,用于主体与施法者不同的触发器。无参数。
上下文里存的是名字或 UUID 而不是实体引用——管道可能在运行前很久就编译好了,把活实体放进上下文键会妨碍回收。
trigger | send_message text="<yellow>你触发了机关</yellow>"origin
管道的空间参考点,作为一个位置目标。无参数。
origin | spawn_particle particle=flame count=20looking_at
施法者准星指向的实体。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
range | DOUBLE | 否 | 5 | 射线长度。 |
width | DOUBLE | 否 | 0.5 | 射线宽度。 |
looking_at range=12 | damage amount=6
looking_at | keep | send_message text="<red>你被锁定了</red>"nearby
管道参考点周围的实体。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
radius | DOUBLE | 否 | 1 | 搜索半径。 |
limit | INTEGER | 否 | 1 | 最多返回的实体数。 |
type | ENTITY_TYPE | 否 | "" | 实体类型过滤,空表示不过滤。 |
include_players | BOOLEAN | 否 | false | 是否把玩家算进候选。 |
结果按到参考点的距离排序后截断到 limit。找不到任何实体是“空”,会跳过后续段;写了一个不存在的实体类型是“无效”,会报错——旧实现把打错的类型当成跳过,服主根本看不到自己写错了。
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
管道参考点周围的玩家。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
radius | DOUBLE | 否 | 1 | 搜索半径。 |
limit | INTEGER | 否 | 0 | 最多返回数,0 表示全部。 |
和 nearby 共用同一套过滤逻辑,只是候选只取玩家。limit 默认为 0(全部)而不是 1,因为面向队伍或区域的效果通常要覆盖所有玩家,而不是最近的那一个。
nearby_players radius=10 | send_message text="<gold>队伍获得了增益</gold>"offset
相对管道参考点偏移出的一个位置。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
x | DOUBLE | 否 | 0 | X 偏移。 |
y | DOUBLE | 否 | 0 | Y 偏移。 |
z | DOUBLE | 否 | 0 | Z 偏移。 |
relative | BOOLEAN | 否 | false | 是否沿参考点自身朝向偏移。 |
relative=false 时沿世界坐标轴偏移;relative=true 时沿参考点自身朝向,此时 z 是前方、x 是右方。
offset y=2 | spawn_particle particle=end_rod count=30
offset z=3 relative=true | explosion power=2 break_blocks=falseat
一个绝对坐标,或相对参考点的坐标。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
world | STRING | 否 | "" | 世界名。空则使用参考点所在世界。 |
x | STRING | 否 | ~ | X 坐标,支持 ~。 |
y | STRING | 否 | ~ | Y 坐标,支持 ~。 |
z | STRING | 否 | ~ | Z 坐标,支持 ~。 |
坐标是 STRING 而不是 DOUBLE,这样 ~ 写法才能继续用:~ 表示“参考点在这一轴上的值”,~5 表示“比它多 5”,与原版命令坐标语法一致。
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
按名字或 UUID 指定的在线玩家。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
name | STRING | 是 | — | 玩家名或 UUID。 |
name 是位置参数,可以省略 name=。
player_by_name Notch | send_message text="<yellow>你被点名了</yellow>"
player_by_name %var.winner% | give_money amount=500内置 gate
gate 负责过滤目标流、控制时序,或往管道上下文写值。它们本身不产生效果。
where
条件不成立时清空目标流。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
condition | STRING | 是 | — | 布尔条件。 |
condition 是位置参数。条件在调用前已经完成占位符替换,所以能用的变量就是占位符能提供的那些:管道自己写入的 %var.<名字>%、CoreLib 自带的玩家占位符、调用模块写入上下文的变量,以及 PlaceholderAPI 提供的占位符。
当前粒度是整条流而不是逐个目标:gate 每条流只被调用一次,参数是针对流里第一个目标渲染的,所以 where 的结果是保留整条流或清空整条流。单目标流(也就是最常见的 looking_at | where ... | damage 形态)两种语义没有区别。
self | set threshold=10 | looking_at | where %var.threshold%>5 | kill_entity
nearby radius=6 limit=10 | where %player_world%=="world" | damage amount=4chance
概率没通过就结束管道。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
chance | PERCENTAGE | 是 | — | 概率,如 50%、0.5、1/3。 |
chance 是位置参数。没摇中是“停止”而不是“无效”——摇不中本来就是这个段的意义所在;而 chance abc 是打错了,服主需要看到报错。
需要“必定发生,但发生哪一种按概率分配”时用权重分支,不要用多行 chance 拼概率——那样每行独立判定,可能一条都不中,也可能同时中好几条。
self | chance 10% | broadcast_message text="<gold>%player_name% 触发了稀有奖励!</gold>"
self | chance 1/3 | give_money amount=100 provider=vaultlimit
保留流里前 count 个目标,保持原有顺序。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
count | INTEGER | 是 | — | 保留几个目标。 |
count 是位置参数。
nearby radius=15 limit=50 | sort_by distance | limit 3 | damage amount=12sort_by
按距离或生命值给目标流排序。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
key | STRING | 是 | — | distance 或 health。 |
order | STRING | 否 | asc | asc 或 desc。 |
key 是位置参数。搭配 limit 就能表达“最近的 3 个”或“血最少的那个”。
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;其余的原样保存。
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 会覆盖第一个。
looking_at | keep | send_message text="<gray>已锁定目标</gray>"stop
在此结束管道。无参数。
上报的是“停止”,所以整条管道的结果是跳过而不是失败——刻意停下来不是错误。主要用在分支里。
looking_at | if %var.dead% [ stop ] else [ damage amount=5 ]create_item
构建一个物品,并作为管道的物品值发布出去。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
item_source | STRING | 否 | "" | 物品来源。 |
amount | INTEGER | 否 | 1 | 物品数量。 |
它被登记为 gate 而不是 action,因为写入带类型的上下文是 gate 的职责:只有 gate 的通过结果才能把数据回灌到管道上下文。这也是它原样放行目标流的原因——这个段只是添了一个值,并不消耗目标。
管道里只有一个物品键,所以没有名字可起,旧的 id 参数不存在了;同一条管道里第二个 create_item 会替换掉前一个的值。
item_source 使用 CoreLib 物品来源短横线简写,详见 物品来源,例如原版 minecraft-diamond、自定义来源 emakiitem-flame_blade。原版来源不接受 minecraft:diamond 这种冒号写法。
self | create_item item_source=minecraft-golden_apple amount=2 | send_item
self | create_item item_source=emakiitem-flame_sword | send_itemafter
延迟它之后的每一个段。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
delay | DURATION | 是 | — | 延迟,如 10t、500ms、2s。 |
delay 是位置参数。这是个时序段而不是流变换:解释器认出 after,把管道余下的部分当成它的主体,然后带延迟调度这个主体。主体运行前会重新校验施法者、目标和归属插件,所以等待期间消失的目标会导致跳过而不是报错。
self | after 1s | send_message text="<gray>1 秒后发送的提示"
self | send_message text="<gold>准备</gold>" | after 20t | send_message text="<red>开始!</red>"every
按间隔重复它之后的每一个段。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
interval | DURATION | 否 | 1t | 间隔,如 20t、1s。 |
times | INTEGER | 否 | 0 | 首次之后额外执行的次数。 |
写作 every <间隔> times <次数>。times 计的是首次之外的额外次数,所以 times 0(默认值)表示主体只跑一次。次数受 action.pipeline.max_repeat_times(默认 100)限制,超限会拒绝该条配置。
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 适合一条管道内的短促重复。需要长时间运行、能按 key 取消的循环,用 start_task。
内置 action
action 是真正产生效果的段。参数表里的“目标要求”说明这个段需要什么样的目标流;不满足时该段会被跳过,而不是让整条管道失败。
NONE:完全不需要目标。OPTIONAL:有无目标都能跑,零目标时仍执行一次。REQUIRED_ENTITY:至少要一个实体目标。REQUIRED_LOCATION:至少要一个位置目标。REQUIRED_ANY:至少要一个目标,实体或位置都可以。
消息与反馈
send_message
发送 MiniMessage 格式聊天消息。目标要求 REQUIRED_ENTITY。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
text | STRING | 是 | — | MiniMessage 格式的消息文本。 |
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。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
text | STRING | 是 | — | MiniMessage 格式的 ActionBar 文本。 |
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。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
title | STRING | 是 | — | 主标题(MiniMessage 格式)。 |
subtitle | STRING | 否 | "" | 副标题(MiniMessage 格式)。 |
fade_in | DURATION | 否 | 10t | 淡入时间。 |
stay | DURATION | 否 | 40t | 停留时间。 |
fade_out | DURATION | 否 | 10t | 淡出时间。 |
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。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
text | STRING | 是 | — | MiniMessage 格式的广播文本。 |
不需要目标,所以可以不写 source:
actions:
# 全服公告
- 'broadcast_message text="<gold>[公告] <white>服务器将在 5 分钟后重启</white></gold>"'
# 玩家成就广播
- 'broadcast_message text="<light_purple>✦ %player_name% 完成了传说级锻造!✦</light_purple>"'play_sound
给目标播放声音。目标要求 REQUIRED_ENTITY。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
sound | SOUND | 是 | — | 声音键名。可写 Bukkit Sound 枚举名或 minecraft: 风格键。 |
volume | DOUBLE | 否 | 1 | 音量(0 以上,大于 1 时增加可听距离)。 |
pitch | DOUBLE | 否 | 1 | 音调(0.5–2.0,1 为正常音调)。 |
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。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
particle | STRING | 是 | — | 粒子键名(Bukkit Particle 枚举名)。 |
count | INTEGER | 否 | 1 | 粒子数量。 |
offset_x | DOUBLE | 否 | 0 | X 轴偏移扩散范围。 |
offset_y | DOUBLE | 否 | 0 | Y 轴偏移扩散范围。 |
offset_z | DOUBLE | 否 | 0 | Z 轴偏移扩散范围。 |
extra | DOUBLE | 否 | 0 | 额外参数(速度或数据,依粒子类型而定)。 |
坐标不再是这个段的参数:位置由 source 决定,self 是玩家位置,at、offset、origin 给出指定位置。
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 再次调用会更新已有血条。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
id | STRING | 是 | — | Boss 血条 ID,用于后续更新或隐藏。 |
title | STRING | 是 | — | 血条标题(MiniMessage 格式)。 |
progress | DOUBLE | 否 | 1 | 进度,取值 0 到 1。 |
color | STRING | 否 | purple | 血条颜色。 |
style | STRING | 否 | solid | 血条样式。 |
flags | STRING | 否 | "" | 逗号分隔的血条标志。 |
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。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
id | STRING | 是 | — | Boss 血条 ID,或写 all 隐藏全部。 |
actions:
- 'self | boss_bar_hide id=forge_progress'
- 'self | boss_bar_hide id=all'生命与状态
heal
恢复目标生命。目标要求 REQUIRED_ENTITY。恢复后不超过最大生命值。
IMPORTANT
此段直接设置生命值,不触发 Bukkit EntityRegainHealthEvent。不受任何治疗加成或减免影响。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
amount | DOUBLE | 是 | — | 治疗量(半心为单位,1 = 半颗心)。 |
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)。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
amount | DOUBLE | 是 | — | 伤害量。 |
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 到最大生命值之间。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
amount | DOUBLE | 是 | — | 目标生命值。 |
想让一个玩家死亡就用这个段设为 0,kill_entity 拒绝玩家目标。
actions:
- 'self | set_health amount=20'
- 'self | set_health amount=1'
- 'looking_at | set_health amount=0'feed
恢复目标饱食度。目标要求 REQUIRED_ENTITY。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
amount | INTEGER | 否 | 20 | 恢复的饱食度点数。 |
saturation | DOUBLE | 否 | 0 | 恢复的饱和度。 |
actions:
- 'self | feed'
- 'self | feed amount=6 saturation=3'ignite
点燃目标。目标要求 REQUIRED_ENTITY。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
duration | DURATION | 否 | 5s | 燃烧持续时间。 |
actions:
- 'looking_at | ignite duration=8s'
- 'nearby radius=5 limit=10 | ignite'extinguish
熄灭目标身上的火。目标要求 REQUIRED_ENTITY。无参数。
actions:
- 'self | extinguish'kill_entity
移除目标实体。目标要求 REQUIRED_ENTITY。无参数。
这是旧的 killentity 去掉搜索之后剩下的部分:radius / limit / type / include_players 全部搬到了 source nearby。选择哪些实体从来不是这个段的事,现在它只是把流交给它的东西移除掉。
玩家目标会被拒绝。对玩家调用 Entity#remove 不是受支持的操作;要让玩家死亡请用 set_health amount=0。
actions:
# 清掉周围 8 格内最多 5 只僵尸
- 'nearby radius=8 limit=5 type=zombie | kill_entity'
# 清掉准星上的实体
- 'looking_at range=20 | kill_entity'projectile
从施法者发射一枚自驱动的抛射物。目标要求 OPTIONAL。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
speed | DOUBLE | 否 | 1.5 | 每 tick 飞行的方块数。 |
gravity | DOUBLE | 否 | 0.05 | 每 tick 的下坠量。 |
lifetime | INTEGER | 否 | 60 | 最长存活 tick 数。 |
hit_radius | DOUBLE | 否 | 0.5 | 命中判定半径。 |
pierce | INTEGER | 否 | 0 | 额外穿透的实体数。 |
homing | BOOLEAN | 否 | false | 是否追踪当前目标。 |
homing_strength | DOUBLE | 否 | 0.1 | 追踪转向强度。 |
particle | STRING | 否 | flame | 尾迹粒子键名。 |
damage | DOUBLE | 否 | 0 | 命中时造成的伤害,0 表示不造成伤害。 |
direction | STRING | 否 | look | 初始方向:look(视线)或 target(目标)。 |
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。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
type | STRING | 是 | — | 药水效果类型。可写 speed、minecraft:strength 等。 |
level | INTEGER | 是 | — | 效果等级,从 1 起算。1 对应 Bukkit amplifier 0(游戏内 I 级)。 |
duration | DURATION | 是 | — | 持续时间。如 100t、5s、10000ms。 |
ambient | BOOLEAN | 否 | false | 是否为环境效果(粒子更稀疏、更透明)。 |
particles | BOOLEAN | 否 | true | 是否显示粒子。 |
icon | BOOLEAN | 否 | true | 是否在 HUD 显示效果图标。 |
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。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
type | STRING | 是 | — | 药水效果类型。 |
actions:
- 'self | remove_potion_effect type=slowness'
- 'self | remove_potion_effect type=minecraft:weakness'clear_potion_effects
清除目标当前所有药水效果。目标要求 REQUIRED_ENTITY。无参数。
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 支持:
mainhand、main_hand、handoffhand、off_handhelmet、chestplate/chest、leggings/legs、boots- 背包索引
0到35,也可写slot_0、hotbar_0
send_item
把管道物品交给目标。目标要求 REQUIRED_ENTITY。无参数。
这个段声明了自己需要管道物品,所以没有前置 create_item 的 send_item 是加载期错误而不是运行时空值:校验器会拿这个声明和触发阶段承诺提供的上下文比对。
旧的 id 和 keep 参数都没了。管道里只有一个物品键而不是一张按名字索引的表,而读取上下文的值并不会消耗它,所以“保留在临时仓库里”已经无从谈起。
actions:
- 'self | create_item item_source=minecraft-golden_apple amount=2 | send_item'give_item
把物品来源对应的物品发放到目标背包。目标要求 REQUIRED_ENTITY。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
item_source | STRING | 否 | "" | 物品来源。 |
amount | INTEGER | 否 | 1 | 数量。 |
和 create_item + send_item 的区别在于:这个段直接按来源发放,不经过管道物品,所以不需要两段配合,但也无法在发放前对物品做别的处理。
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。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
slot | STRING | 否 | mainhand | 背包槽位。 |
item_source | STRING | 否 | "" | 物品来源。 |
amount | INTEGER | 否 | 1 | 数量。 |
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。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
slot | STRING | 是 | — | 背包槽位。 |
item_source | STRING | 否 | "" | 期望的物品来源。指定后只有匹配时才清空,不匹配则跳过。 |
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_source | STRING | 否 | "" | 期望扣除的物品来源。 |
amount | INTEGER | 否 | 1 | 扣除数量。 |
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_source | STRING | 否 | "" | 物品来源。 |
amount | INTEGER | 否 | 1 | 物品数量。 |
坐标由 source 提供,不再是这个段的参数。
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。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
slot | STRING | 否 | mainhand | 背包槽位。 |
amount | INTEGER | 否 | 0 | 修复的耐久点数;小于等于 0 表示完全修复。 |
actions:
- 'self | repair_item'
- 'self | repair_item slot=chestplate amount=50'damage_item
增加目标槽位中物品的损耗。目标要求 REQUIRED_ENTITY。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
slot | STRING | 否 | mainhand | 背包槽位。 |
amount | INTEGER | 否 | 1 | 增加的损耗点数。 |
delete_item | BOOLEAN | 否 | false | 耐久耗尽时是否移除该物品。 |
actions:
- 'self | damage_item amount=10'
- 'self | damage_item slot=mainhand amount=100 delete_item=true'方块与世界
place_block
在目标位置放置物品来源对应的方块。目标要求 REQUIRED_ANY。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
item_source | STRING | 否 | "" | 方块物品来源。 |
支持原版方块以及 CraftEngine、ItemsAdder、Nexo、Oraxen 自定义方块。若来源不是可放置方块,该段会跳过而不是中断整条管道。有玩家上下文时会触发 Bukkit 放置事件,保护插件取消时不会放置。
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。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
material | STRING | 否 | "" | 方块材质名。 |
block_data | STRING | 否 | "" | Bukkit BlockData 字符串。 |
apply_physics | BOOLEAN | 否 | true | 是否触发方块物理更新。 |
和 place_block 的区别:这个段直接写材质,不走物品来源,也不触发 Bukkit 放置事件,所以能设置那些没有对应物品的方块状态。
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_items | BOOLEAN | 否 | false | 是否掉落方块物品。 |
apply_physics | BOOLEAN | 否 | true | 清除方块时是否触发物理更新。 |
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。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
power | DOUBLE | 否 | 0 | 爆炸威力。 |
fire | BOOLEAN | 否 | false | 是否引燃。 |
break_blocks | BOOLEAN | 否 | false | 是否破坏方块。 |
默认威力为 0、不引燃、不破坏方块,也就是只有视听效果,需要真实破坏时要显式打开。
actions:
# 纯特效爆炸
- 'self | explosion power=2'
# 会破坏方块的爆炸
- 'looking_at range=15 | explosion power=4 fire=true break_blocks=true'spawn_entity
在目标位置生成实体。目标要求 REQUIRED_ANY。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
type | ENTITY_TYPE | 是 | — | 实体类型。 |
count | INTEGER | 否 | 1 | 生成数量。 |
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。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
world | STRING | 否 | "" | 目标世界名。空则使用目标当前世界。 |
x | STRING | 否 | ~ | X 坐标。支持 ~ 相对当前坐标(如 ~5 表示 X+5)。 |
y | STRING | 否 | ~ | Y 坐标。支持 ~ 相对坐标。 |
z | STRING | 否 | ~ | Z 坐标。支持 ~ 相对坐标。 |
yaw | DOUBLE | 否 | 目标当前朝向 | 偏航角(水平朝向,0–360)。缺省时保留当前朝向。 |
pitch | DOUBLE | 否 | 目标当前朝向 | 俯仰角(垂直朝向,-90–90)。缺省时保留当前朝向。 |
坐标是这个段自己的参数而不是靠 source 提供:source 决定的是“传送谁”,坐标决定的是“传到哪”,两者都是必要信息。
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'经济
三个经济段共用同一组参数。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
amount | DOUBLE | 是 | — | 金额。 |
provider | STRING | 否 | auto | 经济提供者。可选:auto、vault、excellenteconomy。 |
currency | STRING | 否 | "" | 货币类型。ExcellentEconomy 多货币时必须指定。 |
give_money
给目标增加余额。目标要求 REQUIRED_ENTITY。
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。余额不足时该段失败。
actions:
- 'self | take_money amount=25.5 provider=vault'
- 'self | take_money amount=10 provider=excellenteconomy currency=coins'set_money
把目标余额设为指定值。目标要求 REQUIRED_ENTITY。
actions:
- 'self | set_money amount=0 provider=vault'
- 'self | set_money amount=1000 provider=auto'经验
三个经验段共用同一组参数。目标要求都是 REQUIRED_ENTITY。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
amount | INTEGER | 是 | — | 经验点数或等级数。 |
mode | STRING | 否 | points | 模式:points(经验点数)或 levels(等级)。 |
give_exp
给目标增加经验。
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。
actions:
- 'self | take_exp amount=200 mode=points'
- 'self | take_exp amount=1 mode=levels'set_exp
把目标总经验或等级设为指定值。
actions:
- 'self | set_exp amount=30 mode=levels'
- 'self | set_exp amount=0 mode=points'命令
三个命令段共用同一个参数。命令值会自动去掉开头的 /,但为了可读性建议配置里不要写。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
command | STRING | 是 | — | 要执行的命令(不含开头 /)。 |
run_command_as_player
以目标玩家身份执行命令。目标要求 REQUIRED_ENTITY。受玩家权限限制。
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,或者用权限插件做临时授权。
actions:
- 'self | run_command_as_op command="lp user %player_name% permission set example.vip true"'run_command_as_console
以控制台身份执行命令。目标要求 NONE(占位符仍会解析)。
不需要目标,所以可以不写 source:
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。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
sequence | STRING | 是 | — | 要重复执行的序列名。 |
times | INTEGER | 否 | 1 | 执行次数。 |
interval | DURATION | 否 | 20t | 执行间隔。 |
initial_delay | DURATION | 否 | 0t | 首次执行前的延迟。 |
key | STRING | 否 | "" | 任务 key,用于后续取消。 |
on_conflict | STRING | 否 | replace | key 冲突策略:replace、ignore、allow_duplicate。 |
stop_when_offline | BOOLEAN | 否 | true | 玩家离线时停止。 |
stop_when_dead | BOOLEAN | 否 | false | 玩家死亡时停止。 |
stop_when | STRING | 否 | "" | 条件成立时停止。 |
stop_on_failure | BOOLEAN | 否 | false | 序列执行失败时停止。 |
这一个段取代了旧的 loopsync 和 loopasync。旧实现里 async 标记只是选了个不同的最小间隔并多跑一次前置检查,调度方式两者完全一样;真正的线程归属来自每个段自己声明的执行域,所以配置层面的同步/异步开关已经无从表达。
不在上表里的参数会被当成序列体的参数传下去,在序列里读作 %var.<名字>%。这取代了旧的 with. 前缀——那个前缀存在的唯一原因是旧参数模型分不清“声明过的参数”和“额外的参数”,现在能分清了。
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。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
key | STRING | 是 | — | 要取消的任务 key。 |
match | STRING | 否 | exact | 匹配方式:exact 或 prefix。 |
什么都没取消到会被当成跳过而不是失败:配置里经常要取消一个可能并没在跑的任务,比如在一个两种情况都会触发的事件上清理增益循环,把这种情况当错误会让日志被正确的配置刷满。
actions:
- 'stop_task key=burn_%player_name%'
- 'stop_task key=buff_ match=prefix'脚本
三个 js_* 段执行 GraalJS 脚本。它们共用同一组参数,区别只在目标要求与线程域:选错会导致脚本在错误的线程上碰 Bukkit API。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
code | STRING | 是 | — | JavaScript 代码。 |
timeout | INTEGER | 否 | 5000 | 超时时间,单位毫秒。 |
timeout 写 0 或负数时按默认值处理。代码为空、超时或被中断都算跳过而不是失败;脚本抛错才算失败。
脚本的求值结果只写入该段执行结果里的 script_result 一个键,不会自动变成管道变量,所以后续段读不到 %var.script_result%。
js_compute
纯计算脚本,不操作 Bukkit 状态。目标要求 NONE,执行域 ASYNC_COMPUTE。绑定 context;当前目标是玩家时额外绑定 player。
因为跑在异步线程,脚本里不得触碰 Bukkit 状态。
脚本里可以用 context.getVariable(名字) 与 context.hasVariable(名字) 读取管道变量。
actions:
- 'self | set level=%player_level% | js_compute code="context.getVariable(''level'') * 10"'js_entity
操作玩家/实体的脚本。目标要求 REQUIRED_ENTITY,执行域 CONTEXT_ENTITY。绑定 player 与 context;目标不是玩家时跳过。
js_location
操作方块/位置的脚本。目标要求 REQUIRED_LOCATION,执行域 LOCATION_REGION。绑定 location 与 context;当前目标是玩家时额外绑定只读的 player。
MythicMobs 与技能
cast_mythic_skill 和 cast_skill 由 EmakiSkills 模块提供,不是 CoreLib 内置段,参数与行为见 Skills CoreLib 动作。
在命令中查看与执行管道
管理员可在服务端内直接查看当前注册表,并手动执行一条管道:
/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 内置段。
子插件注册的段已拆分到对应插件自己的动作页:
| 来源模块 | 文档 |
|---|---|
| EmakiAttribute | Attribute CoreLib 动作 |
| EmakiForge | Forge CoreLib 动作 |
| EmakiStrengthen | Strengthen CoreLib 动作 |
| EmakiCooking | Cooking CoreLib 动作 |
| EmakiGem | Gem CoreLib 动作 |
| EmakiLevel | Level CoreLib 动作 |
| EmakiSkills | Skills CoreLib 动作 |
| EmakiItem | Item CoreLib 动作 |
| EmakiCodex | Codex CoreLib 动作 |
如果一个段来自某个业务插件,它的参数、上下文变量和执行时机以该插件文档为准。
第三方插件注册段
外部插件开发者可以只依赖 emaki-corelib-api,按角色调用对应的注册方法把自定义段注册进 CoreLib 的共享注册表:
EmakiCoreLibApi.registerActionStage(plugin, stage)— 注册 actionEmakiCoreLibApi.registerActionSource(plugin, source)— 注册 sourceEmakiCoreLibApi.registerActionGate(plugin, gate)— 注册 gate
可用 actionStages() / actionStage(id) 与 actionTriggers() / actionTrigger(id) 查询当前只读注册表。CoreLib 重建注册表后,onStageRegistryRebuilt(plugin, callback) 兼容地按 owner 替换旧回调;同一插件需要多个独立回调时用 addStageRegistryRebuildListener(plugin, callback),它们会独立追加并返回可关闭句柄。插件禁用时 CoreLib 会自动撤销该 owner 的段与回调;调用方仍应在生命周期结束时显式关闭返回句柄。
action 实现 CoreActionStage,声明 id、category、description、parameters、targetRequirement、requiredContext 与 executionTarget。CoreLib 会复用同一套词法、语法、参数校验、占位符渲染、调度与 debug 输出。
executionTarget 没有默认实现,这是有意的:一个段必须自己说清楚它要在哪个线程上跑,不能靠继承一个默认值含糊过去。requiredContext 则是让“缺少前置段”变成加载期错误的机制——send_item 声明自己需要管道物品,校验器就能在加载配置时发现少了 create_item。
最小示例:
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 解析。
完整管道示例
以下是一个锻造成功后的完整配置,展示多种段的组合使用:
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_money 和 send_message 都会执行。旧写法要在两行上各写一次概率,两次独立摇点,结果可能出现给了钱却没提示;现在一条管道摇一次点,后续段共享这次结果。