-
Notifications
You must be signed in to change notification settings - Fork 0
Script Engine
Noogear edited this page Mar 9, 2026
·
4 revisions
GloomLib Script — 高性能声明式 YAML 脚本引擎,将事件处理逻辑编译为 JVM 字节码执行。
数学表达式完整语法 → 数学表达式
集合操作详细参考 → 集合操作
id: "my-script" # 脚本标识符(可选,默认 AnonymousScript)
event: "com.example.MyEvent" # 必填,事件/载荷类的全限定名
priority: 0 # 可选,事件优先级(默认 0)
ignore-cancelled: true # 可选,已取消事件是否跳过(默认 true)
variables:
# ...
flow:
# ...| 字段 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
id |
String | 否 | AnonymousScript |
脚本唯一标识,用于日志和错误追踪 |
event |
String | 是 | — | 载荷类全限定名 |
priority |
Integer | 否 | 0 |
事件监听优先级 |
ignore-cancelled |
boolean | 否 | true |
true 时已被取消的事件不触发脚本 |
variables |
Map | 否 | {} |
变量声明映射 |
flow |
List | 否 | [] |
流程节点列表 |
将载荷对象的属性映射为脚本变量。
variables:
# 简单属性:调用 event.getDamage()
damage: "damage"
# 链式属性:event.getEntity().getName()
entityName: "entity.name"
# 集合索引:event.getPlayer().getInventory().get(0)
firstItem: "player.inventory[0]"
# Map 索引:event.getMetadata().get("key")
metaValue: "metadata[key]"
quotedKey: "metadata['damage_all']"
# payload 别名:直接引用载荷对象本身
event: "$self"| 语法 | 示例 | 解析方式 |
|---|---|---|
| 简单属性 | damage |
调用 getDamage() 或 damage()
|
| 链式属性 | entity.name |
依次调用 getEntity() → getName()
|
| List 索引 | inventory[0] |
调用 getInventory() → List.get(0)
|
| Map 索引 | metadata[key] |
调用 getMetadata() → Map.get("key")
|
| payload 别名 | $self |
直接引用载荷对象 |
-
getXxx()— 标准 Java Bean getter -
isXxx()— boolean getter -
xxx()— Record 风格 accessor
flow 是一个有序列表,引擎按顺序执行每个节点。共支持 8 种节点类型:
| 类型 | YAML 触发键 | 说明 |
|---|---|---|
| CHECK | check |
条件判断 |
| ACTION |
action 或动态推断 |
调用已注册动作 |
| RETURN | return |
返回值并终止 |
| SWITCH | switch |
多分支选择 |
| MATH | math |
数学表达式求值 |
| ANY | any |
OR 复合条件 |
| ALL | all |
AND 复合条件 |
| COLLECT | collect |
集合遍历与筛选 |
节点类型通过触发键自动推断,无需显式声明 type 字段。
# 基本格式
- check: <variable>
op: "<operator>"
value: <value>
# 带失败动作
- check: damage
op: ">"
value: 100
on_fail:
- action: "log"
args: ["条件不满足"]| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
check |
String | 是 | 检查的变量名 |
op |
String | 是 | 操作符(支持 ! 前缀取反) |
value |
any | 视操作符 | 比较目标值 |
on_fail |
List | 否 | 条件不满足时执行的节点列表 |
# 数值
value: 100
value: 3.14
# 字符串
value: "sword"
# 布尔
value: true
# 列表(用于 in / between)
value: ["sword", "bow", "axe"] # in:候选值列表
value: [10.0, 50.0] # between:[下界, 上界] 闭区间
# 变量引用
value: "{maxHp}"
# 数学表达式(数值操作符时自动解析)
value: "{maxHp} * 0.5"
# 类名(用于 instanceof)
value: "com.example.PlayerEntity"# 标准格式
- action: "showIndicator"
args: ["{target}", "{source}", "{value}", "CRITICAL"]
# 短语法(动态推断)
# 当节点中不含保留字段时,第一个字段被视为 action 名
- showIndicator: ["{target}", "{source}", "{value}", "CRITICAL"]
# 捕获返回值
- action: "someAction"
args: ["arg1"]
store: "result"| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
action |
String | 是 | 已注册的动作名 |
args |
List | 否 | 参数列表,支持 {变量} 模板插值 |
store |
String | 否 | 将返回值存入此变量名 |
当节点不包含任何保留键(check、switch、return、any、all、math、collect、action、type)时,引擎将第一个字段视为动作名,其值视为参数列表。
# 以下两种写法等价
- action: "sendMessage"
args: ["hello"]
- sendMessage: ["hello"]# 1. 空返回(返回 null)
- return
# 2. 变量返回
- return: "{damage}"
# 3. 模板字符串返回
- return: "HP:{hp} 伤害:{damage}"
# 4. 字面量返回
- return: 42
- return: true
- return: "固定文本"
# 5. 集合字面量返回
- return: [1, "{hp}", "fixed", true]- switch: weaponType
cases:
SWORD:
- action: "log"
args: ["剑类武器"]
BOW:
- action: "log"
args: ["弓类武器"]
DAGGER:
- action: "log"
args: ["匕首类武器"]| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
switch |
String | 是 | 用于分支匹配的变量名 |
cases |
Map | 是 | 分支映射,key 为匹配值,value 为节点列表 |
若无匹配分支,switch 不执行任何操作,继续后续流程。
- math: "{damage} * 1.5 + {bonus}"
store: "finalDamage"| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
math |
String | 是 | 数学表达式(支持 {变量} 引用) |
store |
String | 是 | 结果存入此变量名(double 类型) |
完整的运算符、函数、优先级参考 → 数学表达式
# ANY(OR 短路):任一子条件成立即通过
- any:
- check: damage
op: ">"
value: 50
- check: weaponName
op: "=="
value: "sword"
on_fail:
- action: "log"
args: ["所有条件都不满足"]
# ALL(AND 短路):全部子条件成立才通过
- all:
- check: damage
op: ">"
value: 10
- check: weaponName
op: "=="
value: "sword"
on_fail:
- return| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
any / all
|
List | 是 | 子条件节点列表 |
on_fail |
List | 否 | 条件不满足时执行的节点列表 |
支持任意深度的嵌套组合:
- any:
- all:
- check: damage
op: ">"
value: 100
- check: rank
op: ">="
value: 5
- check: weaponName
op: "=="
value: "legendary_sword"遍历集合变量,按条件筛选或聚合。
- collect: items
op: exists
match:
- op: "contains"
value: "sword"
on_fail:
- return| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
collect |
String | 是 | 集合变量名 |
op |
String | 是 | 操作类型 |
match |
List | 是 | 匹配条件列表(多条件 AND 关系) |
store |
String | 视操作 | 结果存储变量名(count/index/find/filter 必填) |
on_fail |
List | 否 | 量词操作失败时执行的节点列表 |
| 操作 | 取反 | 结果 | 说明 |
|---|---|---|---|
exists |
!exists |
控制流 | 是否存在匹配元素(∃) |
all |
!all |
控制流 | 是否全部元素匹配(∀) |
count |
— | int → store
|
统计匹配元素数量 |
index |
— | int → store
|
首个匹配元素索引(-1 表示未找到) |
find |
— | Object → store
|
首个匹配元素(null 表示未找到) |
filter |
— | List → store
|
所有匹配元素组成的列表 |
集合操作的详细语法与示例 → 集合操作
所有操作符前均可加 ! 前缀取反(如 !null、!contains、!==)。
| 操作符 | 分类 | 说明 |
|---|---|---|
null |
空值检测 | 检查变量是否为 null |
instanceof |
类型检测 | 检查类型并自动窄化 |
== |
相等 | 相等比较 |
!= |
相等 | 不等比较 |
> |
数值 | 大于 |
>= |
数值 | 大于等于 |
< |
数值 | 小于 |
<= |
数值 | 小于等于 |
between |
数值 | 闭区间范围检查,value 为 [low, high]
|
starts_with |
字符串 | 前缀匹配 |
ends_with |
字符串 | 后缀匹配 |
matches |
字符串 | Java 正则全匹配 |
contains |
成员 | 字符串包含 / 集合成员检查 |
in |
成员 | 检查变量值是否在给定列表中 |
引擎内置常见拼写错误的自动提示:
| 错误拼写 | 建议修正 |
|---|---|
startsWith、startswith、start_with
|
starts_with |
endsWith、endswith、end_with
|
ends_with |
match、regex
|
matches |
include、includes、has
|
contains |
eq、is、equal、equals
|
== |
ne、neq、not
|
!= |
gt / lt / gte / lte
|
> / < / >= / <=
|
instanceof 检查成功后,变量类型自动窄化为目标类,后续节点可直接访问子类属性:
variables:
entity: "entity"
flow:
- check: entity
op: "instanceof"
value: "org.bukkit.entity.Player"
# 窄化后可直接访问 Player 独有属性
- action: "log"
args: ["玩家名: {entity.playerName}"]使用 {变量名} 语法在字符串中插入变量值。
# 单变量引用
args: ["{damage}"]
# 多变量模板
args: ["玩家 {playerName} 受到 {damage} 点伤害"]
# 链式属性引用
args: ["{entity.name}"]
# return 中使用模板
- return: "HP:{hp} DMG:{damage}"属性路径支持多级嵌套:
player.inventory[0].amount
│ │ │ └── getAmount() → int
│ │ └──────── List.get(0) → ItemStack
│ └─────────────── getInventory() → List<ItemStack>
└─────────────────────── getPlayer() → Player
| 类型 | 语法 | 说明 |
|---|---|---|
| 属性访问 | property |
标准 getter 调用 |
| List 索引 | list[index] |
List.get(int) |
| Map 索引 | map[key] |
Map.get(String),支持引号包裹 key |
id: "damage-indicator"
event: "org.bukkit.event.entity.EntityDamageByEntityEvent"
priority: 3
variables:
target: "entity"
source: "damager"
value: "finalDamage"
cause: "cause"
critical: "critical"
flow:
- check: target
op: "instanceof"
value: "org.bukkit.entity.LivingEntity"
- check: value
op: ">"
value: 0
- check: critical
op: "!="
value: true
on_fail:
- showIndicator: ["{target}", "{source}", "{value}", "CRITICAL"]
- return
- showIndicator: ["{target}", "{source}", "{value}", "{cause.name}"]id: "pvp-guard"
event: "org.bukkit.event.entity.EntityDamageByEntityEvent"
variables:
damage: "damage"
entity: "entity"
weapon: "damager.itemInMainHand.type.name"
flow:
- all:
- check: damage
op: ">"
value: 5
- check: weapon
op: "in"
value: ["DIAMOND_SWORD", "IRON_SWORD", "NETHERITE_SWORD"]
- check: entity
op: "!null"
on_fail:
- return
- any:
- check: damage
op: "between"
value: [100, 999]
- check: weapon
op: "starts_with"
value: "NETHERITE"
- showIndicator: ["{entity}", "{entity}", "{damage}", "SPECIAL"]id: "rpg-damage"
event: "com.example.CombatEvent"
variables:
attack: "attacker.attack"
defense: "target.defense"
level: "attacker.level"
weaponType: "weapon.type"
flow:
- math: "clamp({attack} * (1 + {level} * 0.05) - {defense} * 0.3, 1, 99999)"
store: "finalDmg"
- switch: weaponType
cases:
SWORD:
- math: "{finalDmg} * 1.2"
store: "finalDmg"
BOW:
- math: "{finalDmg} * 0.9"
store: "finalDmg"
- action: "applyDamage"
args: ["{finalDmg}"]id: "inventory-check"
event: "com.example.TradeEvent"
variables:
items: "player.inventory"
flow:
# 必须存在稀有物品
- collect: items
op: exists
match:
- op: "contains"
value: "rare"
on_fail:
- action: "sendMessage"
args: ["背包中没有稀有物品"]
- return
# 统计匹配数量
- collect: items
op: count
store: "rareCount"
match:
- op: "contains"
value: "rare"
- action: "log"
args: ["找到 {rareCount} 个稀有物品"]