Oyuncular ve karakterler
online, onCharacter, notify, yakındaki oyuncular, konumlar ve oyuncu başına depolar: PlayerMap, Cooldowns, KeyedCooldowns, SourceLock, RateLimit, KeyWatchers.
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
online.sources(): IterableIterator<number>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
readonly online.size: numberLua karşılığı yok: #aiLib.online.sources().
online.character
Bir oyuncunun yüklü karakteri.
Taraf: Server · Bekler: hayır
online.character(source: number): CharacterInfo | nulllocal character = aiLib.online.character(source) -- table ya da nilProp
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
online.charId(source: number): number | undefinedLua 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
online.name(source: number, options?: { fallback?: boolean }): stringlocal 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
online.jobs(source: number): readonly string[]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
online.gangs(source: number): readonly string[]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
online.jobGrade(source: number, job: string): number | nulllocal grade = aiLib.online.jobGrade(source, job) -- integer ya da nilProp
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
online.gangGrade(source: number, gang: string): number | nulllocal grade = aiLib.online.gangGrade(source, gang) -- integer ya da nilDö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
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
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
online.dutyJob(source: number): string | nulllocal job = aiLib.online.dutyJob(source) -- string ya da nilDö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
online.onDutyIn(source: number, jobs: readonly string[]): booleanLua 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
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
online.countOnDuty(jobs: readonly string[]): numberLua 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
online.allows(source: number, rule: AccessRule): booleanLua 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
onCharacter(handlers: CharacterHandlers): voidaiLib.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
notify(source: number, text: string, kind?: NotificationKind, duration?: number): voidaiLib.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
isOnline(source: number): booleanLua 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
playersNear(source: number, radius: number): number[]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
playersNearEntity(entity: number, radius: number): number[]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
nearestPlayerTo(source: number, metres: number, filter?: (other: number) => boolean): number | nulllocal other = aiLib.nearestPlayerTo(source, metres, filter) -- integer ya da nilProp
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
coordsOf(source: number): Vec3Dö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
entityCoords(entity: number): Vec3Dö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
isNear(source: number, point: Vec3, metres: number): booleanProp
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
vehicleOf(source: number, options?: { driver?: boolean }): numberProp
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
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
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| Metot | Ne yapar | Dö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
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): voidCooldowns 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
new SourceLock()
run<T, B>(source: number, busy: B, task: () => Promise<T>): Promise<T | B>
release(source: number): voidProp
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
new RateLimit(count: number, windowMs: number)
take(source: number): boolean
release(source: number): voidlocal 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
new KeyWatchers<K>()
watch(source: number, key: K): void
stop(source: number): void
of(key: K): ReadonlySet<number> | undefined
keyOf(source: number): K | undefined| Metot | Ne yapar | Dö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
}