Salta ai contenuti

Architettura

bs_core possiede lo stato. Denaro, XP, bisogni, posizione, dati del personaggio: solo bs_core li scrive e solo bs_core salva la riga. Ogni altra risorsa chiede a bs_core tramite export. È ciò che mantiene onesti i salvataggi: c’è un solo posto che segna un personaggio come modificato e un solo posto che scrive.

NUI (html in each resource, shared shell in bs_ui)
| SendNUIMessage / fetch (NUI callbacks)
Client Lua mirrors state, never authoritative
| net events + BSB callbacks
Server Lua Player objects, validation, hooks
| oxmysql (behind bs_core/server/sv_database.lua)
MariaDB migrations in bs_core/sql

Il client chiede, il server decide, il server rimanda il risultato. Tutto ciò che un client invia viene ricontrollato sul server. I bisogni, per esempio, da un report del client possono solo scendere, ripristinare la fame è un’azione del server da un consumabile.

Cosa fa ciascuna è nell’elenco delle resource.

GruppoRisorse
Nucleobs_core, bs_loading, bs_multichar, bs_esx_bridge
UIbs_ui (HUD, notifiche, parti NUI condivise), bs_radial, bs_menu (hub), bs_chat, bs_scoreboard
Giocatorebs_inventory, bs_skills, bs_medical, bs_bodily, bs_emotes, bs_boosters, bs_gadgets, bs_drugs
Mondobs_world, bs_interactions (ALT target dot, containers), bs_economy (loot), bs_traders, bs_vehicles, bs_building, bs_crafting, bs_gates, bs_stash, bs_recycler, bs_mining, bs_scenes, bs_events, bs_extraction
Zombiebs_zombies, bs_zombiepeds, bs_zombiesfx, bs_atlas, nodes
Socialebs_factions, bs_faction_bases, bs_jobs, bs_vip
Staffbs_admin, bs_tester, bs_config, bs_guard, bs_cinecam
Assetbs_assets
connecting -> playerConnecting: identity resolved, ban checked
loading -> client handshake (bs:session:clientReady)
select -> limbo, frozen and invisible, character list/create/delete/select
playing -> Player object exists server side, ped spawned

/logout e una disconnessione passano entrambi per BS.UnloadPlayer, che prima forza il salvataggio. Lato client, exports.bs_core:GetSessionState() dice dove sei, e exports.bs_core:IsLoaded() è il semplice controllo “c’è un personaggio”.

L’oggetto Player vive nello stato Lua di bs_core. Le funzioni non attraversano il confine degli export, quindi non ottieni l’oggetto, chiami export che fanno il lavoro:

-- server
local p = exports.bs_core:GetPlayerSummary(src) -- plain table: charId, name, level, xp, accounts, needs, role...
exports.bs_core:AddMoney(src, 'cash', 50, 'my_reason')
exports.bs_core:RemoveMoney(src, 'bank', 200, 'my_reason')
exports.bs_core:AddXP(src, 25, 'my_reason')
local v = exports.bs_core:GetPlayerMeta(src, 'my_key', nil)
exports.bs_core:SetPlayerMeta(src, 'my_key', { any = 'table' })
exports.bs_core:AwardAction(src, 'zombie_kill') -- uses Config.Rewards

Lato client: exports.bs_core:GetData(), GetNeeds(), IsLoaded().

I metadati del personaggio (GetPlayerMeta/SetPlayerMeta) sono il posto facile per piccoli dati per personaggio. bs_drugs tiene lì la dipendenza senza una tabella propria.

bs_core ha un pub/sub in-process (BS.On / BS.Emit) e ritrasmette ogni hook come un normale evento chiamato bs:hook:<name>. Dalla tua risorsa:

AddEventHandler('bs:hook:player:loaded', function(data) ... end)
AddEventHandler('bs:hook:player:unloaded', function(...) ... end)
HookLato
core:readyentrambi
player:loaded, player:spawned, player:unloadedentrambi
player:money, player:levelUpentrambi
player:xp, player:actionserver
character:created, character:selectedserver
session:state, needs:changedclient

