Skip to content

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

MethodReturnDescription
available()booleanWhether the API is installed.
scriptActionRegistry()SkillScriptActionRegistryScript action registry.
java
if (EmakiSkillsApi.available()) {
    SkillScriptActionRegistry registry = EmakiSkillsApi.scriptActionRegistry();
}

If you prefer a helper, check availability first:

java
if (!EmakiSkillsApiProvider.available()) {
    return;
}

EmakiSkillsApiProvider.requireAvailable();
SkillScriptActionRegistry registry = EmakiSkillsApi.scriptActionRegistry();

SkillScriptActionRegistry

MethodDescription
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

java
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:

ModeDescription
SYNCInvoked on the script's owned Bukkit/Paper/Folia scheduler domain.
ASYNC_IOInvoked 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).

EmakiSkillsProtocol is a pure protocol module, not a server plugin. Do not place it in plugins/. Runtime modules embed and relocate the protocol classes into their own jars.

Static semantics provided by EquipmentSkillPdcCodec:

MethodDescription
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.EmakiSkills dependency in paper-plugin.yml.
  • Use plugin-prefixed action ids to avoid conflicts.
  • Call registry.unregisterAll(this) in your own onDisable(). The registry does not listen for PluginDisableEvent; it only cleans up opportunistically when byOwner(owner) observes a disabled owner.
  • Do not call Bukkit API directly from ASYNC_IO actions. This suite supports Folia, so use the entity/region schedulers rather than Bukkit.getScheduler().runTask().
  • scriptActionRegistry() returns null when 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.

MethodDescription
available()Whether the Skills API is registered.
hasScriptAction(actionId)Whether a skill script action id is registered.
registeredScriptActions()Return registered skill script action ids.
js
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.

js
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:

yaml
script:
  actions:
    cast:
      - 'js_lightning_strike damage=18'

The ctx object passed to JavaScript skill actions also exposes chaining helpers:

MethodDescription
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.
js
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;
}