API
EmakiSkills exposes a static facade, EmakiSkillsApi. Third-party plugins can use it to obtain the script action registry and register custom script actions that extend skill effects.
EmakiSkillsApi
| Method | Return | Description |
|---|---|---|
available() | boolean | Whether the API is installed. |
scriptActionRegistry() | SkillScriptActionRegistry | Script action registry. |
if (EmakiSkillsApi.available()) {
SkillScriptActionRegistry registry = EmakiSkillsApi.scriptActionRegistry();
}If you prefer a helper, check availability first:
if (!EmakiSkillsApiProvider.available()) {
return;
}
EmakiSkillsApiProvider.requireAvailable();
SkillScriptActionRegistry registry = EmakiSkillsApi.scriptActionRegistry();SkillScriptActionRegistry
| Method | Description |
|---|---|
register(Plugin owner, SkillScriptAction action) | Register a custom action; returns a SkillActionResult. |
unregister(String actionId) | Unregister one action. |
unregisterAll(Plugin owner) | Unregister all actions owned by a plugin. |
get(String actionId) | Get an action instance. |
ownerOf(String actionId) | Get action owner. |
all() | Get all registered actions. |
byOwner(Plugin owner) | Get all actions owned by a plugin. |
SkillScriptAction
public interface SkillScriptAction {
String id();
CompletableFuture<SkillActionResult> execute(SkillScriptContext context, Map<String, String> arguments);
default String category() { return "skill"; }
default String description() { return id(); }
default List<SkillActionParameter> parameters() { return List.of(); }
default boolean acceptsDynamicParameter(String name) { return false; }
default SkillActionExecutionMode executionMode() { return SkillActionExecutionMode.SYNC; }
default long timeoutMillis() { return 30_000L; }
default SkillActionResult validate(Map<String, String> arguments) { return SkillActionResult.ok(); }
// Entry points actually invoked by the runtime; default to execute(...)
default CompletionStage<SkillActionResult> executeAsync(SkillScriptContext context,
Map<String, String> arguments);
default CompletionStage<SkillActionResult> executeAsync(SkillScriptContext context,
Map<String, String> arguments,
CancellationToken cancellationToken);
}The default validate(...) checks for missing required arguments and type validity against parameters().
Execution modes
SkillActionExecutionMode has exactly two values:
| Mode | Description |
|---|---|
SYNC | Invoked on the script's owned Bukkit/Paper/Folia scheduler domain. |
ASYNC_IO | Invoked on CoreLib's asynchronous task scheduler; Bukkit ownership rules still apply. |
Returning a future does not by itself move the call off the current domain. The runtime selects the invocation domain from executionMode() and applies timeoutMillis() (default 30000 ms) while awaiting the returned stage. Implementations must marshal later Bukkit/Paper/Folia work to the correct scheduler themselves.
Parameter types
SkillActionParameterType supports STRING, INTEGER, DOUBLE, BOOLEAN, and TIME. TIME accepts ms, s, and t suffixes; a bare number is parsed as ticks.
Cancellation token
The executeAsync overload taking a CancellationToken is called by the runtime. The token is cancelled once an action times out, so long-running actions can check token.isCancelled() and abandon late work.
Equipment skill PDC protocol
The three equipment-skill PDC keys are owned solely by EquipmentSkillPdcCodec in the standalone protocol module EmakiSkillsProtocol (Maven coordinates emaki.jiuwu.craft:emaki-skills-protocol, version 2.6.0).
EmakiSkillsProtocolis a pure protocol module, not a server plugin. Do not place it inplugins/. Runtime modules embed and relocate the protocol classes into their own jars.
Static semantics provided by EquipmentSkillPdcCodec:
| Method | Description |
|---|---|
normalize(skillIds, activeSlot, boundTriggers) | Normalize into an EquipmentSkillPayload. Skill ids are de-duplicated and sorted; slot names are normalized. |
read(itemStack) | Read and decode into an EquipmentSkillPayload. |
readRaw(itemStack) | Read the undecoded RawSnapshot. |
write(itemStack, ...) | Write the payload; an empty payload behaves like clear. Returns a SkillPdcMutation. |
clear(itemStack) | Remove all three PDC keys. Returns a SkillPdcMutation. |
copy(original, rebuilt) | Copy the skill payload from the original item onto a rebuilt item. |
hasPayload(itemStack) | Whether the item carries any of the skill PDC keys. |
matchesSlot(actualSlot, requiredSlot) | Whether the actual slot satisfies the required slot. all matches anything; hand matches both main and off hand. |
Slot constants: all, hand, main_hand, off_hand, helmet, chestplate, leggings, boots.
CoreLib's SkillPdcGateway is now only a @Deprecated(forRemoval = true) delegating adapter kept as a legacy compatibility surface. New code should use EquipmentSkillPdcCodec directly.
Integration notes
- Declare an optional
dependencies.server.EmakiSkillsdependency inpaper-plugin.yml. - Use plugin-prefixed action ids to avoid conflicts.
- Call
registry.unregisterAll(this)in your ownonDisable(). The registry does not listen forPluginDisableEvent; it only cleans up opportunistically whenbyOwner(owner)observes a disabled owner. - Do not call Bukkit API directly from
ASYNC_IOactions. This suite supports Folia, so use the entity/region schedulers rather thanBukkit.getScheduler().runTask(). scriptActionRegistry()returnsnullwhen EmakiSkills is not installed; null-check before use.
JavaScript access
CoreLib JavaScript scripts can query Skills through emaki.module("skills"). Extension scripts under scripts/extensions/skills/ can also register full JavaScript skill actions.
| Method | Description |
|---|---|
available() | Whether the Skills API is registered. |
hasScriptAction(actionId) | Whether a skill script action id is registered. |
registeredScriptActions() | Return registered skill script action ids. |
function main(ctx) {
if (emaki.module("skills").available() && emaki.module("skills").hasScriptAction("damage")) {
emaki.logger.info("Skills damage action is available");
}
return true;
}Register a JavaScript skill action
File: plugins/EmakiCoreLib/scripts/extensions/skills/js_lightning_strike.js. This example is released by the Skills plugin through CoreLib's script repository when the target file is missing.
function register(skills) {
skills.registerAction({
id: "js_lightning_strike",
category: "javascript",
description: "Lightning skill fully handled by JavaScript",
executionMode: "SYNC",
timeoutMillis: 1000,
parameters: [
{ name: "damage", type: "DOUBLE", required: false, defaultValue: "10" }
],
execute: "executeLightning"
});
}
function executeLightning(ctx, args) {
const caster = ctx.caster();
const target = ctx.target();
if (!target.exists()) {
return { skipped: true, message: "No target" };
}
const loc = target.location();
target.world().strikeLightningEffect(loc.x, loc.y, loc.z);
emaki.module("attribute").applyDamage(caster, target, "lightning", Number(args.damage || 10), {
skill_id: ctx.skillId(),
source: "js_lightning_strike"
});
return { success: true, message: "JavaScript lightning executed" };
}Skill configuration can use the registered action id:
script:
actions:
cast:
- 'js_lightning_strike damage=18'The ctx object passed to JavaScript skill actions also exposes chaining helpers:
| Method | Description |
|---|---|
ctx.runAction(id, args) | Run a CoreLib global action from the current skill context. |
ctx.runActionLine(line) | Execute one CoreLib action line. |
ctx.castMythic(skill, params) | Cast a MythicMobs skill as the current caster. |
ctx.applyDamage(target, damageType, baseDamage, context) | Apply Attribute pipeline damage to a target. |
function execute(ctx, args) {
ctx.runAction("js_broadcast", { text: "Skill " + ctx.skillId() + " triggered" });
ctx.castMythic("SomeMythicSkill", { power: "2" });
ctx.applyDamage(ctx.target(), "fire", 12, { source: "skills_js" });
return true;
}