How Integrations Work#
The core of City Builder does not know which framework, inventory or clothing script you run. Each of those sits behind a small, unencrypted bridge file, and when the resource starts it looks for a matching resource that is already started and loads the right bridge.
Layer | Supported | Without it |
|---|---|---|
Framework | es_extended · qb-core · qbx_core | Required. Admin checks, jobs and money come from it. |
Inventory | qs-inventory · ox_inventory · qb-inventory · ps-inventory · core_inventory · codem-inventory · origen_inventory · tgiann-inventory · inventory (Chezza) | Stash props show their prompt but cannot open. |
Clothing | qs-appearance · illenium-appearance · fivem-appearance · qb-clothing · codem-appearance · ak47_clothing · raid_clothes · rcore_clothes · rcore_clothing · sleek-clothestore · tgiann-clothing · p_appearance · mAppearance | Wardrobe props fall back to the framework's own outfit menu ( |
Text UI | qs-textui | The [E] prompts are drawn as native 3D text; other prompts use ox_lib. |
HUD | qs-interface | Only the minimap is hidden while building. |
Anticheat | FiveGuard | Nothing to do — unless your anticheat flags the free camera. |
Image hosting | FiveManage | The photo studio cannot upload; paste image URLs by hand. |
Database | oxmysql | Required. The only one. |
Qbox runs through the QBCore bridge —
qbx_coreis detected and reported asqb. There is nothing extra to install.
Inventories and Stashes#
When a player opens a stash prop, City Builder calls your inventory with a stash id unique to that placed object — citybuilder_<id> — and the slots and weight from the catalogue. Contents are kept by your inventory, so they survive restarts like any other stash.
ox_inventory — the stash is registered on the server the first time it is opened, then opened. Nothing to add to ox_inventory's own files.
qs-inventory, qb-inventory, ps-inventory and the rest — opened through each inventory's usual stash event or export.
Selling or deleting a prop does not empty its stash in your inventory's database. Remove the citybuilder_<id> stash there if you want the items gone too.
Clothing and Wardrobes#
A wardrobe prop calls openWardrobe() in the bridge of your clothing script, which opens that script's own outfit menu. With no supported script found, the default bridge opens QBCore's outfit menu on QBCore, or a saved-outfits menu built on esx_skin on ESX.
Notifications, Text UI and the HUD#
Notifications#
Messages such as "Furniture item created successfully" are shown by City Builder's own interface, so they always match its style. To send them to another notification resource instead, edit the Notification function in custom/client.lua — commented examples for qs-interface and ox_lib are already there.
-- custom/client.lua
function Notification(msg, type) -- type: 'info' | 'error' | 'success'
lib.notify({ title = 'City Builder', description = msg, type = type })
endText UI#
With qs-textui running, the [E] prompts over stashes, wardrobes and interactive props use it. Otherwise they are drawn as plain 3D text. Both are defined in custom/client.lua (DrawText3D, ShowTextUI, HideTextUI) if you want another style.
Hiding the HUD#
Decoration mode and the Furniture Creator hide the minimap, and also the qs-interface HUD when it is running. For any other HUD, add its hide call to ToggleHud in custom/client.lua:
function ToggleHud(bool)
DisplayRadar(bool)
if GetResourceState('qs-interface') == 'started' then
exports['qs-interface']:ToggleHud(bool)
end
-- your HUD here, e.g.:
-- exports['my-hud']:SetVisible(bool)
endFiveGuard#
The free camera moves the view far from the character, which anticheats read as freecam or noclip. With FiveGuard, write its resource name and City Builder grants the builder a temporary bypass while decoration mode is open, and removes it on close:
-- config/config.lua
Config.FiveGuard = 'fiveguard' -- your FiveGuard resource name, or falseFor another anticheat, whitelist builders in that anticheat, or hook the server event citybuilder:fiveguard:freecam (it receives true on open and false on close).
Overriding Detection#
Each layer picks the first started resource from its list in config/config.lua. To force one — two inventories running at once, a renamed resource, or mAppearance — replace the detection with a fixed value:
-- Auto, the default: the first started resource in the list wins.
Config.Inventory = DependencyCheck(inventories) or 'default'
-- Forced: always use this bridge, whatever is running.
Config.Inventory = 'ox_inventory'
Config.Wardrobe = 'mAppearance'A renamed resource can also be added to the list instead — the key is the resource name, the value is the bridge to use:
local inventories = {
['my-ox-inventory'] = 'ox_inventory',
-- ...
}Adding a Backend#
Supporting an inventory or clothing script that is not on the list takes one small file and one line.
Copy the closest bridge
Inventory bridges live in
client/custom/inventory/, clothing bridges inclient/custom/wardrobe/. Copy the one that works most like yours and rename it.Change the guard and the calls
The first line makes the file run only when it is the chosen bridge. Below it, implement the one function the bridge provides:
-- client/custom/inventory/my-inventory.lua if Config.Inventory ~= 'my-inventory' then return end ---@param customData {maxweight: number, slots: number} | nil ---@param uniq string -- e.g. 'citybuilder_12' function openStash(customData, uniq) local data = customData or Config.DefaultStashData exports['my-inventory']:OpenStash(uniq, data.slots, data.maxweight) end-- client/custom/wardrobe/my-clothing.lua if Config.Wardrobe ~= 'my-clothing' then return end function openWardrobe() exports['my-clothing']:OpenOutfits() endRegister it for detection
Add one entry to the
inventoriesorwardrobestable inconfig/config.lua, then restart.lua ['my-inventory'] = 'my-inventory',
Frameworks work the same way: custom/esx/ and custom/qb/ hold the client and server bridge. That is also where to change what counts as an admin — framework:playerIsAdmin in the server file — or which account money is taken from.
Languages#
27 translations ship in locales/: Arabic (ar), Bulgarian (bg), Czech (cs), Danish (da), German (de), Greek (el), English (en), Spanish (es), Persian (fa), French (fr), Hebrew (he), Hindi (hi), Hungarian (hu), Italian (it), Japanese (jp), Korean (ko), Dutch (nl), Norwegian (no), Polish (pl), Portuguese (pt), Romanian (ro), Russian (ru), Slovenian (sl), Swedish (sv), Turkmen (tk), Turkish (tr) and Simplified Chinese (zh-CN).
Pick one with Config.Locale. To add a language, copy locales/en.json to a new file named after its code, translate the values (never the keys), and set Config.Locale to that code.



