AI Core
Справочник APIСобытия и состояние

Серверные события

Все серверные локальные события, которые отправляет ai-core - персонажи, деньги, работы, дежурство, банды, метаданные, определения, предметы, смерти - с аргументами и обработчиками на TypeScript и Lua.

ai-core отправляет эти события на сервере через TriggerEvent (локально: их слышат другие серверные ресурсы, клиенты никогда). Зеркало online из библиотеки и мосты ESX / QB / vRP построены на них, поэтому это самый дешёвый способ следить за игроками: без вызова экспортов, без опроса.

Обработчик каждого события выполняется синхронно, пока ai-core находится посреди изменения: делайте обработчики короткими и никогда не ждите внутри них. Если нужно подождать (запись в базу данных), запускайте это без блокировки.

ai-core:playerLoaded

Персонаж игрока загружен, и его данные отправлены на клиент. Срабатывает один раз на каждую загрузку персонажа, в том числе после смены персонажа. Если включён выбор места появления, срабатывает до того, как игрок выберет, где появиться; ai-core:playerSpawned идёт после выбора.

Сторона: сервер · Константа: CORE.playerLoadedEvent

Аргументы

Prop

Type

Обработчик

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})`);
});

Для персонажей, загруженных до старта вашего ресурса, это событие для вас никогда не срабатывает. Используйте onCharacter({ loaded }) из библиотеки (aiLib.onCharacter в Lua), который воспроизводит их.

ai-core:playerSpawned

Загруженный персонаж помещён в мир. Срабатывает сразу после ai-core:playerLoaded или после того, как игрок выбрал точку появления, когда сервер даёт выбор. Используйте для того, чему нужен пед в мире (приветственное сообщение, телепорт).

Сторона: сервер · Константа: CORE.playerSpawnedEvent

Аргументы

Prop

Type

Обработчик

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

Персонаж отпускается: игрок покинул сервер или вернулся на экран персонажей. Срабатывает до того, как ai-core выгрузит персонажа, поэтому экспорты ai-core внутри вашего обработчика ещё отвечают по нему. Очищайте здесь всё, что храните для игрока.

Сторона: сервер · Константа: CORE.playerUnloadedEvent

Аргументы

Prop

Type

Обработчик

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})`);
});

При reason = "switch" тот же серверный id через мгновение может загрузить другого персонажа. Привязывайте данные персонажа к charId или очищайте их здесь; никогда не считайте, что за серверным id закреплён один персонаж.

ai-core:moneyChanged

Изменился баланс счёта загруженного персонажа. Срабатывает после применения изменения, один раз на каждое изменение, только когда баланс действительно сдвинулся.

Сторона: сервер · Константа: CORE.moneyChangedEvent

Аргументы

Prop

Type

Обработчик

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

// Помечает крупные платежи наличными для персонала.
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

Изменился список работ загруженного персонажа: работа добавлена или удалена или изменился ранг.

Сторона: сервер · Константа: CORE.jobsChangedEvent

Аргументы

Prop

Type

Обработчик

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

Загруженный персонаж заступил на дежурство по работе или ушёл с дежурства. Игрок одновременно на дежурстве не более чем по одной работе.

Сторона: сервер · Константа: CORE.dutyChangedEvent

Аргументы

Prop

Type

