AI Core
Referencia de la APILib: cliente

Hablar con el servidor

Preguntar al servidor y esperar su respuesta, ejecutar acciones del servidor con rechazos traducidos, escuchar eventos del servidor y responder a callbacks del servidor.

Un script de cliente nunca confía en sí mismo: pregunta al servidor, y el servidor decide. Estas utilidades llevan esas preguntas y respuestas. Las mitades de servidor están en Entrada del cliente (onClientCallback, onClientEvent, triggerClientCallback).

QuieresUsa
Ejecutar algo que el servidor debe permitir (comprar, alquilar, abrir) y mostrar su rechazocallAction
Pedir datos al servidortriggerServerCallback
Escuchar algo que envía el servidoronServerEvent
Responder a una pregunta que el servidor hace a este clienteonServerCallback

triggerServerCallback

Pregunta al servidor y espera su respuesta. El servidor responde con onClientCallback(name, ...). Úsalo para leer datos que guarda el servidor (una lista de precios, los registros propios de un jugador). Para una acción que puede rechazarse, usa callAction: muestra el rechazo por ti.

Lado: Cliente · Espera: sí (Promise en TypeScript, cede el hilo en Lua)

Firma
triggerServerCallback(name: string, args?: unknown[], timeoutMs?: number): Promise<unknown>
Lua
local result, err = aiLib.triggerServerCallback(name, args, timeout) -- llamar dentro de un hilo

Parámetros

Prop

Type

Devuelve

TypeScript: Promise<unknown>. Se resuelve con lo que devolvió el handler del servidor (null cuando no devolvió nada). El valor no se comprueba: prueba su tipo antes de usarlo. La promise se rechaza cuando el handler del servidor lanzó un error, la puerta del servidor rechazó la llamada (límite de frecuencia, una guarda, argumentos incorrectos, un bloqueo) o no llegó ninguna respuesta dentro de timeoutMs.

Lua: dos valores. result es la respuesta; err es nil si todo fue bien, 'failed' cuando el servidor rechazó o falló, 'timeout' cuando no llegó ninguna respuesta a tiempo. Con un error, result es nil.

Ejemplo

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

En TypeScript gestiona siempre el rechazo (.catch(() => null) o try): una llamada rechazada o agotada se rechaza. El servidor limita cada callback a 20 llamadas por segundo por jugador por defecto, así que nunca lo llames en un bucle ni en cada frame. El formato en la red es el mismo en ambos lenguajes: un cliente Lua puede llamar a un servidor TypeScript y al revés.

callAction

Ejecuta una acción en el servidor y muestra el rechazo por ti. El handler del servidor (onClientCallback) responde un ActionResult: { ok: true, ...data } cuando funcionó, o fail("LANG_KEY") cuando lo rechazó. Con un rechazo, un tiempo agotado o un rechazo de la puerta, callAction busca la clave de error en el archivo de idioma de tu recurso y la muestra como notificación de error. Úsalo para cada botón que cambia algo: comprar, vender, alquilar, reparar.

Lado: Cliente · Espera: sí (Promise en TypeScript, cede el hilo en Lua)

Firma
callAction(name: string, args: unknown[] | undefined, options: CallActionOptions): Promise<({ ok: true } & Record<string, unknown>) | null>
Lua
local reply = aiLib.callAction(name, args, options) -- table o nil, llamar dentro de un hilo

Parámetros

Prop

Type

CallActionOptions

Prop

Type

Devuelve

Promise<({ ok: true } & Record<string, unknown>) | null>: la respuesta ok completa (con los campos extra que añadió el servidor) cuando la acción funcionó; null cuando se rechazó, falló o se agotó el tiempo (la notificación ya se mostró, salvo con silent). Los campos extra no se comprueban: prueba el tipo de cada uno que leas. Lua devuelve la tabla de respuesta o nil.

Ejemplo

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; // el rechazo ya está en pantalla
  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 };
  },
);

La clave de error se busca en el archivo de idioma del recurso que llama a callAction, así que pon cada clave que pueda responder el servidor (NO_MONEY, NO_ROOM, UNKNOWN_ITEM) y la clave fallback en el locales/en.json de ese recurso. Una clave que falta ahí se muestra como la propia clave.