Richiesta/risposta su eventi net. Includi il file nel tuo manifest e ottieni BSB nel tuo stato Lua:

shared_scripts { '@bs_core/shared/sh_bridge.lua' }
-- server
BSB.RegisterCallback('mything:get', function(src, a, b)
return { ok = true, value = a + b }
end)
-- client
local res = BSB.Await('mything:get', 5000, 1, 2) -- blocks this thread, nil on timeout

L’altro verso, il server chiede a un client:

-- client
BSB.RegisterClientCallback('mything:where', function() return GetEntityCoords(PlayerPedId()) end)
-- server
local pos = BSB.AwaitClient(src, 'mything:where', 3000)

Come funziona: una richiesta viene trasmessa a ogni risorsa e risponde quella che ha registrato il nome, le altre tacciono. Quindi:

  • un nome sconosciuto o fermato va in timeout, non dà errore
  • i nomi sono globali, prefissali con il tuo sistema (squad:invite, factions:roster, vendor:buy)
  • il server limita ogni client a 40 richieste al secondo per risorsa
  • un gestore che lancia un errore stampa callback "x" errored e il client riceve nil

La convenzione per i ritorni è una tabella con ok, ed error quando fallisce: { ok = false, error = 'Not in a squad.' }.

C’è anche BSB.RegisterCommand(name, ace, help, args, handler) (controllo ACE, pcall e suggerimento in chat in uno) e BSB.Radial(spec) sul client per la ruota radiale. Usali invece di scrivere i tuoi.

@bs_core/shared/sh_services.lua fornisce BS.Provide / BS.Use. È un modo controllato di chiamare gli export: il fornitore dice cosa offre, il consumatore ottiene un handle o una riga di log rumorosa, mai un nil silenzioso.

local inv = BS.Use('inventory')
if inv then inv.GiveItem(src, 'bandage', 1) end

È nuovo, la maggior parte del codice chiama ancora gli export direttamente. Funzionano entrambi.

BagSuImpostato daSignificato
bsDownedgiocatorebs_medicalil giocatore è a terra
bsDead, bsRevivablegiocatorebs_medical
bsDrugModsgiocatore (locale)bs_drugsdrain, regen, cap, infection, sway
bsSteadygiocatore (locale)bs_boostersmoltiplicatore di rinculo/oscillazione
bsDronegiocatorebs_gadgetspilotaggio di un drone
bsSquadgiocatorebs_factionsinfo sulla squadra
bsFactionStyle, bsFactionTag, bsFactionCanInvite, bsFactionSafeZonegiocatorebs_factionsstile del tag, permessi, zona sicura
bsVipTaggiocatorebs_vip{ tier, colour, style, hex, blip, hud }, nil senza VIP
bsInRaid, bsRoguegiocatorebs_extractionstato della zona PvP
bsSittinggiocatorebs_emotes
bsZombieentitàbs_zombiesid del record del camminatore. Anche zType, zGait, zState… in modalità di rete
bsZombieDeadentitàbs_zombiescadavere in rete
bsCorpseentitàbs_medicalcorpo del giocatore
bsProtected, bsKeepentitàmoltila pulizia di bs_world lo salta
bsVehicle, bsLocked, bsOwned, bsWindowsDownveicolobs_vehicles
bsBuiltentitàbs_building

Se fai comparire un’entità che non deve essere ripulita da bs_world, imposta bsProtected su di essa.

bs:<system>:<thing>, lower camel case alla fine:

bs:session:clientReady bs:characters:select bs:player:money
bs:inventory:state bs:squad:sync bs:z:add
bs:ui:notify bs:ui:opened bs:medical:died

bs_core tiene i propri nomi in BS.Events (bs_core/shared/sh_events.lua) così nulla è scritto a mano. Gli hook sono bs:hook:*. I nomi dei callback usano system:action senza il prefisso bs:.

Ogni evento e callback per risorsa è nel riferimento API.