Arquitectura
La regla única
Sección titulada «La regla única»bs_core es dueño del estado. Dinero, XP, necesidades, posición, datos del personaje: solo bs_core los escribe, y solo bs_core guarda la fila. Todos los demás recursos se lo piden a bs_core mediante exports. Eso mantiene los guardados honestos: hay un único sitio que marca un personaje como modificado y un único sitio que lo escribe.
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/sqlEl cliente pide, el servidor decide, el servidor devuelve el resultado. Todo lo que envía un cliente se vuelve a comprobar en el servidor. Las necesidades, por ejemplo, solo pueden bajar por un reporte del cliente; restaurar el hambre es una acción del servidor a partir de un consumible.
Mapa de recursos
Sección titulada «Mapa de recursos»Qué hace cada uno está en la lista de resources.
| Grupo | Recursos |
|---|---|
| Core | bs_core, bs_loading, bs_multichar, bs_esx_bridge |
| UI | bs_ui (HUD, notify, partes NUI compartidas), bs_radial, bs_menu (hub), bs_chat, bs_scoreboard |
| Jugador | bs_inventory, bs_skills, bs_medical, bs_bodily, bs_emotes, bs_boosters, bs_gadgets, bs_drugs |
| Mundo | 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 |
| Zombis | 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 |
Estados de sesión
Sección titulada «Estados de sesión»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 y una desconexión pasan ambos por BS.UnloadPlayer, que fuerza el guardado primero. Del lado del cliente, exports.bs_core:GetSessionState() te dice dónde estás, y exports.bs_core:IsLoaded() es la comprobación simple de “hay un personaje”.
Datos del jugador desde otro recurso
Sección titulada «Datos del jugador desde otro recurso»El objeto Player vive en el estado Lua de bs_core. Las funciones no sobreviven a la frontera de los exports, así que no recibes el objeto: llamas a exports que hacen el trabajo:
-- 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.RewardsDel lado del cliente: exports.bs_core:GetData(), GetNeeds(), IsLoaded().
Los metadatos del personaje (GetPlayerMeta/SetPlayerMeta) son el sitio fácil para datos pequeños por personaje. bs_drugs guarda ahí la adicción, sin tabla propia.
bs_core tiene un pub/sub en proceso (BS.On / BS.Emit) y vuelve a difundir cada hook como un evento normal llamado bs:hook:<name>. Desde tu recurso:
AddEventHandler('bs:hook:player:loaded', function(data) ... end)AddEventHandler('bs:hook:player:unloaded', function(...) ... end)| Hook | Lado |
|---|---|
core:ready | ambos |
player:loaded, player:spawned, player:unloaded | ambos |
player:money, player:levelUp | ambos |
player:xp, player:action | servidor |
character:created, character:selected | servidor |
session:state, needs:changed | cliente |
El puente de callbacks (BSB)
Sección titulada «El puente de callbacks (BSB)»Petición/respuesta sobre net events. Incluye el archivo en tu manifest y tendrás BSB en tu propio estado 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 timeoutEn el otro sentido, el servidor pregunta a un cliente:
-- clientBSB.RegisterClientCallback('mything:where', function() return GetEntityCoords(PlayerPedId()) end)-- serverlocal pos = BSB.AwaitClient(src, 'mything:where', 3000)Cómo funciona: una petición se difunde a todos los recursos y el que registró el nombre responde, los demás callan. Por tanto:
- un nombre desconocido o de un recurso detenido da timeout, no da error
- los nombres son globales, ponles el prefijo de tu sistema (
squad:invite,factions:roster,vendor:buy) - el servidor limita cada cliente a 40 peticiones por segundo por recurso
- un handler que lanza un error imprime
callback "x" erroredy el cliente recibenil
La convención para los retornos es una tabla con ok, y error cuando falla: { ok = false, error = 'Not in a squad.' }.
También existe BSB.RegisterCommand(name, ace, help, args, handler) (comprobación ACE, pcall y sugerencia en el chat en uno) y BSB.Radial(spec) en el cliente para la rueda radial. Úsalos en vez de montar los tuyos.
Servicios
Sección titulada «Servicios»@bs_core/shared/sh_services.lua da BS.Provide / BS.Use. Es una forma comprobada de llamar a exports: el proveedor dice lo que ofrece, el consumidor recibe un handle o una línea de log bien visible, nunca un nil silencioso.
local inv = BS.Use('inventory')if inv then inv.GiveItem(src, 'bandage', 1) endEs nuevo, la mayor parte del código aún llama a los exports directamente. Ambos funcionan.
Statebags usados
Sección titulada «Statebags usados»| Bag | En | Lo define | Significado |
|---|---|---|---|
bsDowned | jugador | bs_medical | el jugador está caído |
bsDead, bsRevivable | jugador | bs_medical | |
bsDrugMods | jugador (local) | bs_drugs | drain, regen, cap, infection, sway |
bsSteady | jugador (local) | bs_boosters | multiplicador de retroceso/vaivén |
bsDrone | jugador | bs_gadgets | pilotando un dron |
bsSquad | jugador | bs_factions | información de escuadra |
bsFactionStyle, bsFactionTag, bsFactionCanInvite, bsFactionSafeZone | jugador | bs_factions | estilo de tag, permisos, zona segura |
bsVipTag | jugador | bs_vip | { tier, colour, style, hex, blip, hud }, nil sin VIP |
bsInRaid, bsRogue | jugador | bs_extraction | estado de la zona PvP |
bsSitting | jugador | bs_emotes | |
bsZombie | entidad | bs_zombies | id del registro del walker. También zType, zGait, zState… en modo en red |
bsZombieDead | entidad | bs_zombies | cadáver en red |
bsCorpse | entidad | bs_medical | cuerpo de jugador |
bsProtected, bsKeep | entidad | varios | el barrido de limpieza de bs_world la omite |
bsVehicle, bsLocked, bsOwned, bsWindowsDown | vehículo | bs_vehicles | |
bsBuilt | entidad | bs_building |
Si creas una entidad que bs_world no debe limpiar, define bsProtected en ella.
Nombres de eventos
Sección titulada «Nombres de eventos»bs:<system>:<thing>, en lowerCamelCase al final:
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 guarda sus propios nombres en BS.Events (bs_core/shared/sh_events.lua) para que nada los escriba a mano. Los hooks son bs:hook:*. Los nombres de callbacks usan system:action sin el prefijo bs:.
Cada evento y callback por recurso está en la referencia de la 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.