Build for the world already here.
These docs are for people adding to BeyondSurvival, not replacing it. The framework is standalone: your resource talks to bs_core, uses the shared BSB bridge for request/response work, and leaves ownership of player data to the core.
Before you write code
Start the server with bs_core, then add one small resource. Test it with a fresh character and an existing character. Never write directly to the character tables just because an export feels one step slower.
Resource layout
Server resources live under resources/[beyond]. Keep a feature in its own folder: configuration, client logic, server authority, shared helpers and NUI belong together.
resources/[beyond]/bs_example/
fxmanifest.lua
config/config.lua
shared/sh_example.lua
client/cl_main.lua
server/sv_main.lua
html/index.htmlLoad order matters. Configuration and shared helpers must exist before the files that use them. If your resource needs database-backed player data, list bs_core as a dependency and start it after the core.
Your first resource
fx_version 'cerulean'
game 'gta5'
lua54 'yes'
shared_scripts {
'@bs_core/shared/sh_bridge.lua',
'@bs_core/shared/sh_services.lua',
'config/config.lua'
}
server_script 'server/sv_main.lua'
client_script 'client/cl_main.lua'
dependencies { 'bs_core' }Do not copy the old callback files from examples. The shared bridge is the framework-wide transport. It is loaded through @bs_core/shared/sh_bridge.lua.
Core API
Use exports for actions that change player state. The server is authoritative: money, XP, needs, character data and saves must be changed server-side.
| Export | Use |
|---|---|
exports.bs_core:GetPlayer(src) | Get the loaded player object for a server ID. |
AddXP(src, amount, reason) | Reward an action with a readable reason. |
AddMoney(src, account, amount, reason) | Give cash or another supported account value. |
RemoveMoney(src, account, amount, reason) | Charge a player on the server. |
GetPlayerMeta / SetPlayerMeta | Read or persist small per-character feature state. |
SavePlayer(src, force) | Request a save after a meaningful server-side change. |
SetNeedsFor(src, hunger, thirst, infection) | Use for controlled need changes and staff tooling. |
-- server/sv_main.lua
local paid = exports.bs_core:RemoveMoney(source, 'cash', 120, 'example purchase')
if not paid then return end
exports.bs_core:AddXP(source, 25, 'example contract')Callbacks & commands
BSB is for a request that needs an answer. Events are still useful for one-way notifications, but a menu should not guess whether a server action worked.
-- server
BSB.RegisterCallback('bs_example:quote', function(src, item)
return { ok = true, price = 120, item = item }
end)
-- client
local quote = BSB.Await('bs_example:quote', 5000, 'repair_kit')For a staff or player command, use the bridge command helper so permission checks and help text stay consistent:
BSB.RegisterCommand('example', 'bs.developer', 'Runs the example action', {}, function(src)
-- server-authoritative work here
end)Player state & persistence
Character state is loaded by the core. Wait until the player is loaded before asking for it on the client; use exports.bs_core:IsLoaded() and GetData(). On the server, get the player through the core instead of reconstructing identifiers yourself.
Use metadata for feature state that belongs to a character. Keep a small, documented key namespace such as example:last_reward. Never trust a value sent by the client when it can be recalculated on the server.
World systems
Zombie population and nodes
The zombie system is not a proximity spawner. It combines world population, sectors, nodes, points of interest, streaming, sight checks and noise. Add content through its configuration and service boundaries; do not create peds on every client and call that a horde.
Traders, contracts and NPCs
Trader records describe an NPC, what they sell, what they buy, stock and offered missions. Contracts update the HUD and should award through core services, not direct SQL. Use the NPC editor for fast placement, then move stable definitions into configuration.
Inventory and equipment
Inventory owns item movement. A feature can request an item action, but it should not silently alter item stacks or equipment slots. Clothing, carry capacity and visuals are connected, so test both the paper doll and the spawned ped.
NUI & UI rules
Keep browser UI thin. It renders state and sends an intent; the server validates the intent and returns the result. Every NUI callback needs a clear close path, a timeout-friendly request flow, and a stable UI state when the server refuses something.
Match the current visual language: dark panels, amber for focus, readable spacing, and no blur that hides the player unless the menu needs it. Test 16:9, ultrawide and a player who opens another menu halfway through yours.
Testing & debug
- Restart only the resource you changed, then test its dependency order with a full server restart.
- Test with an ordinary survivor and an administrator. Admin debug panels must never leak to normal players.
- Test reconnect, character switch, death and resource restart. Those are where persistence bugs show up.
- Use the NPC debug panel and map debug teleports for content work; do not hardcode a temporary coordinate into production logic.
- When touching maps, test the location at day and night and watch for culling, collisions and duplicated props.
Current audit notes
The active resources have their explicit local manifest files present, and there are no current client-side os.time() calls. The old cl_callback.lua and sv_callback.lua files still exist in bs_core but are intentionally not loaded; new work should use BSB rather than reviving those legacy files.