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.
import { EntityState, PlayerState, GlobalKey, GlobalGroup, stateNumber, stateBoolean, stateString } from "@aicore-fivem/lib/client";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
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 carburantstateBoolean
Accepte true ou false ; toute autre chose est lue comme undefined.
Côté : client et serveur · Attend : non
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
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 actifPour 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-voiceClasses
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
new EntityState<T>(key: string, parse: StateParse<T>)Paramètres
Prop
Type
Méthodes
Prop
Type
Exemple
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
new PlayerState<T>(key: string, parse: StateParse<T>)Paramètres
Prop
Type
Méthodes
Prop
Type
Exemple
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
new GlobalKey<T>(key: string, parse: StateParse<T>)Paramètres
Prop
Type
Méthodes
Prop
Type
Exemple
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
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> :
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.
Planification et cycle de vie
every, onStop, caller, ByResource, Reasons, sleep et waitFor : travail périodique, attente et données qui appartiennent à la ressource qui les a enregistrées.
Actions et entrées du client
fail, isActionOk, replyError et les lecteurs stricts readInt, readFinite, readId, readText, readVec3, readVec4, oneArg, asArgs, isRecord.