Writing a resource on top of bs_core
Let’s build a tiny one: a “radio tower” you can use once every 10 minutes for $50 cash and a few XP. It’s made up, the parts it uses are real.
Folder
Section titled “Folder”resources/[beyond]/bs_radiotower/├── fxmanifest.lua├── config/config.lua├── client/cl_main.lua└── server/sv_main.luaPrefix it bs_. FiveM resource names are one global namespace, a resource called radio will collide with somebody’s script sooner or later.
fxmanifest.lua
Section titled “fxmanifest.lua”fx_version 'cerulean'game 'gta5'lua54 'yes'
name 'bs_radiotower'description 'BeyondSurvival - radio tower example'
shared_scripts { '@bs_core/shared/sh_bridge.lua', -- BSB callbacks + BSB.RegisterCommand 'config/config.lua',}client_scripts { 'client/cl_main.lua' }server_scripts { 'server/sv_main.lua' }
dependencies { 'bs_core' }
-- live overrides from the F1 console, AFTER your config filesshared_script '@bs_config/shared/apply.lua'-- crash breadcrumbsclient_script '@bs_core/shared/sh_crumbs.lua'Then add ensure bs_radiotower to server.cfg, after bs_core and after anything you call.
config/config.lua
Section titled “config/config.lua”RadioTower = {}RadioTower.Settings = { reward = 50, xp = 20, cooldown = 600, -- seconds pos = vector3(0.0, 0.0, 0.0), range = 3.0,}A global table with your resource’s name. That’s what the F1 apply helper patches (see Config apply helper).
server/sv_main.lua
Section titled “server/sv_main.lua”local last = {} -- [charId] = os.time()
BSB.RegisterCallback('radiotower:use', function(src) local p = exports.bs_core:GetPlayerSummary(src) if not p then return { ok = false, error = 'Not loaded.' } end
-- never trust the client: check distance here too local pos = GetEntityCoords(GetPlayerPed(src)) if #(pos - RadioTower.Settings.pos) > RadioTower.Settings.range + 2.0 then return { ok = false, error = 'Too far.' } end
local now = os.time() if last[p.charId] and now - last[p.charId] < RadioTower.Settings.cooldown then return { ok = false, error = 'Nothing on the air yet.' } end last[p.charId] = now
exports.bs_core:AddMoney(src, 'cash', RadioTower.Settings.reward, 'radiotower') exports.bs_core:AddXP(src, RadioTower.Settings.xp, 'radiotower') return { ok = true }end)Money and XP only through bs_core exports. Never write to the DB columns yourself.
client/cl_main.lua
Section titled “client/cl_main.lua”local function use() if not exports.bs_core:IsLoaded() then return end Crumb('radiotower', 'use') local res = BSB.Await('radiotower:use', 5000) if not res then return end -- timed out, server side not running TriggerEvent('bs:ui:notify', { type = res.ok and 'success' or 'error', message = res.ok and 'You picked up a signal.' or res.error, })end
RegisterCommand('radiotower', use, false)For a real interaction you’d register an option with bs_interactions (the ALT dot) instead of a command, look at how bs_recycler or bs_mining do it.
If you teleport players
Section titled “If you teleport players”Call exports.bs_guard:Allow(src, 'teleport', 3000) before moving them on the server, or bs_guard flags every use.
Traps (all of these happened)
Section titled “Traps (all of these happened)”- Globals of another resource are invisible.
Items.Definitionsinside bs_admin is an empty table, not bs_inventory’s. Use exports. - Functions don’t cross exports. Passing a callback function to another resource’s export won’t work. That’s why BSB is a file you include, not an export.
- Don’t
Waitinside an export someone else calls. It runs on the caller’s thread in your runtime and throws. bs_radial had this bug, that’s whyBSB.Radialexists. local a, b = x and f() or y- Lua only keeps the first return off()there. Write it with an if.- A local used above its declaration is a global nil.
luac5.4 -l file.lua | grep _ENVfinds them. - Guard
GetEntityModelwithDoesEntityExist. On a dead handle it hard crashes the game client. - No NUI messages every frame while idle. Send on change.
- Every
bs:ui:openedneeds abs:ui:closedon every exit path, see UI/NUI conventions.
bs19.org · Privacy · Terms · Cookie settings