SimpleScripting
Server-side JavaScript for Hytale with per-mod isolation, lifecycle hooks, and opt-in cross-mod services.
What it does
- Loads JS mods from
<server-root>/mods/SimpleScripting/mods-js/<mod-id>/. - Validates
mod.json(schema below) and reports actionable errors. - Runs each mod in its own Rhino context/scope; hooks
onEnable/onDisable/onReloadare optional. - Tracks shared services so mods can expose and call APIs intentionally.
- Seeds practical example mods on first run (
welcome-rewards,home-warps,afk-manager,simple-stats). - Provides helpers to bootstrap mods (
/createmod <mod-id>) and refresh types (/updatetypes <mod-id>). - Injects JS facades for
server,events,commands,players,worlds,net,assets,ui, andlogso mods do not need Java imports. - Supports
require("./other-file.js")to split mods into multiple files within the mod folder.
Build & install
./gradlew build -PhytaleHome=./.hytale-home
Copy build/libs/*.jar into your server plugins folder. On first startup the plugin creates <server-root>/mods/SimpleScripting/mods-js/ and installs the examples.
Mod layout
mods/SimpleScripting/mods-js/
<mod-id>/
mod.json # manifest
main.js # default entrypoint (can be renamed via manifest)
Manifest schema (mod.json)
{
"id": "my-mod", // required, [a-z0-9_-]+
"name": "My Mod", // required
"version": "1.0.0", // required, semver-like
"enabled": true, // optional, defaults to true
"entrypoint": "main.js", // optional, defaults to main.js
"preload": false, // optional, load earlier when order is free
"dependencies": ["other-mod"], // optional, ensures these mods load first
"requiredAssetPacks": [], // optional
"permissions": [], // optional
"description": "What this mod does"
}
Validation rejects missing required fields, bad ids, non-semver versions, .. in entrypoint, missing entrypoint file, blank/invalid dependency names, and self-dependencies.
Entry script shape
function onEnable() {
console.info("Enabled");
}
function onDisable() {
console.info("Disabled");
}
function onReload() {
console.info("Reload");
}
Hooks are optional; they are invoked if defined.
JS API surface (no Java imports needed)
server:runLater/runRepeating,shutdown(reason?),isBooted,name.events:on/once/off/clear, resolves common events by name (e.g.PlayerChat,BreakBlock,Boot).events.knownEvents()lists the built-ins.commands:register("modid:cmd", handler, { description, permission, allowExtraArgs })returns a handle forunregister.players: JS wrappers only (PlayerHandlewith username/id/language/message/kick/isOnline);all/find/require/names/count/message/broadcast/disconnect`.worlds:list/get/getDefaultWorld/message/hasWorldreturningWorldHandle(name, players, sendMessage).net: high-levelbroadcast/send/kick/warn(no raw packets exposed).assets: placeholder wrapper (raw registry intentionally not exposed yet).ui: build colored text viaui.raw/ui.color/ui.join;sendMessage/broadcast/replyaccept these objects (no nativeMessageexposed).economy: multi-provider economy integration for player balances (balance/withdraw/deposit/has/isAvailable/getName); optional - economy features are available when VaultUnlocked and/or EliteEssentials plugin is installed separately on the server.log/console: info/warn/error; references are read-only so mods cannot clobber each other.- Event payloads are wrapped (not raw Hytale objects). Example:
PlayerChatprovidesgetPlayer()/getMessage()/setMessage()/cancel(). - Commands use the native parser under the hood; JS handlers get
CommandContext+ parsed args (command token removed) viactx.args()/ctx.rawInput().
Imports between JS files
Use require("./relative-path") to pull another .js file from the same mod directory (no .. allowed). Modules get exports/module automatically:
// util/math.js
exports.sum = function(a, b) { return a + b; };
// main.js
const math = require("./util/math");
log.info("2 + 3 = " + math.sum(2, 3));
Wrapper conventions & naming
- Methods on the injected facades use
camelCase; tests enforce this (WrapperConventionsTest). - Command ids should be namespaced:
modid:command(auto-prefixed if missing). - Event names use simple forms (e.g.
PlayerChat,BreakBlock,Boot); seeevents.knownEvents(). - IDs/paths stay within the mod folder;
requirerejects..traversal and absolute paths. - Keep per-mod resources registered via the facades; disable/reload unregisters events/commands/tasks automatically.
Shared services (opt-in cross-mod APIs)
- Expose from a mod:
SharedServices.expose("greetings", { greet: function(name) { return "Hello " + name; } }); - Consume from another mod:
var result = SharedServices.call("greetings", "greet", ["Traveler"]); console.info("Got: " + result); - Declare
dependenciesinmod.jsonto ensure providers load before consumers. Services are cleared when a mod reloads/disables. Names are first-come; duplicates by other mods are rejected.
Bundled examples
Practical server plugins that showcase the full SimpleScripting API:
- welcome-rewards: Complete welcome system with starter kit, join/leave broadcasts, and playtime tracking (
/playtime,/online) - home-warps: Teleportation with player homes and server warps (
/sethome,/home,/warp) - afk-manager: Auto-detect and manage idle players with configurable timeouts (
/afk,/afklist) - simple-stats: Player statistics tracker with leaderboards (
/stats,/top). Demonstrates modular code withrequire()
Example mods are installed on first run with "enabled": false so they don't auto-load. Set "enabled": true in their mod.json to activate them. Extensions can also bundle examples that are automatically installed (e.g., EconomySS includes a player shops example).
Inspect them under src/main/resources/examples/ (copied to mods-js/ on first run with complete type definitions).
Commands
/createmod <mod-id>: scaffolds a new mod folder from the template (mod.json, main.js, index.d.ts with core + extension types)./updatetypes <mod-id>: copies the latestindex.d.tsinto an existing mod (includes types from all registered extensions - run after plugin/extension upgrades).
Debugging Hytale APIs quickly
- Jar path (Gradle cache):
~/.gradle/caches/modules-2/files-2.1/com.hypixel.hytale/hytale-server/1.0.2/<hash>/hytale-server-1.0.2.jar - List classes:
jar tf ~/.gradle/.../hytale-server-1.0.2.jar | rg 'server/core/plugin' - Inspect signatures:
javap -classpath ~/.gradle/.../hytale-server-1.0.2.jar com.hypixel.hytale.server.core.plugin.JavaPlugin
Extension System
External Hytale plugins can extend SimpleScripting by implementing the SimpleScriptingExtension interface:
- Global APIs: Register custom APIs that appear in every JS mod (e.g.,
economy,permissions) - Type Definitions: Provide TypeScript
.d.tsfiles that merge with core types - Example Mods: Bundle example mods that showcase your extension's features
- Event Bus: Cross-extension communication via namespaced events
- Priority Loading: Control initialization order (0-50 core, 50-100 third-party)
See extensions documentation for creating extensions. Example: EconomySS adds economy APIs and a player shops example.
Notes
- Each mod is sandboxed to its own Rhino scope; globals do not leak.
- Keep names unique; duplicate mod ids are skipped.
- Shared services are intentionally opt-in; only data/functions you expose are reachable by other mods.
- Extensions register during plugin
setup()phase and are initialized before mods load.
