Skip to content

C ABI reference (NodeApi)

Generated from sdk/api.toml by sdk/tools/apigen.py docs. Do not edit; edit the schema.

NodeApi (sdk/node.h) is a positional struct of function pointers handed to node_plugin_init. The index shown with each entry is its position in the struct: that order IS the ABI and never changes; new capabilities are appended. Grouping below is by subject, so a group can span several places in the struct. ABI version: 1. Strings are UTF-8 and NUL-terminated; out buffers take a capacity and return the length written or needed; JSON views return the length even when the buffer was too small, so a caller can size and retry.

See Native modules for how a module is built and loaded.

Two plain fields at the top of NodeApi. A native module reads them to assert it was built against a compatible server; the loader has already refused a module whose major differs before any of this is touched. Not part of the Lua surface.

NODE_ABI_VERSION as the SERVER was built with. The loader has already compared this against the module’s node_plugin_abi() before calling node_plugin_init, so a module that is running at all matches; the field is here so a module can assert it too.

sizeof(NodeApi) as the SERVER built it. A module built against an older minor sees a LARGER value than its own sizeof and can rely on every field it knows about; a module built against a newer minor sees a smaller one and must not touch anything past it.

Writing to the server console. Every line is tagged; a resource’s plain node.log carries the resource’s own name as the tag, node.logTag uses one of the server’s known tags, node.logCustom a tag of your own in a chosen colour. A log sink lets a resource observe every line the server prints (asynchronously, on the plugin worker).

Lua: node.raw.log

Log under the server’s “Module” / “Warn” / “Error” console tag. Inline, thread-safe.

Lua: node.raw.logWarn

Log under the server’s “Module” / “Warn” / “Error” console tag. Inline, thread-safe.

Lua: node.raw.logError

Log under the server’s “Module” / “Warn” / “Error” console tag. Inline, thread-safe.

void log_tag(const char* tag, const char* msg) (index 5)

Section titled “void log_tag(const char* tag, const char* msg) (index 5)”

Lua: node.raw.logTag

Log under an explicit server console tag. Known tags (case-insensitive): Core, Net, Res, Mods, Module, Join, Leave, Kick, Veh, Warn, Error, Debug. Unknown tags fall back to “Module”. The line uses the house format (HH:MM:SS Tag > msg) with the tag’s standard color. Inline.

void log_custom(const char* tag, uint32_t rgb, const char* msg) (index 6)

Section titled “void log_custom(const char* tag, uint32_t rgb, const char* msg) (index 6)”

Lua: node.raw.logCustom

Print a line with the module’s OWN tag label in a chosen color, in the house line format. rgb is 0xRRGGBB (e.g. 0xFF8800); the server maps it to the nearest ANSI-256 color, so the exact shade depends on the terminal palette. Keep tags <= 6 characters to preserve the column alignment of the console. The line is mirrored ANSI-stripped into logs/server.log like every other line. Inline.

Lua: node.raw.logRaw

Write raw text bypassing the timestamp/tag format (like the startup banner). The text may contain its own ANSI escapes; the copy written to logs/server.log is ANSI-stripped. No newline handling beyond one line per call. Inline.

void set_log_sink(NodeLogSink sink, void* user) (index 8)

Section titled “void set_log_sink(NodeLogSink sink, void* user) (index 8)”

Lua: node.raw.setLogSink

Install a log sink that observes - and may suppress - every server log line (see NodeLogSink). ONE sink slot exists per server: the last registration wins, pass (NULL, NULL) to remove. After set_log_sink(NULL, NULL) returns, the previous sink is guaranteed not to be running and will not be called again (safe to free its user data). The framework clears the sink automatically before modules are unloaded. Inline.

void set_console_title(const char* title) (index 9)

Section titled “void set_console_title(const char* title) (index 9)”

Lua: node.raw.setConsoleTitle

Set the console window/tab title. On Windows this calls SetConsoleTitle; elsewhere it emits an OSC escape when the terminal supports ANSI (silently does nothing otherwise). Inline.

Read the server’s identity (name, map, version, port, limits), change the runtime limits and the name, read time, and pull the metrics snapshot. Limit changes apply to the next check – a connecting player, a spawn request – and never kick anyone retroactively.

int get_server_name(char* buf, int buflen) (index 53)

Section titled “int get_server_name(char* buf, int buflen) (index 53)”

Lua: node.raw.getServerName
Note: [General] Name

The string getters write a NUL-terminated value into buf and return its length (excluding the NUL), or -1 if buf is too small (128 bytes is always enough for name/version; maps can be longer, 256 is safe). All inline.

int get_map(char* buf, int buflen) (index 54)

Section titled “int get_map(char* buf, int buflen) (index 54)”

Lua: node.raw.getMap
Note: [General] Map

The string getters write a NUL-terminated value into buf and return its length (excluding the NUL), or -1 if buf is too small (128 bytes is always enough for name/version; maps can be longer, 256 is safe). All inline.

int get_server_version(char* buf, int buflen) (index 55)

Section titled “int get_server_version(char* buf, int buflen) (index 55)”

Lua: node.raw.getServerVersion
Note: e.g. “3.9.3”

The string getters write a NUL-terminated value into buf and return its length (excluding the NUL), or -1 if buf is too small (128 bytes is always enough for name/version; maps can be longer, 256 is safe). All inline.

Lua: node.raw.getPort
Note: TCP+UDP listening port

The string getters write a NUL-terminated value into buf and return its length (excluding the NUL), or -1 if buf is too small (128 bytes is always enough for name/version; maps can be longer, 256 is safe). All inline.

Lua: node.raw.getMaxPlayers
Note: [General] MaxPlayers

The string getters write a NUL-terminated value into buf and return its length (excluding the NUL), or -1 if buf is too small (128 bytes is always enough for name/version; maps can be longer, 256 is safe). All inline.

Lua: node.raw.getMaxCars
Note: [General] MaxCars

The string getters write a NUL-terminated value into buf and return its length (excluding the NUL), or -1 if buf is too small (128 bytes is always enough for name/version; maps can be longer, 256 is safe). All inline.

Lua: node.raw.getServerUptime

Seconds since the server started (monotonic, fractional). Inline.

Lua: node.raw.getUnixTime

Current unix time in seconds. Inline.

Lua: node.raw.setMaxCars

Changes the live [General] limits (enforced on the next check; no client is kicked retroactively) / the server name label. Values < 0 are rejected (-1). Inline, thread-safe.

Lua: node.raw.setMaxPlayers

Changes the live [General] limits (enforced on the next check; no client is kicked retroactively) / the server name label. Values < 0 are rejected (-1). Inline, thread-safe.

int set_server_name(const char* name) (index 88)

Section titled “int set_server_name(const char* name) (index 88)”

Lua: node.raw.setServerName

Changes the live [General] limits (enforced on the next check; no client is kicked retroactively) / the server name label. Values < 0 are rejected (-1). Inline, thread-safe.

int get_metrics_json(char* buf, int buflen) (index 97)

Section titled “int get_metrics_json(char* buf, int buflen) (index 97)”

Lua: node.raw.getMetrics

Writes a JSON object of live server metrics: players, vehicles, uptime, unix time, TCP/UDP packet counters, flood kicks, connection- limiter stats, plugin queue depth, timer/async task counts. Returns the JSON length or -1 when buf is too small (4 KB is plenty).

int is_node_grab_enabled(void) (index 105)

Section titled “int is_node_grab_enabled(void) (index 105)”

Lua: node.raw.isNodeGrabEnabled

1 iff the experimental node grabber is enabled in server.toml ([Experimental] NodeGrab). Even when enabled, every request still needs an explicit ALLOW from the fail-closed vehicleNodeGrabRequest hook. Inline.

Who is connected, what they are called, where they are and what they sit in. Player ids are small integers assigned at connect and REUSED after a disconnect, so never keep one across a playerLeft. Every getter returns nil (Lua) / a failure code (C) for an id that is not connected.

Lua: node.raw.getPlayerCount

Number of connected players. Inline.

int get_players(int* ids, int max) (index 11)

Section titled “int get_players(int* ids, int max) (index 11)”

Lua: not exposed

Writes up to max player ids into ids; returns the number written. Inline.

int get_player_name(int id, char* buf, int buflen) (index 12)

Section titled “int get_player_name(int id, char* buf, int buflen) (index 12)”

Lua: node.raw.getPlayerName

Writes the NUL-terminated player name into buf; returns the name length (excluding the NUL), or -1 if the player does not exist or buf is too small. Inline.

int is_player_connected(int id) (index 13)

Section titled “int is_player_connected(int id) (index 13)”

Lua: node.raw.isPlayerConnected

Returns 1 if a player with this id is currently connected, else 0. Inline.

Lua: node.raw.getPlayerPing

Seconds since the last keepalive ping was received from this player, or -1 if the player does not exist. Node does not measure round-trip latency; this is a connection-freshness metric (0-1 for a healthy client, growing when the client stalls). Inline.

int get_player_current_vehicle(int id) (index 15)

Section titled “int get_player_current_vehicle(int id) (index 15)”

Lua: not exposed

globalId of the vehicle this player is currently DRIVING, or -1 if it is not driving one (on foot, a passenger, or not found). For the role-aware lookup that also finds a passenger seat, use get_player_vehicle. Inline.

int get_player_ip(int id, char* buf, int buflen) (index 59)

Section titled “int get_player_ip(int id, char* buf, int buflen) (index 59)”

Lua: node.raw.getPlayerIp

Writes the player’s remote IP (IPv4 dotted / IPv6, NUL-terminated; v4-mapped v6 is normalized to plain v4) into buf; returns its length, or -1 if the player does not exist or buf is too small (64 bytes is always enough). Inline.

