How Integrations Work#
Safezone Creator reaches your framework and inventory through small, unencrypted bridge files. At start it looks at which resources are running, loads the matching bridge, and prints the framework it chose to the console.
Layer | Supported | Without it |
|---|---|---|
Framework |
| Required. The resource stops at start: jobs, grades, gangs and the admin check all come from it. |
Inventory |
| Runs standalone: no weapons to choose as bypass weapons, and a repeating console warning. Protection, healing, speed limits and access control still work. |
Gangs | QBCore and Qbox gangs · | The gang selector is hidden from Access Control; jobs work as usual. |
HUD |
| Only the minimap is hidden while the creator is open. |
Library |
| Required. Every safe zone is an ox_lib polygon zone. |
Database |
| Required. |
Detection only sees resources that are already started, so everything above must be ensured before
qs-safezone-creatorinserver.cfg.
Frameworks#
The framework is picked in config/main.lua from the resources it finds running. Qbox goes through the QBCore bridge, with nothing extra to set.
local frameworks = {
['es_extended'] = 'esx',
['qb-core'] = 'qb',
['qbx_core'] = 'qb'
}
Config.Framework = DependencyCheck(frameworks) or 'none'If your framework resource has been renamed, put your name on the left of that table. To skip detection entirely, replace the last line with Config.Framework = 'esx' or 'qb'.
Bridge: custom/esx/client.lua and custom/esx/server.lua.
Jobs and grades from
ESX.GetJobs(); the player's job from their player data, kept up to date byesx:setJob.Admins: the
adminandsuperadmingroups.Gangs from
qs-gangswhen it is running — every gang exceptneutral.
Bridge: custom/qb/client.lua and custom/qb/server.lua.
Jobs from
QBCore.Shared.Jobsand gangs fromQBCore.Shared.Gangs; the player's grade fromjob.grade.level.Admins: the
adminorgodpermission, or thecommandace.Job changes are picked up live through
QBCore:Client:OnJobUpdate.
Inventories#
The inventory is used for one thing: the bypass weapons list. The creator offers every item whose name starts with weapon_, with the label and picture your inventory gives it. Detection works from this table in config/main.lua:
local inventories = {
['qs-inventory'] = 'qs',
['ox_inventory'] = 'ox',
['qb-inventory'] = 'qb',
['tgiann-inventory'] = 'tgiann',
['codem-inventory'] = 'codem',
['origen_inventory'] = 'origen'
}
Config.Inventory = DependencyCheck(inventories) or 'standalone'Each inventory has its own bridge in server/custom/inventory/, which also says where its item pictures live:
Bridge | Pictures read from |
|---|---|
|
|
| The |
|
|
|
|
|
|
|
|
If the pictures in the creator show broken, that path is the one to check. On QBCore the item list comes from the framework's shared items; on ESX, from ESX's item list; with ox_inventory and tgiann-inventory, from the inventory itself.
Notifications and the HUD#
Notifications#
The enter and leave messages and the creator's confirmations are shown as the script's own toasts, styled like the creator. To use your server's notification system instead, rewrite Notification in custom/client.lua — the file ships with commented examples for qs-interface and ox_lib:
-- custom/client.lua
function Notification(msg, type) -- type: 'info' | 'error' | 'success'
lib.notify({
title = 'Safe Zone',
description = msg,
type = type
})
endMessages sent by the server arrive through the safezone:notification event and end up in the same function, so one edit covers both sides.
Hiding the HUD#
While the creator is open the minimap is hidden, and so is the qs-interface HUD when it is running; both come back when the creator closes. Any other HUD goes in ToggleHud, in the same file:
-- custom/client.lua
function ToggleHud(bool)
DisplayRadar(bool)
if GetResourceState('qs-interface') == 'started' then
exports['qs-interface']:ToggleHud(bool)
end
-- add your own HUD here
endDiscord Logs#
Every zone created, edited and deleted is posted to Discord as an embed, with the admin's name, server id and identifiers — Steam, Discord, license, Xbox, FiveM and, if you allow it, IP. Set the webhook in server/webhooks.lua:
-- server/webhooks.lua
Config.ShowWebhookIP = false -- true shows the admin's IP in the log
_G['webhook'] = {
revive = '',
heal = '',
death = '',
creator = 'https://discord.com/api/webhooks/...' -- zones created, edited and deleted
}Adding a Framework or Inventory#
Supporting something that is not on the list takes a bridge and one line of configuration. Nothing else in the resource changes, because nothing else refers to your framework or inventory by name.
Copy the closest bridge
For a framework, copy
custom/qb/orcustom/esx/tocustom/<name>/, with bothclient.luaandserver.lua. For an inventory, copy the nearest file inserver/custom/inventory/.Fill in the functions
A framework bridge returns a table of small functions — the player's job and grade, the list of jobs, gangs, the admin check, the identifier. An inventory bridge needs only
formatItemList, which returns every item with its name, label and picture:lua if Config.Inventory ~= 'my_inventory' then return end function sv_inventory:formatItemList() local items = {} for name, item in pairs(exports['my_inventory']:Items()) do items[name] = { name = name, label = item.label, image = ('nui://my_inventory/images/%s.png'):format(name) } end return items endRegister it for detection
Add your resource to the
frameworksorinventoriestable inconfig/main.lua. The value on the right is the folder name of a framework bridge, or the name your inventory bridge checks for —['my_inventory'] = 'my_inventory'.
If that is more than you want to take on, send us the name of the resource.
Languages#
Nineteen translations ship in locales/: Arabic (ar), Bulgarian (bg), Danish (da), German (de), English (en), Spanish (es), Persian (fa), French (fr), Hindi (hi), Hungarian (hu), Italian (it), Japanese (ja), Korean (ko), Dutch (nl), Portuguese (pt), Romanian (ro), Russian (ru), Turkish (tr) and Simplified Chinese (zh-CN). Set Config.Locale in config/main.lua; the creator, the notifications and the Discord logs follow it.
To add another, copy en.json to the new code, translate it and set the locale to that code. Any string missing from a translation falls back to English, and a few seconds after start the console names each missing key so you can add it.



