AI Core
API başvurusuLib: server

Oyuncular ve karakterler

online, onCharacter, notify, yakındaki oyuncular, konumlar ve oyuncu başına depolar: PlayerMap, Cooldowns, KeyedCooldowns, SourceLock, RateLimit, KeyWatchers.

TypeScript
import { online, onCharacter, notify, playersNear, coordsOf, PlayerMap, Cooldowns } from "@aicore-fivem/lib/server";

Oyuncu, server id’si (source) olan bağlı bir client’tır. Karakter ise oynadığı kişidir; karakter ekranındaki bir oyuncunun yüklü karakteri yoktur. Oyuncuların ve karakterlerin sahibi ai-core’dur; bu yardımcılar onları okur.

online

online, resource’unun ai-core’un yüklü oyuncularından tuttuğu kopyadır: karakterler, rütbeleriyle meslek ve çete üyelikleri, meslek ve çete başına üye listeleri ve görev durumu. İlk kullanımda ai-core’u bir kez okur, sonra ai-core’un event’lerini takip eder; bu yüzden bir okuma bellekten bakmaktır. Sık çalışan yollarda (her sohbet mesajı, her ihbar) ai-core’un export’larını çağırmak yerine bunu kullan.

Lua’da aiLib.online her çağrıda ai-core’un export’larına sorar: aksiyonlar için uygundur, her tick için değil. character, name, jobs, gangs, jobGrade, gangGrade, dutyJob ve sources vardır; geri kalanının Lua karşılığı yoktur.

online.sources

Yüklü karakteri olan her oyuncunun server id’si.

Taraf: Server · Bekler: hayır

Signature
online.sources(): IterableIterator<number>
Lua
local list = aiLib.online.sources() -- integer[]

Dönüş değeri

IterableIterator<number> (Lua: bir dizi). Karakter ekranındaki oyuncular içinde değildir.

Örnek

import { online, notify } from "@aicore-fivem/lib/server";

for (const source of online.sources()) notify(source, "Server restart in 5 minutes", "warning", 10_000);

online.size

Kaç oyuncunun yüklü karakteri olduğu.

Taraf: Server · Bekler: hayır

Signature
readonly online.size: number

Lua karşılığı yok: #aiLib.online.sources().

online.character

Bir oyuncunun yüklü karakteri.

Taraf: Server · Bekler: hayır

Signature
online.character(source: number): CharacterInfo | null
Lua
local character = aiLib.online.character(source) -- table ya da nil

Prop

Type

Dönüş değeri

CharacterInfo (id, citizenid, slot, firstname, lastname, birthdate, sex, nationality) ya da oyuncunun yüklü karakteri yoksa null (Lua’da nil).

Örnek

const character = online.character(source);
if (character) console.log(`${character.citizenid} is ${character.firstname}`);

online.charId

Oyuncunun yüklü karakterinin id’si (veritabanı id’si, yeniden bağlanmalarda değişmez).

Taraf: Server · Bekler: hayır

Signature
online.charId(source: number): number | undefined

Lua karşılığı yok: (aiLib.online.character(source) or {}).id.

Dönüş değeri: karakter id’si ya da hiçbiri yüklü değilse undefined. Yeniden bağlanmadan sonra da kalması gereken verileri (cooldown’lar, sahiplik) source ile değil, bu id ile anahtarla.

const charId = online.charId(source);
if (charId === undefined) return fail("NO_CHARACTER");

online.name

Karakterin "Ad Soyad"ı ya da karakter yüklü değilse FiveM oyuncu adı.

Taraf: Server · Bekler: hayır

Signature
online.name(source: number, options?: { fallback?: boolean }): string
Lua
local name = aiLib.online.name(source, fallback) -- fallback: boolean?

Prop

Type

Dönüş değeri string.

notify(target, `${online.name(source)} sent you a gift`, "success");

online.jobs

Görevde olsun ya da olmasın oyuncunun üyesi olduğu her meslek. ai-core bir karakterin birkaç meslek tutmasına (paneldeki maxJobs) ve aynı anda tek bir meslekte görevde olmasına izin verir.

