Lua runtime function contracts
This reference covers the documented CfxLua helpers in the pinned Lua function sources . A helper is not necessarily a game native, and availability on the client does not imply availability on the server. Empty upstream pointer/promise pages are identified in runtime internals and source limits, not filled with guessed signatures.
Scheduling and logging
| Function | Contract | Failure boundary |
|---|---|---|
Citizen.CreateThread(handler) / CreateThread(handler) | Start cooperative asynchronous work in the scripting runtime. | Not an operating-system thread. An unyielding loop can block other work. |
Citizen.Wait(milliseconds) / Wait(milliseconds) | Yield the current coroutine for a scheduling interval. Wait(0) yields to a following tick. | Requires a yieldable coroutine; intervals are not exact deadlines. Frame-dependent drawing needs per-frame execution. |
Citizen.SetTimeout(milliseconds, callback) / SetTimeout(milliseconds, callback) | Schedule a later callback without pausing the calling code. | The documented contract does not expose cancellation. Invalidate stale callbacks explicitly. |
Citizen.Await(awaitable) | Await a runtime-compatible awaitable. | The source does not specify a timeout argument or the behavior of every promise combinator. Handle failure in the calling coroutine. |
Citizen.Trace(message) | Send a string to trace listeners, including the console/log. | No newline is appended automatically. Exclude secrets and excessive personal data. |
Lua scheduling supplies a complete cancellable-by-generation timer resource and the distinction between preserving source and preserving a player’s session identity. Use an event for one-time work instead of polling every frame.
Local and network events
| Function | Side | Arguments and return use |
|---|---|---|
AddEventHandler(eventName, callback) | Client or server | Add a handler in the current runtime; retain the returned handler data when it must be removed. |
RemoveEventHandler(handlerData) | Client or server | Pass the exact data returned by AddEventHandler, not an event-name string or an invented numeric ID. |
RegisterNetEvent(eventName, callback?) | Client or server | Permit network reception. With no callback, register a separate handler. Local invocation remains possible. |
TriggerEvent(eventName, ...) | Client or server | Trigger locally, without a recipient argument. |
TriggerServerEvent(eventName, ...) | Client | Send to the server; arguments begin immediately after the event name. |
TriggerClientEvent(eventName, playerId, ...) | Server | Send to one session ID; -1 explicitly broadcasts to all clients. |
An original local-only listener example can run in either script context:
local handler = AddEventHandler('doc:localProbe', function(value)
if type(value) == 'string' then print(value) end
end)
TriggerEvent('doc:localProbe', 'First message is observed.')
RemoveEventHandler(handler)
TriggerEvent('doc:localProbe', 'Removed listener must not print this.')Registering an event does not authorize its caller. Save the server’s source before yielding, validate requests against server-owned state, and reject stale sessions before a delayed mutation. See event security, cancellation and latent delivery, and the engine event contracts for event-specific argument lists and cancellation rules.
Enumerate players without confusing IDs
GetPlayers() is a server helper returning the currently connected player identifiers used as server session IDs. It is a snapshot, not a stable account registry. Check the session is still present before later work, and do not assume an ID remains assigned after disconnect.
GetPlayerIdentifiers(player) returns prefixed identifier strings. The documented prefixes are steam (hexadecimal), discord (decimal), license and license2 (ROS hashes), fivem (decimal Cfx.re ID), and ip (address string). Not every provider is present. license2 can equal license. An IP address is not a durable account identifier.
To retrieve one provider, prefer GetPlayerIdentifierByType when supported by the chosen runtime rather than repeatedly scanning the complete table. Handle a missing result. Keep identifiers server-side, do not print them in routine diagnostics, and do not derive privileges from values supplied by a client. Tokens returned by GetPlayerToken are a different API and should not be exposed as public player IDs. See connection admission and ID conversion.
HTTP requests: callback and await forms
PerformHttpRequest is server-side and uses this argument order:
-- Signature notation, not a request to execute:
-- PerformHttpRequest(url, callback, method, data, headers, options)
-- callback(statusCode, body, responseHeaders, errorData)The documented defaults are method GET, empty data, empty headers, and options = { followLocation = true }. There are four callback results. Transport failure and an HTTP non-success status are different failures; do not decode an absent or HTML response as JSON unconditionally.
PerformHttpRequestAwait(url, method, data, headers, options) returns the same four results and requires server build 9515 or newer according to its source. It waits through the runtime’s coroutine mechanism; it does not make a client HTTP API available or make an unbounded request safe. Check the target build before choosing the await form.
This complete server script makes a fixed, console-only diagnostic request. It does not accept a client URL, follow a redirect to an arbitrary host, or print the response body:
-- server.lua; declare server_script 'server.lua' in fxmanifest.lua.
local pending = false
RegisterCommand('dochttp', function(source)
if source ~= 0 or pending then return end
pending = true
local completed = false
local function finish(message)
if completed then return end
completed = true
pending = false
print(message)
end
SetTimeout(10000, function()
-- This abandons the result; it does not cancel the underlying HTTP request.
finish('Diagnostic deadline exceeded; any late result will be ignored.')
end)
PerformHttpRequest('https://example.com/', function(status, body, headers, errorData)
if completed then return end
if type(status) ~= 'number' or status < 200 or status >= 300 then
finish(('Diagnostic failed with status %s.'):format(tostring(status)))
return
end
local bytes = type(body) == 'string' and #body or 0
finish(('Diagnostic HTTP %d, received %d bytes.'):format(status, bytes))
end, 'GET', '', {}, { followLocation = false })
end, false)Run dochttp in the server console. A successful response reports its status and size; a network failure or deadline reports an error. This exercise depends on external connectivity. For production, use an owned fixed endpoint, rate limits, response-size validation and a transport with suitable timeout/cancellation support. Ignoring a late callback is not a global concurrency limit: repeated abandoned requests can still consume network resources. Never expose this pattern as an unrestricted network event.
NUI helpers and migration
SendNUIMessage(table) serializes a Lua table for the resource’s browser UI. Receive it with a browser message listener and validate its shape; it is not a request/reply call. The underlying SEND_NUI_MESSAGE native takes JSON text, so do not double-encode the Lua wrapper’s input.
The older RegisterNUICallback(name, callback) Lua helper remains documented as a legacy API. The current manual uses RegisterNuiCallback, the wrapper for REGISTER_NUI_CALLBACK, across Lua, JavaScript and C#. Capitalization distinguishes the old helper from the newer native wrapper. Use current NUI callback registration for JSON input, bounded requests and exactly-once completion; retain an older bridge only for an explicitly supported older runtime.
Native invocation
Citizen.InvokeNative(hash, ...) invokes a native by identifier. Prefer the named wrapper when one exists. Obtain the complete hash, argument types, return convention and client/server context from the native reference; a model’s 32-bit Jenkins hash is not a substitute for a native identifier.
Do not cast a JavaScript-style object into a Lua pointer helper, guess a struct layout, or select a result coercion merely because a call returned an unexpected type. The upstream pointer-helper pages do not document enough detail to establish a generic safe ABI recipe. Check implementation and the exact target runtime before using those low-level interfaces.
Vector and quaternion constructors
| Constructor or alias | Result |
|---|---|
vector(...) / vec(...) | Choose a number, vector2, vector3 or vector4 from one through four components. |
vector1(x) / vec1(x) | A number, not a separate one-dimensional userdata type. |
vector2(x, y) / vec2(x, y) | Two-component vector. |
vector3(x, y, z) / vec3(x, y, z) | Three-component vector. |
vector4(x, y, z, w) / vec4(x, y, z, w) | Four-component vector; application-defined w is not automatically a quaternion scalar. |
quat(w, x, y, z) | Quaternion with scalar first. |
quat(angle, axis) | Angle in degrees and vector3 axis. Use a nonzero normalized axis. |
quat(fromDirection, toDirection) | Rotation between two vector3 directions. Treat zero-length and degenerate inputs deliberately. |
Vectors support component access, arithmetic, unary negation, unpacking and swizzles such as position.yx or position.xyx. #vector is magnitude, not the number of components. Quaternion unpacking follows w, x, y, z; independently check the ordering of a receiving native. GET_ENTITY_QUATERNION uses x, y, z, w output ordering.
dot accepts matching vector2/3/4 or quaternion operands and returns a number. norm returns a normalized value of the same supported type. inv is a quaternion inverse. slerp blends matching supported vector or quaternion values; use a bounded factor for interpolation. cross supports vector2 pairs (number), vector3 pairs (vector3), quaternion pairs (quaternion), and mixed vector3/quaternion operands (vector3). Rotation composition is order-dependent; do not replace it with componentwise multiplication.
For actual distance assertions, zero-length guards, angle clamping and quaternion examples use Lua vectors. The factual contract review and website tests do not certify engine execution of these types.