Architektura
Jedna zasada
Dział zatytułowany „Jedna zasada”bs_core jest właścicielem stanu. Pieniądze, XP, potrzeby, pozycja, dane postaci: tylko bs_core je zapisuje i tylko bs_core zapisuje wiersz. Wszystkie inne zasoby pytają bs_core przez eksporty. Dzięki temu zapisy są uczciwe: jest jedno miejsce, które oznacza postać jako zmienioną, i jedno, które zapisuje.
Warstwy
Dział zatytułowany „Warstwy”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/sqlKlient pyta, serwer decyduje, serwer odsyła wynik. Wszystko, co wysyła klient, jest sprawdzane ponownie na serwerze. Na przykład potrzeby z raportu klienta mogą tylko spadać, przywracanie głodu to akcja serwera z przedmiotu zużywalnego.
Mapa zasobów
Dział zatytułowany „Mapa zasobów”Co robi każdy z nich, opisuje lista resource’ów.
| Grupa | Zasoby |
|---|---|
| Rdzeń | bs_core, bs_loading, bs_multichar, bs_esx_bridge |
| UI | bs_ui (HUD, powiadomienia, wspólne elementy NUI), bs_radial, bs_menu (hub), bs_chat, bs_scoreboard |
| Gracz | bs_inventory, bs_skills, bs_medical, bs_bodily, bs_emotes, bs_boosters, bs_gadgets, bs_drugs |
| Świat | 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 |
| Zombie | bs_zombies, bs_zombiepeds, bs_zombiesfx, bs_atlas, nodes |
| Społeczność | bs_factions, bs_faction_bases, bs_jobs, bs_vip |
| Personel | bs_admin, bs_tester, bs_config, bs_guard, bs_cinecam |
| Zasoby graficzne | bs_assets |
Stany sesji
Dział zatytułowany „Stany sesji”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 i rozłączenie przechodzą przez BS.UnloadPlayer, który najpierw wymusza zapis. Po stronie klienta exports.bs_core:GetSessionState() mówi, gdzie jesteś, a exports.bs_core:IsLoaded() to proste sprawdzenie „czy jest postać”.
Dane gracza z innego zasobu
Dział zatytułowany „Dane gracza z innego zasobu”Obiekt Player żyje w stanie Lua bs_core. Funkcje nie przechodzą przez granicę eksportu, więc nie dostajesz obiektu, tylko wywołujesz eksporty, które robią robotę:
-- 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.RewardsStrona klienta: exports.bs_core:GetData(), GetNeeds(), IsLoaded().
Metadane postaci (GetPlayerMeta/SetPlayerMeta) to łatwe miejsce na małe dane na postać. bs_drugs trzyma tam uzależnienie bez własnej tabeli.
bs_core ma wewnątrzprocesowy pub/sub (BS.On / BS.Emit) i rozsyła każdy hook ponownie jako zwykłe zdarzenie bs:hook:<name>. Z twojego zasobu:
AddEventHandler('bs:hook:player:loaded', function(data) ... end)AddEventHandler('bs:hook:player:unloaded', function(...) ... end)| Hook | Strona |
|---|---|
core:ready | obie |
player:loaded, player:spawned, player:unloaded | obie |
player:money, player:levelUp | obie |
player:xp, player:action | server |
character:created, character:selected | server |
session:state, needs:changed | client |
Most callbacków (BSB)
Dział zatytułowany „Most callbacków (BSB)”Żądanie/odpowiedź przez zdarzenia net. Dołącz plik w manifeście, a dostaniesz BSB we własnym stanie 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 timeoutW drugą stronę, serwer pyta jednego klienta:
-- clientBSB.RegisterClientCallback('mything:where', function() return GetEntityCoords(PlayerPedId()) end)-- serverlocal pos = BSB.AwaitClient(src, 'mything:where', 3000)Jak to działa: żądanie jest rozsyłane do każdego zasobu, odpowiada ten, który zarejestrował nazwę, reszta milczy. Czyli:
- nieznana lub zatrzymana nazwa kończy się timeoutem, nie błędem
- nazwy są globalne, dodawaj prefiks swojego systemu (
squad:invite,factions:roster,vendor:buy) - serwer ogranicza każdego klienta do 40 żądań na sekundę na zasób
- handler, który rzuca błąd, wypisuje
callback "x" errored, a klient dostajenil
Konwencja zwrotów to tabela z ok i error przy niepowodzeniu: { ok = false, error = 'Not in a squad.' }.
Jest też BSB.RegisterCommand(name, ace, help, args, handler) (sprawdzenie ACE, pcall i podpowiedź na czacie w jednym) oraz BSB.Radial(spec) po stronie klienta dla kółka radialnego. Używaj ich zamiast pisać własne.
@bs_core/shared/sh_services.lua daje BS.Provide / BS.Use. To sprawdzany sposób wywoływania eksportów: dostawca mówi, co oferuje, a konsument dostaje uchwyt albo głośną linię w logu, nigdy cichego nil.
local inv = BS.Use('inventory')if inv then inv.GiveItem(src, 'bandage', 1) endTo nowość, większość kodu nadal wywołuje eksporty bezpośrednio. Działają oba sposoby.
Używane statebagi
Dział zatytułowany „Używane statebagi”| Bag | Na | Ustawia | Znaczenie |
|---|---|---|---|
bsDowned | gracz | bs_medical | gracz jest powalony |
bsDead, bsRevivable | gracz | bs_medical | |
bsDrugMods | gracz (lokalnie) | bs_drugs | drain, regen, cap, infection, sway |
bsSteady | gracz (lokalnie) | bs_boosters | mnożnik odrzutu/chwiania |
bsDrone | gracz | bs_gadgets | pilotowanie drona |
bsSquad | gracz | bs_factions | informacje o oddziale |
bsFactionStyle, bsFactionTag, bsFactionCanInvite, bsFactionSafeZone | gracz | bs_factions | styl tagu, uprawnienia, strefa bezpieczna |
bsVipTag | gracz | bs_vip | { tier, colour, style, hex, blip, hud }, nil bez VIP |
bsInRaid, bsRogue | gracz | bs_extraction | stan strefy PvP |
bsSitting | gracz | bs_emotes | |
bsZombie | encja | bs_zombies | id rekordu szwendacza. Także zType, zGait, zState… w trybie sieciowym |
bsZombieDead | encja | bs_zombies | sieciowe zwłoki |
bsCorpse | encja | bs_medical | ciało gracza |
bsProtected, bsKeep | encja | wiele | sprzątanie bs_world go pomija |
bsVehicle, bsLocked, bsOwned, bsWindowsDown | pojazd | bs_vehicles | |
bsBuilt | encja | bs_building |
Jeśli tworzysz encję, której bs_world nie może sprzątnąć, ustaw na niej bsProtected.
Nazewnictwo zdarzeń
Dział zatytułowany „Nazewnictwo zdarzeń”bs:<system>:<thing>, na końcu lower camel case:
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 trzyma własne nazwy w BS.Events (bs_core/shared/sh_events.lua), żeby nic nie było wpisane na sztywno. Hooki to bs:hook:*. Nazwy callbacków używają system:action bez prefiksu bs:.
Każde zdarzenie i callback dla każdego zasobu jest w dokumentacji 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.