Lua: not exposed

The player’s remote TCP port, or -1 if the player does not exist. Inline.

int get_player_position(int id, float out_pos[3]) (index 61)

Section titled “int get_player_position(int id, float out_pos[3]) (index 61)”

Lua: not exposed

World position of the player: the vehicle it occupies (any role), or its walking-avatar unicycle when on foot. Writes x,y,z into out_pos and returns 0, or -1 when the player does not exist or no position snapshot arrived yet. Inline.

int get_players_json(char* buf, int buflen) (index 113)

Section titled “int get_players_json(char* buf, int buflen) (index 113)”

Lua: node.raw.getPlayers

Connected players as a JSON array of { id, name }. An array of records rather than a map of id to name: a map keyed by a number has no faithful JSON form, so it could not be the same shape in two languages. Returns the length written. Ask with buf = NULL for the size.

int get_player_endpoint_json(int id, char* buf, int buflen) (index 114)

Section titled “int get_player_endpoint_json(int id, char* buf, int buflen) (index 114)”

Lua: node.raw.getPlayerEndpoint

A player’s network endpoint as JSON { ip, port }. get_player_ip and get_player_port answer the same two things separately, which is fine for C and awkward for a scripting caller. Returns the length written, -1 if there is no such player. Ask with buf = NULL for the size.

int get_player_position_json(int id, char* buf, int buflen) (index 115)

Section titled “int get_player_position_json(int id, char* buf, int buflen) (index 115)”

Lua: node.raw.getPlayerPosition

A player’s last known world position as JSON { x, y, z }, taken from the vehicle they occupy. Returns the length written, -1 when there is no such player or no position has arrived yet. Ask with buf = NULL for the size.

int get_player_vehicle_json(int id, char* buf, int buflen) (index 116)

Section titled “int get_player_vehicle_json(int id, char* buf, int buflen) (index 116)”

Lua: node.raw.getPlayerVehicle

What the player is in, as JSON { gid, role } with role “driver” or “passenger”. get_player_current_vehicle answers the driver case only. Returns the length written, -1 if the player is on foot or does not exist. Ask with buf = NULL for the size.

int get_player_inputs_json(int player_id, char* buf, int buflen) (index 132)

Section titled “int get_player_inputs_json(int player_id, char* buf, int buflen) (index 132)”

Lua: node.raw.getPlayerInputs

Everything the player is pressing, in one JSON object: { vehicle (the gid driven), controls: { steering, throttle, brake, clutch, parkingBrake, gear? } or null, controlsAgeSeconds, axes: { … } (may be empty), axesAgeSeconds }. Only a DRIVER’s inputs reach the server, so -1 when the player drives nothing (on foot, or a passenger). Returns the length written, -1 when there is no such player or nothing was reported yet; ask with buf = NULL for the size. ABI 1.9.

int get_player_focus_json(int player_id, char* buf, int buflen) (index 138)

Section titled “int get_player_focus_json(int player_id, char* buf, int buflen) (index 138)”

Lua: node.raw.getPlayerFocus

The player’s focus point as JSON { pos: { x, y, z }, ageSeconds }: the position the distance-based interest filter uses for this recipient – its head/camera pose when it sends one, else the position of the vehicle it drives. -1 when the player is unknown or no focus was ever recorded. Returns the length written, -1 when there is no such player or nothing was reported yet; ask with buf = NULL for the size. ABI 1.9.

int get_player_head_pose_json(int player_id, char* buf, int buflen) (index 139)

Section titled “int get_player_head_pose_json(int player_id, char* buf, int buflen) (index 139)”

Lua: node.raw.getPlayerHeadPose

The player’s last head/camera pose (State::HeadPose, ~10 Hz over UDP) as JSON { pos: { x, y, z }, rot: { x, y, z, w }, freeCamera, ageSeconds }. -1 when the player is unknown or never sent a pose. Returns the length written, -1 when there is no such player or nothing was reported yet; ask with buf = NULL for the size. ABI 1.9.

int get_player_camera_json(int player_id, char* buf, int buflen) (index 140)

Section titled “int get_player_camera_json(int player_id, char* buf, int buflen) (index 140)”

Lua: node.raw.getPlayerCamera

The vehicle the player’s camera is on, from its last Vehicle::Camera report, as JSON { vehicle (gid, -1 for none), ageSeconds }. -1 when the player is unknown or never reported. Returns the length written, -1 when there is no such player or nothing was reported yet; ask with buf = NULL for the size. ABI 1.9. In a strict session (player_set_strict, ABI 1.12) a report the server refused to relay – its target is a vehicle the player does not occupy – is recorded too, with “denied”: true in the JSON; the key is absent for a relayed report.

int get_player_session_json(int player_id, char* buf, int buflen) (index 141)

Section titled “int get_player_session_json(int player_id, char* buf, int buflen) (index 141)”

Lua: node.raw.getPlayerSession

The player’s session as one JSON object: { id, name, ip, port, connectedSeconds, synced (finished joining), udpConnected (the UDP channel is bound), pingSeconds (since the last keepalive), unicycle (the walking avatar’s gid or -1), vehicle (gid occupied or -1), role (“driver” | “passenger” | “none”), vehicles (count driven), focusAgeSeconds (-1 when no focus), verified (the join ticket was redeemed with the directory, wire v17), guest (no account behind the name: a Test Drive session, or nothing verified), accountId (the directory’s account id, or null), accountRoles (the directory’s role string, “” for a guest), identifiers ([“nodemp:<id>”, “discord:<id>”, “ip:<addr>”, …] as the directory listed them; empty when unverified) }. -1 for an unknown player. Returns the length written, -1 when there is no such player or nothing was reported yet; ask with buf = NULL for the size. ABI 1.9; the account fields since 1.10.

Roles are per-session strings a plugin assigns and reads back (they are cleared at disconnect and mean nothing to the server itself; the client shows a role’s tag next to the name). Kicks disconnect with a reason. Bans are by IP, persist in bans.json across restarts, and refuse FUTURE connects – banning an IP does not drop an existing session; banPlayer does both.

int kick_player(int id, const char* reason) (index 16)

Section titled “int kick_player(int id, const char* reason) (index 16)”

Lua: node.raw.kickPlayer

Kicks a player. Returns 0 on success, -1 if the player does not exist. Inline (the disconnect itself completes asynchronously).

int set_player_role(int id, const char* role) (index 62)

Section titled “int set_player_role(int id, const char* role) (index 62)”

Lua: node.raw.setPlayerRole

Assigns/reads a free-form role string on a CONNECTED player. Roles are per-session (keyed by player id, cleared on disconnect) - persist anything long-lived yourself via the storage API keyed by IP. set: 0 on success, -1 unknown player (empty role clears). get: role length, -1 unknown player / no role / buf too small.

int get_player_role(int id, char* buf, int buflen) (index 63)

Section titled “int get_player_role(int id, char* buf, int buflen) (index 63)”

Lua: node.raw.getPlayerRole

Assigns/reads a free-form role string on a CONNECTED player. Roles are per-session (keyed by player id, cleared on disconnect) - persist anything long-lived yourself via the storage API keyed by IP. set: 0 on success, -1 unknown player (empty role clears). get: role length, -1 unknown player / no role / buf too small.

int ban_player(int id, const char* reason) (index 64)

Section titled “int ban_player(int id, const char* reason) (index 64)”

Lua: node.raw.banPlayer

Bans the player’s IP (persisted to bans.json next to server.toml) and kicks it with reason. Returns 0, or -1 if the player is unknown. NOTE: identity is the IP only (VPN-evadable) - a stronger per-install identity needs a wire change and is deferred.

int ban_ip(const char* ip, const char* reason) (index 65)

Section titled “int ban_ip(const char* ip, const char* reason) (index 65)”

Lua: node.raw.banIp

Bans/unbans one IP string (normalized like get_player_ip). ban_ip affects FUTURE connects only (existing sessions stay). Returns 0 on success; unban_ip returns -1 when the IP was not banned.

Lua: node.raw.unbanIp

Bans/unbans one IP string (normalized like get_player_ip). ban_ip affects FUTURE connects only (existing sessions stay). Returns 0 on success; unban_ip returns -1 when the IP was not banned.

int is_ip_banned(const char* ip) (index 67)

Section titled “int is_ip_banned(const char* ip) (index 67)”

Lua: node.raw.isBanned

1 iff the IP is currently banned. Inline.

int get_bans_json(char* buf, int buflen) (index 68)

Section titled “int get_bans_json(char* buf, int buflen) (index 68)”

Lua: node.raw.getBans

Writes the whole ban list as a NUL-terminated JSON array [{“ip”,“account”,“reason”,“at”,“name”},…] into buf; returns its length or -1 when buf is too small. An IP ban has “ip” set and “account” null; an account ban (v17) has “account” set (the directory’s id) and “ip” empty.

int ban_account(int64_t account_id, const char* reason) (index 142)

Section titled “int ban_account(int64_t account_id, const char* reason) (index 142)”

Lua: node.raw.banAccount

Bans a directory account (wire v17) for FUTURE joins: a player whose redeemed ticket names this account is refused at the handshake, whatever address they come from. Persisted in bans.json as “nodemp:<id>” beside the IP bans. Existing sessions stay (ban_player kicks). Returns 0 on success, -1 for a negative id. ABI 1.10.

int unban_account(int64_t account_id) (index 143)

Section titled “int unban_account(int64_t account_id) (index 143)”

Lua: node.raw.unbanAccount

Lifts an account ban. Returns 0 on success, -1 when the account was not banned. ABI 1.10.

int is_account_banned(int64_t account_id) (index 144)

Section titled “int is_account_banned(int64_t account_id) (index 144)”

