AI Core
Dokumentacja APILib: klient

Rozmowa z serwerem

Pytanie serwera i czekanie na odpowiedź, akcje serwera z przetłumaczonymi odmowami, nasłuchiwanie zdarzeń serwera i odpowiadanie na callbacki serwera.

Skrypt klienta nigdy nie ufa sam sobie: pyta serwer, a serwer decyduje. Te funkcje przenoszą pytania i odpowiedzi. Połowy serwerowe są na stronie Wejście z klienta (onClientCallback, onClientEvent, triggerClientCallback).

ChceszUżyj
Wykonać coś, na co serwer musi pozwolić (kupić, wynająć, otworzyć), i pokazać jego odmowęcallAction
Poprosić serwer o danetriggerServerCallback
Usłyszeć coś, co wysyła serweronServerEvent
Odpowiedzieć na pytanie, które serwer zadaje temu klientowionServerCallback

triggerServerCallback

Pyta serwer i czeka na jego odpowiedź. Serwer odpowiada przez onClientCallback(name, ...). Używaj go do odczytu danych, które trzyma serwer (cennik, własne rekordy gracza). Dla akcji, która może zostać odrzucona, użyj callAction: pokazuje odmowę za ciebie.

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

Sygnatura
triggerServerCallback(name: string, args?: unknown[], timeoutMs?: number): Promise<unknown>
Lua
local result, err = aiLib.triggerServerCallback(name, args, timeout) -- wywołuj wewnątrz wątku

Parametry

Prop

Type

Zwracana wartość

TypeScript: Promise<unknown>. Rozwiązuje się tym, co zwrócił handler serwera (null, gdy nic nie zwrócił). Wartość nie jest sprawdzana: sprawdź jej typ, zanim jej użyjesz. Promise się odrzuca, gdy handler serwera rzucił błąd, bramka serwera odrzuciła wywołanie (limit częstotliwości, strażnik, złe argumenty, blokada) albo żadna odpowiedź nie przyszła w ciągu timeoutMs.

Lua: dwie wartości. result to odpowiedź; err to nil przy sukcesie, 'failed', gdy serwer odmówił albo zawiódł, 'timeout', gdy odpowiedź nie przyszła na czas. Przy błędzie result to nil.

Przykład

resource/client/index.ts
import { createLocale, notify, triggerServerCallback } from "@aicore-fivem/lib/client";

const Locale = createLocale<typeof import("../../locales/en.json")>();

RegisterCommand(
  "mycash",
  async () => {
    const cash = await triggerServerCallback("myscript:cash").catch(() => null);
    if (typeof cash !== "number") return notify(Locale("ERROR"), "error");
    notify(Locale("YOUR_CASH", cash), "info");
  },
  false,
);
resource/server/index.ts
import { onClientCallback, resourceExport } from "@aicore-fivem/lib/server";

const core = resourceExport("ai-core");

onClientCallback("myscript:cash", (source) => core.GetMoney(source, "cash"));

W TypeScript zawsze obsłuż odrzucenie (.catch(() => null) albo try): odrzucone albo przeterminowane wywołanie się odrzuca. Serwer domyślnie ogranicza każdy callback do 20 wywołań na sekundę na gracza, więc nigdy nie wywołuj go w pętli ani co klatkę. Format sieciowy jest taki sam w obu językach: klient Lua może wywołać serwer TypeScript i odwrotnie.

callAction

Wykonuje akcję na serwerze i pokazuje odmowę za ciebie. Handler serwera (onClientCallback) odpowiada ActionResult: { ok: true, ...data }, gdy się udało, albo fail("LANG_KEY"), gdy odmówił. Przy odmowie, przekroczeniu czasu albo odmowie bramki callAction szuka klucza błędu w pliku językowym twojego zasobu i pokazuje go jako powiadomienie o błędzie. Używaj go dla każdego przycisku, który coś zmienia: kup, sprzedaj, wynajmij, napraw.

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

Sygnatura
callAction(name: string, args: unknown[] | undefined, options: CallActionOptions): Promise<({ ok: true } & Record<string, unknown>) | null>
Lua
local reply = aiLib.callAction(name, args, options) -- tabela albo nil, wywołuj wewnątrz wątku

Parametry

Prop

Type

