AI Core
Référence de l’APILib : client

Parler au serveur

Interroger le serveur et attendre sa réponse, lancer des actions serveur avec des refus traduits, écouter les événements serveur et répondre aux callbacks serveur.

Un script client ne se fait jamais confiance : il demande au serveur, et le serveur décide. Ces helpers transportent ces questions et ces réponses. Les moitiés serveur sont sur Entrées client (onClientCallback, onClientEvent, triggerClientCallback).

Vous voulezUtilisez
Lancer quelque chose que le serveur doit autoriser (acheter, louer, ouvrir) et afficher son refuscallAction
Demander des données au serveurtriggerServerCallback
Recevoir ce que le serveur envoieonServerEvent
Répondre à une question que le serveur pose à ce clientonServerCallback

triggerServerCallback

Interroge le serveur et attend sa réponse. Le serveur répond avec onClientCallback(name, ...). Utilisez-le pour lire des données que le serveur conserve (une liste de prix, les enregistrements propres à un joueur). Pour une action qui peut être refusée, utilisez callAction : il affiche le refus pour vous.

Côté : client · Attend : oui (Promise en TypeScript, cède la main (yield) du thread en Lua)

Signature
triggerServerCallback(name: string, args?: unknown[], timeoutMs?: number): Promise<unknown>
Lua
local result, err = aiLib.triggerServerCallback(name, args, timeout) -- à appeler dans un thread

Paramètres

Prop

Type

Retour

TypeScript : Promise<unknown>. Elle se résout avec ce que le gestionnaire serveur a renvoyé (null quand il n’a rien renvoyé). La valeur n’est pas vérifiée : testez son type avant de l’utiliser. La promesse est rejetée quand le gestionnaire serveur a levé une erreur, quand le filtre du serveur a refusé l’appel (limite de débit, un garde, de mauvais arguments, un verrou), ou quand aucune réponse n’est arrivée dans les timeoutMs.

Lua : deux valeurs. result est la réponse ; err vaut nil en cas de succès, 'failed' quand le serveur a refusé ou échoué, 'timeout' quand aucune réponse n’est arrivée à temps. En cas d’erreur, result vaut nil.

Exemple

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, gérez toujours le rejet (.catch(() => null) ou try) : un appel refusé ou expiré est rejeté. Par défaut, le serveur limite chaque callback à 20 appels par seconde et par joueur, donc ne l’appelez jamais dans une boucle ni à chaque frame. Le format sur le réseau est le même dans les deux langages : un client Lua peut appeler un serveur TypeScript et inversement.

callAction

Lance une action sur le serveur et affiche le refus pour vous. Le gestionnaire serveur (onClientCallback) répond un ActionResult : { ok: true, ...data } quand cela a fonctionné, ou fail("LANG_KEY") quand il a refusé. En cas de refus, d’expiration ou de refus du filtre, callAction cherche la clé d’erreur dans le fichier de langue de votre ressource et l’affiche comme notification d’erreur. Utilisez-le pour chaque bouton qui change quelque chose : acheter, vendre, louer, réparer.