Lua: node.raw.isAccountBanned

1 iff the directory account is currently banned on this server. Inline. ABI 1.10.

Strict session: install verification and the camera rule

Section titled “Strict session: install verification and the camera rule”

The server-side half of a strict session (ABI 1.12). Every join already runs the game-install check that [General] VerifyGame asks for: the player’s launcher helper compares the install with the game’s own manifest at level size (file lengths), scripts (plus a hash of the game’s lua/ and ui/), full (hash everything, archives included – slow, an audit) or strict (the archive tables of contents against the server’s reference manifest, the whole game root, the user folder), and the server judges the report – a mismatch is a kick unless VerifyGame is off. That report reaches plugins as the playerVerifyReported notification, after the verdict. player_verify asks the helper to run the check again in the middle of the session, at a level of the plugin’s choosing; the answer arrives as another playerVerifyReported. player_set_strict marks a session strict on the server, which changes one relay rule: Vehicle::Camera frames FROM the strict player that target a vehicle it does not drive or occupy are refused – not relayed – so a strict client cannot follow other people’s cars with its camera; a refused report still fires playerCameraChanged and shows up in get_player_camera_json with “denied”: true. Frames from other players to the strict player are unaffected. The check runs on the player’s machine, by a launcher the player could patch: it catches damaged installs and casual edits, and it is not a security boundary.

Contract for the server side (what the generated bindings and the prelude rely on): playerVerifyReported is dispatched like playerSeatChanged – the three-argument notification form with global_id -1 and the report as JSON – for the join-time report and for every mid-session one, after the server’s own verdict; player_verify counts a request as pending for 60 s from the moment it was sent, report or no report, and answers -3 inside that window – the check every join runs does not count, so a player_verify from a playerJoined handler goes out at once; get_player_camera_json writes “denied”: true only for a refused frame of the player’s own and leaves the key out otherwise, so the record’s shape is unchanged for everyone else.

int player_verify(int player_id, int level) (index 151)

Section titled “int player_verify(int player_id, int level) (index 151)”

Lua: node.raw.verifyPlayer

Asks the player’s launcher helper to verify the game install again, now, at level: NODE_VERIFY_SIZE (1, every manifest entry’s length), NODE_VERIFY_SCRIPTS (2, plus a hash of the game’s lua/ and ui/), NODE_VERIFY_FULL (3, hash everything, archives included – slow, an audit) or NODE_VERIFY_STRICT (4, the archive tables of contents against the server’s reference manifest, the whole game root, the user folder). The request goes out as a VerifyRequest in the middle of the session; the helper answers when it has finished (seconds for size, longer for full), and the answer arrives as the “playerVerifyReported” notification – with the server’s own verdict already applied: a “differs” or “failed” report kicks the player when [General] VerifyGame is not off, exactly as at join time, before the event fires. Returns 0 when the request was sent, -1 when no player has that id, -2 when that level is not available for this player – the client’s launcher is too old for it, or the level was not on offer (strict needs a reference manifest on the server; a level outside 1..4 is never available), -3 when a verification is already pending for this player: at most one request per player per 60 s, and a request counts as pending for those 60 s whether or not its report has arrived. The check every join runs does not count: a player_verify from a playerJoined handler goes out at once. Inline (the request is written directly). ABI 1.12.

int player_set_strict(int player_id, int on) (index 152)

Section titled “int player_set_strict(int player_id, int on) (index 152)”

Lua: node.raw.setPlayerStrict

Marks the player’s session strict (on nonzero) or lifts the mark (0). A strict session changes one relay rule, outbound only: a Vehicle::Camera frame FROM the player that targets a vehicle it does not drive or ride in is refused – not relayed, so nobody sees it spectating. Frames from other players to it are unaffected. A refused frame still updates get_player_camera_json and fires “playerCameraChanged”, and the record then carries “denied”: true (the key is absent otherwise), so an observer can tell a spectate attempt from a switch to an own car. Server-side only: it tells the client nothing by itself – the resource sends its own session config for that. Per session, cleared at disconnect. Returns 0 on success, -1 when no player has that id. Inline. ABI 1.12.

The vehicle registry: global ids, who spawned and who drives each one, the occupants, and the full JSON record. Global ids are unique for the life of the server process. A vehicle’s spawner may be a player or -1 for a server-spawned one; its sync AUTHORITY is the client that simulates it and is not necessarily the spawner.

Lua: node.raw.getVehicleCount

Number of vehicles currently registered in the world. Inline.

int get_vehicles(int* ids, int max) (index 18)

Section titled “int get_vehicles(int* ids, int max) (index 18)”

Lua: not exposed

Writes up to max vehicle globalIds into ids; returns the number written. Inline.

int get_vehicle_info(int global_id, NodeVehicleInfo* out) (index 19)

Section titled “int get_vehicle_info(int global_id, NodeVehicleInfo* out) (index 19)”

Lua: not exposed

Fills out with a snapshot of the vehicle. Returns 0 on success, -1 if the vehicle does not exist (out is left untouched). Inline.

int get_player_vehicles(int player_id, int* ids, int max) (index 20)

Section titled “int get_player_vehicles(int player_id, int* ids, int max) (index 20)”

Lua: node.raw.getPlayerVehicles

Writes up to max globalIds of vehicles this player spawned or is driving into ids; returns the number written. Inline.

int get_player_vehicle(int player_id, int* out_role) (index 21)

Section titled “int get_player_vehicle(int player_id, int* out_role) (index 21)”

Lua: not exposed

The single vehicle a player currently occupies in ANY role (wire v9). Returns its globalId, or -1 when the player is on foot / not found. When out_role is non-NULL it receives the seat role (NODE_ROLE_NONE/DRIVER/ PASSENGER). Inline.

int get_vehicle_occupants(int global_id, int* ids, int* roles, int max) (index 22)

Section titled “int get_vehicle_occupants(int global_id, int* ids, int* roles, int max) (index 22)”

Lua: not exposed

Occupants of a vehicle (wire v9): the driver first (role NODE_ROLE_DRIVER) then each passenger (NODE_ROLE_PASSENGER). Writes up to max player ids into ids and, when roles is non-NULL, the matching role into roles[i]; returns the occupant count (0 if the vehicle does not exist or is empty). Inline.

int get_vehicle_json(int global_id, char* buf, int buflen) (index 111)

Section titled “int get_vehicle_json(int global_id, char* buf, int buflen) (index 111)”

Lua: node.raw.getVehicle

The complete vehicle record as a JSON object: id, spawner, driver, authority, isUnicycle, name, idleSeconds, passengers, spawnerOnline, lockMode, lockWhitelist, configHash, damageCounter, damageBlobCounter, damageBlobSize, tags. get_vehicle_info fills a fixed struct and cannot carry the variable-length parts (passengers, the lock whitelist, tags), which is why this exists alongside it. Returns the length written, -1 if there is no such vehicle. Ask with buf = NULL for the size.

int get_vehicles_json(char* buf, int buflen) (index 112)

Section titled “int get_vehicles_json(char* buf, int buflen) (index 112)”

Lua: node.raw.getVehicles

Every vehicle as a JSON array of the records get_vehicle_json returns. Returns the length written. Ask with buf = NULL for the size.

int get_vehicle_occupants_json(int global_id, char* buf, int buflen) (index 118)

Section titled “int get_vehicle_occupants_json(int global_id, char* buf, int buflen) (index 118)”

Lua: node.raw.getVehicleOccupants

Who is in a vehicle, driver first, as a JSON array of { playerId, role } with role “driver” or “passenger”. get_vehicle_occupants returns the same information as two parallel arrays, which is harder to consume from a scripting language. Returns the length written, -1 if there is no such vehicle. Ask with buf = NULL for the size.

What the server knows about a vehicle’s state. LIVE state (transform, controls) comes from the last position/state snapshot the authority sent and is a few ticks old. CACHED state (config, couplers, powertrain, engine, controllers, electrics, damage) is the merge of everything relayed so far and is what a late joiner receives. Config hash is the FNV-1a-32 marker clients echo so the server can spot a desynced config.

int get_vehicle_damage_counter(int global_id) (index 23)

Section titled “int get_vehicle_damage_counter(int global_id) (index 23)”

Lua: not exposed

The damage version last reported by the vehicle’s sync authority (0 = pristine; grows as the vehicle takes damage; resets on repair/ respawn). -1 if the vehicle does not exist. Pair with the “vehicleDamageChanged” builtin to detect changes and offer a resync (resync_vehicle). Inline.

int get_vehicle_transform(int global_id, NodeVehicleTransform* out) (index 24)

Section titled “int get_vehicle_transform(int global_id, NodeVehicleTransform* out) (index 24)”

Lua: not exposed

Fills out with the vehicle’s kinematics decoded from its last State/Pos snapshot (the high-rate stream the sync authority sends over UDP). Returns 0 on success, -1 if the vehicle does not exist or no snapshot has arrived yet (out is left untouched). Inline.

int get_vehicle_controls(int global_id, NodeVehicleControls* out) (index 25)

Section titled “int get_vehicle_controls(int global_id, NodeVehicleControls* out) (index 25)”

Lua: not exposed

Fills out with the driving controls folded into the same snapshot. Returns 0 on success, -1 if the vehicle does not exist, no snapshot has arrived yet, or the last snapshot carried no valid controls (the sender was not the driver); out is left untouched on -1. Inline.

int get_vehicle_electrics_json(int global_id, char* buf, int buflen) (index 26)

Section titled “int get_vehicle_electrics_json(int global_id, char* buf, int buflen) (index 26)”

Lua: node.raw.getVehicleElectrics