CallActionOptions

Prop

Type

Zwracana wartość

Promise<({ ok: true } & Record<string, unknown>) | null>: cała odpowiedź ok (z dodatkowymi polami dodanymi przez serwer), gdy akcja się udała; null, gdy została odrzucona, zawiodła albo przekroczyła czas (powiadomienie jest już pokazane, chyba że silent). Dodatkowe pola nie są sprawdzane: sprawdź typ każdego, które czytasz. Lua zwraca tabelę odpowiedzi albo nil.

Przykład

resource/client/index.ts
import { callAction, createLocale, notify } from "@aicore-fivem/lib/client";

const Locale = createLocale<typeof import("../../locales/en.json")>();

export async function buy(itemId: string): Promise<void> {
  const reply = await callAction("myshop:buy", [itemId], { fallback: "BUY_FAILED" });
  if (!reply) return; // odmowa jest już na ekranie
  const paid = typeof reply.paid === "number" ? reply.paid : 0;
  notify(Locale("BOUGHT", paid), "success");
}
resource/server/index.ts
import { inventory, onClientCallback, resourceExport } from "@aicore-fivem/lib/server";
import { fail, oneArg, readText } from "@aicore-fivem/lib/shared";

const core = resourceExport("ai-core");
const PRICES: Record<string, number> = { water: 5, bread: 8 };

onClientCallback(
  "myshop:buy",
  { args: oneArg((v) => readText(v, 32)) },
  (source, item) => {
    const price = PRICES[item];
    if (price === undefined) return fail("UNKNOWN_ITEM");
    if (!inventory.canCarryItem(source, item, 1)) return fail("NO_ROOM");
    if (!core.RemoveMoney(source, "cash", price, "shop")) return fail("NO_MONEY");
    inventory.addItem(source, item, 1);
    return { ok: true, paid: price };
  },
);

Klucz błędu jest szukany w pliku językowym zasobu, który wywołuje callAction, więc umieść każdy klucz, którym serwer może odpowiedzieć (NO_MONEY, NO_ROOM, UNKNOWN_ITEM), i klucz fallback w locales/en.json tego zasobu. Klucz, którego tam brakuje, jest pokazywany jako sam klucz.

isActionOk

Mówi, czy odpowiedź to udany ActionResult (ok jest dokładnie true). Używaj jej, gdy sam wywołujesz triggerServerCallback i czytasz odpowiedź ręcznie.

Strona: Klient i serwer (z /shared, ponownie eksportowane przez /client) · Czeka: nie

Sygnatura
isActionOk(reply: unknown): reply is { ok: true } & Record<string, unknown>

Brak odpowiednika w Lua: sprawdź type(reply) == 'table' and reply.ok == true.

Parametry

Prop

Type

Zwracana wartość

boolean: true, gdy reply jest obiektem (nie tablicą), którego ok to true. W TypeScript zawęża też reply do { ok: true } & Record<string, unknown>.

Przykład

import { isActionOk, replyError, triggerServerCallback } from "@aicore-fivem/lib/client";

export async function buyWater(): Promise<void> {
  const reply = await triggerServerCallback("myshop:buy", ["water"]).catch(() => null);
  if (isActionOk(reply)) console.log("bought");
  else console.log("refused with", replyError(reply, "BUY_FAILED"));
}

replyError

Klucz tłumaczenia odrzuconej odpowiedzi: jej pole error albo fallback, gdy odpowiedzi brak albo nie ma tekstu błędu. Używaj jej z isActionOk, gdy pokazujesz błąd po swojemu (na stronie NUI zamiast powiadomienia).

Strona: Klient i serwer (z /shared, ponownie eksportowane przez /client) · Czeka: nie

Sygnatura
replyError(reply: unknown, fallback: string): string | null

Brak odpowiednika w Lua.

Parametry

Prop

Type

Zwracana wartość

string | null: null, gdy odpowiedź jest ok; error odpowiedzi, gdy jest niepustym stringiem; w przeciwnym razie fallback.

Przykład

import { replyError, triggerServerCallback } from "@aicore-fivem/lib/client";

export async function buyFromPage(item: string): Promise<void> {
  const reply = await triggerServerCallback("myshop:buy", [item]).catch(() => null);
  const error = replyError(reply, "BUY_FAILED");
  if (error) SendNUIMessage({ action: "error", data: error });
}

