AI Core

Server events

Every server-side local event ai-core fires - characters, money, jobs, duty, gangs, metadata, definitions, items, deaths - with arguments and listeners in TypeScript and Lua.

ai-core fires these events on the server with TriggerEvent (local: other server resources hear them, clients never do). The lib's online mirror and the ESX / QB / vRP bridges are built on them, so they are the cheapest way to follow players: no export call, no polling.

Each event's handler runs synchronously while ai-core is in the middle of the change: keep handlers short and never wait inside them. If you need to wait (a database write), start it without blocking.

ai-core:playerLoaded

A player's character was loaded and its data sent to their client. Fires once per character load, also after a character switch. With spawn choice on, it fires before the player picks where to appear; ai-core:playerSpawned follows the pick.

Side: Server · Constant: CORE.playerLoadedEvent

Arguments

Prop

Type

Listener

resource/server/index.ts
import { CORE, type CharacterInfo } from "@aicore-fivem/shared";

on(CORE.playerLoadedEvent, (src: number, character: CharacterInfo) => {
  console.log(`#${character.id} ${character.firstname} ${character.lastname} loaded (src ${src})`);
});

Characters already loaded before your resource started never fire this for you. Use the lib's onCharacter({ loaded }) (aiLib.onCharacter in Lua), which replays them.

ai-core:playerSpawned

The loaded character was put into the world. Fires right after ai-core:playerLoaded, or after the player picked a spawn point when the server lets them choose. Use it for things that need the ped in the world (a welcome message, a teleport).

Side: Server · Constant: CORE.playerSpawnedEvent

Arguments

Prop

Type

Listener

resource/server/index.ts
import { notify } from "@aicore-fivem/lib/server";
import { CORE } from "@aicore-fivem/shared";
import { Locale } from "common";

on(CORE.playerSpawnedEvent, (src: number, _charId: number) => {
  notify(src, Locale("WELCOME_BACK"), "info");
});

ai-core:playerUnloaded

A character is being let go: the player left the server, or went back to the character screen. Fires before ai-core drops the character, so ai-core's exports still answer for it inside your handler. Clean up everything you keep per player here.

Side: Server · Constant: CORE.playerUnloadedEvent

Arguments

Prop

Type

Listener

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

const activeJobs = new Map<number, string>();

on(CORE.playerUnloadedEvent, (src: number, _charId: number, reason: "dropped" | "switch") => {
  activeJobs.delete(src);
  console.log(`${src} unloaded (${reason})`);
});

With reason = "switch" the same server id may load another character a moment later. Key your per-character data by charId or clear it here; never assume a server id keeps one character.

ai-core:moneyChanged

An account balance of a loaded character changed. Fires after the change is applied, once per change, only when the balance really moved.

Side: Server · Constant: CORE.moneyChangedEvent

Arguments

Prop

Type

Listener

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

// Flags large cash payments for staff.
on(CORE.moneyChangedEvent, (src: number, account: string, balance: number, delta: number, reason: string) => {
  if (account === "cash" && delta >= 100_000)
    console.log(`${src} got $${delta} cash (${reason}), now $${balance}`);
});

ai-core:jobsChanged

The list of jobs a loaded character holds changed: a job added or removed, or a grade changed.

Side: Server · Constant: CORE.jobsChangedEvent

Arguments

Prop

Type

Listener

resource/server/index.ts
import { CORE, type JobMembership } from "@aicore-fivem/shared";

on(CORE.jobsChangedEvent, (src: number, jobs: JobMembership[]) => {
  const police = jobs.find((m) => m.job === "police");
  console.log(police ? `${src} is police grade ${police.grade}` : `${src} is not police`);
});

ai-core:dutyChanged

A loaded character went on duty in a job, or off duty. A player is on duty in at most one job at a time.

Side: Server · Constant: CORE.dutyChangedEvent

Arguments

Prop

Type

Listener

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

on(CORE.dutyChangedEvent, (src: number, job: string | null) => {
  console.log(job ? `${src} on duty: ${job}` : `${src} off duty`);
});

To know how many players are on duty in a job, read GlobalState ai:duty:<job> (see State bags) instead of counting these events yourself.

ai-core:gangsChanged

The list of gangs a loaded character belongs to changed: a gang added or removed, or a grade changed.

Side: Server · Constant: CORE.gangsChangedEvent

Arguments

Prop

Type

Listener

resource/server/index.ts
import { CORE, type GangMembership } from "@aicore-fivem/shared";

on(CORE.gangsChangedEvent, (src: number, gangs: GangMembership[]) => {
  console.log(`${src} is in ${gangs.length} gang(s)`);
});

ai-core:activeGangChanged

A loaded character switched the gang they act for, or now acts for none.

Side: Server · Constant: CORE.activeGangChangedEvent

Arguments

Prop

Type

Listener

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

on(CORE.activeGangChangedEvent, (src: number, gang: string | null) => {
  console.log(`${src} now acts for ${gang ?? "no gang"}`);
});

ai-core:metaChanged

A metadata value of a loaded character changed. Metadata is a free key / value bag stored with the character; the ESX / QB bridges keep framework metadata (hunger, thirst, stress, isdead...) there.

Side: Server · Constant: CORE.metaChangedEvent

Arguments

Prop

Type

Listener

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

