Errors and the sandbox
Error model
The scripting API reports a failure in one of two ways, depending on whose mistake it is.
nil, err: the server said no
A call that is well formed but cannot be done returns nil and a readable error message instead of raising. On success it returns true, a handle or the value it reads.
local ok, err = player:set_health(100)
if not ok then
print("set_health failed: " .. err)
end
local vehicle, err = dlmp.create_vehicle({ model = 411, x = 2470.0, y = -1660.0, z = 13.3 })Typical reasons: the element is gone (a player who left, a destroyed vehicle), a value is out of range, or the current state forbids it (a seat that is taken, a wreck). The server checks every request itself; the message is for logs and for the player, not for parsing. A few lookups answer nil without a message when there is simply nothing to find: dlmp.get_player(id) for a free id.
Raised errors: the script is wrong
A programming mistake raises a Lua error inside the running callback:
- an argument of the wrong type, a missing required field, or a handle of the wrong family (
dlmp.destroy_vehicle(pickup)); - a string outside a fixed set where the API names one (an unknown
priority, an unknown option field where the reference says so); - a call that reads or changes the game at load time, at the top level of a script or module: the server is reachable only from callbacks, while registration (
dlmp.add_event_handler,dlmp.add_command_handler,dlmp.add_console_command,dlmp.set_timer,require,dlmp.import) works at load time; dlmp.importof a resource that is not a declared dependency, and a call through an import that cannot be made (Calling other resources);- a
requirethat cannot load its module; - the time limit.
pcall catches a raised error like any Lua error. The reference dlmp.lua notes the cases that raise where the rules above do not make them obvious.
Errors in callbacks
An error that escapes a callback is logged with its resource and stops only that callback. The resource keeps running, and what the callback already did before the error stays done: the server applies each call when it is made, there is no rollback.
| Callback | What an escaping error does |
|---|---|
| top-level script code, a module loaded at start | the start fails and is rolled back (Start) |
resource_start handler | the start fails; what the start created is removed |
resource_stop handler | logged; the stop goes on |
| handler of a refusable event | logged; counts as a refusal; the other handlers still run |
player_auth handler | logged; the connection is turned away, unless the handler had already accepted or rejected it |
| any other event handler | logged; the other handlers still run |
| player command handler | logged; the command is not consumed and the next handler runs |
| console command, timer, storage, view, dialog, menu and offer callbacks | logged; a repeating timer keeps running |
| an exported function | raised again in the caller, at the import call |
Refusable events fail closed
A refusable event (Refusable events) is refused when any handler returns false, raises an error or runs past the time limit, also when the handler caught the time-limit error itself with pcall. A broken filter therefore blocks what it was meant to judge instead of letting it through (ADR 0038). The same holds for the auth resource: a player_auth handler that fails turns the connection away.
Sandbox and runtime limits
The sandbox keeps a broken or hostile resource from trivially taking over the server process: a resource has no file, process or network access of its own and cannot stall the server for long with an endless loop. It is not complete isolation: there is no memory limit, so a server should run only resources its operator trusts.
One VM per resource
Every resource runs in a Lua 5.4 VM of its own. Resources share no globals, tables or handles; they meet only through exports, whose arguments are copied (Calling other resources). Scripts never see the server's internals, the network or the wire protocol: they act only through dlmp.
Libraries
| Available | Not available |
|---|---|
the base library without load, loadfile and dofile; print writes to the server log, tagged [lua:<resource>] | os, io, debug, package |
string, table, math | coroutine, utf8 |
require for the resource's own modules (Modules) | native modules |
Files and network
A resource has no general file, process or network access. It reaches only:
- its own
.luamodules, read throughrequire; - its own map documents in
maps/<name>.toml, read and written throughdlmp.read_map,dlmp.save_map,dlmp.list_mapsanddlmp.load_map(ADR 0043); - its own storage namespace through
dlmp.db(ADR 0054); - its client files, which the server reads at start and serves to players.
Time limit
Every entry into a resource's Lua code has a budget of 1 second of wall-clock time: a script or module at load, an event handler, a command, a timer, a callback and an exported function called by another resource (which gets a budget of its own).
- The VM checks the budget every 10 000 instructions. Past it, the check raises
resource callback exceeded the time limit, and keeps raising at every later check, so catching it withpcalldoes not buy more time. - A single long library call, such as sorting a huge table, is noticed only after it returns.
- The error is handled like any other error of that callback (Errors in callbacks); the VM goes on running.
- A resource that has slow work to do splits it over timers; storage requests run off the main loop by design.
Memory
There is no memory limit per VM. A resource that keeps allocating can exhaust the memory of the whole server.
Threading and timing
- All Lua code runs on the server's main loop, one callback at a time; nothing runs in parallel, and no callback is interrupted by another. A callback runs to its end before the server goes on.
- Without coroutines a callback cannot wait. Work that takes time answers later through a callback: storage requests in a later server step, a player's dialog answer when it comes, a timer when it is due.
- A server step has one time (ADR 0046); timers count from the time of the step that set them.
- Recursion between resources is bounded: an export call nested more than 16 deep raises an error.