Taraf: Server · Bekler: hayır

Signature
online.jobs(source: number): readonly string[]
Lua
local memberships = aiLib.online.jobs(source) -- { { job = 'police', grade = 2 }, ... }

Dönüş değeri: meslek adları (hiç yoksa ya da karakter yoksa boş). Lua karşılığı adları değil, üyelik tablolarını { job, grade } döndürür.

const isMechanic = online.jobs(source).includes("mechanic");

online.gangs

Oyuncunun içinde olduğu her çete.

Taraf: Server · Bekler: hayır

Signature
online.gangs(source: number): readonly string[]
Lua
local memberships = aiLib.online.gangs(source) -- { { gang = 'ballas', grade = 0 }, ... }

Dönüş değeri: çete adları (hiç yoksa boş). Lua karşılığı üyelik tablolarını { gang, grade } döndürür.

if (online.gangs(source).length === 0) return fail("NOT_IN_GANG");

online.jobGrade

Oyuncunun bir meslekteki rütbesi.

Taraf: Server · Bekler: hayır

Signature
online.jobGrade(source: number, job: string): number | null
Lua
local grade = aiLib.online.jobGrade(source, job) -- integer ya da nil

Prop

Type

Dönüş değeri: rütbe numarası ya da oyuncu üye değilse null (Lua’da nil). 0 rütbesi bir üyedir: truthy kontrolüyle değil, null ile karşılaştır.

const grade = online.jobGrade(source, "police");
if (grade === null || grade < 3) return fail("RANK_TOO_LOW");

online.gangGrade

Oyuncunun bir çetedeki rütbesi.

Taraf: Server · Bekler: hayır

Signature
online.gangGrade(source: number, gang: string): number | null
Lua
local grade = aiLib.online.gangGrade(source, gang) -- integer ya da nil

Dönüş değeri: rütbe ya da üye değilse null (Lua’da nil).

const boss = (online.gangGrade(source, "ballas") ?? -1) >= 4;

online.jobMembers

Görevde olsun ya da olmasın bir meslekte üyeliği olan her oyuncu.

Taraf: Server · Bekler: hayır

Signature
online.jobMembers(job: string): ReadonlySet<number>

Lua karşılığı yok.

Dönüş değeri: canlı bir server id set’i (hiç yoksa boş). Oyuncular mesleğe girip çıktıkça değişir: bir await’ten önce onu kopyala ([...set]).

for (const source of online.jobMembers("mechanic")) notify(source, "A customer is waiting", "info");

online.gangMembers

Bir çetedeki her oyuncu.

Taraf: Server · Bekler: hayır

Signature
online.gangMembers(gang: string): ReadonlySet<number>

Lua karşılığı yok. Dönüş değeri: canlı bir server id set’i; bir await’ten önce onu kopyala.

const members = [...online.gangMembers("ballas")];

online.dutyJob

Oyuncunun görevde olduğu meslek.

Taraf: Server · Bekler: hayır

Signature
online.dutyJob(source: number): string | null
Lua
local job = aiLib.online.dutyJob(source) -- string ya da nil

Dönüş değeri: meslek adı ya da görevde değilse null (Lua’da nil).

if (online.dutyJob(source) !== "ambulance") return fail("NOT_ON_DUTY");

online.onDutyIn

Oyuncunun birkaç meslekten birinde (tüm polis meslekleri, tüm EMS meslekleri) görevde olup olmadığı.

Taraf: Server · Bekler: hayır

Signature
online.onDutyIn(source: number, jobs: readonly string[]): boolean

Lua karşılığı yok. Dönüş değeri: görevdeki meslek jobs içindeyse true.

const POLICE = ["police", "sheriff"];
if (!online.onDutyIn(source, POLICE)) return fail("NOT_POLICE");

online.onDutyFor

Bir meslekte görevde olan oyuncular.

Taraf: Server · Bekler: hayır

Signature
online.onDutyFor(job: string): ReadonlySet<number>

