Skip to content

Bridges ​

np_hud talks to frameworks and voice resources only through Hud.Framework and Hud.Voice (client side). Each bridge file calls Hud.Bridge.shouldLoad(kind, name, resource) and returns early unless its resource is started and selected (Config.Framework / Config.Voice, or 'auto'). Files are numbered so auto prefers qbx over qb (qbx also provides qb-core) and pma-voice over the others. The 9_* files are fallbacks: standalone and none claim the slot when nothing else loaded, so the HUD always starts.

Optional exports are called through pcall, so a missing or broken dependency degrades instead of erroring. Detection runs once when the resource starts, so start optional resources before np_hud in server.cfg.

The server side has no framework bridge. It only counts players (GlobalState.np_hud_players) and routes notifications.

Framework (client), bridge/framework/client/ ​

Contract: name, caps { money, job, hunger, thirst, stress }, isLoaded(), isDead(), get() → { cash, bank, job = { name, label, grade }, hunger, thirst, stress }, onLoaded(cb), onUnloaded(cb), onChange(cb). client/modules/framework.lua re-reads get() on every onChange, sends only the diff, and also refreshes every Config.Intervals.world as a safety net.

Contractqbx (qbx_core)qb (qb-core)esx (es_extended)standalone
dataexports.qbx_core:GetPlayerData()QBCore.Functions.GetPlayerData()ESX.GetPlayerData()–
cash / bankmoney.cash / money.banksameaccounts[name='money'/'bank'].moneynot shown
jobjob.label, job.grade.namesamejob.label, job.grade_labelnot shown
hunger / thirstplayer state bag hunger/thirst, hud:client:UpdateNeeds, metadata.*hud:client:UpdateNeeds, metadata.*esx_status:onTick (percent), only if esx_status is startednot shown
stressstate bag stress, hud:client:UpdateStress, metadata.stresshud:client:UpdateStress, metadata.stressesx_status entry stress if a server adds onenot shown
change eventsQBCore:Player:SetPlayerData, needs events, state bag handlersQBCore:Player:SetPlayerData, needs eventsesx:setAccountMoney, esx:setJob, esx_status:onTick–
isLoadedLocalPlayer.state.isLoggedIn / load eventsame + citizenidESX.IsPlayerLoaded()NetworkIsPlayerActive
isDeadmetadata.isdead / inlaststand, LocalPlayer.state.isDeadmetadata.isdead / inlaststandesx:onPlayerDeath / esx:onPlayerSpawn, PlayerData.deadped only
onLoaded / onUnloadedQBCore:Client:OnPlayerLoaded / QBCore:Client:OnPlayerUnloadsameesx:playerLoaded / esx:onPlayerLogout–

The HUD stays hidden (hud:visible { reason = 'loading' }) until isLoaded() returns true.

Voice (client), bridge/voice/client/ ​

Contract: name, get() → { range 1..3, label, radio, radioTalking, muted }, isTalking(), onChange(cb). client/modules/voice.lua refreshes on every onChange and polls every Config.Intervals.voice (200 ms), because pma-voice and none only expose talking through NetworkIsPlayerTalking.

Contractpma-voiceSaltyChat (saltychat)YACA (yaca-voice)none
rangeLocalPlayer.state.proximity.index (+ .mode as label), event pma-voice:setTalkingModeSaltyChat_VoiceRangeChanged(range, index0, count)yaca:external:voiceRangeUpdate(range) mapped to an index via exports['yaca-voice']:getVoiceRanges()fixed 2
talkingNetworkIsPlayerTalkingSaltyChat_TalkStateChanged(isTalking)yaca:external:isTalking(state)NetworkIsPlayerTalking
radio channelLocalPlayer.state.radioChannel (0 = off, set server-side by pma-voice)SaltyChat_RadioChannelChanged(channel, isPrimary) (primary only), initial exports.saltychat:GetRadioChannel(true)yaca:external:setRadioFrequency(channel, frequency) + yaca:external:changedActiveRadioChannel(channel), '0' = left–
radio talkingLocalPlayer.state.radioActive / pma-voice:radioActiveSaltyChat_RadioTrafficStateChanged(_, primaryTransmit, _, secondaryTransmit)yaca:external:isRadioTalking(state, channel)–
muted–SaltyChat_MicStateChanged(muted)yaca:external:microphoneMuteStateChanged(state)–

More than three voice ranges are spread over the HUD's three bars (ceil(index / count * 3)).

Notes on the YACA API ​

  • YACA has no yaca:external:setRadioChannel event. The documented events are yaca:external:setRadioFrequency (channel, frequency) and yaca:external:changedActiveRadioChannel (channel), and the bridge uses those. yaca:external:muteStateChanged is deprecated in favour of yaca:external:microphoneMuteStateChanged.

Fuel, client/modules/vehicle.lua ​

Config.Vehicle.fuel = 'auto' picks the first started resource: ox_fuel (Entity(veh).state.fuel), LegacyFuel (exports.LegacyFuel:GetFuel), cdn-fuel (exports['cdn-fuel']:GetFuel), ps-fuel (exports['ps-fuel']:GetFuel), otherwise GetVehicleFuelLevel. Errors fall back to the native per call.

Sources (checked 2026-10-04) ​

Not verified against a running server ​

  • The qbx_core player state bags hunger / thirst / stress. They're read when present and the bridge falls back to metadata and the hud:client:* events.
  • hud:client:UpdateNeeds / hud:client:UpdateStress are sent by qb-smallresources and other qb scripts, not by qb-core itself. Servers without them still update through QBCore:Player:SetPlayerData.
  • The SaltyChat index is 0-based according to the README, and the bridge adds 1.
  • Minimap: the SETUP_HEALTH_ARMOUR value used to restore the bars on stop (0), and the minimap component math (see Development → Capability notes).

np_* FiveM resources