Writes the vehicle’s current electrics (the merged view of the State/Electrics delta stream: lights, signals, gauges, …) as a NUL-terminated JSON object string into buf. Returns the string length (excluding the NUL), or -1 if the vehicle does not exist, no electrics arrived yet, or buf is too small (16 KB is comfortably enough in practice). Inline.

int get_vehicle_config_json(int global_id, char* buf, int buflen) (index 69)

Section titled “int get_vehicle_config_json(int global_id, char* buf, int buflen) (index 69)”

Lua: node.raw.getVehicleConfig

The vehicle’s current spawn/edit config JSON (what a joiner receives inside Vehicle::Spawn).

int get_vehicle_couplers_json(int global_id, char* buf, int buflen) (index 70)

Section titled “int get_vehicle_couplers_json(int global_id, char* buf, int buflen) (index 70)”

Lua: node.raw.getVehicleCouplers

Merged coupler/door state as a JSON array of coupler entries (the same schema the clients exchange; one entry per known coupler).

int get_vehicle_powertrain_json(int global_id, char* buf, int buflen) (index 71)

Section titled “int get_vehicle_powertrain_json(int global_id, char* buf, int buflen) (index 71)”

Lua: node.raw.getVehiclePowertrain

Merged powertrain / engine device state objects.

int get_vehicle_engine_json(int global_id, char* buf, int buflen) (index 72)

Section titled “int get_vehicle_engine_json(int global_id, char* buf, int buflen) (index 72)”

Lua: node.raw.getVehicleEngine

Merged powertrain / engine device state objects.

int get_vehicle_controllers_json(int global_id, char* buf, int buflen) (index 73)

Section titled “int get_vehicle_controllers_json(int global_id, char* buf, int buflen) (index 73)”

Lua: node.raw.getVehicleControllers

Merged controller-call map: {“controller|function”: {call json}}.

int get_vehicle_damage_blob(int global_id, uint8_t* buf, int buflen, uint32_t* out_counter) (index 74)

Section titled “int get_vehicle_damage_blob(int global_id, uint8_t* buf, int buflen, uint32_t* out_counter) (index 74)”

Lua: not exposed

Copies the vehicle’s baked-damage blob into buf and returns its size (0 = pristine). When out_counter is non-NULL it receives the damage counter the blob was made at. buf may be NULL to query the size (buflen ignored then); with a non-NULL buf that is too small, -1. Returns -1 when the vehicle does not exist.

int get_world_snapshot_json(char* buf, int buflen) (index 75)

Section titled “int get_world_snapshot_json(char* buf, int buflen) (index 75)”

Lua: node.raw.getWorldSnapshot

One-call world snapshot: {“time”:unix,“players”:[{id,name,ip,vehicle, role,pos:[x,y,z]|null}],“vehicles”:[{id,spawner,driver,authority, unicycle,pos,rot,vel}]} - the bulk position read for radars/maps. Returns the JSON length or -1 when buf is too small.

int get_vehicle_config_hash(int global_id, uint32_t* out_hash) (index 104)

Section titled “int get_vehicle_config_hash(int global_id, uint32_t* out_hash) (index 104)”

Lua: node.raw.getVehicleConfigHash

The vehicle’s current config marker (FNV-1a-32 over the cached config JSON bytes exactly as Spawn/Resync send them) – what clients echo in their periodic Vehicle::ConfigHash reports. Writes it into out_hash and returns 0, or -1 for an unknown vehicle. Inline.

int get_vehicle_damage_json(int global_id, char* buf, int buflen) (index 117)

Section titled “int get_vehicle_damage_json(int global_id, char* buf, int buflen) (index 117)”

Lua: node.raw.getVehicleDamage

Damage telemetry as JSON { counter, blobCounter, size }: the authority-reported damage version, the version the stored blob was made at, and the blob size (0 = pristine). Returns the length written, -1 if there is no such vehicle. Ask with buf = NULL for the size.

int get_vehicle_transform_json(int global_id, char* buf, int buflen) (index 120)

Section titled “int get_vehicle_transform_json(int global_id, char* buf, int buflen) (index 120)”

Lua: node.raw.getVehicleTransform

Kinematics from the last position snapshot as JSON: pos, rot (a quaternion), vel, angVel, time, ping, seq, paused, transitioning, teleport, velocityStep, hasControls, ageSeconds. Velocities are m/s, angular velocity rad/s, time and ping the sender’s own timers in seconds, ageSeconds since the server accepted the snapshot. teleport / velocityStep are the sender’s step flags for that snapshot (receivers snap instead of blending); hasControls says whether the control bytes were meaningful (the sender was the driver). get_vehicle_transform fills a fixed struct with the same values. Returns the length written, -1 if there is no such vehicle or no snapshot arrived. Ask with buf = NULL for the size.

int get_vehicle_controls_json(int global_id, char* buf, int buflen) (index 121)

Section titled “int get_vehicle_controls_json(int global_id, char* buf, int buflen) (index 121)”

Lua: node.raw.getVehicleControls

Driving controls from the same snapshot as JSON: steering, throttle, brake, clutch, parkingBrake, and gear when the sender reported one – a number for manual boxes, a letter for the automatic ones (“P”, “R”, “N”, “D”, “S”, “2”, “1”, “M3”). Returns the length written, -1 until a snapshot arrives whose controls are meaningful, which is only when the sender was the driver. Ask with buf = NULL for the size.

int get_vehicle_inputs_json(int global_id, char* buf, int buflen) (index 131)

Section titled “int get_vehicle_inputs_json(int global_id, char* buf, int buflen) (index 131)”

Lua: node.raw.getVehicleInputs

The vehicle’s non-core input axes as JSON { axes: { name: value, … }, ageSeconds }. State::Inputs carries only the axes that are NOT in the Pos snapshot (modded/extra inputs; the five driving controls and gear are get_vehicle_controls_json) and is delta-gated – a value is sent only when it changes – so the registry merges the deltas and this is the CURRENT set. ageSeconds is since the last delta. Returns the length written, -1 when there is no such vehicle or nothing arrived yet; ask with buf = NULL for the size. ABI 1.9.

int get_vehicle_nodes_json(int global_id, char* buf, int buflen) (index 133)

Section titled “int get_vehicle_nodes_json(int global_id, char* buf, int buflen) (index 133)”

Lua: node.raw.getVehicleNodes

The last State::Nodes body the vehicle’s authority sent, as the JSON text it sent (validated, not re-encoded): node positions / deformation in the client’s nodemp.sync.nodes schema. Cached for the join replay, so this costs nothing extra. Returns the length written, -1 when there is no such vehicle or nothing arrived yet; ask with buf = NULL for the size. ABI 1.9.

int get_vehicle_break_groups_json(int global_id, char* buf, int buflen) (index 134)

Section titled “int get_vehicle_break_groups_json(int global_id, char* buf, int buflen) (index 134)”

Lua: node.raw.getVehicleBreakGroups

The set of break groups reported broken on the vehicle, merged from the delta stream, as a JSON array of group names. Returns the length written, -1 when there is no such vehicle or nothing arrived yet; ask with buf = NULL for the size. ABI 1.9.

int get_vehicle_paints_json(int global_id, char* buf, int buflen) (index 135)

Section titled “int get_vehicle_paints_json(int global_id, char* buf, int buflen) (index 135)”

Lua: node.raw.getVehiclePaints

The vehicle’s paints as a JSON array (the client vcf.paints schema: one entry per paint layer, baseColor and the material fields), taken from the cached spawn/edit config – Paint packets are merged into it. Empty array when the config carries none. -1 only for an unknown vehicle. Returns the length written, -1 when there is no such vehicle or nothing arrived yet; ask with buf = NULL for the size. ABI 1.9.

int get_vehicle_sync_json(int global_id, char* buf, int buflen) (index 136)

Section titled “int get_vehicle_sync_json(int global_id, char* buf, int buflen) (index 136)”

Lua: node.raw.getVehicleSync

Sync health of the vehicle as JSON: { authority (player id simulating it, -1 none), authorityAgeSeconds (since its last accepted Pos or its assignment – the liveness heartbeat behind grace/release), seatEpoch (the seat/authority transaction counter), spawnedAgeSeconds, idleSeconds (0 while driven), configChangedAgeSeconds, hasPos, posAgeSeconds (since the last accepted Pos), posSeq }. Returns the length written, -1 when there is no such vehicle or nothing arrived yet; ask with buf = NULL for the size. ABI 1.9.

int get_vehicle_transforms_json(char* buf, int buflen) (index 137)

Section titled “int get_vehicle_transforms_json(char* buf, int buflen) (index 137)”

Lua: node.raw.getVehicleTransforms

Every vehicle’s last kinematic snapshot in one JSON array – the shape of get_vehicle_transform_json plus id – taken under one registry lock without copying vehicle state. Vehicles with no snapshot yet are absent. Ascending by id. Returns the length written; ask with buf = NULL for the size. ABI 1.9.

Acting on vehicles from the server: delete, spawn ownerless vehicles, open and close couplers, seat, unseat and swap players, lock vehicles against new seat claims, execute a controller call on the sync authority, and resync a vehicle or a player. Server-driven seating bypasses locks; locks gate NEW client claims only and never evict anyone.

int delete_vehicle(int global_id) (index 27)

Section titled “int delete_vehicle(int global_id) (index 27)”

Lua: node.raw.deleteVehicle

Deletes a vehicle: broadcasts the removal to every client, removes it from the registry and fires the “vehicleDeleted” builtin (delivered asynchronously on the worker thread). Returns 0 on success, -1 if the vehicle does not exist. Inline.

Lua: node.raw.deleteAllVehicles

Deletes every vehicle in the world; returns how many were deleted. Inline.

int set_vehicle_coupler(int global_id, const char* name, int open) (index 29)

