JavaScript tick and player helpers
setTick, clearTick and getPlayers are FiveM runtime helpers, not browser APIs or functions supplied by a normal Node.js installation. Put each script on the correct side of its resource manifest.
| Helper | Runtime | Contract |
|---|---|---|
setTick(callback) | Client or server | Schedules repeated work on game frames or server ticks; returns an ID. |
clearTick(id) | Same runtime as the registration | Stops the tick associated with that ID. |
getPlayers() | Server | Returns connected players’ server IDs as an array of strings. |
A client player index, server ID and entity handle are different identifiers. See network and local IDs before passing one into a native.
Start a tick once and stop it explicitly
Create resources/[local]/doc_js_helpers/fxmanifest.lua:
fx_version 'cerulean'
game 'gta5'
client_script 'client.js'
server_script 'server.js'Create client.js. This small diagnostic counts callback invocations without printing on every frame:
// example: client-tick-lifecycle
let tickId = null;
let count = 0;
function stopCounter() {
if (tickId === null) return;
clearTick(tickId);
tickId = null;
}
RegisterCommand('docstart', () => {
if (tickId !== null) return;
count = 0;
tickId = setTick(() => {
count += 1;
});
}, false);
RegisterCommand('doccount', () => {
console.log(`Tick callbacks: ${count}`);
}, false);
RegisterCommand('docstop', stopCounter, false);
on('onClientResourceStop', (resourceName) => {
if (resourceName === GetCurrentResourceName()) stopCounter();
});Run refresh and ensure doc_js_helpers on the server. In F8, run docstart twice, then doccount. There should still be only one active counter. Run docstop, wait briefly and read doccount again: the count should stay unchanged. Starting again resets the count.
Use null as the inactive sentinel, not a truthiness check such as if (tickId). Treat the returned ID as opaque; cleanup must not accidentally ignore an ID of zero. The example’s local regression test deliberately supplies zero as the first mocked ID.
A tick is not a fixed-rate clock. Do not derive elapsed seconds by dividing this counter by an assumed frame rate. Use ticks for work that actually needs them, such as frame-dependent rendering. For occasional updates, choose a timer or event and measure the result with the profiler. An asynchronous operation started on every tick can overlap; awaiting inside a callback is not an application-level concurrency limit.
Read the server’s player list
Create server.js:
// example: server-player-snapshot
RegisterCommand('docplayers', (source) => {
if (source !== 0) return;
const playerIds = getPlayers();
console.log(`Connected players: ${playerIds.length}`);
}, true);Run docplayers in the server console. The command reports a count, not a dump of player identifiers. It intentionally refuses player-originated execution; do not remove that boundary merely to make the command available in chat.
getPlayers() returns strings such as "12", not player objects. Convert an ID to a number only when the called API requires it. A captured list is not a permanent reservation of those sessions: a player may disconnect before later work uses the result. For asynchronous account or inventory changes, use the session-safe JavaScript workflow.
Do not mix the three JavaScript environments
The manifest’s server_script runs on FXServer; client_script runs in the game runtime; files loaded by an NUI HTML page run in the browser context. A Node version declaration does not install server modules into NUI or make server-only player APIs available on the client.
For an NUI button, call the documented NUI callback bridge, then let the appropriate runtime perform the action. Keep filesystem and HTTP work on the intended side. For Node I/O followed by a native call, follow scheduler handoff rather than assuming every callback is already on the game thread.
Checks and cleanup
The regression script executes the exact two JavaScript blocks above with mocked commands and tick APIs. It checks repeated starts/stops, tick ID zero, reset behavior, another resource stopping, own-resource cleanup, empty/nonempty player lists and console-only execution. These are control-flow tests, not an FXServer simulator.
For runtime acceptance, repeat the F8 procedure, restart this resource, and verify that server-console counts follow joins and disconnects. Stop doc_js_helpers and remove its startup entry when finished. No real client/server execution is claimed by the documentation tests.
Primary sources
The current contracts are in setTick , clearTick and the pinned getPlayers source . For the lifecycle event, see client event contracts.