State bags
EntityState, PlayerState, GlobalKey, GlobalGroup и парсеры stateNumber, stateBoolean, stateString: чтение реплицируемых значений из памяти.
State bag это значение, которое FiveM копирует с сервера на клиенты, которым оно нужно: топливо транспорта (Entity(vehicle).state.fuel), лежит ли игрок без сознания (Player(id).state.dead), флаг на весь сервер (GlobalState["ai:pvp"]).
Прямое чтение bag медленное: каждое чтение создаёт прокси, вызывает несколько нативов и заново декодирует значение. Эти классы держат один обработчик изменений на ключ и копию каждого значения в памяти, поэтому чтение сводится к поиску в Map. Используйте их для любого bag, который читаете больше одного раза.
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";Этих классов нет в @aicore-fivem/lib/fivem: импортируйте их из точки входа клиента или сервера. State bags пишет только сервер (sv_stateBagStrictMode); клиент просит сервер через колбэк, и сервер задаёт значение. Серверные методы set описаны на странице Lib: сервер, State bags.
Lua: ни у одного из них нет Lua-аналога. Lua сам читает bags (Entity(vehicle).state.fuel, Player(id).state.dead, GlobalState['ai:pvp']) и слушает их через AddStateBagChangeHandler.
Ключи, которые используют ai-core и скрипты ai-*, перечислены на странице События и состояние.
Парсеры
Каждый класс принимает парсер: функцию, которая превращает сырое реплицированное значение в ваш тип или в undefined, если значение неверное. Тип: StateParse<T> = (value: unknown) => T | undefined. Очищенный bag (null) всегда читается как undefined; парсер его не видит.
stateNumber
Принимает конечное число; всё остальное (текст, NaN, Infinity) читается как undefined.
Сторона: Клиент и сервер · Ждёт: нет
stateNumber: StateParse<number>Пример
import { EntityState, stateNumber } from "@aicore-fivem/lib/client";
const fuel = new EntityState("fuel", stateNumber); // 0-100, задаёт скрипт топливаstateBoolean
Принимает true или false; всё остальное читается как undefined.
Сторона: Клиент и сервер · Ждёт: нет
stateBoolean: StateParse<boolean>Пример
import { PlayerState, stateBoolean } from "@aicore-fivem/lib/client";
const dead = new PlayerState("dead", stateBoolean); // true, пока игрок без сознания (ai-death)stateString
Принимает строку; всё остальное читается как undefined.
Сторона: Клиент и сервер · Ждёт: нет
stateString: StateParse<string>Пример
import { GlobalKey, stateString } from "@aicore-fivem/lib/client";
const inventoryScript = new GlobalKey("ai:inventory", stateString); // имя ресурса активного инвентаряДля значения-объекта напишите собственный парсер, который проверяет каждое поле и возвращает undefined для неверного значения:
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); // дальность pma-voice, которую записывает ai-voiceКлассы
EntityState
Один ключ state bags сущностей (fuel, ai:owned...) для каждой сетевой сущности. Значения хранятся по сетевому id; чтение принимает хэндл сущности.
Сторона: Клиент и сервер · Ждёт: нет
new EntityState<T>(key: string, parse: StateParse<T>)Параметры
Prop
Type
Методы
Prop
Type
Пример
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 });
});Память сама остаётся небольшой: сервер забывает сущность, когда её удаляют, а клиент каждые 30 секунд отбрасывает сущности, которые вышли из его зоны видимости. Сетевой id, заново занятый новой сущностью, читается заново.
PlayerState
Один ключ state bags игроков (dead, cuffed, proximity из pma-voice...) по серверному id.
Сторона: Клиент и сервер · Ждёт: нет
new PlayerState<T>(key: string, parse: StateParse<T>)Параметры
Prop
Type
Методы
Prop
Type
Пример
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 };
});На сервере игрок, который вышел, забывается во всех PlayerState вашего ресурса (один общий обработчик playerDropped).
GlobalKey
Один ключ GlobalState: значение, которое нужно каждому игроку (переключатель PvP, активный инвентарь). Читается один раз при старте, затем поддерживается в актуальном виде обработчиком изменений.
Сторона: Клиент и сервер · Ждёт: нет
new GlobalKey<T>(key: string, parse: StateParse<T>)Параметры
Prop
Type
Методы
Prop
Type
Пример
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
Много ключей GlobalState с общим префиксом (ai:duty:police, ai:duty:ambulance), которые читаются по части после префикса. Все группы ресурса делят один обработчик.
Сторона: Клиент и сервер · Ждёт: нет
new GlobalGroup<T>(prefix: string, parse: StateParse<T>)Параметры
Prop
Type
Методы
Prop
Type
Пример
ai-core публикует, сколько игроков на дежурстве в каждой работе, под ключом 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;
}Создавайте каждый класс один раз на ключ, в начале файла, и никогда внутри часто вызываемой функции: каждый экземпляр добавляет обработчик изменений. GlobalState отправляется каждому игроку, поэтому кладите туда только значения, которые нужны каждому игроку.
Планирование и жизненный цикл
every, onStop, caller, ByResource, Reasons, sleep и waitFor: периодическая работа, ожидание и данные, принадлежащие ресурсу, который их зарегистрировал.
Действия и ввод от клиента
fail, isActionOk, replyError и строгие функции чтения readInt, readFinite, readId, readText, readVec3, readVec4, oneArg, asArgs, isRecord.