onServerEvent

Nasłuchuje zdarzenia sieciowego, które serwer wysyła przez emitNet (TriggerClientEvent w Lua). Wywołania, które nie przyszły z serwera (lokalne emit innego zasobu), są porzucane, więc menu cheatów nie może ich podrobić. Każdy klient ai-* słucha serwera w ten sposób, nigdy przez gołe onNet. W TypeScript handler, który przyjmuje argumenty, musi podać parser args: sprawdza on surowe wartości, a null porzuca wywołanie.

Strona: Klient · Czeka: nie

Sygnatura
onServerEvent(name: string, handler: () => unknown): void
onServerEvent<A extends readonly unknown[]>(name: string, options: { args: (raw: unknown[]) => A | null }, handler: (...args: A) => unknown): void
Lua
aiLib.onServerEvent(name, handler) -- handler(...) dostaje surowe argumenty

Parametry

Prop

Type

Zwracana wartość

void.

Przykład

resource/client/index.ts
import { onServerEvent, Points } from "@aicore-fivem/lib/client";

let renting = false;
const points = new Points();

// Bez argumentów: krótka forma.
onServerEvent("myscript:reset", () => points.clear());

// Jeden argument boolean: sparsuj go, null porzuca złe wywołanie.
onServerEvent(
  "myscript:state",
  { args: ([active]) => (typeof active === "boolean" ? [active] : null) },
  (active) => {
    renting = active;
    points.refresh();
  },
);

Serwer wysyła zdarzenie przez emitNet("myscript:state", source, true) (TypeScript) albo TriggerClientEvent('myscript:state', source, true) (Lua). Handlery Lua dostają surowe argumenty: sprawdź ich typy sam.

fromServer

Czy obsługiwane w tej chwili zdarzenie sieciowe przyszło z serwera. onServerEvent już jej używa; potrzebujesz jej tylko w gołym handlerze onNet, którego nie możesz zastąpić.

Strona: Klient · Czeka: nie

Sygnatura
fromServer(): boolean

Brak odpowiednika w Lua: w handlerze zdarzenia sieciowego Lua source jest puste dla lokalnych emitów.

Zwracana wartość

boolean: true, gdy bieżące zdarzenie niesie liczbowe source serwera; false dla lokalnego emitu dowolnego zasobu.

Przykład

import { fromServer } from "@aicore-fivem/lib/client";

onNet("legacy:event", (value: unknown) => {
  if (!fromServer()) return; // lokalny emit, nie serwer
  console.log("server said", value);
});

Odczytaj ją przed pierwszym await handlera: po await bieżącego zdarzenia już nie ma i odpowiedź jest błędna.

onServerCallback

Odpowiada na pytanie, które serwer zadaje temu klientowi przez triggerClientCallback(name, source, ...). Wartość, którą zwraca twój handler (albo którą rozwiązuje się jego promise), jest odsyłana. Rzucony błąd odpowiada niepowodzeniem, a promise serwera się odrzuca.

Strona: Klient · Czeka: nie (handler może być async)

Sygnatura
onServerCallback(name: string, handler: (...args: unknown[]) => unknown): void
Lua
aiLib.onServerCallback(name, handler) -- wartość zwrócona przez handler jest odpowiedzią

Parametry

Prop

Type

Zwracana wartość

void.

Przykład

resource/client/index.ts
import { onServerCallback, player } from "@aicore-fivem/lib/client";

// Serwer pyta, jak daleko od punktu stoi ten gracz.
onServerCallback("myscript:distance", (x, y, z) => {
  if (typeof x !== "number" || typeof y !== "number" || typeof z !== "number") return null;
  return player.distanceTo({ x, y, z });
});

W Lua handler, który zwraca false, odpowiada nil (serwer dostaje null): zwracaj true / nil, liczbę albo tabelę, gdy serwer musi odróżnić false od "braku odpowiedzi". Odpowiedź pochodzi z komputera gracza, więc oszust może ją zmienić. Używaj odpowiedzi klienta do rzeczy, które zna tylko klient (co jest na ekranie, lokalna encja), i nigdy jako dowodu przy pieniądzach albo przedmiotach.

Na tej stronie