AI Core
Référence de l’APILib : shared et fivem

State bags

EntityState, PlayerState, GlobalKey, GlobalGroup et les parseurs stateNumber, stateBoolean, stateString : lire les valeurs répliquées depuis la mémoire.

Un state bag est une valeur que FiveM copie du serveur vers les clients qui en ont besoin : le carburant d’un véhicule (Entity(vehicle).state.fuel), le fait qu’un joueur soit à terre (Player(id).state.dead), un indicateur valable pour tout le serveur (GlobalState["ai:pvp"]).

Lire un bag directement est lent : chaque lecture construit des proxys, appelle plusieurs natives et décode à nouveau la valeur. Ces classes gardent un seul gestionnaire de changement par clé et une copie de chaque valeur en mémoire : une lecture n’est donc qu’une recherche dans une map. Utilisez-les pour tout bag que vous lisez plus d’une fois.

Fichier client : versions en lecture seule (get, onChange)
import { EntityState, PlayerState, GlobalKey, GlobalGroup, stateNumber, stateBoolean, stateString } from "@aicore-fivem/lib/client";
Fichier serveur : les mêmes classes plus set(...)
import { EntityState, PlayerState, GlobalKey, GlobalGroup, stateNumber, stateBoolean, stateString } from "@aicore-fivem/lib/server";

Les classes ne sont pas sur @aicore-fivem/lib/fivem : importez-les depuis l’entrée client ou serveur. Seul le serveur écrit les state bags (sv_stateBagStrictMode) ; un client demande au serveur via un callback et le serveur définit la valeur. Les méthodes set du serveur se trouvent sur Lib : serveur, State bags.

Lua : aucun de ces utilitaires n’a d’équivalent Lua. Lua lit les bags lui-même (Entity(vehicle).state.fuel, Player(id).state.dead, GlobalState['ai:pvp']) et les écoute avec AddStateBagChangeHandler.

Les clés qu’utilisent ai-core et les scripts ai-* sont listées sur Événements et état.

Parseurs

Chaque classe prend un parseur : une fonction qui transforme la valeur répliquée brute en votre type, ou en undefined quand ce n’est pas une valeur valide. Le type est StateParse<T> = (value: unknown) => T | undefined. Un bag vidé (null) est toujours lu comme undefined ; le parseur ne le voit jamais.

stateNumber

Accepte un nombre fini ; toute autre chose (texte, NaN, Infinity) est lue comme undefined.

Côté : client et serveur · Attend : non

Signature
stateNumber: StateParse<number>

Exemple

import { EntityState, stateNumber } from "@aicore-fivem/lib/client";

const fuel = new EntityState("fuel", stateNumber); // 0-100, défini par le script de carburant

stateBoolean

Accepte true ou false ; toute autre chose est lue comme undefined.

Côté : client et serveur · Attend : non

Signature
stateBoolean: StateParse<boolean>

Exemple

import { PlayerState, stateBoolean } from "@aicore-fivem/lib/client";

const dead = new PlayerState("dead", stateBoolean); // true tant que le joueur est à terre (ai-death)

stateString

Accepte une chaîne ; toute autre chose est lue comme undefined.

Côté : client et serveur · Attend : non

Signature
stateString: StateParse<string>

Exemple

import { GlobalKey, stateString } from "@aicore-fivem/lib/client";

const inventoryScript = new GlobalKey("ai:inventory", stateString); // le nom de ressource de l’inventaire actif

Pour une valeur objet, écrivez votre propre parseur qui vérifie chaque champ et renvoie undefined pour une mauvaise valeur :

import { PlayerState, type StateParse } from "@aicore-fivem/lib/client";

