AI Core
Referência da APILib: cliente

Falando com o servidor

Pergunte ao servidor e espere a resposta, execute ações no servidor com recusas traduzidas, escute eventos do servidor e responda callbacks do servidor.

Um script de cliente nunca confia em si mesmo: ele pergunta ao servidor, e o servidor decide. Estes helpers levam essas perguntas e respostas. As metades do servidor estão em Entrada do cliente (onClientCallback, onClientEvent, triggerClientCallback).

Você querUse
Executar algo que o servidor precisa permitir (comprar, alugar, abrir) e mostrar a recusacallAction
Pedir dados ao servidortriggerServerCallback
Ouvir algo que o servidor enviaonServerEvent
Responder uma pergunta que o servidor faz a este clienteonServerCallback

triggerServerCallback

Pergunta ao servidor e espera a resposta. O servidor responde com onClientCallback(name, ...). Use para ler dados que o servidor guarda (uma lista de preços, os próprios registros de um jogador). Para uma ação que pode ser recusada, use callAction: ela mostra a recusa para você.

Lado: Cliente · Espera: sim (Promise em TypeScript, pausa a thread em Lua)

Signature
triggerServerCallback(name: string, args?: unknown[], timeoutMs?: number): Promise<unknown>
Lua
local result, err = aiLib.triggerServerCallback(name, args, timeout) -- chame dentro de uma thread

Parâmetros

Prop

Type

Retorno

TypeScript: Promise<unknown>. Resolve com o que o handler do servidor retornou (null quando não retornou nada). O valor não é verificado: teste o tipo antes de usar. A promise rejeita quando o handler do servidor lançou um erro, o portão do servidor recusou a chamada (limite de taxa, um guard, argumentos inválidos, um lock) ou nenhuma resposta chegou dentro de timeoutMs.

Lua: dois valores. result é a resposta; err é nil em caso de sucesso, 'failed' quando o servidor recusou ou falhou, 'timeout' quando nenhuma resposta chegou a tempo. Em caso de erro, result é nil.

Exemplo

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

Em TypeScript sempre trate a rejeição (.catch(() => null) ou try): uma chamada recusada ou que estourou o tempo rejeita. O servidor limita cada callback a 20 chamadas por segundo por jogador por padrão, então nunca a chame em um loop ou a cada frame. O formato na rede é o mesmo nas duas linguagens: um cliente Lua pode chamar um servidor TypeScript e vice-versa.

callAction

Executa uma ação no servidor e mostra a recusa para você. O handler do servidor (onClientCallback) responde um ActionResult: { ok: true, ...data } quando funcionou, ou fail("LANG_KEY") quando recusou. Em uma recusa, um timeout ou uma recusa do portão, callAction procura a chave de erro no arquivo de idioma do seu recurso e a mostra como notificação de erro. Use para todo botão que muda algo: comprar, vender, alugar, consertar.

Lado: Cliente · Espera: sim (Promise em TypeScript, pausa a thread em Lua)

Signature
callAction(name: string, args: unknown[] | undefined, options: CallActionOptions): Promise<({ ok: true } & Record<string, unknown>) | null>
Lua
local reply = aiLib.callAction(name, args, options) -- table ou nil, chame dentro de uma thread

Parâmetros

Prop

Type

CallActionOptions

Prop

Type

Retorno

Promise<({ ok: true } & Record<string, unknown>) | null>: a resposta ok inteira (com os campos extras que o servidor adicionou) quando a ação funcionou; null quando foi recusada, falhou ou estourou o tempo (a notificação já foi mostrada, a menos que silent). Os campos extras não são verificados: teste o tipo de cada um que você lê. Lua retorna a tabela da resposta ou nil.

Exemplo

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; // a recusa já está na tela
  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 };
  },
);

A chave de erro é procurada no arquivo de idioma do recurso que chama callAction, então coloque toda chave que o servidor pode responder (NO_MONEY, NO_ROOM, UNKNOWN_ITEM) e a chave de fallback no locales/en.json desse recurso. Uma chave que falta ali é mostrada como a própria chave.

isActionOk