Lua karşılığı yok. Dönüş değeri: canlı bir server id set’i; bir await’ten önce onu kopyala.

for (const medic of online.onDutyFor("ambulance")) notify(medic, "Patient at Pillbox", "info");

online.countOnDuty

Birkaç meslek genelinde kaç oyuncunun görevde olduğu.

Taraf: Server · Bekler: hayır

Signature
online.countOnDuty(jobs: readonly string[]): number

Lua karşılığı yok. Dönüş değeri: toplam sayı (maliyet oyuncu sayısıyla değil, meslek sayısıyla artar).

if (online.countOnDuty(["police", "sheriff"]) < 2) return fail("NOT_ENOUGH_POLICE");

online.allows

Oyuncunun bir erişim kuralıyla (meslekler, çeteler, minimum rütbeler, görevde olma) kilitlenmiş bir yeri kullanıp kullanamayacağı. Client’ın allows’u aynı kuralı kullanır; böylece oyuncunun gördüğü, server’ın kullanmasına izin verdiğiyle aynıdır.

Taraf: Server · Bekler: hayır

Signature
online.allows(source: number, rule: AccessRule): boolean

Lua karşılığı yok.

Prop

Type

Dönüş değeri: listeler boşsa, oyuncu gangs’ten birinde minimum rütbesindeyse ya da jobs’tan birinde minimum rütbesindeyse (ve onDuty ayarlıysa o meslekte görevdeyse) true.

import { online } from "@aicore-fivem/lib/server";
import { readAccess, fail } from "@aicore-fivem/lib/shared";

const rule = readAccess(stashRecord); // { jobs: ["police"], gangs: [], grades: { police: 2 }, onDuty: true }
if (!online.allows(source, rule)) return fail("NO_ACCESS");

Karakter yaşam döngüsü

onCharacter

Bir karakter dünyaya girdiğinde ve ayrıldığında kodunu çalıştırır. Resource’un (yeniden) başladığında dünyada zaten olan karakterleri de yeniden oynatır ve oyundan çıkmaları kapsar; bu yüzden yanında bir playerDropped handler’ına ihtiyacın olmaz.

Taraf: Server · Bekler: hayır

Signature
onCharacter(handlers: CharacterHandlers): void
Lua
aiLib.onCharacter({ loaded = function(source, character) end, unloaded = function(source, charId, reason) end })

CharacterHandlers

Prop

Type

Dönüş değeri

void.

Örnek

import { onCharacter, PlayerMap } from "@aicore-fivem/lib/server";

const playtime = new PlayerMap<{ charId: number; since: number }>();

onCharacter({
  loaded(source, character) {
    playtime.set(source, { charId: character.id, since: Date.now() });
  },
  unloaded(source, charId, reason) {
    const entry = playtime.get(source);
    if (entry) console.log(`character ${charId} played ${Date.now() - entry.since} ms (${reason})`);
    playtime.delete(source);
  },
});

Onu bir server dosyasının en üst seviyesinde bir kez çağır. Yukarıdaki PlayerMap oyundan çıkışta kendiliğinden boşalır; bir çıkış için unloaded handler’ı yine önce çalışır, böylece veriyi orada kaydedebilirsin.

Oyuncularla konuşmak

notify

Bir oyuncunun ekranında bir bildirim kartı gösterir (onu ai-lib ya da sahibin ai-core panelinde seçtiği bildirim arayüzü çizer).

Taraf: Server · Bekler: hayır

Signature
notify(source: number, text: string, kind?: NotificationKind, duration?: number): void
Lua
aiLib.notify(source, text, kind, duration)

Parametreler

Prop

Type

Dönüş değeri

void.

Örnek

import { notify, createLocale } from "@aicore-fivem/lib/server";

const Locale = createLocale();
notify(source, Locale("PAID", 250), "success");

Bir client aksiyonuna cevap vermek için notify çağırmak yerine fail("KEY") döndür: client’ın callAction’ı çevrilmiş hatayı kendisi gösterir.

isOnline

