简体中文
JS 脚本扩展
EmakiAttribute 自 4.8.2 起支持用 JavaScript 声明属性定义. 把 .js 文件放进 plugins/EmakiAttribute/scripts/ 目录, 通过 attributes.register({...}) 注册的属性与 attributes/*.yml 中的属性同权: 可被物品 ea_attribute 效果, Lore 解析, 占位符与属性快照收集消费
宿主机制, 目录加载顺序, 求值超时与全开放沙箱规则与 CoreLib 一致, 契约见 CoreLib JS 脚本扩展 的安全声明与线程模型章节
attributes 绑定 API
payload 字段
字段与 attributes/*.yml 的 schema 对齐:
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
id | string | 是 | — | 属性 ID; 缺失或空白时该条跳过并报错 |
display_name | string | 否 | — | 显示名, 作为 Lore 关键字与别名匹配的基础 |
value_kind | string | 否 | "FLAT" | FLAT / PERCENT / CHANCE / REGEN / RESOURCE / DERIVED; 非法值该条跳过 |
target_type | string | 否 | "GENERIC" | DAMAGE / RESOURCE / GENERIC / VANILLA; 非法值该条跳过 |
target_id | string | 否 | — | 目标 ID: DAMAGE 对应伤害类型 ID, RESOURCE 对应资源 ID, VANILLA 对应原版属性名 |
mmoitems_stat | string | 否 | — | MMOItems 统计项映射 |
default_value | number | 否 | 0 | 默认值; 非数值该条跳过 |
min_value | number | 否 | — | 下限; 非数值该条跳过 |
max_value | number | 否 | — | 上限; 非数值该条跳过 |
allow_negative | boolean | 否 | true | 是否允许负值 |
priority | number | 否 | 0 | Lore 读取优先级, 数值越大越优先 (取整) |
lore_format_id | string | 否 | — | 词条格式 (如 default_flat / default_percent / default_regen / default_resource) |
lore_patterns | string 数组 | 否 | [] | 自定义 Lore 正则模板 (%Key% 与 %Value% 占位); 不是数组该条跳过 |
description | string | 否 | — | 描述文本 |
attribute_power | number | 否 | 1 | 战力系数; 非数值该条跳过 |
tags | string 数组 | 否 | [] | 标签列表 |
temporary_stack_mode | string | 否 | "REPLACE" | 临时属性叠加模式: REPLACE / STACK; 非法值该条跳过 |
数值字段的入参契约: 既接受数字, 也接受可解析为数字的字符串
注册与覆盖规则
- 校验通过的 payload 映射为属性定义, 经内部注册表以来源
"script"注册 - 同 id 重复注册 (同文件或跨文件): 后注册者覆盖, 并在加载报告中输出提示 (
script.register_override) - 任一字段校验失败时整条注册跳过 (错误报告含文件, 字段与非法值), 同文件其余条目与其余文件不受影响
- 注册成功的属性在属性系统内的消费方式与 YAML 属性完全一致
线程与异常隔离
- 属性注册发生在脚本载入期 (经宿主锁串行化), 注册完成后由属性系统按既有逻辑消费, 运行期没有 JS 回调, 不存在脚本热路径
- 单文件编译/求值失败由宿主隔离 (该文件跳过, 其余继续); 单条 payload 校验失败逐条跳过 (见上文)
配置与重载
config.yml 中的 scripts 节:
yaml
scripts:
enabled: true
timeout_ms: 5000| 字段 | 类型 | 必填 | 默认值 | 取值范围 | 说明 |
|---|---|---|---|---|---|
scripts.enabled | boolean | 否 | true | true / false | 关闭后不扫描不加载脚本, 已注册的脚本属性随之注销 |
scripts.timeout_ms | integer | 否 | 5000 | 正整数 | 单次脚本求值超时毫秒数; 非正数回退 5000 |
重载行为: 脚本随 EmakiAttribute 既有 reload 流程重载 -- 先按来源 "script" 清空全部脚本属性注册, 再关闭并重建 JS Context, 按文件顺序重新求值与注册. 修改后的定义立即生效, 已删除文件对应的属性不复存在. GraalJS 运行库缺失时输出一条告警并跳过脚本加载, 其余功能不受影响
错误排查
| 控制台输出 | 原因 | 处置 |
|---|---|---|
script.engine_unavailable: "GraalJS 引擎不可用, 脚本属性加载已跳过: %reason%" | GraalJS 运行库缺失或宿主创建失败 | 按 %reason% 排查; 其余功能不受影响 |
script.disabled: "脚本属性支持已关闭 (scripts.enabled = false)" | scripts.enabled: false | 需要时开启后 reload |
script.loaded: "脚本属性加载完成: 成功 %loaded% 个, 跳过 %skipped% 个" | 每次加载/重载的汇总 | 跳过数异常时查看随后的 file_error / register_error |
script.file_error: "脚本 %file% 加载失败: %error%" | 单文件语法错误, 求值超时或运行时异常 | 按 %error% 修复该文件; 其余文件不受影响 |
script.register_error: "属性注册被跳过: %error%" | payload 校验失败 (对应下述 payload_* 消息) | 按 %error% 修正字段 |
script.register_override: "脚本属性 %id% 覆盖了已注册的同名属性" | 同 id 重复注册 | 确认覆盖是否符合预期; 不想覆盖请改 id |
script.payload_missing_id: "%file% 缺少必填的 id 字段" | 注册对象缺 id | 补充 id |
script.payload_invalid_enum: "%file% 的字段 %field% 枚举值无效: %value%" | value_kind / target_type / temporary_stack_mode 拼写错误 | 改为合法枚举值 (见 payload 表) |
script.payload_invalid_number: "%file% 的字段 %field% 不是有效数字: %value%" | 数值字段写了非法文本 | 修正为数字或数字字符串 |
script.payload_invalid_list: "%file% 的字段 %field% 必须为字符串列表" | lore_patterns / tags 不是数组 | 改为数组 |
安全声明
脚本环境默认全开放 (可访问任意 Java 类与服务器 API), 具备与服务器同级权限, 请只加载你自己审查过的脚本; 重载会重建 Context 并清空脚本顶层状态. 完整声明见 CoreLib JS 脚本扩展
示例脚本
以下示例随插件首次释放到 scripts/attributes/example_attribute.js, 可直接修改或删除
js
// EmakiAttribute 脚本属性示例 (attributes 绑定)
// attributes.register ({...}) 以字段声明一个属性定义, 注册后与 attributes/*.yml 中的属性同权:
// 可被物品 ea_attribute 效果, Lore 解析, 占位符与属性快照收集消费
//
// 字段说明 (与 attributes/*.yml schema 对齐):
// id 必填, 属性 ID (同 ID 重复注册时后注册者覆盖并在报告中提示)
// display_name 显示名, 作为 Lore 关键字与别名匹配的基础
// value_kind 数值类型: FLAT / PERCENT / CHANCE / REGEN / RESOURCE / DERIVED
// target_type 目标类型: DAMAGE / RESOURCE / GENERIC / VANILLA
// target_id 目标 ID (资源或原版属性标识, 可选)
// mmoitems_stat MMOItems 统计项映射 (可选)
// default_value 默认值; min_value / max_value 为可选上下限
// allow_negative 是否允许负值, 默认 true
// priority Lore 读取优先级, 数值越大越优先, 默认 0
// lore_format_id 词条格式 (default_flat / default_percent / default_regen / default_resource)
// lore_patterns 自定义 Lore 正则模板 (可选, %Key% 与 %Value% 占位)
// description 描述文本 (可选)
// attribute_power 战力系数, 默认 1
// tags 标签列表 (可选)
// temporary_stack_mode 临时属性叠加模式: REPLACE / STACK
//
// 本文件随插件首次释放; 已存在的 scripts/ 目录不会被升级覆盖, 可自由修改或删除
attributes.register({
id: "script_example_mana_cost",
display_name: "施法消耗减免",
value_kind: "FLAT",
target_type: "GENERIC",
target_id: "mana_cost",
default_value: 0.0,
min_value: 0.0,
max_value: 100.0,
allow_negative: false,
priority: 10,
lore_format_id: "default_flat",
lore_patterns: ["%Key%.*?: ?%Value%$"],
description: "脚本示例属性:降低施法消耗。",
tags: ["script", "example"],
temporary_stack_mode: "REPLACE"
});相关页面
- 属性定义: 属性字段与
value_kind,target_type枚举的完整含义 - CoreLib JS 脚本扩展: 宿主机制, 目录结构, 线程模型与安全声明