JavaScript Scripting
CoreLib provides a GraalJS-based JavaScript scripting system. Scripts can be invoked from action lists through the runjs action.
This page summarizes the CoreLib JavaScript scripting system for the English documentation.
Basic Usage
Place scripts under:
plugins/EmakiCoreLib/scripts/Common directories include:
scripts/
├── global/
├── mythic/
├── extensions/
│ └── global/
├── templates/
└── examples/These folders come from the default value of script.paths.create_directories and can be extended as needed. The script system is disabled by default: set script.enabled: true in config.yml to turn it on.
Example action:
actions:
- 'runjs script=examples/hello.js'Example script:
function main(ctx) {
emaki.logger.info("Hello from Emaki JavaScript.");
if (emaki.player.exists()) {
emaki.player.sendMessage("[EmakiJS] Hello, " + emaki.player.name() + "!");
}
return true;
}Exposed APIs
Scripts can use the injected emaki object:
| API | Purpose |
|---|---|
emaki.context | Read action context, placeholders, attributes, and arguments. |
emaki.player | Read player information and send messages. |
emaki.item | Read contextual ItemStack information. |
emaki.action | Dispatch CoreLib actions from scripts. |
emaki.logger | Write script-prefixed logs. |
emaki.random | Generate random values. |
emaki.state | Store temporary shared state in the current action context. |
emaki.text | Text utility methods. |
Emaki Series dynamic module entry points
CoreLib only provides a dynamic module registry; it does not hard-code business plugin fields. When a business plugin is enabled, it registers its scripting capabilities under emaki.module("moduleId"); when disabled or reloaded, it unregisters them.
Common rules:
emaki.module(id)is equivalent toemaki.modules.get(id).emaki.modules.ids()lists all currently registered module IDs.- Missing, disabled, or not-yet-registered business plugins return an unavailable module with
available() === false. - Script examples for business modules should always check
available()first to avoid softdepend failures. emaki.itemis the context ItemStack helper, not the EmakiItem plugin API; the EmakiItem plugin API usesemaki.module("items").
Baseline probe example:
function main(ctx) {
const ids = emaki.modules.ids();
emaki.logger.info("registered modules=" + ids.join(","));
const level = emaki.module("level");
if (level.available()) {
emaki.logger.info("Level module is available");
}
return true;
}Common module IDs
| Module ID | Registered by | What it covers | Details |
|---|---|---|---|
attribute | EmakiAttribute | PDC attribute payloads, dynamic attributes, damage hooks, damage calculation and application. | Attribute JavaScript |
strengthen | EmakiStrengthen | Strengthenable checks, state summaries, chance rules, and result hooks. | Strengthen JavaScript |
skills | EmakiSkills | Skill script action registry queries and custom skill action registration. | Skills JavaScript |
items | EmakiItem | Item definition lookup, shared item/components snapshots, definition/factory registration, and context item identification. | Item JavaScript |
forge | EmakiForge | Forge rule and result hook registration, plus readiness probes. | Forge JavaScript |
cooking | EmakiCooking | Result rule and complete hook registration, plus readiness probes. | Cooking JavaScript |
gem | EmakiGem | Socket rule and set bonus registration, plus readiness probes. | Gem JavaScript |
level | EmakiLevel | Level exp lookup, exp rule, and level-up hook registration. | Level JavaScript |
Why not list every business method here
Business plugin scripting modules are registered by the corresponding plugin, and their parameters, return values, and behavior evolve with that plugin’s API. To avoid turning the CoreLib page into a stale monolithic table, CoreLib documents only the dynamic module mechanism and shared safety rules. Plugin-specific methods, action IDs, configuration examples, and full script examples belong on the corresponding plugin pages.
emaki.server
emaki.server is a controlled server API facade for trusted JavaScript scripts. It exposes common Bukkit operations without enabling unrestricted Java class access by default.
| Method | Description |
|---|---|
pluginEnabled(name) | Check whether a plugin is enabled. |
onlinePlayers() | Return wrapped online players. |
player(nameOrUuid) | Return a wrapped player. |
world(name) | Return a wrapped world. |
broadcast(message) | Broadcast a message. |
dispatchCommandAsConsole(command) | Run a console command; requires server_api.allow_console_command: true. |
runSync(fn) / runSyncAndWait(fn) | Switch to the main thread. |
type(className) | Extreme mode Java class access; requires server_api.allow_type_access: true. |
function main(ctx) {
const player = emaki.server.player(emaki.player.uuid());
if (player.exists()) {
player.sendMessage("Hello from JavaScript server API");
}
return true;
}type() example:
function main(ctx) {
const Bukkit = emaki.server.type("org.bukkit.Bukkit");
emaki.logger.info("online=" + Bukkit.getOnlinePlayers().size());
return true;
}
type()is close to Java-plugin-level power. Only enable it for trusted scripts.
JavaScript extension scripts
Besides one-shot runjs, business plugins can load extension scripts:
plugins/EmakiCoreLib/scripts/extensions/
├── global/
│ └── *.js
├── skills/
│ └── *.js
└── attribute/
└── *.jsExtension scripts usually export register(api):
function register(api) {
// Register global CoreLib actions/placeholders, skill actions, dynamic attributes, providers, or damage hooks.
}Business plugins clear old registrations and rerun register during reload.
CoreLib bundles default resource scripts and releases missing files into the script repository:
scripts/extensions/global/js_broadcast_action.js: registers thejs_broadcastJavaScript global action for any CoreLib action list.scripts/extensions/global/js_placeholders.js: registers the example%js_online_count%and%js_player_world%placeholders.scripts/extensions/global/js_event_examples.js: bundles controlled event examples forplayer_join,player_interact, andentity_damage_by_entity; disabled by default until you edit the script switch.
Business plugins can also release their own example scripts into CoreLib's script repository. For example, Skills releases scripts/extensions/skills/js_lightning_strike.js, while Attribute releases scripts/extensions/attribute/js_fire_mastery.js and scripts/mythic/mythic_js_damage.js. Their registration source, parameters, and behavior belong to the corresponding business plugin pages.
Bundled scripts are only copied when the target file is missing; modified server-side files are not overwritten.
Actions registered from extensions/global are tracked by owner/source. CoreLib reload only clears registrations from the corresponding script source, preventing stale JavaScript actions or placeholders from remaining active.
Global placeholder example:
function register(api) {
api.registerPlaceholder({
id: "js_online_count",
function: "onlineCount"
});
}
function onlineCount(ctx, args) {
return emaki.server.onlinePlayers().size();
}After registration, use %js_online_count% in text rendered through CoreLib placeholders.
Controlled event listener example:
function register(api) {
api.onEvent({
id: "right_click_log",
event: "player_interact",
priority: "NORMAL",
ignoreCancelled: true,
function: "onInteract"
});
}
function onInteract(event, args) {
if (event.rightClick()) {
emaki.logger.info(event.player().name() + " right clicked with " + event.itemType());
}
return true;
}Initial whitelist:
player_interactplayer_joinentity_damage_by_entity
The event wrapper exposes safe basic information by default. event.cancel(), event.setCancelled(...), event.setJoinMessage(...), and event.setDamage(...) only work outside MONITOR priority; MONITOR listeners are treated as read-only.
Security Notes
The default configuration disables host class lookup, IO, threads, native access, and environment access. Production servers should keep these safe defaults unless they fully understand the risks.