AI Core
Dokumentacja APILib: shared i fivem

Harmonogram i cykl życia

every, onStop, caller, ByResource, Reasons, sleep i waitFor: praca okresowa, czekanie i dane należące do zasobu, który je zarejestrował.

TypeScript
import { every, onStop, caller, ByResource, Reasons } from "@aicore-fivem/lib/fivem";
import { sleep, waitFor } from "@aicore-fivem/lib/shared";

Praca okresowa

every

Uruchamia funkcję co ms milisekund (0 = co klatkę) z jedynego harmonogramu twojego zasobu. Używaj jej zamiast setInterval, setTick albo pętli CreateThread z Wait: wszystkie zadania zasobu dzielą jeden tick, a ten tick istnieje tylko wtedy, gdy co najmniej jedno zadanie jest włączone, więc bezczynny zasób nic nie kosztuje w resmonie.

Strona: Klient i serwer · Czeka: nie

Sygnatura
every(ms: number, fn: () => unknown, options?: EveryOptions): Job
Lua
local job = aiLib.every(ms, fn, options) -- job:set(active), job:stop(), job.running

Parametry

Prop

Type

EveryOptions

Prop

Type

Zwracana wartość

Job: uchwyt do włączania i wyłączania zadania.

Prop

Type

Przykład

Zadanie na każdą klatkę, które działa tylko wtedy, gdy ma coś do zrobienia:

import { every, cache, onCache } from "@aicore-fivem/lib/client";

// Ukrywa celownik co klatkę, ale tylko gdy broń jest w ręku.
const hideCrosshair = every(0, () => HideHudComponentThisFrame(14), {
  active: cache.weapon !== false,
  name: "hide crosshair",
});

onCache("weapon", (weapon) => hideCrosshair.set(weapon !== false));

Zadanie nigdy nie może czekać: żadnego Wait w Lua, żadnego długiego łańcucha await w TypeScript, który ma się skończyć przed następnym uruchomieniem. W Lua Wait wewnątrz zadania wstrzymuje każde inne zadanie zasobu; do tego uruchom wątek z zadania. Błędne zadanie jest logowane najwyżej raz na 10 sekund (błędy pomiędzy są doliczane do następnej linii).

Czekanie

sleep

Czeka ms milisekund.

Strona: Klient, serwer i NUI · Czeka: tak (Promise)

Sygnatura
sleep(ms: number): Promise<void>

Lua: brak odpowiednika; użyj Wait(ms) wewnątrz wątku.

Parametry

Prop

Type

Zwracana wartość

Promise<void>, który się rozwiązuje po ms milisekundach.

Przykład

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

Sprawdza warunek, aż da on wartość, potem zwraca tę wartość. Używaj jej, żeby czekać na coś, co gra robi we własnym tempie: aż encja zacznie istnieć, model się wczyta, wartość przyjdzie.

Strona: Klient, serwer i NUI · Czeka: tak (Promise w TypeScript, w Lua wstrzymuje wątek)

Sygnatura
waitFor<T>(check: () => T | undefined, timeoutMs?: number | false, intervalMs?: number): Promise<T>
Lua
local value = aiLib.waitFor(check, timeout, interval) -- wartość albo nil po upływie limitu czasu; wywołuj wewnątrz wątku

Parametry

Prop

Type

Zwracana wartość

TypeScript: Promise<T>, który się rozwiązuje pierwszą wartością zwróconą przez check, która nie była undefined. Po upływie limitu czasu odrzuca z Error("waitFor timed out after <ms> ms").

Lua: wartość albo nil po upływie limitu czasu (nie rzuca wyjątku).

Przykład

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; // nie wczytał się w ciągu 5 sekund
  }
}

W TypeScript zawsze obsłuż odrzucenie (try / catch albo .catch()) albo podaj false jako limit czasu. W Lua waitFor działa tylko wewnątrz wątku.

Cykl życia zasobu

onStop

Uruchamia funkcję, gdy twój zasób się zatrzymuje (po stronie, na której działa kod). Używaj jej do usuwania tego, co stworzyły natywki, a czego nic innego nie usuwa: zespawnowanych encji, kamer, fokusu NUI.

Strona: Klient i serwer · Czeka: nie

Sygnatura
onStop(fn: () => void): void

Lua: brak odpowiednika; użyj AddEventHandler('onResourceStop', function(name) if name == GetCurrentResourceName() then ... end end).

Parametry

Prop

Type

Zwracana wartość

void.

Przykład

resource/server/index.ts
import { onStop } from "@aicore-fivem/lib/server";

const spawned = new Set<number>();

onStop(() => {
  for (const entity of spawned) if (DoesEntityExist(entity)) DeleteEntity(entity);
});

Wszystkie onStop, ByResource i Reasons zasobu dzielą jeden listener zatrzymania, więc dodanie wielu nic dodatkowo nie kosztuje.

caller

Nazwa zasobu, który wywołał aktualnie działający eksport. W twoim własnym kodzie (nie w wywołaniu eksportu) to nazwa twojego zasobu.

Strona: Klient i serwer · Czeka: nie

Sygnatura
caller(): string

Lua: brak odpowiednika; użyj GetInvokingResource() or GetCurrentResourceName().

Zwracana wartość

string: nazwa wywołującego zasobu albo nazwa tego zasobu, gdy żaden inny zasób nie wywołuje.

Przykład

resource/server/index.ts
import { registerExport, caller } from "@aicore-fivem/lib/server";

registerExport("Ping", () => {
  console.log(`${caller()} asked for a ping`);
  return true;
});

ByResource

Mapa, której wpisy należą do zasobu, który je ustawił. Gdy ten zasób się zatrzymuje, jego wpisy są od razu usuwane (a onDrop działa dla każdego z nich). Używaj jej do rzeczy, które inne zasoby rejestrują przez twoje eksporty: hooki, prompty, przedmioty, pozycje menu.

Strona: Klient i serwer · Czeka: nie

Sygnatura
new ByResource<K, V>(options?: { onDrop?: (resource: string, key: K, value: V) => void })

Lua: brak odpowiednika.

Parametry

Prop

Type

Metody

Prop

Type

Przykład

resource/server/index.ts
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`),
});

// Inny zasób: 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 = zasób, który wywołuje 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

Przełącznik, który jest włączony, dopóki ustawiony jest co najmniej jeden powód. Każdy zasób ustawia własne nazwane powody, a powody zatrzymywanego zasobu są czyszczone. Używaj go do "ukryj HUD", "wstrzymaj synchronizację", "zablokuj menu": kilka skryptów może prosić naraz, a stan wraca dopiero wtedy, gdy ostatni z nich odpuści.

Strona: Klient i serwer · Czeka: nie

Sygnatura
new Reasons()

Lua: brak odpowiednika.

Metody

Prop

Type

Przykład

resource/client/index.ts
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 }));

// Inne zasoby: exports["my-hud"].Hide("cutscene") i 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);
});

Na tej stronie