Bir oyuncunun hâlâ bağlı olup olmadığı. Uzun bir await’ten sonra bir şey vermeden önce kontrol et.

Taraf: Server · Bekler: hayır

Signature
isOnline(source: number): boolean

Lua karşılığı yok: GetPlayerName(source) ~= nil.

Dönüş değeri: oyuncu bağlıyken true.

await new Promise((resolve) => setTimeout(resolve, 60_000));
if (!isOnline(source)) return;

Yakındaki oyuncular

Bunlar, server’daki her oyuncuyu dolaşmak yerine ai-core’a hangi oyuncuların birbirinin OneSync menzilinde (yaklaşık 424 metre) olduğunu sorar ve yalnızca o birkaç oyuncuyu ölçer. Yaklaşık 424 m’den büyük bir yarıçap daha uzaktaki kimseyi bulmaz.

playersNear

Bir oyuncuya radius metre içindeki oyuncular.

Taraf: Server · Bekler: hayır

Signature
playersNear(source: number, radius: number): number[]
Lua
local list = aiLib.playersNear(source, radius) -- integer[]

Prop

Type

Dönüş değeri: bir server id dizisi. TypeScript source’un kendisini de içerir; Lua onu dışarıda bırakır.

for (const other of playersNear(source, 20)) if (other !== source) notify(other, "You hear a scream", "warning");

playersNearEntity

Ağa bağlı bir entity’ye (bir araç, bir prop) radius metre içindeki oyuncular: sahibi ve sahibinin menzilindeki oyuncular.

Taraf: Server · Bekler: hayır

Signature
playersNearEntity(entity: number, radius: number): number[]
Lua
local list = aiLib.playersNearEntity(entity, radius) -- integer[]

Prop

Type

Dönüş değeri: bir server id dizisi. Entity yoksa ya da hiçbir oyuncu onun sahibi değilse boş (sahibi yalnızca server olan bir entity’nin yakınında kimse yoktur).

const witnesses = playersNearEntity(vehicle, 30);

nearestPlayerTo

Bir oyuncuya metres mesafe içindeki, filtrenden geçen en yakın diğer oyuncu.

Taraf: Server · Bekler: hayır

Signature
nearestPlayerTo(source: number, metres: number, filter?: (other: number) => boolean): number | null
Lua
local other = aiLib.nearestPlayerTo(source, metres, filter) -- integer ya da nil

Prop

Type

Dönüş değeri: en yakın eşleşmenin server id’si ya da hiç yoksa null (Lua’da nil).

const target = nearestPlayerTo(source, 2, (other) => online.character(other) !== null);
if (target === null) return fail("NOBODY_NEAR");

Konumlar