Côté : client · Attend : oui (Promise en TypeScript, cède la main (yield) du thread en 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, à appeler dans un thread

Paramètres

Prop

Type

CallActionOptions

Prop

Type

Retour

Promise<({ ok: true } & Record<string, unknown>) | null> : toute la réponse ok (avec les champs supplémentaires ajoutés par le serveur) quand l’action a fonctionné ; null quand elle a été refusée, a échoué ou a expiré (la notification est déjà affichée, sauf avec silent). Les champs supplémentaires ne sont pas vérifiés : testez le type de chacun de ceux que vous lisez. Lua renvoie la table de réponse ou nil.

Exemple

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; // le refus est déjà à l’écran
  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 clé d’erreur est cherchée dans le fichier de langue de la ressource qui appelle callAction : mettez donc chaque clé que le serveur peut répondre (NO_MONEY, NO_ROOM, UNKNOWN_ITEM) ainsi que la clé fallback dans le locales/en.json de cette ressource. Une clé absente y est affichée telle quelle.

isActionOk

Indique si une réponse est un ActionResult ok (ok vaut exactement true). Utilisez-le quand vous appelez triggerServerCallback vous-même et lisez la réponse à la main.

Côté : client et serveur (depuis /shared, réexporté par /client) · Attend : non

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

Pas d’équivalent Lua : testez type(reply) == 'table' and reply.ok == true.

Paramètres

Prop

Type

Retour

boolean : true quand reply est un objet (pas un tableau) dont ok vaut true. En TypeScript, il restreint aussi reply à { ok: true } & Record<string, unknown>.

Exemple

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 clé de langue d’une réponse refusée : son champ error, ou fallback quand la réponse manque ou n’a pas de texte d’erreur. Utilisez-le avec isActionOk quand vous affichez l’erreur à votre façon (dans une page NUI au lieu d’une notification).

Côté : client et serveur (depuis /shared, réexporté par /client) · Attend : non

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

Pas d’équivalent Lua.

Paramètres

Prop

Type

Retour

string | null : null quand la réponse est ok ; l’error de la réponse quand c’est une chaîne non vide ; sinon fallback.

Exemple

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

Écoute un événement réseau que le serveur envoie avec emitNet (TriggerClientEvent en Lua). Les appels qui ne viennent pas du serveur (un emit local d’une autre ressource) sont ignorés, si bien qu’un menu de triche ne peut pas les simuler. Chaque client ai-* écoute le serveur de cette façon, jamais avec un onNet nu. En TypeScript, un gestionnaire qui prend des arguments doit indiquer un parseur args : il vérifie les valeurs brutes, et null ignore l’appel.

Côté : client · Attend : non

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(...) reçoit les arguments bruts

Paramètres

Prop

Type

Retour

void.

Exemple

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

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

// Aucun argument : la forme courte.
onServerEvent("myscript:reset", () => points.clear());

// Un argument booléen : analysez-le, null ignore un mauvais appel.
onServerEvent(
  "myscript:state",
  { args: ([active]) => (typeof active === "boolean" ? [active] : null) },
  (active) => {
    renting = active;
    points.refresh();
  },
);

Le serveur envoie l’événement avec emitNet("myscript:state", source, true) (TypeScript) ou TriggerClientEvent('myscript:state', source, true) (Lua). Les gestionnaires Lua reçoivent les arguments bruts : vérifiez leurs types vous-même.

fromServer

Indique si l’événement réseau en cours de traitement vient du serveur. onServerEvent l’utilise déjà ; vous n’en avez besoin que dans un gestionnaire onNet nu que vous ne pouvez pas remplacer.

Côté : client · Attend : non

Signature
fromServer(): boolean

Pas d’équivalent Lua : dans un gestionnaire d’événement réseau Lua, source est vide pour les émissions locales.

Retour

boolean : true quand l’événement en cours porte la source numérique du serveur ; false pour une émission locale par n’importe quelle ressource.

Exemple

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

onNet("legacy:event", (value: unknown) => {
  if (!fromServer()) return; // une émission locale, pas le serveur
  console.log("server said", value);
});

Lisez-le avant le premier await du gestionnaire : après un await, l’événement en cours n’existe plus et la réponse est fausse.

onServerCallback

Répond à une question que le serveur pose à ce client avec triggerClientCallback(name, source, ...). La valeur que votre gestionnaire renvoie (ou vers laquelle sa promesse se résout) est renvoyée au serveur. Une erreur levée répond un échec, et la promesse du serveur est rejetée.

Côté : client · Attend : non (le gestionnaire peut être async)

Signature
onServerCallback(name: string, handler: (...args: unknown[]) => unknown): void
Lua
aiLib.onServerCallback(name, handler) -- la valeur renvoyée par le gestionnaire est la réponse

Paramètres

Prop

Type

Retour

void.

Exemple

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

// Le serveur demande à quelle distance d’un point se trouve ce joueur.
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 gestionnaire qui renvoie false répond nil (le serveur reçoit null) : renvoyez true / nil, un nombre ou une table quand le serveur doit distinguer false d’une « absence de réponse ». La réponse vient de l’ordinateur du joueur, donc un tricheur peut la modifier. Utilisez les réponses client pour ce que seul le client connaît (ce qui est à l’écran, une entité locale), et jamais comme preuve pour de l’argent ou des objets.

Sur cette page