How Integrations Work#
Everything that depends on another resource sits behind a small, open-source bridge file. Interface picks the bridge when it starts, by looking for the first resource on its list that is already running.
Layer | Supported | Without it |
|---|---|---|
Framework | es_extended · qb-core (Qbox through its qb-core layer) | Required. |
Hunger and thirst | esx_status · QBCore metadata | Both stay at 0. |
Voice | pma-voice · saltychat | Talking is shown; the range stays at the shortest. |
Fuel | lc_fuel · qs-fuelstations | The vehicle's native fuel level — correct for any fuel script that sets it. |
Multicharacter | qs-multicharacter | The HUD starts on the framework's player-loaded event. |
Loading screen | qs-loadingscreen | The game's loading screen is shut down when the first-time setup opens. |
Seatbelt sounds | InteractSound ( | Silent buckle; the notification still shows. |
Log screenshots | screenshot-basic + a Fivemanage token | Logs are sent without a picture. |
VIP purchases | Tebex, through |
|
Detection happens once, at start, and only sees resources that are already running. Start your voice and fuel resources before
qs-interface, or that part silently runs standalone.
Overriding Detection#
Each list is a small table in config/config.lua, mapping a resource name to a bridge. A renamed framework or voice resource only needs its new name added on the left:
-- config/config.lua
local frameworks = {
['es_extended'] = 'esx',
['qb-core'] = 'qb',
['my-renamed-core'] = 'qb', -- added
}
Config.Framework = DependencyCheck(frameworks) or 'standalone'
-- or skip detection and force a bridge:
Config.Fuel = 'lc_fuel'The same applies to voiceChats, fuels and multicharacters. This file is read before the server starts, so restart after editing it.
Adding a Fuel or Voice Script#
A fuel bridge is one function, GetFuel(vehicle), returning 0–100. Most fuel scripts already write the native level and need nothing. For one that keeps fuel elsewhere — ox_fuel's state bag, for example — create a file next to the others:
-- client/custom/fuel/ox_fuel.lua
if Config.Fuel ~= 'ox_fuel' then return end
function GetFuel(vehicle)
return Entity(vehicle).state.fuel or GetVehicleFuelLevel(vehicle)
endthen add ['ox_fuel'] = 'ox_fuel' to the fuels table of config/config.lua and restart.
A voice bridge, in client/custom/voices/, is two functions: IsTalking() and GetProximityDistance(), which returns 1.5, 3 or 6 — whisper, normal and shout — to size the voice ring. Copy pma.lua as the starting point and add the resource to voiceChats.
Adapting the Framework Bridge#
custom/esx/ and custom/qb/ hold everything Interface asks the framework: money, job, status, names, stress and who is a chat admin. They are unencrypted and commented, so a modified core is supported by editing the matching function:
You want to change | Edit |
|---|---|
Who counts as chat staff |
|
Where hunger and thirst come from |
|
The roleplay name in chat |
|
Which jobs appear in Job Channels |
|
Where stress is stored |
|
What relieves stress | The events at the bottom of |
Out of the box, stress is relieved by esx_status:add, esx_basicneeds:healPlayer and death on ESX, and by eating, drinking, alcohol, hospital respawn and revive on QBCore.
Using It as Your Progress Bar and Notifications#
Route your framework's own functions through Interface and every script on the server uses the player's chosen styles. On QBCore, replace QBCore.Functions.Progressbar in qb-core/client/functions.lua:
function QBCore.Functions.Progressbar(name, label, duration, useWhileDead, canCancel, disableControls, animation, prop, propTwo, onFinish, onCancel)
if prop then
prop = { model = prop.model, bone = prop.bone, pos = prop.coords, rot = prop.rotation }
end
while GetResourceState('qs-interface') ~= 'started' do Wait(500) end
local success = exports['qs-interface']:ProgressBar({
duration = duration,
label = label,
useWhileDead = useWhileDead,
canCancel = canCancel,
disable = disableControls,
anim = animation and { dict = animation.animDict, clip = animation.anim, flag = animation.flags },
prop = prop,
})
if success then
if onFinish then onFinish() end
elseif onCancel then
onCancel()
end
endNotifications follow the same idea: point your framework's notify function at exports['qs-interface']:AddNotify(text, title, time, icon). Keep a copy of the originals — framework updates overwrite these files.
Stress From Other Scripts#
Interface listens to the same events as qb-hud, so drugs, robberies or gyms written for it raise and lower stress without changes:
-- client side
TriggerServerEvent('hud:server:GainStress', math.random(2, 4))
TriggerServerEvent('hud:server:RelieveStress', 25)Gaining only works with Stress switched on in /interfaceconfig and never for a job in Jobs without stress; relieving always works. Values are kept between 0 and 100.
Discord Logs#
Every chat message, admin action and VIP change can be posted to Discord. Set one webhook as the fallback and override the channels you want separated:
-- server/webhooks.lua
_G['webhook'] = {
chat = 'https://discord.com/api/webhooks/...', -- fallback for everything below
chat_me = '',
chat_do = '',
chat_ooc = '',
chat_anon = '', -- logs the real sender of anonymous messages
chat_admin = '',
chat_report = '',
chat_dice = '',
chat_rps = '',
chat_message = '', -- messages typed without a command
chat_job = '', -- job channels
chat_admin_action = '', -- /chatadmin: saves, clears, announcements
chat_vip = '', -- VIP granted, removed and redeemed
}
_G['fivemanage'] = { webhook = '' } -- Fivemanage token, adds a screenshot to logs
Config.ShowWebhookIP = false -- true includes the player's IPEach log names the player with their identifiers. Logs are queued and sent in order, respecting Discord's rate limits. Changes made in /interfaceconfig are written to the server console with the name of whoever made them.
Selling VIP Tags#
With Tebex#
Add your Tebex secret key to server.cfg and sell one package per tier. After buying, the player types /redeemvip tbx-xxxx <tier>; the transaction is checked with Tebex and can be redeemed only once. Redeemed tags are permanent.
set sv_tebexSecret "your-secret-key"From your own store or Discord bot#
Write the row yourself, then tell Interface to reload it:
INSERT INTO chat_vip_tags (license, tag_id, expires_at, granted_by)
VALUES ('license:1a2b3c...', 'elite', DATE_ADD(UTC_TIMESTAMP(), INTERVAL 30 DAY), 'Store')
ON DUPLICATE KEY UPDATE tag_id = VALUES(tag_id), expires_at = VALUES(expires_at);exports['qs-interface']:refreshVip('license:1a2b3c...')tag_id is one of vip, premium, elite, legend, immortal; expires_at in UTC, or NULL for permanent. Without the refresh, the change shows within the hour.
Languages#
41 translations ship in locales/: ar, bg, bn, ca, cs, da, de, el, en, es, et, fa, fi, fr, he, hi, hr, hu, id, it, ja, ko, lt, lv, ms, nl, no, pl, pt, ro, ru, sk, sl, sv, sw, th, tr, uk, ur, vi and zh. Pick one in /interfaceconfig → General → Language — every text on every screen switches at once, without a restart.
Until someone picks one there, the language comes from the convar shared by every Quasar resource:
setr qs:locale "es"Any key missing from your language falls back to English, with a console warning naming it. Money is formatted separately, with General → Money format.



