Membership and player data
hasJob, inGang, onDutyIn, allows, onMembershipChange, getPlayerData, onPlayerData and seedPlayerData - the local player's character, money, jobs, duty and gangs as ai-core pushes them.
ai-core pushes the local player's own data to their client: one snapshot when the character loads, then only the values that change. These helpers read that copy, so a check costs a memory read and never asks the server.
Use them for what the player sees (a point's canShow, a radial item, a menu row). The server decides what the player may do with the same data (online on Players and characters): a client check is never proof.
Player data
The values ai-core keeps, by key (AiPlayerData):
Prop
Type
The member types are CharacterInfo, JobMembership and GangMembership.
getPlayerData
The last value of one key. On the first use in your resource it also reads everything ai-core already pushed (from ai-core's GetPlayerData export), so a resource restarted after the player loaded has the values at once.
Side: Client · Waits: no
getPlayerData<K extends keyof AiPlayerData>(key: K): AiPlayerData[K] | undefinedlocal value = aiLib.playerData.get(key)Parameters
Prop
Type
Returns
AiPlayerData[K] | undefined: the value, or undefined (nil in Lua) when nothing arrived yet (no character loaded, ai-core not started).
Example
import { getPlayerData, notify } from "@aicore-fivem/lib/client";
RegisterCommand(
"whoami",
() => {
const character = getPlayerData("character");
const job = getPlayerData("job");
const cash = getPlayerData("money")?.cash ?? 0;
if (!character) return notify("No character loaded", "error");
notify(`${job?.job ?? "unemployed"} (${job?.grade ?? 0}), $${cash}`, "info");
},
false,
);onPlayerData
Runs a handler every time ai-core pushes a key for this player.
Side: Client · Waits: no
onPlayerData<K extends keyof AiPlayerData>(key: K, handler: (value: AiPlayerData[K]) => void): () => voidaiLib.playerData.onChange(key, function(value) end)Parameters
Prop
Type
Returns
TypeScript: () => void, a function that removes the handler. Lua returns nothing.
Example
import { onPlayerData } from "@aicore-fivem/lib/client";
const stop = onPlayerData("money", (money) => {
SendNUIMessage({ action: "money", data: { cash: money.cash ?? 0, bank: money.bank ?? 0 } });
});
// stop() when the HUD closes for goodValues pushed before your resource started are not replayed to the handler. Read the current value once with getPlayerData at start, then follow changes with onPlayerData.
seedPlayerData
Fills values by hand. Keys that already have a value are kept. You do not need it: the lib seeds itself from ai-core on first use.
Side: Client · Waits: no
seedPlayerData(data: Partial<AiPlayerData>): voidNo Lua twin.
Parameters
Prop
Type
Returns
void.
Example
import { seedPlayerData } from "@aicore-fivem/lib/client";
// A test harness without ai-core.
seedPlayerData({ jobs: [{ job: "police", grade: 3 }], duty: "police" });Membership checks
hasJob
Whether the player holds one of the jobs at a minimum grade, on duty or not.
Side: Client · Waits: no
hasJob(jobs: readonly string[], minGrade?: number): booleanNo Lua twin: walk aiLib.playerData.get('jobs') (see the points.add Lua example).
Parameters
Prop
Type
Returns
boolean: true when the list is empty, or the player holds one of jobs at minGrade or above.
Example
import { hasJob, onMembershipChange, Points } from "@aicore-fivem/lib/client";
const points = new Points();
points.add("armory", {
coords: { x: 482.6, y: -995.2, z: 30.7 },
label: "Armory",
canShow: () => hasJob(["police", "sheriff"], 1),
onPressed: () => emitNet("mypolice:armory"),
});
onMembershipChange(() => points.refresh());inGang
Whether the player is in one of the gangs at a minimum grade.
Side: Client · Waits: no
inGang(gangs: readonly string[], minGrade?: number): booleanNo Lua twin: walk aiLib.playerData.get('gangs').
Parameters
Prop
Type
Returns
boolean: true when the list is empty, or the player is in one of gangs at minGrade or above.
Example
import { inGang, radial } from "@aicore-fivem/lib/client";
radial.add({
id: "mygang:stash",
label: "Gang stash",
icon: "box",
canShow: () => inGang(["ballas", "vagos"], 2),
onPressed: () => emitNet("mygang:stash"),
});onDutyIn
Whether the player is on duty in one of the jobs.
Side: Client · Waits: no
onDutyIn(jobs: readonly string[]): booleanNo Lua twin: aiLib.playerData.get('duty') is the job on duty.
Parameters
Prop
Type
Returns
boolean: true when the player's duty job is in jobs.
Example
import { onDutyIn, Points } from "@aicore-fivem/lib/client";
const points = new Points();
points.addVehicle("mymech:repair", {
label: "Repair",
icon: "wrench",
canShow: () => onDutyIn(["mechanic"]),
onPressed: (vehicle) => emitNet("mymech:repair", NetworkGetNetworkIdFromEntity(vehicle)),
});onMembershipChange
Runs a function when the player's jobs, duty or gangs change. Use it to refresh points and blips whose canShow reads them.
Side: Client · Waits: no
onMembershipChange(fn: () => void): voidNo Lua twin: aiLib.playerData.onChange for jobs, duty and gangs.
Parameters
Prop
Type
Returns
void.
Example
import { onMembershipChange, Points, radial } from "@aicore-fivem/lib/client";
const points = new Points();
onMembershipChange(() => {
points.refresh();
radial.refresh();
});allows
Whether the local player may use a place with an access rule. The server's online.allows(source, rule) decides with the same rule, so what the player sees is exactly what the server lets them use. Read the rule from a panel record with readAccess(record) (@aicore-fivem/lib/shared).
Side: Client · Waits: no
allows(rule: AccessRule): booleanNo Lua twin.
Parameters
Prop
Type
AccessRule
Prop
Type
The type is AccessRule.
Returns
boolean: true when both lists are empty (everyone); when the player is in one of gangs at its minimum grade; or when the player holds one of jobs at its minimum grade (and is on duty in it when onDuty). Otherwise false.
Example
import { AdminWorld, allows, callAction, onMembershipChange, Points } from "@aicore-fivem/lib/client";
import { readAccess, readVec3, RecordObjects, type AccessRule } from "@aicore-fivem/lib/shared";
const world = new AdminWorld(["stashes"]);
const points = new Points();
interface Stash { coords: { x: number; y: number; z: number }; access: AccessRule }
const readStash = (_key: string, raw: Record<string, unknown>): Stash | null => {
const coords = readVec3(raw.coords);
return coords ? { coords, access: readAccess(raw) } : null;
};
new RecordObjects(world, "stashes", readStash, (id, stash) => {
points.add(id, {
coords: stash.coords,
label: "Stash",
canShow: () => allows(stash.access),
onPressed: () => callAction("mystash:open", [id], { fallback: "STASH_FAILED" }),
});
return () => points.remove(id);
});
onMembershipChange(() => points.refresh());