Skip to content

Java scripts

Pavel Erokhin edited this page Oct 19, 2026 · 1 revision

With scripts.enabled: true, a token with the code permission can send Java to the server. WorldAPI compiles it with the Eclipse compiler bundled in the jar, so a JRE is enough, and loads it in its own class loader.

Only owner has code. Give it only to people you'd trust with SSH.

Endpoint Permission What it does
GET /scripts code Loaded scripts
POST /scripts code Load a script that stays: it can listen to events and schedule tasks
POST /scripts/run code Compile and run Java once, and return what it returns
GET /scripts/{id} code One script with its source
DELETE /scripts/{id} code Unload a script: remove its listeners and tasks
GET /scripts/{id}/logs code The latest lines a script logged

Run something once

curl -H "Authorization: Bearer $CODE_TOKEN" -H 'Content-Type: application/json' \
     -X POST http://localhost:8110/api/v1/scripts/run \
     -d '{"source": "return server.getWorlds().stream().map(w -> w.getName() + \": \" + w.getEntityCount()).toList();"}'

A snippet is the body of a method with ctx and server in scope and the common Bukkit imports. A whole class works too.

Keep a script loaded

A loaded script reacts to events and runs tasks:

import com.mairwunnx.worldapi.script.api.*;
import org.bukkit.event.player.PlayerJoinEvent;

public class Welcome implements WorldApiScript {
    @Override
    public void enable(ScriptContext ctx) {
        ctx.on(PlayerJoinEvent.class, event -> event.getPlayer().sendMessage("Welcome back!"));
        ctx.every(20 * 60, 20 * 60 * 10, () -> ctx.log("still here"));
    }
}
  1. Send it to POST /scripts with an id, and persistent: true to load it again after restarts.
  2. Watch it with GET /scripts/{id}/logs or the script.log topic.
  3. DELETE /scripts/{id} calls disable(), removes every listener and task the script registered through ctx, closes its class loader, and deletes its classes.

Limits

Scripts see only Java and the Paper API (Bukkit, Paper, Adventure, and what their signatures expose). Before a script loads, WorldAPI checks its bytecode against scripts.blockedApis: processes, reflection, class loaders, native access, System.exit, environment variables, and the file system are blocked by default.

That is a guard rail, not a sandbox. Java has no way to fully contain code that runs in the same process: a script can still freeze the tick with an endless loop on the server thread, or do anything the Bukkit API allows, such as running console commands. Every script, run or loaded, goes to the audit log in full.

Clone this wiki locally