Section titled “int set_vehicle_coupler(int global_id, const char* name, int open) (index 29)”

Lua: node.raw.setVehicleCoupler

Server-driven coupler/door actuation: opens (open = 1) or closes (open = 0) the named advanced-coupler group (e.g. “doorFLCoupler”, “hood_latch_coupler”) on EVERY client - including the driver - and merges the resulting absolute state into the join-replay cache, so a late joiner sees the door in its new state. Skips the vehicleCouplerRequest permission builtin (the server never asks itself). Returns 0 on success, -1 if the vehicle does not exist. Inline.

int seat_player(int player_id, int global_id, int seat_kind) (index 30)

Section titled “int seat_player(int player_id, int global_id, int seat_kind) (index 30)”

Lua: not exposed

Server-driven seating (Vehicle Authority V2). These move a player between driver seats WITHOUT the cancellable vehicleEnterRequest/vehicleExitRequest hooks (the server never asks itself, like set_vehicle_coupler) but emit the same Driver/Authority/SeatFree/PlayerVehicle broadcasts, so the target client’s camera/control follow exactly like an ordinary enter/exit and observers fire as usual. All inline; the seat mutation itself completes before the call returns, the client-side camera move follows asynchronously over the network. seat_player: seats player_id in global_id. seat_kind is NODE_SEAT_DRIVER or NODE_SEAT_PASSENGER. As a DRIVER the player leaves any car it drives, its walking-avatar unicycle is deleted, and a previous driver is put on foot. As a PASSENGER it joins the occupant list (allowed even in a driverless car; the wheel is untouched). Returns 0 on success, -1 on invalid args or a unicycle target.

int unseat_player(int player_id) (index 31)

Section titled “int unseat_player(int player_id) (index 31)”

Lua: node.raw.unseatPlayer

unseat_player: forces the player out of the car it occupies as DRIVER or PASSENGER (on foot; the client spawns its unicycle as on a normal exit). Returns 0 on success, -1 if the player is on foot (in no vehicle).

int swap_players(int player_a, int player_b) (index 32)

Section titled “int swap_players(int player_a, int player_b) (index 32)”

Lua: node.raw.swapPlayers

swap_players: atomically exchanges the two players’ seats – role AND vehicle – in one seat transaction, implementing the full driver/ passenger/pedestrian matrix (driver<->driver swaps cars; driver<-> passenger of one car exchanges roles; a seat<->pedestrian swap rides one and walks the other, …). Returns 0 on success, -1 when player_a == player_b, both are on foot, or they share the same passenger seat.

int resync_vehicle(int global_id, int player_id) (index 33)

Section titled “int resync_vehicle(int global_id, int player_id) (index 33)”

Lua: node.raw.resyncVehicle

Re-sends a vehicle’s full cached authoritative state (spawn/config, position, electrics, doors/couplers, powertrain, controller state, driver/authority; the damage blob rides along) to one player, or to every synced player when player_id is -1. Pass global_id -1 to resync ALL vehicles. Returns the number of vehicle bundles sent. The client re-applies the bundle like a join replay (“any desync can always be made a sync”). Inline.

int resync_player(int player_id) (index 34)

Section titled “int resync_player(int player_id) (index 34)”

Lua: node.raw.resyncPlayer

Re-sends EVERY vehicle’s full cached state to one player; equivalent to resync_vehicle(-1, player_id). Returns the number of vehicle bundles sent. Inline.

int spawn_vehicle(const char* config_json, const char* owner_name) (index 76)

Section titled “int spawn_vehicle(const char* config_json, const char* owner_name) (index 76)”

Lua: node.raw.spawnVehicle

Spawns a vehicle FROM THE SERVER: registers it (spawner -1 = ownerless), broadcasts the Vehicle::Spawn to every client and assigns the best available sync authority (nobody takes the driver seat; use seat_player to seat someone). config_json must be a JSON object in the same schema clients send in SpawnReq (jbm, vcf, pos, rot, …); unicycle configs are rejected. owner_name is the display label (NULL -> “server”). Returns the new globalId, or -1 on invalid config. Inline; fires the “vehicleSpawned” builtin asynchronously.

int set_vehicle_lock(int global_id, int mode, const int* whitelist, int count) (index 101)

Section titled “int set_vehicle_lock(int global_id, int mode, const int* whitelist, int count) (index 101)”

Lua: not exposed

Sets the vehicle’s lock: mode is NODE_LOCK_ above, whitelist is an array of count player ids that are ALWAYS allowed (NULL/0 = empty; <= 32 entries, an over-limit set is rejected). “unlocked” clears the whitelist. Broadcast to every client as Vehicle::Lock, replayed to late joiners, fires the “vehicleLockChanged” builtin. Returns 0 on success, -1 on an unknown vehicle / bad mode / over-limit whitelist. Inline.

int get_vehicle_lock(int global_id, int* out_whitelist, int max, int* out_count) (index 102)

Section titled “int get_vehicle_lock(int global_id, int* out_whitelist, int max, int* out_count) (index 102)”

Lua: not exposed

Reads the vehicle’s lock: returns the NODE_LOCK_ mode (or -1 for an unknown vehicle) and, when out_whitelist is non-NULL, writes up to max whitelisted player ids into it, storing the count in out_count (out_count may be NULL). Inline.

int trigger_vehicle(int global_id, const char* call_json) (index 103)

Section titled “int trigger_vehicle(int global_id, const char* call_json) (index 103)”

Lua: node.raw.triggerVehicle

Has the vehicle’s SYNC AUTHORITY execute one controller call (call_json = the Vehicle::Trigger schema: {“controllerName”:…, “functionName”:…, …}). Skips the vehicleTriggerRequest hook (the server never asks itself); never cached – the resulting state changes ride the normal sync channels. Returns 0 on success, -1 when the vehicle is gone, idle (no authority) or call_json is not a JSON object. Inline.

int get_vehicle_lock_json(int global_id, char* buf, int buflen) (index 122)

Section titled “int get_vehicle_lock_json(int global_id, char* buf, int buflen) (index 122)”

Lua: node.raw.getVehicleLock

The vehicle lock as JSON { mode, whitelist }: mode is “unlocked”, “locked” or “driver-only”, whitelist the player ids allowed in regardless. get_vehicle_lock answers the same through an out array and an out count. Returns the length written, -1 if there is no such vehicle. Ask with buf = NULL for the size.

int set_vehicle_lock_named(int global_id, const char* mode, const char* whitelist_json) (index 123)

Section titled “int set_vehicle_lock_named(int global_id, const char* mode, const char* whitelist_json) (index 123)”

Lua: node.raw.setVehicleLock

Sets the lock by NAMED mode: “unlocked”, “locked” or “driver-only” (case-insensitive; “lockedAll” and “driverOnly” are accepted too). whitelist_json is an optional JSON array of the player ids allowed in regardless – NULL for none. set_vehicle_lock takes the wire’s integer and a C array instead, which is the transport’s business rather than the caller’s. Returns 0, or -1 for an unknown mode, an unparseable whitelist or an unknown vehicle.

int seat_player_role(int player_id, int global_id, const char* role) (index 124)

Section titled “int seat_player_role(int player_id, int global_id, const char* role) (index 124)”

Lua: node.raw.seatPlayer

Seats a player by NAMED role: “driver” (the default when role is NULL or empty) or “passenger”. seat_player takes the wire’s integer instead, which is the transport’s business rather than the caller’s. Returns 0 on success, -1 for an unknown role or when the seat cannot be taken.

Key/value strings a plugin attaches to any vehicle. Tags survive edits, resets and respawns, ride the join replay to late joiners and are broadcast to every client on change (read-only client-side, readable via the vehicle record). Limits: key 1-64 bytes, value 1-256 bytes, 32 distinct keys per vehicle – a set that breaks a limit is rejected, never truncated.

int set_vehicle_tag(int global_id, const char* key, const char* value) (index 35)

Section titled “int set_vehicle_tag(int global_id, const char* key, const char* value) (index 35)”

Lua: node.raw.setVehicleTag

Stores/overwrites one tag. Returns 0 on success, -1 when the vehicle does not exist, key/value is NULL or empty, or a limit is broken (empty values are not legal tags – remove with remove_vehicle_tag).

int get_vehicle_tag(int global_id, const char* key, char* buf, int buflen) (index 36)

Section titled “int get_vehicle_tag(int global_id, const char* key, char* buf, int buflen) (index 36)”

Lua: node.raw.getVehicleTag

Writes the NUL-terminated tag value into buf; returns the value length (excluding the NUL), or -1 if the vehicle or the tag does not exist or buf is too small (257 bytes always fits).

int remove_vehicle_tag(int global_id, const char* key) (index 37)

Section titled “int remove_vehicle_tag(int global_id, const char* key) (index 37)”

Lua: node.raw.removeVehicleTag

Removes one tag. Returns 0 when the tag existed and was removed, -1 otherwise.

int get_vehicle_tags_json(int global_id, char* buf, int buflen) (index 38)

Section titled “int get_vehicle_tags_json(int global_id, char* buf, int buflen) (index 38)”

Lua: node.raw.getVehicleTags

Writes the vehicle’s whole tag set as a NUL-terminated JSON object string ({“key”:“value”,…}, keys sorted) into buf. Returns the string length (excluding the NUL), or -1 if the vehicle does not exist or buf is too small (16 KB always fits the 32x(64+256) worst case).

int get_vehicles_by_tag(const char* key, const char* value_or_null, int* out_ids, int max) (index 39)

Section titled “int get_vehicles_by_tag(const char* key, const char* value_or_null, int* out_ids, int max) (index 39)”

Lua: node.raw.getVehiclesByTag