isActionOk

Indica si una respuesta es un ActionResult ok (ok es exactamente true). Úsalo cuando llamas tú mismo a triggerServerCallback y lees la respuesta a mano.

Lado: Cliente y servidor (desde /shared, reexportado por /client) · Espera: no

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

Sin equivalente en Lua: prueba type(reply) == 'table' and reply.ok == true.

Parámetros

Prop

Type

Devuelve

boolean: true cuando reply es un objeto (no un array) cuyo ok es true. En TypeScript también estrecha reply a { ok: true } & Record<string, unknown>.

Ejemplo

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

La clave de idioma de una respuesta rechazada: su campo error, o fallback cuando falta la respuesta o no tiene texto de error. Úsalo con isActionOk cuando muestras el error a tu manera (en una página NUI en lugar de una notificación).

Lado: Cliente y servidor (desde /shared, reexportado por /client) · Espera: no

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

Sin equivalente en Lua.

Parámetros

Prop

Type

Devuelve

string | null: null cuando la respuesta es ok; el error de la respuesta cuando es un string no vacío; en otro caso fallback.

Ejemplo

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

Escucha un evento de red que el servidor envía con emitNet (TriggerClientEvent en Lua). Las llamadas que no vinieron del servidor (el emit local de otro recurso) se descartan, así que un menú de trampas no puede falsificarlas. Cada cliente ai-* escucha al servidor de esta forma, nunca con un onNet a secas. En TypeScript, un handler que recibe argumentos debe indicar un parser args: comprueba los valores en bruto, y null descarta la llamada.

Lado: Cliente · Espera: no

Firma
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(...) recibe los argumentos en bruto

Parámetros

Prop

Type

Devuelve

void.

Ejemplo

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

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

// Sin argumentos: la forma corta.
onServerEvent("myscript:reset", () => points.clear());

// Un argumento booleano: procésalo, null descarta una llamada incorrecta.
onServerEvent(
  "myscript:state",
  { args: ([active]) => (typeof active === "boolean" ? [active] : null) },
  (active) => {
    renting = active;
    points.refresh();
  },
);

El servidor envía el evento con emitNet("myscript:state", source, true) (TypeScript) o TriggerClientEvent('myscript:state', source, true) (Lua). Los handlers Lua reciben los argumentos en bruto: comprueba sus tipos tú mismo.

fromServer

Si el evento de red que se está gestionando ahora mismo vino del servidor. onServerEvent ya lo usa; solo lo necesitas en un handler onNet a secas que no puedas reemplazar.

Lado: Cliente · Espera: no

Firma
fromServer(): boolean

Sin equivalente en Lua: en un handler de evento de red Lua, source está vacío para los emits locales.

Devuelve

boolean: true cuando el evento actual lleva el source numérico del servidor; false para un emit local de cualquier recurso.

Ejemplo

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

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

Léelo antes del primer await del handler: después de un await el evento actual ya no existe y la respuesta es incorrecta.

onServerCallback

Responde a una pregunta que el servidor hace a este cliente con triggerClientCallback(name, source, ...). El valor que devuelve tu handler (o con el que se resuelve su promise) se envía de vuelta. Un error lanzado responde un fallo, y la promise del servidor se rechaza.

Lado: Cliente · Espera: no (el handler puede ser async)

Firma
onServerCallback(name: string, handler: (...args: unknown[]) => unknown): void
Lua
aiLib.onServerCallback(name, handler) -- el valor de retorno del handler es la respuesta

Parámetros

Prop

Type

Devuelve

void.

Ejemplo

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

// El servidor pregunta a qué distancia está este jugador de un punto.
onServerCallback("myscript:distance", (x, y, z) => {
  if (typeof x !== "number" || typeof y !== "number" || typeof z !== "number") return null;
  return player.distanceTo({ x, y, z });
});

En Lua, un handler que devuelve false responde nil (el servidor recibe null): devuelve true / nil, un número o una tabla cuando el servidor deba distinguir false de "sin respuesta". La respuesta viene del ordenador del jugador, así que un tramposo puede cambiarla. Usa las respuestas del cliente para cosas que solo sabe el cliente (lo que hay en pantalla, una entidad local), y nunca como prueba para dinero u objetos.

En esta página