AI Core
API-ReferenzLib: Shared und fivem

State Bags

EntityState, PlayerState, GlobalKey, GlobalGroup und die Parser stateNumber, stateBoolean, stateString: replizierte Werte aus dem Speicher lesen.

Ein State Bag ist ein Wert, den FiveM vom Server zu den Clients kopiert, die ihn brauchen: der Sprit eines Fahrzeugs (Entity(vehicle).state.fuel), ob ein Spieler am Boden ist (Player(id).state.dead), ein serverweites Flag (GlobalState["ai:pvp"]).

Einen Bag direkt zu lesen ist langsam: Jeder Lesezugriff baut Proxys, ruft mehrere Natives auf und dekodiert den Wert erneut. Diese Klassen halten einen Change-Handler pro Schlüssel und eine Kopie jedes Werts im Speicher, daher ist ein Lesezugriff ein Map-Nachschlagen. Nutze sie für jeden Bag, den du mehr als einmal liest.

Client file: read-only versions (get, onChange)
import { EntityState, PlayerState, GlobalKey, GlobalGroup, stateNumber, stateBoolean, stateString } from "@aicore-fivem/lib/client";
Server file: the same classes plus set(...)
import { EntityState, PlayerState, GlobalKey, GlobalGroup, stateNumber, stateBoolean, stateString } from "@aicore-fivem/lib/server";

Die Klassen sind nicht auf @aicore-fivem/lib/fivem: Importiere sie aus dem Client- oder Server-Einstieg. Nur der Server schreibt State Bags (sv_stateBagStrictMode); ein Client fragt den Server über einen Callback, und der Server setzt den Wert. Die set-Methoden des Servers stehen unter Lib: Server, State Bags.

Lua: Keine davon hat einen Lua-Zwilling. Lua liest Bags selbst (Entity(vehicle).state.fuel, Player(id).state.dead, GlobalState['ai:pvp']) und hört mit AddStateBagChangeHandler zu.

Schlüssel, die ai-core und die ai-*-Skripte nutzen, sind unter Events und State aufgelistet.

Parser

Jede Klasse nimmt einen Parser: eine Funktion, die den rohen replizierten Wert in deinen Typ umwandelt, oder undefined, wenn er kein gültiger Wert ist. Der Typ ist StateParse<T> = (value: unknown) => T | undefined. Ein geleerter Bag (null) wird immer als undefined gelesen; der Parser sieht ihn nie.

stateNumber

Akzeptiert eine endliche Zahl; alles andere (Text, NaN, Infinity) wird als undefined gelesen.

Seite: Client und Server · Wartet: nein

Signatur
stateNumber: StateParse<number>

Beispiel

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

const fuel = new EntityState("fuel", stateNumber); // 0-100, vom Sprit-Skript gesetzt

stateBoolean

Akzeptiert true oder false; alles andere wird als undefined gelesen.

Seite: Client und Server · Wartet: nein

Signatur
stateBoolean: StateParse<boolean>

Beispiel

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

const dead = new PlayerState("dead", stateBoolean); // true, solange bewusstlos (ai-death)

stateString

Akzeptiert einen String; alles andere wird als undefined gelesen.

Seite: Client und Server · Wartet: nein

Signatur
stateString: StateParse<string>

Beispiel

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

const inventoryScript = new GlobalKey("ai:inventory", stateString); // der Ressourcenname des aktiven Inventars

Für einen Objektwert schreibe deinen eigenen Parser, der jedes Feld prüft und bei einem fehlerhaften Wert undefined zurückgibt:

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); // die Reichweite von pma-voice, von ai-voice geschrieben

Klassen

EntityState

Ein Schlüssel der Entity-State-Bags (fuel, ai:owned...), für jede vernetzte Entity. Werte werden nach Netzwerk-ID gehalten; ein Lesezugriff nimmt das Entity-Handle.

Seite: Client und Server · Wartet: nein

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

Parameter

Prop

Type

Methoden

Prop

Type

Beispiel

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 });
});

Der Speicher bleibt von selbst klein: Der Server vergisst eine Entity, wenn sie gelöscht wird, und ein Client verwirft alle 30 Sekunden Entities, die seine Reichweite verlassen haben. Eine Netzwerk-ID, die von einer neuen Entity wiederverwendet wird, wird neu gelesen.

PlayerState

Ein Schlüssel der Spieler-State-Bags (dead, cuffed, proximity von pma-voice...), nach Server-ID.

Seite: Client und Server · Wartet: nein

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

Parameter

Prop

Type

Methoden

Prop

Type

Beispiel

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 };
});

Auf dem Server wird ein Spieler, der geht, in jedem PlayerState deiner Ressource vergessen (ein gemeinsamer playerDropped-Listener).

GlobalKey

Ein GlobalState-Schlüssel: ein Wert, den jeder Spieler braucht (der PvP-Schalter, das aktive Inventar). Einmal beim Start gelesen, danach von seinem Change-Handler aktuell gehalten.

Seite: Client und Server · Wartet: nein

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

Parameter

Prop

Type

Methoden

Prop

Type

Beispiel

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

Viele GlobalState-Schlüssel mit gemeinsamem Präfix (ai:duty:police, ai:duty:ambulance), gelesen nach dem Teil nach dem Präfix. Alle Gruppen einer Ressource teilen sich einen Handler.

Seite: Client und Server · Wartet: nein

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

Parameter

Prop

Type

Methoden

Prop

Type

Beispiel

ai-core veröffentlicht unter ai:duty:<job>, wie viele Spieler pro Job im Dienst sind:

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;
}

Erstelle jede Klasse einmal pro Schlüssel, am Anfang einer Datei, nie in einer Funktion, die oft läuft: Jede Instanz fügt einen Change-Handler hinzu. GlobalState wird an jeden Spieler gesendet, lege dort also nur Werte ab, die jeder Spieler braucht.

Auf dieser Seite