Планирование и жизненный цикл
every, onStop, caller, ByResource, Reasons, sleep и waitFor: периодическая работа, ожидание и данные, принадлежащие ресурсу, который их зарегистрировал.
import { every, onStop, caller, ByResource, Reasons } from "@aicore-fivem/lib/fivem";
import { sleep, waitFor } from "@aicore-fivem/lib/shared";Периодическая работа
every
Выполняет функцию каждые ms миллисекунд (0 = каждый кадр) из единого планировщика вашего ресурса. Используйте вместо setInterval, setTick или цикла CreateThread с Wait: все задачи ресурса делят один тик, и этот тик существует, только пока включена хотя бы одна задача, поэтому простаивающий ресурс ничего не стоит в resmon.
Сторона: Клиент и сервер · Ждёт: нет
every(ms: number, fn: () => unknown, options?: EveryOptions): Joblocal job = aiLib.every(ms, fn, options) -- job:set(active), job:stop(), job.runningПараметры
Prop
Type
EveryOptions
Prop
Type
Возвращает
Job: дескриптор для включения и выключения задачи.
Prop
Type
Пример
Покадровая задача, которая работает, только пока ей есть что делать:
import { every, cache, onCache } from "@aicore-fivem/lib/client";
// Скрывает прицел каждый кадр, но только пока в руках оружие.
const hideCrosshair = every(0, () => HideHudComponentThisFrame(14), {
active: cache.weapon !== false,
name: "hide crosshair",
});
onCache("weapon", (weapon) => hideCrosshair.set(weapon !== false));Задача никогда не должна ждать: никакого Wait в Lua, никакой длинной цепочки await в TypeScript, которая должна завершиться до следующего запуска. В Lua Wait внутри задачи задерживает все остальные задачи ресурса; для этого запустите поток из задачи. Сбойная задача логируется не чаще раза в 10 секунд (ошибки в промежутке подсчитываются в следующую строку).
Ожидание
sleep
Ждёт ms миллисекунд.
Сторона: Клиент, сервер и NUI · Ждёт: да (Promise)
sleep(ms: number): Promise<void>Lua: аналога нет; используйте Wait(ms) внутри потока.
Параметры
Prop
Type
Возвращает
Promise<void>, который выполняется через ms миллисекунд.
Пример
import { sleep } from "@aicore-fivem/lib/shared";
import { fadeOut, fadeIn } from "@aicore-fivem/lib/client";
async function blink(): Promise<void> {
await fadeOut(300);
await sleep(1000);
await fadeIn(300);
}waitFor
Проверяет условие, пока оно не даст значение, затем возвращает это значение. Используйте, чтобы дождаться того, что игра делает в своё время: появления сущности, загрузки модели, прихода значения.
Сторона: Клиент, сервер и NUI · Ждёт: да (Promise в TypeScript, в Lua приостанавливает поток)
waitFor<T>(check: () => T | undefined, timeoutMs?: number | false, intervalMs?: number): Promise<T>local value = aiLib.waitFor(check, timeout, interval) -- значение или nil после тайм-аута; вызывайте внутри потокаПараметры
Prop
Type
Возвращает
TypeScript: Promise<T>, который выполняется с первым значением, которое вернула check и которое не было undefined. После тайм-аута он отклоняется с Error("waitFor timed out after <ms> ms").
Lua: значение или nil после тайм-аута (исключение не выбрасывается).
Пример
import { waitFor } from "@aicore-fivem/lib/shared";
async function vehicleFromNet(netId: number): Promise<number | null> {
try {
return await waitFor(() => {
const vehicle = NetworkGetEntityFromNetworkId(netId);
return DoesEntityExist(vehicle) ? vehicle : undefined;
}, 5000);
} catch {
return null; // не подгрузилась за 5 секунд
}
}В TypeScript всегда обрабатывайте отклонение (try / catch или .catch()) либо передавайте false как тайм-аут. В Lua waitFor работает только внутри потока.
Жизненный цикл ресурса
onStop
Выполняет функцию, когда ваш ресурс останавливается (на той стороне, где выполняется код). Используйте, чтобы удалить то, что создали нативы и что больше ничто не удалит: созданные вами сущности, камеры, фокус NUI.
Сторона: Клиент и сервер · Ждёт: нет
onStop(fn: () => void): voidLua: аналога нет; используйте AddEventHandler('onResourceStop', function(name) if name == GetCurrentResourceName() then ... end end).
Параметры
Prop
Type
Возвращает
void.
Пример
import { onStop } from "@aicore-fivem/lib/server";
const spawned = new Set<number>();
onStop(() => {
for (const entity of spawned) if (DoesEntityExist(entity)) DeleteEntity(entity);
});Все onStop, ByResource и Reasons ресурса делят один обработчик остановки, поэтому добавление многих ничего не стоит.
caller
Имя ресурса, который вызвал выполняемый сейчас экспорт. Внутри вашего собственного кода (не в вызове экспорта) это имя вашего ресурса.
Сторона: Клиент и сервер · Ждёт: нет
caller(): stringLua: аналога нет; используйте GetInvokingResource() or GetCurrentResourceName().
Возвращает
string: имя вызывающего ресурса или имя этого ресурса, если никакой другой ресурс его не вызывает.
Пример
import { registerExport, caller } from "@aicore-fivem/lib/server";
registerExport("Ping", () => {
console.log(`${caller()} asked for a ping`);
return true;
});ByResource
Map, записи которого принадлежат ресурсу, который их задал. Когда этот ресурс останавливается, его записи сразу удаляются (и для каждой выполняется onDrop). Используйте для того, что другие ресурсы регистрируют через ваши экспорты: хуки, подсказки, предметы, пункты меню.
Сторона: Клиент и сервер · Ждёт: нет
new ByResource<K, V>(options?: { onDrop?: (resource: string, key: K, value: V) => void })Lua: аналога нет.
Параметры
Prop
Type
Методы
Prop
Type
Пример
import { ByResource, registerExport } from "@aicore-fivem/lib/server";
import { readText } from "@aicore-fivem/lib/shared";
type Hook = (source: number, text: string) => boolean;
const hooks = new ByResource<string, Hook>({
onDrop: (resource, id) => console.log(`${resource} stopped: hook ${id} removed`),
});
// Другой ресурс: exports["my-chat"].AddHook("swear-filter", (source, text) => ...)
registerExport("AddHook", (id: unknown, hook: unknown) => {
const name = readText(id, 64);
if (name === null || typeof hook !== "function") return false;
hooks.set(name, hook as Hook); // owner = ресурс, вызывающий AddHook
return true;
});
export function allowed(source: number, text: string): boolean {
for (const hook of hooks.values()) if (!hook(source, text)) return false;
return true;
}Reasons
Переключатель, который включён, пока задана хотя бы одна причина. Каждый ресурс задаёт свои именованные причины, а причины останавливающегося ресурса сбрасываются. Используйте для «скрыть HUD», «приостановить синхронизацию», «заблокировать меню»: несколько скриптов могут попросить одновременно, а состояние возвращается обратно, только когда отпустит последний.
Сторона: Клиент и сервер · Ждёт: нет
new Reasons()Lua: аналога нет.
Методы
Prop
Type
Пример
import { Reasons, registerExport } from "@aicore-fivem/lib/client";
import { readText } from "@aicore-fivem/lib/shared";
const hidden = new Reasons();
hidden.onChange((any) => SendNUIMessage({ action: "visible", value: !any }));
// Другие ресурсы: exports["my-hud"].Hide("cutscene") и exports["my-hud"].Show("cutscene")
registerExport("Hide", (reason: unknown) => {
const name = readText(reason, 64);
if (name !== null) hidden.set(name, true);
});
registerExport("Show", (reason: unknown) => {
const name = readText(reason, 64);
if (name !== null) hidden.set(name, false);
});Экспорты между ресурсами
registerExport, resourceExport, optionalExport, callExport, provideExport и isResourceStarted: как предлагать и вызывать экспорты из TypeScript.
State bags
EntityState, PlayerState, GlobalKey, GlobalGroup и парсеры stateNumber, stateBoolean, stateString: чтение реплицируемых значений из памяти.