Reverse lookup: writes up to max globalIds of vehicles carrying the tag key into out_ids – any value when value_or_null is NULL, exactly value_or_null otherwise. Returns the number written, sorted ascending by globalId. For a multi-pair AND match call this once and intersect, or use the Lua node.getVehiclesByTags.

int get_vehicles_by_tags(const char* pairs_json, int* out_ids, int max) (index 77)

Section titled “int get_vehicles_by_tags(const char* pairs_json, int* out_ids, int max) (index 77)”

Lua: node.raw.getVehiclesByTags

Writes up to max globalIds of vehicles whose tag set contains EVERY key/value pair of pairs_json (a JSON object {“key”:“value”,…}); returns the number written, ascending. An empty/invalid object matches nothing. The C mirror of Lua node.getVehiclesByTags.

Deciding who sees what. The relayRequest filter (formerly canRelay) answers, per (sender, receiver, packet category, subtype, vehicle), whether a packet is forwarded; verdicts are cached until invalidated. Visibility groups are the coarse form: a player only receives relays from players and vehicles in the same group (group 0 is everyone). The dimensions module is built on groups.

void register_relay_filter(NodeCanRelayCallback cb, void* user) (index 78)

Section titled “void register_relay_filter(NodeCanRelayCallback cb, void* user) (index 78)”

Lua: node.raw.on

void unregister_relay_filter(NodeCanRelayCallback cb, void* user) (index 79)

Section titled “void unregister_relay_filter(NodeCanRelayCallback cb, void* user) (index 79)”

Lua: node.raw.off

void invalidate_relay_cache(void) (index 80)

Section titled “void invalidate_relay_cache(void) (index 80)”

Lua: node.raw.invalidateRelayCache

Drops every cached relay verdict so filters are re-consulted. Call after the data your filter reads changed. Inline, thread-safe.

void set_player_group(int player_id, int64_t group) (index 125)

Section titled “void set_player_group(int player_id, int64_t group) (index 125)”

Lua: node.raw.setPlayerGroup

Puts a player in a visibility group. Two things are visible to each other only when their group numbers match, and 0 – the default – is the world everyone shares. That is the whole rule, and it covers parallel worlds, instanced races, private lobbies and spectator islands.

Prefer this over a relay filter for anything of the kind. A filter lives in a script, scripts run on the framework worker, and the question is asked on the network threads – so every answer is either a wait or a guess. Comparing two numbers needs neither, and cannot be late.

Groups are forgotten when the player disconnects: ids are reused, and a stale group would put the next holder of that id in someone else’s world.

void set_vehicle_group(int global_id, int64_t group) (index 126)

Section titled “void set_vehicle_group(int global_id, int64_t group) (index 126)”

Lua: node.raw.setVehicleGroup

Puts a vehicle in a visibility group; 0 is the shared world. A packet about a vehicle belongs to the VEHICLE’s group, not its driver’s, because a car can be moved between worlds with someone sitting in it. Forgotten when the vehicle is deleted.

int64_t get_player_group(int player_id) (index 127)

Section titled “int64_t get_player_group(int player_id) (index 127)”

Lua: node.raw.getPlayerGroup

The player’s visibility group, 0 when it has never been set or the player is unknown.

int64_t get_vehicle_group(int global_id) (index 128)

Section titled “int64_t get_vehicle_group(int global_id) (index 128)”

Lua: node.raw.getVehicleGroup

The vehicle’s visibility group, 0 when it has never been set or there is no such vehicle.

The event system has three kinds of name. ENGINE events are camelCase builtins the server fires, named <subject><Verb-ed> (playerJoined, vehicleSpawned); the requests a handler can deny are named <subject><Action>Request (playerConnectRequest, vehicleSpawnRequest; relayRequest is the relay filter), and a handler that returns false denies the action. WIRE events are <domain>:<verb> strings a resource defines itself, sent with emitClient/emitAll and received from clients through the same node.on. One node.on covers all of them: the name decides the kind. The pre-1.2.0 names (playerJoin, onPlayerConnectRequest, canRelay, …) are deprecated aliases: they still subscribe to the same event and log one warning per resource per old name; they are removed in 2.0.

int emit_client(int id, const char* event, const char* data) (index 40)

Section titled “int emit_client(int id, const char* event, const char* data) (index 40)”

Lua: node.raw.emitClient

Sends one custom EVENT frame (name + data) to one client over TCP. Returns 0 on success, -1 on failure (unknown player, or player not joined yet). Inline.

void emit_all(const char* event, const char* data) (index 41)

Section titled “void emit_all(const char* event, const char* data) (index 41)”

Lua: node.raw.emitAll

Sends one custom EVENT frame to all fully-connected clients. Inline.

void register_client_event(const char* event, NodeClientEventCallback cb, void* user) (index 42)

Section titled “void register_client_event(const char* event, NodeClientEventCallback cb, void* user) (index 42)”

Lua: node.raw.on

Subscribes to a custom client event by name. The callback runs on the framework worker thread. Inline.

void unregister_client_event(const char* event, NodeClientEventCallback cb, void* user) (index 43)

Section titled “void unregister_client_event(const char* event, NodeClientEventCallback cb, void* user) (index 43)”

Lua: node.raw.off

Removes a client-event subscription previously made with register_client_event. Both cb AND user must match the registration. A dispatch already in flight on the worker thread may still deliver one final call. Inline.

void register_builtin_event(const char* name, NodeBuiltinEventCallback cb, void* user) (index 44)

Section titled “void register_builtin_event(const char* name, NodeBuiltinEventCallback cb, void* user) (index 44)”

Lua: node.raw.on

Subscribes an OBSERVER to a builtin event by name (see NodeBuiltinEventCallback). This also observes the cancellable request builtins without being able to deny them. Unknown builtin names are rejected with a console error. Inline.

void unregister_builtin_event(const char* name, NodeBuiltinEventCallback cb, void* user) (index 45)

Section titled “void unregister_builtin_event(const char* name, NodeBuiltinEventCallback cb, void* user) (index 45)”

Lua: node.raw.off

Removes a builtin-event subscription previously made with register_builtin_event. Both cb AND user must match. Same in-flight caveat as unregister_client_event. Inline.

void register_cancellable_event(const char* name, NodeCancellableEventCallback cb, void* user) (index 46)

Section titled “void register_cancellable_event(const char* name, NodeCancellableEventCallback cb, void* user) (index 46)”

Lua: node.raw.on

Subscribes a VERDICT callback to one of the cancellable request builtins (see NodeCancellableEventCallback for the names and payloads). The callback runs on the framework worker thread and returns nonzero to deny the request. Unknown or non-cancellable names are rejected with a console error. Inline.

void unregister_cancellable_event(const char* name, NodeCancellableEventCallback cb, void* user) (index 47)

Section titled “void unregister_cancellable_event(const char* name, NodeCancellableEventCallback cb, void* user) (index 47)”

Lua: node.raw.off

Removes a verdict subscription previously made with register_cancellable_event. Both cb AND user must match. A dispatch already in flight on the worker thread may still deliver one final call. Inline.

void register_vehicle_event(const char* name, NodeVehicleEventCallback cb, void* user) (index 48)

Section titled “void register_vehicle_event(const char* name, NodeVehicleEventCallback cb, void* user) (index 48)”

Lua: node.raw.on

Subscribes an OBSERVER to a notification event by name (see NodeVehicleEventCallback: “vehicleEdited”, “vehicleReset”, “vehiclePainted”, “playerSeatChanged”, “vehicleCouplerChanged”, “vehicleControllerChanged”, “playerVerifyReported” – the server judged a report the player’s launcher sent; global_id is -1 there – and “resourceUnload” – (-1, -1, reason) right before the registering resource’s state is dropped, reason “reload” or “shutdown”; dispatched to that resource’s own handlers only, so only a hosted resource ever receives it, never a native module). The callback runs on the framework worker thread and carries (player_id, global_id, data); its return value is ignored - the action already happened. Unknown names are rejected with a console error. Inline.

void unregister_vehicle_event(const char* name, NodeVehicleEventCallback cb, void* user) (index 49)

Section titled “void unregister_vehicle_event(const char* name, NodeVehicleEventCallback cb, void* user) (index 49)”

Lua: node.raw.off

Removes a vehicle-notification subscription previously made with register_vehicle_event. Both cb AND user must match. A dispatch already in flight on the worker thread may still deliver one final call. Inline.

int emit_client_bytes(int id, const char* event, const void* data, int len) (index 108)

Section titled “int emit_client_bytes(int id, const char* event, const void* data, int len) (index 108)”

Lua: not exposed

Like emit_client, but the payload is length-delimited and may contain NUL bytes. Event data is opaque on the wire, and Lua resources have always been able to send binary through it; emit_client cannot, because its payload is a C string and stops at the first NUL. Use this one for anything that is not guaranteed text. Returns 0 on success, -1 if the player does not exist.

void emit_all_bytes(const char* event, const void* data, int len) (index 109)

Section titled “void emit_all_bytes(const char* event, const void* data, int len) (index 109)”

Lua: not exposed

Like emit_all, but the payload is length-delimited and may contain NUL bytes. See emit_client_bytes.

int emit_others(int except_player_id, const char* event, const char* data) (index 129)

Section titled “int emit_others(int except_player_id, const char* event, const char* data) (index 129)”

Lua: node.raw.emitOthers

Sends a named event to every connected client EXCEPT one – the sender of whatever is being relayed, typically. The fan-out is emit_all’s, with the excepted player as the SENDER for the relay filter (relayRequest sees from = except_player_id) and visibility groups. Returns 0, or -1 when except_player_id is not a connected player (nothing is sent: an unknown exception is a bug in the caller, not a broadcast). Inline. ABI 1.8.