on(CORE.metaChangedEvent, (src: number, key: string, value: unknown) => {
  if (key === "stress" && typeof value === "number" && value >= 90)
    console.log(`${src} is very stressed`);
});

ai-core:definitionsChanged

A job or a gang was added, changed or deleted in the admin panel. Fires once per job or gang that changed.

Side: Server · Constant: CORE.definitionsChangedEvent

Arguments

Prop

Type

Listener

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

const labelCache = new Map<string, string>();

// Drop a cached label when the panel changes that job.
on(CORE.definitionsChangedEvent, (file: "jobs" | "gangs", name: string) => {
  if (file === "jobs") labelCache.delete(name);
});

ai-core:characterScreen

A player's character screen is loading (after joining, or after going back to it). ai-core reads the cards' looks itself; a clothing script listens to draw portraits for the cards.

Side: Server · Constant: CORE.characterScreenEvent

Arguments

Prop

Type

Listener

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

on(CORE.characterScreenEvent, (src: number, charIds: number[]) => {
  console.log(`${src} sees ${charIds.length} character card(s)`);
});

ai-core:inventoryChanged

The inventory behind ai-core's inventory layer changed: the picked inventory started or stopped, or the owner picked another one in the panel. Drop anything you cached from the old inventory (item labels, definitions).

Side: Server · Constant: CORE.inventoryChangedEvent

Arguments

Prop

Type

Listener

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

let itemLabels: Record<string, string> | null = null;

on(CORE.inventoryChangedEvent, (resource: string | null) => {
  itemLabels = null; // read again from the new inventory on next use
  console.log(`inventory is now ${resource ?? "none"}`);
});

ai-core:itemUsed

A player used an item. Carries the needs the item gives (hunger, thirst, stress), so a HUD or needs script applies them without knowing the inventory. ai-hud listens to it.

Side: Server · Constant: CORE.itemUsedEvent

Arguments

Prop

Type

Listener

resource/server/needs.ts
import { CORE } from "@aicore-fivem/shared";

on(CORE.itemUsedEvent, (src: number, name: string, status: Record<string, number> | null) => {
  if (!status) return;
  for (const [need, amount] of Object.entries(status))
    console.log(`${src} used ${name}: ${need} ${amount > 0 ? "+" : ""}${amount}`);
});

Today only ai-inventory reports item uses to ai-core: with another inventory this event does not fire. Listen to this event, not to an inventory's own use event, so your script works when ai-inventory is the inventory and keeps working when others are added.

ai-core:playerDied

A player with a loaded character died. ai-core takes this from FXServer's own ped death report (FXServer 38039 or newer), so no client is trusted.

Side: Server · Constant: CORE.playerDiedEvent

Arguments

Prop

Type

Listener

resource/server/killfeed.ts
import { CORE } from "@aicore-fivem/shared";

on(CORE.playerDiedEvent, (src: number, killer: number | null, weaponHash: number) => {
  if (killer !== null && killer !== src)
    console.log(`${GetPlayerName(String(killer))} killed ${GetPlayerName(String(src))} (weapon ${weaponHash})`);
});

This is the game's death, not the death script's "down" state: a death script may keep a downed player alive to animate them. For "is this player down", use the server lib's medical or ai-core's server medical exports.

ai-core:vrp:config

The server owner changed the vRP mapping in ai-core's panel (tab "vRP scripts"). ai-core's vRP bridge listens to it; you only need it if you read the vRP mapping yourself.

Side: Server · Constant: VRP.configEvent

Arguments

Prop

Type

Listener

resource/server/index.ts
import { VRP, type VrpConfig } from "@aicore-fivem/shared";

on(VRP.configEvent, (config: VrpConfig) => {
  console.log(`${config.permissions.length} vRP permission rule(s)`);
});

Events of other ai-* scripts

These are fired by ai-* scripts, not ai-core, but many resources listen to them. Their full reference is on each script's developer page.

ai-vehicles:owners

The owners of owned vehicles in memory changed (a character loaded with their garage, a vehicle bought, sold or deleted). Fires at most once per server tick with every change of that tick. Key scripts use it to keep a plate to owner map without asking ai-vehicles each time.

Side: Server · Constant: VEHICLES_EVENTS.owners

Arguments

Prop

Type

Listener

resource/server/owners.ts
import { resourceExport } from "@aicore-fivem/lib/server";
import { VEHICLES_EVENTS } from "@aicore-fivem/shared";

const owners = new Map<string, string>();

// Start from the full list, then follow the changes.
for (const [plate, type, id] of resourceExport("ai-vehicles").GetPlateOwners()) owners.set(plate, `${type}:${id}`);

on(VEHICLES_EVENTS.owners, (puts: Array<[string, string, string]>, removes: string[]) => {
  for (const [plate, type, id] of puts) owners.set(plate, `${type}:${id}`);
  for (const plate of removes) owners.delete(plate);
});

chatMessage

ai-chat fires the classic FiveM chatMessage event before it delivers a player's message. Calling CancelEvent() in your handler drops the message.

Side: Server · Constant: none (FiveM's own name)

Arguments

Prop

Type

Listener

resource/server/filter.ts
on("chatMessage", (source: number, _author: string, text: string) => {
  if (text.toLowerCase().includes("discord.gg")) {
    CancelEvent();
    console.log(`blocked an invite link from ${source}`);
  }
});

On this page