Skip to content

Frameworks

SL-Marketplace runs on ESX Legacy, QBCore and Qbox from the same files. On start it detects which framework your server uses and from then on talks to it with that framework’s own functions and events. You do not need a different download per framework.

Framework Resource Player ID Cash Inventories Owned vehicles
ESX Legacy es_extended identifier money account ox_inventory or native ESX inventory owned_vehicles
QBCore qb-core citizenid cash ox_inventory or qb-inventory player_vehicles
Qbox qbx_core citizenid cash ox_inventory (required by Qbox) player_vehicles

oxmysql and screenshot-basic are required on all three.

Config.Framework = 'auto' -- 'auto', 'esx', 'qb' or 'qbx'

With 'auto', the resource looks for a running qbx_core, then qb-core, then es_extended, and uses the first one it finds. Qbox is checked first because it ships a compatibility layer that also answers as qb-core.

The result is printed when the resource starts:

[sl-marketplace] Framework: qbx
[marketplace] Inventory backend: ox

Detection happens once, when sl-marketplace starts, so your framework must be ensured before it in server.cfg:

server.cfg
ensure oxmysql
ensure screenshot-basic
ensure qbx_core # or es_extended / qb-core, plus your inventory
ensure sl-marketplace

If the console shows No framework detected, the marketplace started too early. Fix the order, or set Config.Framework explicitly.

Config.InventoryBackend = 'auto' -- 'auto', 'ox_inventory', 'esx' or 'qb-inventory'

With 'auto', ox_inventory is used whenever it is running, on any framework. Otherwise the framework’s own inventory is used: the native ESX inventory on ESX, qb-inventory on QBCore. Qbox always needs ox_inventory.

ox_inventory and qb-inventory are slot-based: when a seller lists an item, the marketplace takes exactly the slot they picked and keeps its metadata (weapon serial, ammo, durability…). The buyer receives that same item with that same metadata. With the native ESX inventory, weapons come from the player’s loadout, with their ammo and components.

Item icons come from ox_inventory or qb-inventory automatically; see Config.ItemIcons.

Players can list the vehicles they own. While a vehicle is listed, the marketplace holds it: it disappears from the seller’s garage and goes to the buyer when sold. If the seller cancels the listing it goes straight back to them; if the listing expires, it waits in their mailbox.

Framework While listed When delivered
ESX owned_vehicles.owner becomes marketplace owner becomes the new owner’s identifier
QBCore / Qbox player_vehicles.citizenid and license are left empty citizenid and license are set to the new owner’s

On QBCore and Qbox the vehicle cannot be given a marketplace owner, because player_vehicles.citizenid has a foreign key to players. The marketplace remembers the seller in its own listing instead.

Everything framework-specific lives in bridge/. The rest of the script never calls ESX, QBCore or Qbox directly: it calls Framework.*, and the adapter for the detected framework translates it.

bridge/
framework.lua detection (Config.Framework)
callback/server.lua client → server callbacks
callback/client.lua
server/esx.lua server adapter for each framework
server/qb.lua
server/qbx.lua
client/esx.lua client adapter for each framework
client/qb.lua
client/qbx.lua

Each adapter starts with a check such as if Framework.Name ~= 'qb' then return end, so only the one for your framework runs. The adapters ship unencrypted: you can edit them if your framework is modified.

Function Returns
Framework.Name 'esx', 'qb' or 'qbx'
Framework.GetPlayer(source) { source, identifier, job = { name, grade } } or nil
Framework.GetIdentifier(source) The player’s identifier (identifier or citizenid)
Framework.GetSourceByIdentifier(identifier) The player’s server ID if online, or nil
Framework.GetMoney(source) Cash on hand
Framework.RemoveMoney(source, amount) true if the player had enough and it was removed
Framework.AddMoney(source, amount) true if it was added
Framework.Notify(source, message, type) Shows a framework notification ('info', 'success', 'error', 'warning')
Framework.OnPlayerLoaded(cb) Calls cb(source) when a character finishes loading
Framework.GetOwnedVehicles(identifier) List of { plate, model, data }
Framework.OwnsVehicle(identifier, plate) true or false
Framework.SetVehicleOwner(plate, from, to) Moves a vehicle between owners; 'marketplace' means held by the marketplace
Framework.GetItemNames() Item names from the framework’s catalogue, for automatic categories

Framework.GetPlayer returns the same shape on every framework, so your own code in server/handlers.lua can use it without caring which framework is running:

Handlers.CanCreateListing = function(data)
if data.category == 'weapons' then
local player = Framework.GetPlayer(data.actorSource)
if not player or player.job.name ~= 'police' then
return false, 'Only the police can sell weapons here'
end
end
return true
end
Function What it does
Framework.Notify(message, type) Shows a framework notification
Framework.IsPlayerLoaded() true once the character is loaded
Framework.OnPlayerLoaded(cb) Calls cb() when the character loads
Framework.OnPlayerUnloaded(cb) Calls cb() on logout or character switch
Framework Character loaded Logout
ESX esx:playerLoaded esx:onPlayerLogout
QBCore / Qbox QBCore:Server:PlayerLoaded, QBCore:Client:OnPlayerLoaded QBCore:Client:OnPlayerUnload

If your multicharacter fires different events, change them in the adapter.

Client → server requests use the marketplace’s own callback system (Callback.Register on the server, Callback.Trigger on the client), not the framework’s. It behaves the same on all three frameworks and needs no extra dependency such as ox_lib.

  1. Find the adapter for your framework in bridge/server/ and bridge/client/.
  2. Change only the body of the functions you need: for example, a different cash account, or a renamed export.
  3. Keep the function names and what they return: the rest of the script relies on them.
  4. Restart the resource and check the console for the Framework: and Inventory backend: lines.

Keep a copy of your changes: an update replaces the files in bridge/ just like any other part of the resource.