Серверные события
Все серверные локальные события, которые отправляет ai-core - персонажи, деньги, работы, дежурство, банды, метаданные, определения, предметы, смерти - с аргументами и обработчиками на TypeScript и Lua.
ai-core отправляет эти события на сервере через TriggerEvent (локально: их слышат другие серверные ресурсы, клиенты никогда). Зеркало online из библиотеки и мосты ESX / QB / vRP построены на них, поэтому это самый дешёвый способ следить за игроками: без вызова экспортов, без опроса.
Обработчик каждого события выполняется синхронно, пока ai-core находится посреди изменения: делайте обработчики короткими и никогда не ждите внутри них. Если нужно подождать (запись в базу данных), запускайте это без блокировки.
ai-core:playerLoaded
Персонаж игрока загружен, и его данные отправлены на клиент. Срабатывает один раз на каждую загрузку персонажа, в том числе после смены персонажа. Если включён выбор места появления, срабатывает до того, как игрок выберет, где появиться; ai-core:playerSpawned идёт после выбора.
Сторона: сервер · Константа: CORE.playerLoadedEvent
Аргументы
Prop
Type
Обработчик
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
Обработчик
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
Обработчик
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
Обработчик
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
Обработчик
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
Обработчик
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
Обработчик
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
Обработчик
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
Обработчик
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
Обработчик
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
Обработчик
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
Обработчик
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
Обработчик
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
Обработчик
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
Обработчик
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
Обработчик
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
Обработчик
on("chatMessage", (source: number, _author: string, text: string) => {
if (text.toLowerCase().includes("discord.gg")) {
CancelEvent();
console.log(`blocked an invite link from ${source}`);
}
});События и состояние
Как ai-core сообщает другим скриптам, что что-то изменилось - локальные события, сетевые события, state bag - и как их слушать в TypeScript и Lua.
Клиентские события
Все клиентские локальные события, которые отправляет ai-core - локальный персонаж загружен или выгружен, состояние «лежит», кэшированные значения, видимость HUD, синхронизация мира, определения работ, подсказки клавиш, ремень безопасности - с аргументами и обработчиками.