AI Core

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

Signature
getPlayerData<K extends keyof AiPlayerData>(key: K): AiPlayerData[K] | undefined
Lua
local 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

Signature
onPlayerData<K extends keyof AiPlayerData>(key: K, handler: (value: AiPlayerData[K]) => void): () => void
Lua
aiLib.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 good

Values 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

Signature
seedPlayerData(data: Partial<AiPlayerData>): void

No 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

Signature
hasJob(jobs: readonly string[], minGrade?: number): boolean

No 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

Signature
inGang(gangs: readonly string[], minGrade?: number): boolean

No 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

Signature
onDutyIn(jobs: readonly string[]): boolean

No 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

Signature
onMembershipChange(fn: () => void): void

No 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

Signature
allows(rule: AccessRule): boolean

No 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

resource/client/index.ts
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());

On this page