type Proximity = { index: number; distance: number; mode: string };
const proximity: StateParse<Proximity> = (v) => {
  if (typeof v !== "object" || v === null) return undefined;
  const { index, distance, mode } = v as Record<string, unknown>;
  return typeof index === "number" && typeof distance === "number" && typeof mode === "string"
    ? { index, distance, mode }
    : undefined;
};
const voiceRange = new PlayerState("proximity", proximity); // la portée de pma-voice, écrite par ai-voice

Classes

EntityState

Une clé des state bags d’entité (fuel, ai:owned...), pour chaque entité réseau. Les valeurs sont gardées par network id ; une lecture prend le handle de l’entité.

Côté : client et serveur · Attend : non

Signature
new EntityState<T>(key: string, parse: StateParse<T>)

Paramètres

Prop

Type

Méthodes

Prop

Type

Exemple

resource/client/index.ts
import { EntityState, stateNumber, cache, every } from "@aicore-fivem/lib/client";

const fuel = new EntityState("fuel", stateNumber);

every(1000, () => {
  if (cache.vehicle === false) return;
  SendNUIMessage({ action: "fuel", value: fuel.get(cache.vehicle) ?? 100 });
});

La mémoire reste petite d’elle-même : le serveur oublie une entité quand elle est supprimée, et un client retire toutes les 30 secondes les entités sorties de sa portée. Un network id réutilisé par une nouvelle entité est relu.

PlayerState

Une clé des state bags de joueur (dead, cuffed, proximity de pma-voice...), par server id.

Côté : client et serveur · Attend : non

Signature
new PlayerState<T>(key: string, parse: StateParse<T>)

Paramètres

Prop

Type

Méthodes

Prop

Type

Exemple

resource/server/index.ts
import { PlayerState, stateBoolean, onClientCallback } from "@aicore-fivem/lib/server";
import { fail } from "@aicore-fivem/lib/shared";

const dead = new PlayerState("dead", stateBoolean);
const cuffed = new PlayerState("cuffed", stateBoolean);

onClientCallback("my-shop:open", (source) => {
  if (dead.get(source) || cuffed.get(source)) return fail("CANNOT_NOW");
  return { ok: true };
});

Sur le serveur, un joueur qui part est oublié dans chaque PlayerState de votre ressource (un seul écouteur playerDropped partagé).

GlobalKey

Une clé de GlobalState : une valeur dont chaque joueur a besoin (l’interrupteur PvP, l’inventaire actif). Lue une fois au démarrage, puis tenue à jour par son gestionnaire de changement.

Côté : client et serveur · Attend : non

Signature
new GlobalKey<T>(key: string, parse: StateParse<T>)

Paramètres

Prop

Type

Méthodes

Prop

Type

Exemple

resource/client/index.ts
import { GlobalKey, stateBoolean } from "@aicore-fivem/lib/client";

const pvp = new GlobalKey("ai:pvp", stateBoolean);

pvp.onChange((_id, on) => SendNUIMessage({ action: "pvp", value: on === true }));

GlobalGroup

Plusieurs clés de GlobalState qui partagent un préfixe (ai:duty:police, ai:duty:ambulance), lues par la partie qui suit le préfixe. Tous les groupes d’une ressource partagent un seul gestionnaire.

Côté : client et serveur · Attend : non

Signature
new GlobalGroup<T>(prefix: string, parse: StateParse<T>)

Paramètres

Prop

Type

Méthodes

Prop

Type

Exemple

ai-core publie le nombre de joueurs en service par métier sous ai:duty:<job> :

resource/client/index.ts
import { GlobalGroup, stateNumber } from "@aicore-fivem/lib/client";

const onDuty = new GlobalGroup("ai:duty:", stateNumber);

function policeOnline(): number {
  return onDuty.get("police") ?? 0;
}

Créez chaque classe une seule fois par clé, en haut d’un fichier, jamais dans une fonction qui s’exécute souvent : chaque instance ajoute un gestionnaire de changement. GlobalState est envoyé à chaque joueur, n’y mettez donc que des valeurs dont chaque joueur a besoin.

Sur cette page