Lua karşılıkları yok: Lua’da vektör matematiği vardır (GetEntityCoords(GetPlayerPed(source)), #(a - b)).

coordsOf

Server’ın gördüğü haliyle bir oyuncunun ped konumu.

Taraf: Server · Bekler: hayır

Signature
coordsOf(source: number): Vec3

Dönüş değeri: bir Vec3 { x, y, z }.

const { x, y, z } = coordsOf(source);

entityCoords

Herhangi bir entity’nin konumu (sahibinin senkronunun server’daki kopyası).

Taraf: Server · Bekler: hayır

Signature
entityCoords(entity: number): Vec3

Dönüş değeri: bir Vec3.

import { distance } from "@aicore-fivem/lib/shared";
if (distance(entityCoords(vehicle), coordsOf(source)) > 5) return fail("TOO_FAR");

isNear

Oyuncunun ped’inin bir noktaya metres kadar yakın olup olmadığı. near koruması bunu kullanır.

Taraf: Server · Bekler: hayır

Signature
isNear(source: number, point: Vec3, metres: number): boolean

Prop

Type

Dönüş değeri: menzil içindeyse true.

if (!isNear(source, { x: 25.7, y: -1347.3, z: 29.5 }, 3)) return fail("TOO_FAR");

vehicleOf

Oyuncunun oturduğu araç.

Taraf: Server · Bekler: hayır

Signature
vehicleOf(source: number, options?: { driver?: boolean }): number

Prop

Type

Dönüş değeri: araç entity’si ya da yayaysa (veya driver: true ile sürücü koltuğunda değilse) 0.

const vehicle = vehicleOf(source, { driver: true });
if (!vehicle) return fail("NOT_DRIVING");

Oyuncu başına depolar

Bunların hiçbirinin Lua karşılığı yok: bir Lua tablosu kullan ve onu playerDropped’ta temizle. Her depoyu modül seviyesinde bir kez oluştur, asla bir handler içinde değil.

PlayerMap

Oyuncu server id’siyle anahtarlanan ve oyuncu oyundan çıktığında kendini boşaltan bir Map. Oyuncu başına tuttuğun her şey için kullan (açık bir ekran, seçili bir eşya).

Taraf: Server · Bekler: hayır

Signature
new PlayerMap<V>(options?: { onDrop?: (source: number, value: V) => void })

Prop

Type

Bir JavaScript Map’inin tüm metotlarına sahiptir (get, set, has, delete, size, iterasyon).

import { PlayerMap } from "@aicore-fivem/lib/server";

const carrying = new PlayerMap<string>({
  onDrop: (source, item) => console.log(`${source} left while carrying ${item}`),
});
carrying.set(source, "box");

Cooldowns

Oyun saatine göre oyuncu başına cooldown’lar; oyuncu oyundan çıktığında serbest bırakılır.

Taraf: Server · Bekler: hayır

Signature
new Cooldowns()
ready(source: number): boolean
start(source: number, ms: number): void
take(source: number, ms: number): boolean
left(source: number): number
clear(source: number): void
MetotNe yaparDönüş değeri
ready(source)Oyuncu için çalışan bir cooldown yok.boolean
start(source, ms)Oyuncunun cooldown’ını ms milisaniye için başlatır (ya da yeniden başlatır).void
take(source, ms)Hazırsa cooldown’ı başlatır.başladıysa true, biri hâlâ çalışıyorken false
left(source)Kalan milisaniye.number, hazırsa 0
clear(source)Oyuncunun cooldown’ını bitirir.void
import { onClientCallback, Cooldowns } from "@aicore-fivem/lib/server";
import { fail } from "@aicore-fivem/lib/shared";

const searchCooldown = new Cooldowns();

onClientCallback("my-bins:search", (source) => {
  if (!searchCooldown.take(source, 30_000)) return fail("WAIT");
  return { ok: true };
});

Bir oyuncu yeniden bağlanarak bir Cooldowns girdisini sıfırlayabilir. Yeniden bağlanmadan sonra da kalması gereken bir cooldown, online.charId(source) ile anahtarlanan KeyedCooldowns kullanır.

KeyedCooldowns

Bir bağlantıdan uzun yaşayan herhangi bir anahtara göre cooldown’lar: bir karakter id’si, bir entity, bir kapı id’si. Süresi dolan girdiler map büyüdükçe temizlenir.

Taraf: Server · Bekler: hayır

Signature
new KeyedCooldowns<K = number>()
ready(key: K): boolean
start(key: K, ms: number): void
take(key: K, ms: number): boolean
left(key: K): number
clear(key: K): void

Cooldowns ile aynı metotlar, key’e göre. ms 0 ya da daha az olan bir start cooldown’ı bitirir.

import { KeyedCooldowns, online } from "@aicore-fivem/lib/server";
import { fail } from "@aicore-fivem/lib/shared";

const robbed = new KeyedCooldowns<string>(); // market id’sine göre
const perCharacter = new KeyedCooldowns(); // karakter id’sine göre

function canRob(source: number, store: string) {
  const charId = online.charId(source);
  if (charId === undefined) return fail("NO_CHARACTER");
  if (!robbed.ready(store)) return fail("STORE_EMPTY");
  if (!perCharacter.take(charId, 15 * 60_000)) return fail("WAIT");
  robbed.start(store, 30 * 60_000);
  return { ok: true };
}

SourceLock

Birkaç callback genelinde oyuncu başına çalışan tek bir aksiyon. Bir callback’in kendi lock: true’su yalnızca o callback’i kapsar; birbirini dışlaması gereken iki aksiyon (aynı aracı listelemek ve satmak) tek bir SourceLock paylaşır.

Taraf: Server · Bekler: hayır

Signature
new SourceLock()
run<T, B>(source: number, busy: B, task: () => Promise<T>): Promise<T | B>
release(source: number): void

Prop

Type

Dönüş değeri (run): görevin sonucu ya da busy. release(source) oyuncuyu serbest bırakır; onu playerDropped’tan çağır (hâlâ çalışan bir görev, sonuçlanana kadar kendi girdisini tutar).

import { onClientCallback, SourceLock } from "@aicore-fivem/lib/server";
import { fail } from "@aicore-fivem/lib/shared";

const dealer = new SourceLock();
on("playerDropped", () => dealer.release(Number(globalThis.source)));

onClientCallback("my-dealer:list", (source) => dealer.run(source, fail("BUSY"), async () => ({ ok: true })));
onClientCallback("my-dealer:sell", (source) => dealer.run(source, fail("BUSY"), async () => ({ ok: true })));

RateLimit

Oyuncu başına bir patlama sınırı: herhangi bir windowMs içinde en fazla count aksiyon. Kapı zaten her girdiye hız sınırı uygular; bunu tek bir aksiyon için ek, daha yavaş bir sınır olarak kullan.

Taraf: Server · Bekler: hayır

Signature
new RateLimit(count: number, windowMs: number)
take(source: number): boolean
release(source: number): void
Lua
local limit = aiLib.RateLimit.new(count, windowMs)
local allowed = limit:take(source)
limit:release(source)

Prop

Type

Dönüş değeri (take): true ve bir aksiyonu sayar ya da oyuncu sınırı aşmışsa false (sayılmaz). release(source) oyuncuyu unutur; onu playerDropped’tan çağır.

import { onClientCallback, RateLimit } from "@aicore-fivem/lib/server";
import { fail } from "@aicore-fivem/lib/shared";

const reports = new RateLimit(3, 60_000);
on("playerDropped", () => reports.release(Number(globalThis.source)));

onClientCallback("my-report:send", (source) => {
  if (!reports.take(source)) return fail("TOO_MANY_REPORTS");
  return { ok: true };
});

KeyWatchers

Tek bir anahtarın canlı değişikliklerini alan oyuncular (bir otoparkın arabaları, açık bir karakter sayfası). Oyuncu başına bir anahtar: yeni bir anahtarı izlemek eskisini bırakır. Oyundan çıkışta serbest bırakılır; her işlem sabit zamanlıdır.

Taraf: Server · Bekler: hayır

Signature
new KeyWatchers<K>()
watch(source: number, key: K): void
stop(source: number): void
of(key: K): ReadonlySet<number> | undefined
keyOf(source: number): K | undefined
MetotNe yaparDönüş değeri
watch(source, key)Oyuncu key’i izler ve öncekini izlemeyi bırakır.void
stop(source)Oyuncu izlemeyi bırakır.void
of(key)key’i izleyen oyuncular.bir set ya da kimse izlemiyorsa undefined
keyOf(source)Oyuncunun izlediği anahtar.anahtar ya da undefined
import { onClientCallback, onClientEvent, KeyWatchers } from "@aicore-fivem/lib/server";
import { oneArg, readText } from "@aicore-fivem/lib/shared";

const lots = new KeyWatchers<string>();

onClientCallback("my-lot:open", { args: oneArg((v) => readText(v, 32)) }, (source, lot) => {
  lots.watch(source, lot);
  return { ok: true, cars: [] }; // snapshot
});
onClientEvent("my-lot:close", (source) => lots.stop(source));

function carChanged(lot: string, car: unknown) {
  for (const source of lots.of(lot) ?? []) emitNet("my-lot:car", source, car); // delta
}

Bu sayfada