A raw binary channel keyed by a u32 id, for native modules and their client counterparts that speak their own encoding. Nothing is parsed or logged; use it when JSON events are too slow or too loose. The dimensions module owns channel 0x44494D53 (“DIMS”).

int send_module(int player_id, uint32_t channel, const void* data, int len) (index 98)

Section titled “int send_module(int player_id, uint32_t channel, const void* data, int len) (index 98)”

Lua: node.raw.sendModule

Sends one payload on channel: targeted to player_id, or to EVERY synced client when player_id is -1 (the broadcast respects the relayRequest relay filter). Returns 0 on success, -1 when the target player is unknown/not joined or data is NULL with len > 0. Inline.

void register_module_channel(uint32_t channel, NodeModuleChannelCallback cb, void* user) (index 99)

Section titled “void register_module_channel(uint32_t channel, NodeModuleChannelCallback cb, void* user) (index 99)”

Lua: node.raw.onModule

Subscribes to inbound client payloads on one channel id (see NodeModuleChannelCallback). Inline.

void unregister_module_channel(uint32_t channel, NodeModuleChannelCallback cb, void* user) (index 100)

Section titled “void unregister_module_channel(uint32_t channel, NodeModuleChannelCallback cb, void* user) (index 100)”

Lua: node.raw.offModule

Removes a module-channel subscription. Both cb AND user must match. A dispatch already in flight on the worker thread may still deliver one final call. Inline.

Server-side publish/subscribe between resources (and native modules). Delivery is asynchronous on the plugin worker and includes the sender. This is how the chat resource asks the dimensions module which players share a room.

void emit_resource_event(const char* name, const char* data) (index 91)

Section titled “void emit_resource_event(const char* name, const char* data) (index 91)”

Lua: node.raw.emitResource

Publishes one named event with an opaque data string to every bus subscriber (Lua node.onResource + C register_resource_event), on the framework worker thread. The C emitter appears as source “native”.

void register_resource_event(const char* name, NodeResourceEventCallback cb, void* user) (index 92)

Section titled “void register_resource_event(const char* name, NodeResourceEventCallback cb, void* user) (index 92)”

Lua: node.raw.onResource

Publishes one named event with an opaque data string to every bus subscriber (Lua node.onResource + C register_resource_event), on the framework worker thread. The C emitter appears as source “native”.

void unregister_resource_event(const char* name, NodeResourceEventCallback cb, void* user) (index 93)

Section titled “void unregister_resource_event(const char* name, NodeResourceEventCallback cb, void* user) (index 93)”

Lua: node.raw.offResource

Publishes one named event with an opaque data string to every bus subscriber (Lua node.onResource + C register_resource_event), on the framework worker thread. The C emitter appears as source “native”.

One-shot and repeating timers on the plugin worker thread – the same thread every handler runs on, so a timer callback never races a handler. setImmediate runs after the current batch of handlers.

int set_timeout(uint32_t ms, NodeTimerCallback cb, void* user) (index 50)

Section titled “int set_timeout(uint32_t ms, NodeTimerCallback cb, void* user) (index 50)”

Lua: node.raw.setTimeout

Runs cb once after ms milliseconds, on the framework worker thread. Returns a positive timer id, or -1 if cb is NULL. Pending timers never fire after the framework shuts down. Registration is inline.

int set_interval(uint32_t ms, NodeTimerCallback cb, void* user) (index 51)

Section titled “int set_interval(uint32_t ms, NodeTimerCallback cb, void* user) (index 51)”

Lua: node.raw.setInterval

Runs cb every ms milliseconds until cleared. Same semantics as set_timeout otherwise. Returns a positive timer id, or -1.

Lua: node.raw.clearTimer

Cancels a timer created by set_timeout/set_interval. Safe to call with an already-fired or unknown id (no-op). A callback that is already mid-execution on the worker thread finishes, but an interval will not fire again. Inline.

Two ways to not block the worker. node.async runs a function as a cooperative coroutine: node.sleep and node.wait suspend it while other handlers and the tick keep running (the FiveM Citizen.Wait model). node.job / node.await run a SELF-CONTAINED function on a background pool thread in a scratch Lua state, for real parallelism: no upvalues, no resource globals, JSON-serialisable arguments and result.

int submit_job(NodeJobWorkFn work, NodeJobDoneFn done, void* user) (index 89)

Section titled “int submit_job(NodeJobWorkFn work, NodeJobDoneFn done, void* user) (index 89)”

Lua: node.raw.job

Runs work on a background pool thread and hands its return value to done on the framework worker thread (see NodeJobWorkFn). Returns 0, or -1 when work is NULL. done may be NULL (fire-and-forget).

Asynchronous HTTP requests from the background pool; the callback runs on the worker. Any method (GET, POST, PUT, PATCH, DELETE, HEAD, or a custom uppercase token). About 15 s timeout, 8 MB body cap, 5 redirects. TLS peer verification is OFF unless the hoster sets [Http] CaFile in server.toml (env NODE_HTTP_CA_FILE, server 1.2.0): then every https request verifies the chain against that CA bundle and the certificate’s name against the host, and a failure comes back as status -1 with the TLS error in body.

int http_request(const char* method, const char* url, const char* headers_json, const void* body, int body_len, NodeHttpCallback cb, void* user) (index 90)

Section titled “int http_request(const char* method, const char* url, const char* headers_json, const void* body, int body_len, NodeHttpCallback cb, void* user) (index 90)”

Lua: node.raw.httpGet

Asynchronous HTTP(S) request from a background pool thread; cb runs on the framework worker thread (see NodeHttpCallback). method is a token of uppercase letters A-Z only, at most 16 (“GET”, “POST”, “PUT”, “PATCH”, “DELETE”, “HEAD”, …; anything else is refused with status -1 “invalid HTTP method” in the callback); headers_json an optional JSON object of extra request headers (NULL = none); body/body_len the optional request body (NULL/0 = none). Follows up to 5 redirects, ~15 s total deadline, 8 MB response cap. TLS does NOT verify the peer certificate unless the hoster set [Http] CaFile in server.toml (server 1.2.0): then the chain is verified against that CA bundle and the name against the host, and a failure is reported like any transport error (status -1, the TLS error text as the body). Returns 0, or -1 on invalid arguments.

A per-resource JSON key/value store (storage/<resource>.json). Any JSON-serialisable Lua value; every mutation is flushed atomically, so the store survives a restart or a crash.

int storage_set(const char* store, const char* key, const char* value_json) (index 81)

Section titled “int storage_set(const char* store, const char* key, const char* value_json) (index 81)”

Lua: node.raw.storageSet

Values are JSON: set takes any JSON value text (a bare string that is not valid JSON is stored as a string), get returns the JSON-encoded value. Stores are named files under storage/ next to server.toml (Lua resources use their resource name; pick any [A-Za-z0-9_.-]+ name, sharing one store with a Lua resource is allowed). Data survives server restarts; every mutation is flushed atomically. set: 0 ok / -1 bad store or key. get: value length / -1 missing or buf too small. delete: 0 removed / -1 not present. All inline. Pass store = NULL for the store belonging to the resource making the call, which is what the scripting form means; name a store explicitly to reach one of your own. A NULL store with no resource in scope – a module calling from a thread of its own – is refused rather than written somewhere arbitrary.

int storage_get(const char* store, const char* key, char* buf, int buflen) (index 82)

Section titled “int storage_get(const char* store, const char* key, char* buf, int buflen) (index 82)”

Lua: node.raw.storageGet

Values are JSON: set takes any JSON value text (a bare string that is not valid JSON is stored as a string), get returns the JSON-encoded value. Stores are named files under storage/ next to server.toml (Lua resources use their resource name; pick any [A-Za-z0-9_.-]+ name, sharing one store with a Lua resource is allowed). Data survives server restarts; every mutation is flushed atomically. set: 0 ok / -1 bad store or key. get: value length / -1 missing or buf too small. delete: 0 removed / -1 not present. All inline. Pass store = NULL for the store belonging to the resource making the call, which is what the scripting form means; name a store explicitly to reach one of your own. A NULL store with no resource in scope – a module calling from a thread of its own – is refused rather than written somewhere arbitrary.

int storage_delete(const char* store, const char* key) (index 83)

Section titled “int storage_delete(const char* store, const char* key) (index 83)”

Lua: node.raw.storageDelete

Values are JSON: set takes any JSON value text (a bare string that is not valid JSON is stored as a string), get returns the JSON-encoded value. Stores are named files under storage/ next to server.toml (Lua resources use their resource name; pick any [A-Za-z0-9_.-]+ name, sharing one store with a Lua resource is allowed). Data survives server restarts; every mutation is flushed atomically. set: 0 ok / -1 bad store or key. get: value length / -1 missing or buf too small. delete: 0 removed / -1 not present. All inline. Pass store = NULL for the store belonging to the resource making the call, which is what the scripting form means; name a store explicitly to reach one of your own. A NULL store with no resource in scope – a module calling from a thread of its own – is refused rather than written somewhere arbitrary.

Asynchronous, pooled access to the PostgreSQL the hoster points the server at ([Database] Url in server.toml, NODE_DATABASE_URL in the environment; empty = disabled). Parameterised statements ($1..$n) and transactions pinned to one connection, submitted from any thread and completed on the framework worker – the Lua thread never blocks on the database. Rows come back keyed by column name with Postgres types mapped to plain values; errors carry the SQLSTATE. The pool connects in the background and reconnects on its own; the server runs without a database and statements then fail with 08001. ABI 1.11; node.pg is the Lua face.

