Skip to content

Scripting Overview

Liam Robinson edited this page Nov 8, 2026 · 1 revision

Scripting Overview

Packages can carry scripts written in Luau, a fast, typed dialect of Lua 5.1. The API follows Multi Theft Auto's (elements, element data, events, addEventHandler, triggerClientEvent) with ideas from FiveM (join deferrals, exports, latent events), so most of it will look familiar if you know either.

Where scripts run

Side Runs Declared in resource.toml
server in the dedicated server, for a server resource [scripts] server = ["server/*.luau"]
client in each player's game: a server resource's client scripts on that server, or a mod's in single player [scripts] client = ["client/*.luau"]
shared on both sides, loaded before each side's own scripts [scripts] shared = ["shared/*.luau"]

Server scripts never leave the server. A client script runs only on the player's own game and is never trusted by the server: anything it sends is checked there.

A gamemode package's server scripts also run in single player: the game starts a local script host for the gamemode when you ride it from the Maps page, so a gamemode plays the same alone and on a server.

How scripts are loaded

  • Each pattern names .luau files; files run in pattern order, then by path. A file matched twice runs once.
  • Every script has its own globals. A global one script sets is not seen by another script, even in the same package. Share state through a module: require("shared/state") runs that file once and gives every script the same value it returned.
  • require takes a path from the package root without the extension. On the server every .luau file of the package can be required; on a client only files the client receives (not [scripts] server, not [files] server_only).
  • Files ending in .d.luau are type definitions, never run.
  • resource.toml's api = 1 is the API level the package needs. A game or server with a lower level refuses it.

The sandbox

Scripts get Luau's coroutine, table, string, utf8, bit32, math and buffer libraries; from os only clock, time and date; from debug only traceback. There is no file, process or network access: io, os.execute, getfenv and setfenv do not exist. loadstring compiles text into the calling script's own environment. print goes to the package's output:

Side Where print and errors appear
server the server's log: scripts: <package>: <line>, the whole line up to 4 096 bytes (a longer one is cut there and says how much was cut); control characters are shown escaped
client the game's log (the terminal it was started from; game.log in your Project Avalanche folder when the launcher started it), and the script monitor (F9)

A script that errors in a handler is noted with its traceback; the other handlers still run. A script that breaks a limit below stops its whole package, with the reason in the output.

Budgets

Every call into a script is counted and cut off when it runs too long, so no script can freeze the game or the server.

What Client Server
one call (an event handler, a timer, an export) about 10 ms of work (250 000 checkpoints), at most 80 ms of CPU time about 50 ms (1 250 000 checkpoints), at most 400 ms
one call with everything it calls twice that twice that
all scripts of a side per tick 2 ms 4 ms (work beyond it waits for the next tick)
one package per tick 50 000 checkpoints; over that 30 ticks in a row stops it 100 000 checkpoints; likewise
memory per side 64 MiB 256 MiB

Limits

What Limit
script files per side and package 256, each at most 1 MiB, 4 MiB in all
running packages 256
elements per package; tree depth 4 096; 64
element data per package; one value 16 384 keys, 4 MiB; 64 KiB
event handlers / declared events / timers / commands / exports per package 1 024 / 512 / 1 024 / 128 / 256
names: event 128, element type 64, element id 128, export 64, command 32, data key 128 bytes
timer interval; timer arguments 50 ms to 24 hours; 16
events nested; export calls nested 16; 8
a value sent between the server and a client (strings up to 64 KiB, tables 16 deep, 256 values a table) a remote event's arguments 64 KiB; a latent event 16 MiB
a local event's or an export's arguments; json.encode / json.decode 1 MiB; 1 MiB
storage per package client 1 024 keys, 1 MiB; server 4 096 keys, 16 MiB; one value 64 KiB
a list result (getElementsByType …) 65 536 elements
a command line 1 024 bytes, 32 arguments

Values that cross

Arguments of events, exports, element data and storage are copied as values: nil, booleans, numbers, strings, elements, and tables of those (keys and values). Functions, coroutines and buffers never cross; a table that refers to itself is refused.

Settings

resource.toml's [settings] table holds values the scripts read with get(name):

[settings]
greeting = "Welcome to the mountain"
local text = get("greeting")

Storage

storage.set(key, value) and storage.get(key) keep values across restarts, per package:

Where File
a server resource <data folder>/script-data/<resource>/storage.bin on the server
a client script on a server ScriptData/<server key>/<resource>/ in the player's Project Avalanche folder
a mod's client script in single player ScriptData/local/<package>/
a gamemode's server script in single player ScriptData/local-server/<package>/

The store is written at most once a second and when the package stops. A key is 1 to 64 characters of A-Z a-z 0-9 _ . -.

The monitor

  • Server: the console's scripts prints each package's time, memory, events, timers, data and refused messages.
  • Game: F9 shows the client scripts' monitor over the game.

Type checking

The game's repository has the API's type definitions for the luau-lsp language server: Engine/crates/ssx-script/defs/ssx-api1-server.d.luau and ssx-api1-client.d.luau. Point your editor's luau-lsp at the one for the side you write.

Next

Clone this wiki locally