Scheduling and lifecycle
every, onStop, caller, ByResource, Reasons, sleep and waitFor: periodic work, waiting, and data that belongs to the resource that registered it.
import { every, onStop, caller, ByResource, Reasons } from "@aicore-fivem/lib/fivem";
import { sleep, waitFor } from "@aicore-fivem/lib/shared";Periodic work
every
Runs a function every ms milliseconds (0 = every frame) from your resource's single scheduler. Use it instead of setInterval, setTick or a CreateThread loop with Wait: all jobs of a resource share one tick, and that tick exists only while at least one job is switched on, so an idle resource costs nothing in resmon.
Side: Client and server · Waits: no
every(ms: number, fn: () => unknown, options?: EveryOptions): Joblocal job = aiLib.every(ms, fn, options) -- job:set(active), job:stop(), job.runningParameters
Prop
Type
EveryOptions
Prop
Type
Returns
Job: a handle to switch the job on and off.
Prop
Type
Example
A per-frame job that runs only while it has something to do:
import { every, cache, onCache } from "@aicore-fivem/lib/client";
// Hides the crosshair every frame, but only while a weapon is in hand.
const hideCrosshair = every(0, () => HideHudComponentThisFrame(14), {
active: cache.weapon !== false,
name: "hide crosshair",
});
onCache("weapon", (weapon) => hideCrosshair.set(weapon !== false));A job must never wait: no Wait in Lua, no long await chain that you expect to finish before the next run in TypeScript. In Lua a Wait inside a job holds every other job of the resource; start a thread from the job for that. A failing job is logged at most once every 10 seconds (the errors in between are counted into the next line).
Waiting
sleep
Waits ms milliseconds.
Side: Client, server and NUI · Waits: yes (Promise)
sleep(ms: number): Promise<void>Lua: no twin; use Wait(ms) inside a thread.
Parameters
Prop
Type
Returns
Promise<void> that resolves after ms milliseconds.
Example
import { sleep } from "@aicore-fivem/lib/shared";
import { fadeOut, fadeIn } from "@aicore-fivem/lib/client";
async function blink(): Promise<void> {
await fadeOut(300);
await sleep(1000);
await fadeIn(300);
}waitFor
Checks a condition until it gives a value, then returns that value. Use it to wait for something the game does on its own time: an entity to exist, a model to load, a value to arrive.
Side: Client, server and NUI · Waits: yes (Promise in TypeScript, yields the thread in Lua)
waitFor<T>(check: () => T | undefined, timeoutMs?: number | false, intervalMs?: number): Promise<T>local value = aiLib.waitFor(check, timeout, interval) -- value, or nil after the timeout; call inside a threadParameters
Prop
Type
Returns
TypeScript: Promise<T> that resolves with the first value check returned that was not undefined. It rejects with Error("waitFor timed out after <ms> ms") after the timeout.
Lua: the value, or nil after the timeout (it does not throw).
Example
import { waitFor } from "@aicore-fivem/lib/shared";
async function vehicleFromNet(netId: number): Promise<number | null> {
try {
return await waitFor(() => {
const vehicle = NetworkGetEntityFromNetworkId(netId);
return DoesEntityExist(vehicle) ? vehicle : undefined;
}, 5000);
} catch {
return null; // not streamed in within 5 seconds
}
}In TypeScript, always handle the rejection (try / catch or .catch()), or pass false as the timeout. In Lua, waitFor only works inside a thread.
Resource lifecycle
onStop
Runs a function when your resource stops (on the side the code runs on). Use it to delete what natives created and nothing else removes: entities you spawned, cameras, a NUI focus.
Side: Client and server · Waits: no
onStop(fn: () => void): voidLua: no twin; use AddEventHandler('onResourceStop', function(name) if name == GetCurrentResourceName() then ... end end).
Parameters
Prop
Type
Returns
void.
Example
import { onStop } from "@aicore-fivem/lib/server";
const spawned = new Set<number>();
onStop(() => {
for (const entity of spawned) if (DoesEntityExist(entity)) DeleteEntity(entity);
});All onStop, ByResource and Reasons of a resource share one stop listener, so adding many costs nothing extra.
caller
The name of the resource that called the export now running. Inside your own code (not in an export call) it is your resource's name.
Side: Client and server · Waits: no
caller(): stringLua: no twin; use GetInvokingResource() or GetCurrentResourceName().
Returns
string: the calling resource's name, or this resource's name when no other resource is calling.
Example
import { registerExport, caller } from "@aicore-fivem/lib/server";
registerExport("Ping", () => {
console.log(`${caller()} asked for a ping`);
return true;
});ByResource
A map whose entries belong to the resource that set them. When that resource stops, its entries are removed at once (and onDrop runs for each). Use it for things other resources register through your exports: hooks, prompts, items, menu entries.
Side: Client and server · Waits: no
new ByResource<K, V>(options?: { onDrop?: (resource: string, key: K, value: V) => void })Lua: no twin.
Parameters
Prop
Type
Methods
Prop
Type
Example
import { ByResource, registerExport } from "@aicore-fivem/lib/server";
import { readText } from "@aicore-fivem/lib/shared";
type Hook = (source: number, text: string) => boolean;
const hooks = new ByResource<string, Hook>({
onDrop: (resource, id) => console.log(`${resource} stopped: hook ${id} removed`),
});
// Another resource: exports["my-chat"].AddHook("swear-filter", (source, text) => ...)
registerExport("AddHook", (id: unknown, hook: unknown) => {
const name = readText(id, 64);
if (name === null || typeof hook !== "function") return false;
hooks.set(name, hook as Hook); // owner = the resource calling AddHook
return true;
});
export function allowed(source: number, text: string): boolean {
for (const hook of hooks.values()) if (!hook(source, text)) return false;
return true;
}Reasons
A switch that is on while at least one reason is set. Each resource sets its own named reasons, and a stopping resource's reasons are cleared. Use it for "hide the HUD", "pause the sync", "block the menu": several scripts can ask at once, and the state only flips back when the last one lets go.
Side: Client and server · Waits: no
new Reasons()Lua: no twin.
Methods
Prop
Type
Example
import { Reasons, registerExport } from "@aicore-fivem/lib/client";
import { readText } from "@aicore-fivem/lib/shared";
const hidden = new Reasons();
hidden.onChange((any) => SendNUIMessage({ action: "visible", value: !any }));
// Other resources: exports["my-hud"].Hide("cutscene") and exports["my-hud"].Show("cutscene")
registerExport("Hide", (reason: unknown) => {
const name = readText(reason, 64);
if (name !== null) hidden.set(name, true);
});
registerExport("Show", (reason: unknown) => {
const name = readText(reason, 64);
if (name !== null) hidden.set(name, false);
});