Contract for the server side of the Lua surface (what the prelude relies on): all eleven pg raw names – pgEnabled, pgReady, pgNullSentinel, pgQuery, pgQueryWait, pgTxBegin, pgTxBeginWait, pgTxQuery, pgTxQueryWait, pgTxEnd, pgTxEndWait – are bound unconditionally, database configured or not (the prelude calls raw.pgNullSentinel() at load; one missing binding breaks the prelude for every resource); pgQuery and pgTxQuery answer an unsupported value inside params with the -3 return, never by raising, so that the prelude’s “callback exactly once” holds; pgTxEnd and pgTxEndWait take commit as a Lua boolean; a handle the TxTimeoutMs rollback ended stays addressable until pgTxEnd is called on it, which answers tx_timeout and forgets it.

Lua: node.raw.pgEnabled

1 iff the server was given a database ([Database] Url in server.toml / NODE_DATABASE_URL is non-empty), so the pg_* entries can queue work; 0 when the driver is off, in which case every pg_* submitter returns -1 at once and no callback runs. Says nothing about whether a connection is up – that is pg_ready. Inline, thread-safe. ABI 1.11.

Lua: node.raw.pgReady

1 iff at least one pool connection is established right now; 0 while connecting or reconnecting (and always when pg_enabled is 0). Statements submitted while it is 0 are not held back: they fail at once with SQLSTATE 08001. Connecting happens in the background with exponential retry (0.5 s to 30 s), so the server starts without the database and this turns 1 once it is reached. Inline, thread-safe. ABI 1.11.

int pg_query(const char* sql, const char* params_json, int flags, NodePgCallback cb, void* user) (index 147)

Section titled “int pg_query(const char* sql, const char* params_json, int flags, NodePgCallback cb, void* user) (index 147)”

Lua: node.raw.pgQuery

Queues one parameterised statement on the pool; the first free connection runs it and cb runs on the framework worker thread with the outcome (see NodePgCallback). sql uses $1..$n placeholders; params_json is a JSON array of the values in order – null for NULL, true/false, numbers, strings as text, a JSON object or array as its JSON text (for json/jsonb columns) – or NULL/“[]” for none; at most 1000 parameters. flags bit0 = no rows: the result carries count only (INSERT/UPDATE/DELETE without RETURNING). Returns 0 when queued, -1 when the driver is disabled (no [Database] Url), -2 when 1000 statements are already pending, -3 when sql is NULL or params_json is not a JSON array; a non-zero return means nothing was queued and cb never runs. Each statement runs under the connection’s statement_timeout ([Database] QueryTimeoutMs; SQLSTATE 57014 when exceeded); a result over [Database] MaxRows rows fails with code pg_result_cap; a connection lost mid-statement fails with 08006 and the thread reconnects. Autocommit: every statement here is its own transaction – use pg_tx_begin for several statements on one connection. ABI 1.11.

int pg_tx_begin(NodePgTxCallback cb, void* user) (index 148)

Section titled “int pg_tx_begin(NodePgTxCallback cb, void* user) (index 148)”

Lua: node.raw.pgTxBegin

Opens a transaction: reserves one pool connection, runs BEGIN on it and calls cb on the framework worker thread with the handle (>= 0) that pg_tx_query and pg_tx_end address it by, or -1 and an error (see NodePgTxCallback). The connection stays reserved – unavailable to pg_query – until pg_tx_end, or until [Database] TxTimeoutMs elapses, when the server rolls back on its own and the next call on the handle fails with code tx_timeout. Returns 0 when queued, -1 when the driver is disabled, -2 when the queue is full; non-zero means cb never runs. Only pg_tx_query runs inside the transaction: a plain pg_query issued meanwhile goes to another connection. ABI 1.11.

int pg_tx_query(int64_t handle, const char* sql, const char* params_json, int flags, NodePgCallback cb, void* user) (index 149)

Section titled “int pg_tx_query(int64_t handle, const char* sql, const char* params_json, int flags, NodePgCallback cb, void* user) (index 149)”

Lua: node.raw.pgTxQuery

pg_query on the connection a transaction reserved: the same sql/params_json/flags/cb contract, run in submission order on that connection, inside the open transaction (statements may be queued before the previous one completed). Returns 0 when queued, -1 disabled, -2 queue full, -3 for bad params or an unknown handle – one pg_tx_begin never issued, or one already forgotten by pg_tx_end; non-zero means cb never runs. A handle whose transaction the [Database] TxTimeoutMs rollback ended is still known until pg_tx_end is called on it: pg_tx_query on it queues (returns 0) and cb completes with code tx_timeout. After a failed statement Postgres refuses every further statement of the transaction (25P02) until pg_tx_end rolls it back. ABI 1.11.

int pg_tx_end(int64_t handle, int commit, NodePgCallback cb, void* user) (index 150)

Section titled “int pg_tx_end(int64_t handle, int commit, NodePgCallback cb, void* user) (index 150)”

Lua: node.raw.pgTxEnd

Ends a transaction: COMMIT when commit is nonzero, ROLLBACK when 0, then returns the connection to the pool and forgets the handle. cb runs on the framework worker thread with status 0 when the transaction ended as asked (result_json {“rows”:[],“count”:0,“columns”:[]}), or status < 0 with the error – a COMMIT that fails (a deferred constraint, a serialization failure, a lost connection) is rolled back by Postgres, so the transaction is over either way. A handle whose transaction the [Database] TxTimeoutMs rollback already ended is still known: this call queues, cb answers code tx_timeout, and the handle is forgotten – so pg_tx_end is always the last word on a handle. Returns 0 when queued, -1 disabled, -2 queue full, -3 for an unknown handle (never issued by pg_tx_begin, or already ended by pg_tx_end); non-zero means cb never runs. ABI 1.11.

SHA-256, HMAC-SHA256, cryptographic random bytes, and JSON encode/decode with a depth guard.

void sha256_hex(const void* data, int len, char out_hex65[65]) (index 94)

Section titled “void sha256_hex(const void* data, int len, char out_hex65[65]) (index 94)”

Lua: node.raw.sha256

SHA-256 / HMAC-SHA256 as a lower-case hex string (64 chars + NUL) written into out_hex65. Inline.

void hmac_sha256_hex(const void* key, int key_len, const void* data, int data_len, char out_hex65[65]) (index 95)

Section titled “void hmac_sha256_hex(const void* key, int key_len, const void* data, int data_len, char out_hex65[65]) (index 95)”

Lua: node.raw.hmac

SHA-256 / HMAC-SHA256 as a lower-case hex string (64 chars + NUL) written into out_hex65. Inline.

int random_bytes(void* out, int n) (index 96)

Section titled “int random_bytes(void* out, int n) (index 96)”

Lua: node.raw.randomBytes

Fills out with n cryptographically random bytes (OpenSSL RAND). Returns 0, or -1 when the generator failed / n is out of 1..65536.

Reloading a resource by name. The call returns accepted: the reload runs on the worker after the current handler returns, dropping the resource’s registrations, coroutines and state, then loading it again.

int reload_resource(const char* name) (index 119)

Section titled “int reload_resource(const char* name) (index 119)”

Lua: node.raw.reloadResource

Queues a reload: the resource is handed back to its language host and loaded again from the same folder. Uniform across runtimes, Lua included, because loading is – the host is not told it is a reload. Everything the resource registered is dropped first, so the new instance never shares the dispatch tables with the old one. QUEUED, not immediate: a reload destroys the resource’s registrations, and the usual place to ask for one is inside a handler, which IS one of those registrations, running. It therefore happens on the worker once the current dispatch finishes. Returns 0 when the request was accepted, -1 when no resource by that name is loaded. A host that then refuses the load leaves the resource unloaded rather than half-loaded, and says so in the log.

int get_resource_manifest_json(const char* resource, char* buf, int buflen) (index 130)

Section titled “int get_resource_manifest_json(const char* resource, char* buf, int buflen) (index 130)”

Lua: node.raw.getManifest

The resource’s manifest as JSON: { name, version, type, server: { main }, client: { files (array or null when unlisted), obfuscation (or null) }, config: the [config] table of resource.toml converted as-is (an empty object when absent) }. resource is a resource name, or NULL for the resource making the call. Returns the length written, -1 when there is no such resource or buf is too small; ask with buf = NULL for the size. Read from disk on each call, so a manifest edited while the server runs is seen by the next call. ABI 1.8.

Native-module-only: register a NodeLanguageHost so resources of another type (JavaScript, C#) can be loaded, and name the resource a registration belongs to so it is dropped with that resource. Not exposed to Lua.

int register_language_host(const NodeLanguageHost* host) (index 106)

Section titled “int register_language_host(const NodeLanguageHost* host) (index 106)”

Lua: not exposed

Register a runtime for a resource type (see NodeLanguageHost). Call from node_plugin_init: modules load before resources, so a host registered there is in place when resources are scanned. Returns 0, or -1 when host/type_name is NULL, struct_size does not match, or that type is already taken (including the built-in “lua”).

int unregister_language_host(const char* type_name) (index 107)

Section titled “int unregister_language_host(const char* type_name) (index 107)”

Lua: not exposed

Drop a previously registered host. Its loaded resources are unloaded first, so unload() runs for each while the module is still there. What those resources registered is NOT removed for you – see NodeLanguageHost: undo it in unload(), or the framework will keep calling into a module that is no longer loaded. Returns 0, or -1 when no host owns that type.

void set_resource_owner(const char* resource_name) (index 110)

Section titled “void set_resource_owner(const char* resource_name) (index 110)”

Lua: not exposed

Attributes everything registered from THIS THREAD from now on to resource_name, so the framework can drop it when that resource unloads. Pass NULL to go back to registering on your own behalf, which is the default and means the registration lives as long as the module does. Registrations a host makes while the framework is calling its load() are attributed automatically; this is for the ones made later, from an event handler or the host’s own thread. It applies per thread and until changed, so set it, register, and set NULL again.