Architecture
La règle unique
Section intitulée « La règle unique »bs_core possède l’état. Argent, XP, besoins, position, données du personnage : seul bs_core les écrit, et seul bs_core sauvegarde la ligne. Toutes les autres ressources demandent à bs_core via des exports. C’est ce qui garde les sauvegardes fiables : un seul endroit marque un personnage comme modifié et un seul endroit l’écrit.
NUI (html in each resource, shared shell in bs_ui) | SendNUIMessage / fetch (NUI callbacks)Client Lua mirrors state, never authoritative | net events + BSB callbacksServer Lua Player objects, validation, hooks | oxmysql (behind bs_core/server/sv_database.lua)MariaDB migrations in bs_core/sqlLe client demande, le serveur décide, le serveur renvoie le résultat. Tout ce qu’un client envoie est revérifié côté serveur. Les besoins, par exemple, ne peuvent que baisser à partir d’un rapport du client ; restaurer la faim est une action du serveur depuis un consommable.
Carte des ressources
Section intitulée « Carte des ressources »Ce que fait chacune est détaillé dans la liste des resources.
| Groupe | Ressources |
|---|---|
| Core | bs_core, bs_loading, bs_multichar, bs_esx_bridge |
| UI | bs_ui (HUD, notify, éléments NUI partagés), bs_radial, bs_menu (hub), bs_chat, bs_scoreboard |
| Joueur | bs_inventory, bs_skills, bs_medical, bs_bodily, bs_emotes, bs_boosters, bs_gadgets, bs_drugs |
| Monde | bs_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 |
| Zombies | bs_zombies, bs_zombiepeds, bs_zombiesfx, bs_atlas, nodes |
| Social | bs_factions, bs_faction_bases, bs_jobs, bs_vip |
| Staff | bs_admin, bs_tester, bs_config, bs_guard, bs_cinecam |
| Assets | bs_assets |
États de session
Section intitulée « États de session »connecting -> playerConnecting: identity resolved, ban checkedloading -> client handshake (bs:session:clientReady)select -> limbo, frozen and invisible, character list/create/delete/selectplaying -> Player object exists server side, ped spawned/logout et une déconnexion passent tous deux par BS.UnloadPlayer, qui force d’abord la sauvegarde. Côté client, exports.bs_core:GetSessionState() vous dit où vous en êtes, et exports.bs_core:IsLoaded() est la vérification simple « y a-t-il un personnage ».
Données du joueur depuis une autre ressource
Section intitulée « Données du joueur depuis une autre ressource »L’objet Player vit dans l’état Lua de bs_core. Les fonctions ne traversent pas la frontière des exports, vous n’obtenez donc pas l’objet : vous appelez des exports qui font le travail :
-- serverlocal 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.RewardsCôté client : exports.bs_core:GetData(), GetNeeds(), IsLoaded().
Les métadonnées du personnage (GetPlayerMeta/SetPlayerMeta) sont l’endroit facile pour de petites données par personnage. bs_drugs y garde la dépendance, sans table à lui.
bs_core a un pub/sub en processus (BS.On / BS.Emit) et rediffuse chaque hook comme un événement normal appelé bs:hook:<name>. Depuis votre ressource :
AddEventHandler('bs:hook:player:loaded', function(data) ... end)AddEventHandler('bs:hook:player:unloaded', function(...) ... end)| Hook | Côté |
|---|---|
core:ready | les deux |
player:loaded, player:spawned, player:unloaded | les deux |
player:money, player:levelUp | les deux |
player:xp, player:action | serveur |
character:created, character:selected | serveur |
session:state, needs:changed | client |
Le pont de callbacks (BSB)
Section intitulée « Le pont de callbacks (BSB) »Requête/réponse via des net events. Incluez le fichier dans votre manifest et vous obtenez BSB dans votre propre état Lua :
shared_scripts { '@bs_core/shared/sh_bridge.lua' }-- serverBSB.RegisterCallback('mything:get', function(src, a, b) return { ok = true, value = a + b }end)
-- clientlocal res = BSB.Await('mything:get', 5000, 1, 2) -- blocks this thread, nil on timeoutDans l’autre sens, le serveur interroge un client :
-- clientBSB.RegisterClientCallback('mything:where', function() return GetEntityCoords(PlayerPedId()) end)-- serverlocal pos = BSB.AwaitClient(src, 'mything:where', 3000)Comment ça marche : une requête est diffusée à toutes les ressources et celle qui a enregistré le nom répond, les autres se taisent. Donc :
- un nom inconnu ou arrêté expire en timeout, il ne provoque pas d’erreur
- les noms sont globaux, préfixez-les avec votre système (
squad:invite,factions:roster,vendor:buy) - le serveur limite chaque client à 40 requêtes par seconde par ressource
- un handler qui lève une erreur affiche
callback "x" erroredet le client reçoitnil
La convention pour les retours est une table avec ok, et error en cas d’échec : { ok = false, error = 'Not in a squad.' }.
Il y a aussi BSB.RegisterCommand(name, ace, help, args, handler) (vérification ACE, pcall et suggestion de chat en un) et BSB.Radial(spec) côté client pour la roue radiale. Utilisez-les plutôt que de bricoler les vôtres.
Services
Section intitulée « Services »@bs_core/shared/sh_services.lua fournit BS.Provide / BS.Use. C’est une façon vérifiée d’appeler des exports : le fournisseur dit ce qu’il offre, le consommateur reçoit un handle ou une ligne de log bien visible, jamais un nil silencieux.
local inv = BS.Use('inventory')if inv then inv.GiveItem(src, 'bandage', 1) endC’est récent, la plupart du code appelle encore les exports directement. Les deux fonctionnent.
Statebags utilisés
Section intitulée « Statebags utilisés »| Bag | Sur | Défini par | Signification |
|---|---|---|---|
bsDowned | joueur | bs_medical | le joueur est à terre |
bsDead, bsRevivable | joueur | bs_medical | |
bsDrugMods | joueur (local) | bs_drugs | drain, regen, cap, infection, sway |
bsSteady | joueur (local) | bs_boosters | multiplicateur de recul/balancement |
bsDrone | joueur | bs_gadgets | pilote un drone |
bsSquad | joueur | bs_factions | infos d’escouade |
bsFactionStyle, bsFactionTag, bsFactionCanInvite, bsFactionSafeZone | joueur | bs_factions | style du tag, permissions, safe zone |
bsVipTag | joueur | bs_vip | { tier, colour, style, hex, blip, hud }, nil sans VIP |
bsInRaid, bsRogue | joueur | bs_extraction | état de la zone PvP |
bsSitting | joueur | bs_emotes | |
bsZombie | entité | bs_zombies | id du registre du walker. Aussi zType, zGait, zState… en mode en réseau |
bsZombieDead | entité | bs_zombies | cadavre en réseau |
bsCorpse | entité | bs_medical | corps de joueur |
bsProtected, bsKeep | entité | plusieurs | le nettoyage de bs_world l’ignore |
bsVehicle, bsLocked, bsOwned, bsWindowsDown | véhicule | bs_vehicles | |
bsBuilt | entité | bs_building |
Si vous créez une entité que bs_world ne doit pas nettoyer, définissez bsProtected dessus.
Nommage des événements
Section intitulée « Nommage des événements »bs:<system>:<thing>, en lowerCamelCase à la fin :
bs:session:clientReady bs:characters:select bs:player:moneybs:inventory:state bs:squad:sync bs:z:addbs:ui:notify bs:ui:opened bs:medical:diedbs_core garde ses propres noms dans BS.Events (bs_core/shared/sh_events.lua) pour que rien ne les écrive en dur. Les hooks sont bs:hook:*. Les noms de callbacks utilisent system:action sans le préfixe bs:.
Chaque événement et callback par ressource se trouve dans la référence de l’API.
bs19.org · Privacy · Terms · Cookie settings
Cookies and outside content
The site only needs two essential cookies to keep you logged in and protect forms. Videos and streams from YouTube and Twitch load from their servers, which can set their own cookies, so they stay off until you say yes. Nothing is pre-ticked and you can change your mind any time with "Cookie settings" at the bottom of every page. Details.
Questions about the server, the framework or the site? A real person reads this, usually within a day.