Обработчик

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`);
});

Чтобы узнать, сколько игроков на дежурстве по работе, читайте GlobalState ai:duty:<job> (см. State bag), а не считайте эти события сами.

ai-core:gangsChanged

Изменился список банд загруженного персонажа: банда добавлена или удалена или изменился ранг.

Сторона: сервер · Константа: CORE.gangsChangedEvent

Аргументы

Prop

Type

Обработчик

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

Загруженный персонаж сменил банду, за которую действует, или теперь не действует ни за какую.

Сторона: сервер · Константа: CORE.activeGangChangedEvent

Аргументы

Prop

Type

Обработчик

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

Изменилось значение метаданных загруженного персонажа. Метаданные - свободный набор ключ / значение, хранящийся с персонажем; мосты ESX / QB держат там метаданные фреймворка (hunger, thirst, stress, isdead...).

Сторона: сервер · Константа: CORE.metaChangedEvent

Аргументы

Prop

Type

Обработчик

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

Работа или банда добавлена, изменена или удалена в админ-панели. Срабатывает один раз на каждую изменённую работу или банду.

Сторона: сервер · Константа: CORE.definitionsChangedEvent

Аргументы

Prop

Type

Обработчик

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

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

// Сбросить закэшированное название, когда панель меняет эту работу.
on(CORE.definitionsChangedEvent, (file: "jobs" | "gangs", name: string) => {
  if (file === "jobs") labelCache.delete(name);
});

ai-core:characterScreen

Загружается экран персонажей игрока (после входа или после возврата на него). ai-core сам читает внешности карточек; скрипт одежды слушает, чтобы рисовать портреты для карточек.

Сторона: сервер · Константа: CORE.characterScreenEvent

Аргументы

Prop

Type

Обработчик

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

Изменился инвентарь за слоем инвентаря ai-core: выбранный инвентарь запустился или остановился, или владелец выбрал в панели другой. Сбросьте всё, что закэшировали из старого инвентаря (названия предметов, определения).

Сторона: сервер · Константа: CORE.inventoryChangedEvent

Аргументы

Prop

Type

Обработчик

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; // при следующем использовании прочитать заново из нового инвентаря
  console.log(`inventory is now ${resource ?? "none"}`);
});

ai-core:itemUsed

Игрок использовал предмет. Несёт потребности, которые даёт предмет (голод, жажда, стресс), чтобы HUD или скрипт потребностей применил их, не зная инвентаря. ai-hud слушает его.

Сторона: сервер · Константа: CORE.itemUsedEvent

Аргументы

Prop

Type

Обработчик

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}`);
});

Сейчас только ai-inventory сообщает ai-core об использовании предметов: с другим инвентарём это событие не срабатывает. Слушайте это событие, а не собственное событие использования инвентаря, чтобы ваш скрипт работал, когда инвентарь - ai-inventory, и продолжал работать, когда добавятся другие.

ai-core:playerDied

Игрок с загруженным персонажем умер. ai-core берёт это из собственного отчёта FXServer о смерти педа (FXServer 38039 или новее), поэтому ни одному клиенту не нужно доверять.

Сторона: сервер · Константа: CORE.playerDiedEvent

Аргументы

Prop

Type

Обработчик

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})`);
});

Это смерть в игре, а не состояние «лежит» скрипта смерти: скрипт смерти может оставлять лежащего игрока живым, чтобы анимировать его. Для «лежит ли этот игрок» используйте medical из серверной библиотеки или серверные медицинские экспорты ai-core.

ai-core:vrp:config

Владелец сервера изменил сопоставление vRP в панели ai-core (вкладка «vRP scripts»). Мост vRP в ai-core слушает его; оно нужно вам, только если вы сами читаете сопоставление vRP.

Сторона: сервер · Константа: VRP.configEvent

Аргументы

Prop

Type

Обработчик

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)`);
});

События других скриптов ai-*

Их отправляют скрипты ai-*, а не ai-core, но многие ресурсы их слушают. Полный справочник по ним есть на странице разработчика каждого скрипта.

ai-vehicles:owners

Изменились владельцы личного транспорта в памяти (загрузился персонаж со своим гаражом, транспорт куплен, продан или удалён). Срабатывает не чаще раза за серверный тик со всеми изменениями этого тика. Скрипты ключей используют его, чтобы вести словарь «номер - владелец», не спрашивая ai-vehicles каждый раз.

Сторона: сервер · Константа: VEHICLES_EVENTS.owners

Аргументы

Prop

Type

Обработчик

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

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

// Начать с полного списка, затем следить за изменениями.
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 отправляет классическое событие FiveM chatMessage до того, как доставить сообщение игрока. Вызов CancelEvent() в вашем обработчике отбрасывает сообщение.

Сторона: сервер · Константа: нет (собственное имя FiveM)

Аргументы

Prop

Type

Обработчик

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}`);
  }
});

На этой странице