Skip to content

Client scripting

Client scripts are JavaScript your gamemode sends to every player's game. They run in a sandbox and can:

  • talk to the gamemode on named channels, carrying JSON;
  • open UI documents of their own (HUDs, menus, dialogs) and add pages to the pause menu;
  • show SAGL's message box and get the answer;
  • put markers and images on the map;
  • read the server's client files and branding;
  • read the local player's state.

A client script's HUD

Sending scripts

From the gamemode:

// As code (sent to each player as they join)...
server.clientScripts.set("hud.js", `sagl.log("hello from", sagl.player.id)`);
server.clientScripts.setFile("./client-src/hud.js");

// ...or, better, as a client file players download once and cache.
server.clientScripts.useClientFile("scripts/hud.js");

Scripts run in the order they were first added, all in one shared global scope. Replacing or removing one runs them all again from scratch (their documents, pages, timers, map markers and handlers are cleaned up first), so scripts should set everything up when they run.

Talking to the gamemode

sagl.on("score", ({ cops, robbers }) => {
  hud.setText("cops", cops).setText("robbers", robbers);
});
sagl.emit("shop", { buy: "pistol" });
server.channel("shop").on((player, { buy }) => { /* ... */ });
server.channel("score").broadcast({ cops: 12, robbers: 9 });
player.emit("score", { cops: 12, robbers: 9 });

Data is anything JSON can hold, up to 256 KB a message.

The sandbox

Scripts get the JavaScript language (ES2023, via QuickJS), timers, and the sagl API, and nothing else: no files, network, OS, eval of other scripts' code, or access to SAGL's own UI.

Limit
Memory 32 MB for all scripts together.
Time 50 ms per call into a script (a handler, a timer); 250 ms to run a script when it loads. A script that runs over is interrupted.
Script size 1 MB each.
Messages 256 KB each way; the server also limits each player to 256 KB/s.
UI 16 open documents, 8 pause menu pages, 256 KB of RML each.

Uncaught errors are logged in the player's sagl.log and reported to the gamemode as clientScriptError:

server.on("clientScriptError", (player, { where, message }) => {
  console.warn(`${player.name}: ${where}: ${message}`);
});

Next