Diz se uma resposta é um ActionResult ok (ok é exatamente true). Use quando você mesmo chama triggerServerCallback e lê a resposta à mão.

Lado: Cliente e servidor (de /shared, reexportado por /client) · Espera: não

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

Sem equivalente em Lua: teste type(reply) == 'table' and reply.ok == true.

Parâmetros

Prop

Type

Retorno

boolean: true quando reply é um objeto (não um array) cujo ok é true. Em TypeScript também estreita reply para { ok: true } & Record<string, unknown>.

Exemplo

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

A chave de idioma de uma resposta recusada: o campo error dela, ou fallback quando a resposta não existe ou não tem texto de erro. Use com isActionOk quando você mostra o erro do seu jeito (em uma página NUI em vez de uma notificação).

Lado: Cliente e servidor (de /shared, reexportado por /client) · Espera: não

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

Sem equivalente em Lua.

Parâmetros

Prop

Type

Retorno

string | null: null quando a resposta é ok; o error da resposta quando é uma string não vazia; caso contrário fallback.

Exemplo

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

Escuta um net event que o servidor envia com emitNet (TriggerClientEvent em Lua). Chamadas que não vieram do servidor (o emit local de outro recurso) são descartadas, então um menu de trapaça não consegue falsificá-las. Todo cliente ai-* escuta o servidor assim, nunca com um onNet puro. Em TypeScript um handler que recebe argumentos precisa indicar um parser args: ele verifica os valores brutos, e null descarta a chamada.

Lado: Cliente · Espera: não

Signature
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(...) recebe os argumentos brutos

Parâmetros

Prop

Type

Retorno

void.

Exemplo

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

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

// Sem argumentos: a forma curta.
onServerEvent("myscript:reset", () => points.clear());

// Um argumento boolean: interprete-o, null descarta uma chamada inválida.
onServerEvent(
  "myscript:state",
  { args: ([active]) => (typeof active === "boolean" ? [active] : null) },
  (active) => {
    renting = active;
    points.refresh();
  },
);

O servidor envia o evento com emitNet("myscript:state", source, true) (TypeScript) ou TriggerClientEvent('myscript:state', source, true) (Lua). Handlers Lua recebem os argumentos brutos: verifique os tipos você mesmo.

fromServer

Se o net event sendo tratado agora veio do servidor. onServerEvent já usa isto; você só precisa dele em um handler onNet puro que não pode substituir.

Lado: Cliente · Espera: não

Signature
fromServer(): boolean

Sem equivalente em Lua: em um handler de net event em Lua, source fica vazio para emits locais.

Retorno

boolean: true quando o evento atual traz o source numérico do servidor; false para um emit local de qualquer recurso.

Exemplo

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

onNet("legacy:event", (value: unknown) => {
  if (!fromServer()) return; // um emit local, não o servidor
  console.log("server said", value);
});

Leia antes do primeiro await do handler: depois de um await o evento atual já se foi e a resposta fica errada.

onServerCallback

Responde uma pergunta que o servidor faz a este cliente com triggerClientCallback(name, source, ...). O valor que o seu handler retorna (ou para o qual a promise dele resolve) é enviado de volta. Um erro lançado responde uma falha, e a promise do servidor rejeita.

Lado: Cliente · Espera: não (o handler pode ser async)

Signature
onServerCallback(name: string, handler: (...args: unknown[]) => unknown): void
Lua
aiLib.onServerCallback(name, handler) -- o valor de retorno do handler é a resposta

Parâmetros

Prop

Type

Retorno

void.

Exemplo

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

// O servidor pergunta a que distância de um ponto este jogador está.
onServerCallback("myscript:distance", (x, y, z) => {
  if (typeof x !== "number" || typeof y !== "number" || typeof z !== "number") return null;
  return player.distanceTo({ x, y, z });
});

Em Lua um handler que retorna false responde nil (o servidor recebe null): retorne true / nil, um número ou uma tabela quando o servidor precisa distinguir false de "sem resposta". A resposta vem do computador do jogador, então um trapaceiro pode alterá-la. Use respostas do cliente para coisas que só o cliente sabe (o que está na tela, uma entidade local), e nunca como prova para dinheiro ou itens.

Nesta página