Skip to content

JS Scripting ​

Since 4.8.2, EmakiAttribute supports declaring attribute definitions in JavaScript. Drop .js files into plugins/EmakiAttribute/scripts/ and register them via attributes.register({...}); script attributes have equal standing with attributes from attributes/*.yml: they can be consumed by the item ea_attribute effect, Lore parsing, placeholders, and attribute snapshot collection

The host mechanism, directory load order, evaluation timeout, and fully open sandbox rules are identical to CoreLib; the contract is in the safety notice and thread semantics sections of CoreLib JS Scripting

attributes Binding API ​

Payload fields ​

Fields align with the attributes/*.yml schema:

FieldTypeRequiredDefaultDescription
idstringYes—Attribute id; missing or blank entries are skipped with an error
display_namestringNo—Display name, the basis for Lore keyword and alias matching
value_kindstringNo"FLAT"FLAT / PERCENT / CHANCE / REGEN / RESOURCE / DERIVED; invalid values skip the entry
target_typestringNo"GENERIC"DAMAGE / RESOURCE / GENERIC / VANILLA; invalid values skip the entry
target_idstringNo—Target id: for DAMAGE the damage type id, for RESOURCE the resource id, for VANILLA the vanilla attribute name
mmoitems_statstringNo—MMOItems stat mapping
default_valuenumberNo0Default value; non-numeric values skip the entry
min_valuenumberNo—Lower bound; non-numeric values skip the entry
max_valuenumberNo—Upper bound; non-numeric values skip the entry
allow_negativebooleanNotrueWhether negative values are allowed
prioritynumberNo0Lore read priority, higher wins (truncated to int)
lore_format_idstringNo—Lore format (e.g. default_flat / default_percent / default_regen / default_resource)
lore_patternsstring arrayNo[]Custom Lore regex templates (%Key% and %Value% placeholders); non-arrays skip the entry
descriptionstringNo—Description text
attribute_powernumberNo1Combat power coefficient; non-numeric values skip the entry
tagsstring arrayNo[]Tag list
temporary_stack_modestringNo"REPLACE"Temporary attribute stacking mode: REPLACE / STACK; invalid values skip the entry

Input contract for numeric fields: both numbers and strings parseable as numbers are accepted

Registration & override semantics ​

  • Validated payloads are mapped to attribute definitions and registered through the internal registry with source "script"
  • Duplicate registration of the same id (same file or across files): the later registration overrides the earlier one and the load report notes it (script.register_override)
  • If any field fails validation, the entire entry is skipped (the error report includes file, field, and value); other entries in the same file and other files are unaffected
  • Registered attributes are consumed by the attribute system exactly like YAML attributes

Threading & Exception Isolation ​

  • Attribute registration happens during script loading (serialized through the host lock); afterwards the attribute system consumes definitions through its existing logic — there are no JS callbacks at runtime and no script hot path
  • Per-file compile/evaluation failures are isolated by the host (that file is skipped, the rest continue); per-entry payload validation failures skip only that entry (see above)

Configuration & Reload ​

The scripts section in config.yml:

yaml
scripts:
  enabled: true
  timeout_ms: 5000
FieldTypeRequiredDefaultAllowed valuesDescription
scripts.enabledbooleanNotruetrue / falseWhen disabled, no scripts are scanned or loaded and script attributes registered so far are unregistered
scripts.timeout_msintegerNo5000Positive integerTimeout in milliseconds for a single evaluation; non-positive values fall back to 5000

Reload semantics: scripts reload with EmakiAttribute's existing reload flow — first all script attribute registrations are cleared by source "script", then the JS context is rebuilt and all files are re-evaluated and re-registered in file order. Modified definitions take effect immediately; attributes from deleted files are gone. When the GraalJS runtime is missing, a single warning is logged and script loading is skipped; other features are unaffected

Troubleshooting ​

Console outputCauseFix
script.engine_unavailable: "[Script] GraalJS engine unavailable, script attributes skipped: %reason%"GraalJS runtime missing or host creation failedInspect %reason%; other features are unaffected
script.disabled: "[Script] Script attribute support is disabled (scripts.enabled = false)"scripts.enabled: falseEnable it if desired, then reload
script.loaded: "[Script] Script attributes loaded: %loaded% succeeded, %skipped% skipped"Summary of every load/reloadCheck the following file_error / register_error entries when counts look wrong
script.file_error: "[Script] Script %file% failed to load: %error%"Syntax error, evaluation timeout, or runtime exception in one fileFix that file per %error%; other files are unaffected
script.register_error: "[Script] Attribute registration skipped: %error%"Payload validation failure (maps to the payload_* messages below)Correct the fields per %error%
script.register_override: "[Script] Script attribute %id% overrides a registered attribute with the same id"Duplicate registration of the same idConfirm the override is intended; otherwise change the id
script.payload_missing_id: "%file% is missing the required id field"Registration object lacks idAdd id
script.payload_invalid_enum: "%file% field %field% has invalid enum value: %value%"Misspelled value_kind / target_type / temporary_stack_modeUse a valid enum value (see the payload table)
script.payload_invalid_number: "%file% field %field% is not a valid number: %value%"Numeric field contains invalid textProvide a number or numeric string
script.payload_invalid_list: "%file% field %field% must be a list of strings"lore_patterns / tags is not an arrayProvide an array

Safety Notice ​

The script environment is a fully open sandbox (any Java class and server API reachable) with the same privileges as the server process — only load scripts you have reviewed yourself; reload rebuilds the context and clears top-level script state. See CoreLib JS Scripting for the full statement

Example Script ​

The example below ships with the plugin into scripts/attributes/example_attribute.js on first release; modify or delete it freely

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"
});
  • Attributes: full semantics of attribute fields and the value_kind / target_type enums
  • CoreLib JS Scripting: host mechanism, directory layout, thread semantics, and the safety notice