Skip to content

JavaScript Scripting

EmakiLevel registers a scripting module through the CoreLib scripting system. The module ID is level. Inside a script you obtain the module facade with emaki.module("level") to query level types and player levels and to register exp rules and level-up hooks.

This page covers only the EmakiLevel-specific script methods, registration fields, context and examples. For the generic scripting mechanism see CoreLib JavaScript Scripting.

Always guard with available() before calling any business method. EmakiLevel is a CoreLib softdepend.

Obtaining the module

js
function main(ctx) {
  const level = emaki.module("level");
  if (!level.available()) {
    return { skipped: true, message: "EmakiLevel unavailable" };
  }
  // ...
  return true;
}

Probe methods

MethodReturnsDescription
available()booleanWhether the module is available.

The EmakiLevel module facade only exposes available().

Query methods

MethodReturnsDescription
typeIds()listReturn all level type ids, sorted.
type(typeId)mapReturn details of a single type; empty Map when not found.
level(playerUuid, typeId)intThe player's current level for a type; 0 when the UUID is invalid.
exp(playerUuid, typeId)doubleCurrent exp; 0 when the UUID is invalid.
totalExp(playerUuid, typeId)doubleCumulative total exp; 0 when the UUID is invalid.
requiredExp(playerUuid, typeId, targetLevel)doubleExp required to reach a target level; 0 when the UUID is invalid.

playerUuid must be a valid UUID string, parsed internally with UUID.fromString; on failure the method returns 0.

The Map returned by type(typeId) has fields: id, displayName, description, primary, enabled, startLevel, maxLevel, autoUpgrade, manualUpgrade, attributes.

Exp rules

Exp rules run when a player gains exp. They can read the context and adjust the final exp or cancel the gain.

MethodReturnsDescription
registerExpRule(definition)booleanRegister an exp rule.
registerExpRule(id, definition)booleanOverload: merge id into the definition, then register.
unregisterExpRule(id)voidUnregister a rule by id.
registeredExpRules()listReturn the list of registered exp rule ids.

Rule definition fields

FieldTypeRequiredDefaultDescription
idstringyesnoneUnique rule id (normalized); registration fails when blank.
priorityintno0Priority; lower runs first, ties broken by id.
typeIdsstring / listnoempty (all types)Level types the rule applies to; types also works.
reasonsstring / listnoempty (all reasons)Exp reasons the rule applies to.
functionstringnomodifyExpRule callback function name; execute also works.
timeoutMillislongnoengine defaultPer-call script timeout.

Rule callback ctx fields

FieldTypeDescription
ruleIdstringCurrent rule id.
playerUuidstringPlayer UUID.
typeIdstringLevel type id.
reasonstringExp reason.
originalAmountdoubleOriginal exp amount.
currentAmountdoubleCurrent accumulated exp amount.
multiplierdoubleCurrent multiplier.
multipliedAmountdoubleExp amount after the multiplier.
dailyLimitdoubleDaily exp cap.
gainedTodaydoubleExp already gained today.

Rule callback return fields

Return fieldTypeDescription
cancelbooleanWhen true, sets this exp gain to 0.
actualAmount or amountnumberSet the resulting exp explicitly (clamped non-negative, takes priority over multiplier).
multipliernumberOtherwise use current × multiplier (clamped non-negative).
messagestringMessage written to the trace.

A non-object return or an execution failure keeps the original value and records a trace.

Level-up hooks

Level-up hooks fire after a player levels up, for side effects such as rewards and broadcasts.

MethodReturnsDescription
onLevelUp(definition)booleanRegister a level-up hook.
onLevelUp(id, definition)booleanOverload: merge id into the definition, then register.
unregisterLevelUpHook(id)voidUnregister a level-up hook by id.
registeredLevelUpHooks()listReturn the list of registered level-up hook ids.

Hook definition fields

FieldTypeRequiredDefaultDescription
idstringyesnoneUnique hook id; registration fails when blank.
typeIdsstring / listnoempty (all types)Level types that trigger the hook; type also works.
functionstringnoonLevelUpCallback function name; execute also works.
timeoutMillislongnoengine defaultPer-call script timeout.

Level-up hooks have no priority field; multiple hooks fire in id lexical order.

Hook callback event fields

FieldTypeDescription
hookIdstringHook id.
playerUuidstringPlayer UUID.
playerNamestringPlayer name.
typeIdstringLevel type id.
oldLevelintLevel before the level-up.
newLevelintLevel after the level-up.
oldExpdoubleExp before the level-up.
newExpdoubleExp after the level-up.
causestringLevel-up cause.
requiredExpdoubleRequired exp.

Query example

EmakiLevel ships the trigger-style example script scripts/examples/level_status.js:

js
function main(ctx) {
  const level = emaki.module("level");
  const typeIds = level.typeIds();
  const typeId = typeIds.size() > 0 ? typeIds.get(0) : "main";
  let currentLevel = 0;
  let currentExp = 0;
  if (emaki.player.exists() && typeId) {
    currentLevel = level.level(emaki.player.uuid(), typeId);
    currentExp = level.exp(emaki.player.uuid(), typeId);
    emaki.player.sendMessage("[EmakiJS] Level state: type=" + typeId + " level=" + currentLevel + " exp=" + currentExp);
  }
  return {
    success: true,
    output: {
      level_available: level.available(),
      type_count: typeIds.size(),
      sample_type: String(typeId || ""),
      current_level: currentLevel,
      current_exp: currentExp
    }
  };
}

Registration example

EmakiLevel ships the extension-style example script scripts/examples/level_exp_rule.js:

js
function register() {
  const level = emaki.module("level");

  level.registerExpRule({
    id: "js_weekend_bonus",
    priority: 10,
    function: "modifyExp"
  });

  level.onLevelUp({
    id: "js_levelup_message",
    function: "onLevelUp"
  });
}

function modifyExp(ctx) {
  const day = new Date().getDay();
  if (day === 0 || day === 6) {
    return {
      multiplier: 2.0,
      message: "weekend bonus"
    };
  }
  return {};
}

function onLevelUp(event) {
  emaki.logger.info("Level up hook: " + event.playerName + " " + event.typeId + " " + event.oldLevel + " -> " + event.newLevel);
  return true;
}

Enabling extension scripts

Put the script into CoreLib's global extension directory and reload:

text
plugins/EmakiCoreLib/scripts/extensions/global/level_exp_rule.js
text
/corelib script reload

The example file is released to plugins/EmakiCoreLib/scripts/examples/ by default and is not auto-enabled.

Script debugging

When you enable the debug submodule named script, EmakiLevel writes the per-rule execution trace to the server console every time its JS rules run, which helps server administrators debug script rules.

Enable command:

text
/elv debug module script

Trace fields: rule id, before (value before execution), after (value after execution) and message. This output goes to the server console for server administrators to debug; it is not shown to players.

Notes

  • Every registration method requires the CoreLib scripting system to be enabled; otherwise registration returns false or is silently skipped.
  • When CoreLib is missing the module degrades gracefully: rules and hooks have no effect but raise no error.
  • Exp rules run in ascending priority order (lower first).