Exports & API Reference

Server-side exports for integrating with oxide-radio — channel registration, player channel state, roster tags, and settings.

Reference for developers integrating another resource with oxide-radio. All of oxide-radio's integration API is server-side exports — there are no client exports. If you just run the resource as a server owner, you don't need anything here.

Server Exports

All exports are called on the server.

Channels

Register and inspect restricted channels at runtime. A channel you register this way is owner-scoped (tied to your resource), takes priority over any Config.JobChannels entry on the same frequency, and is released automatically when your resource stops.

RegisterChannel

Registers (or updates) a restricted channel owned by the calling resource.

local ok, reason = exports['oxide-radio']:RegisterChannel(9, {
    label = 'Fire Dispatch',
    jobs = { 'fire', 'lsfd' },   -- case-sensitive job names
    minRank = 0,                 -- optional, default 0 (any grade)
    dutyOnly = false,            -- optional, default false
    default  = false,            -- optional, default false (tune this job in at clock-on)
})

Parameters:

NameTypeDescription
channelnumberThe frequency (a whole number, 1 or higher). May sit outside the open dial range
deftableThe channel definition (see below)

def shape:

FieldTypeRequiredDescription
labelstringYesName shown on the radio for this frequency
jobstableSee noteList of allowed job names (case-sensitive)
gangstableSee noteList of allowed gang names (not case-sensitive). A player qualifies through a listed job or a listed gang
minRanknumberNoMinimum job grade (default 0)
dutyOnlybooleanNoRequire on-duty (default false)
defaultbooleanNoTune qualifying players to this frequency when they clock on (default false)

jobs and gangs are each optional on their own, but at least one of them must have an entry — a channel with neither is rejected. Supply jobs, gangs, or both.

Returns: boolean, string|nil — true on success. On failure, false and a reason: 'invalid_channel' (bad frequency), 'invalid_definition' (missing/empty label, or both jobs and gangs empty), or 'taken' (another resource already owns that frequency).

Re-registering a frequency your own resource already owns updates it. Registering a frequency that also has a Config.JobChannels entry logs a warning and shadows the config entry until you release it.


UnregisterChannel

Releases a channel your resource previously registered.

local ok = exports['oxide-radio']:UnregisterChannel(9)

Parameters:

NameTypeDescription
channelnumberThe frequency to release

Returns: boolean — true if your resource owned that frequency and it was released; false otherwise. You can only unregister a channel your own resource registered. (Channels are also released automatically when your resource stops, so you rarely need to call this.)


GetChannels

Returns the effective set of all restricted channels — both Config.JobChannels and every runtime registration merged together.

local channels = exports['oxide-radio']:GetChannels()
-- channels = {
--   [1] = { label = 'Police Dispatch', jobs = {...}, minRank = 0, dutyOnly = false, default = true,  source = 'settings' },
--   [9] = { label = 'Fire Dispatch',   jobs = {...}, minRank = 0, dutyOnly = false, default = false, source = 'my-fire-resource' },
-- }

Returns: table — a map keyed by frequency. Each entry is { label, jobs, gangs, minRank, dutyOnly, default, source }, where source is 'settings' for a config channel or the owning resource name for a runtime channel. A runtime registration overrides a config entry on the same frequency.


Player channel state

GetPlayerChannel

Returns the frequency a player is currently on.

local freq = exports['oxide-radio']:GetPlayerChannel(source)

Parameters:

NameTypeDescription
sourcenumberThe player's server id

Returns: number — the current frequency, or 0 if the player is not on any channel.


JoinChannel

Puts a player on a channel from the server side (for example, auto-joining an officer to their department channel at clock-on). All the normal access checks still apply — a player who doesn't qualify won't be joined.

local ok, reason = exports['oxide-radio']:JoinChannel(source, 9)

Parameters:

NameTypeDescription
sourcenumberThe player's server id
channelnumberThe frequency to join

Returns: boolean, string|nil — true on success; on failure false and a reason (e.g. 'invalid_source', 'channel_restricted', 'need_radio'). This export does not take a password, so use it for job channels and open frequencies, not for joining a player onto an encrypted frequency.


LeaveChannel

Takes a player off their current channel.

local ok = exports['oxide-radio']:LeaveChannel(source)

Parameters:

NameTypeDescription
sourcenumberThe player's server id

Returns: boolean — true if the player was on a channel and was removed; false if they weren't on one.


Roster: tags and downed flag

SetMemberTag

Sets a short tag shown next to a player's name on the roster and overlay (e.g. a badge or unit number). oxide-police uses this to show officer badge numbers.

exports['oxide-radio']:SetMemberTag(source, '4021')  -- set a tag
exports['oxide-radio']:SetMemberTag(source, nil)     -- clear it

Parameters:

NameTypeDescription
sourcenumberThe player's server id
tagstring | number | nilThe tag to show. Trimmed; capped at 20 characters. nil (or empty) clears it

Returns: nothing. The tag is remembered for the player and re-applied if they switch channels; if they're currently on a channel, the roster updates live.


SetRadioDead

Flags a player as "down" on the roster (or clears the flag). Use this from a death/medical resource.

exports['oxide-radio']:SetRadioDead(source, true)   -- mark down
exports['oxide-radio']:SetRadioDead(source, false)  -- mark back up

Parameters:

NameTypeDescription
sourcenumberThe player's server id
isDeadbooleantrue to show them as down, false to clear

Returns: nothing. The flag is remembered and re-applied on join. Note that oxide-radio already picks up death/downed state automatically through o-link's death events (see below), so you only need this export if you run a death system that o-link doesn't relay.


Settings

Read and write the resource's database-backed configuration at runtime, keyed by the setting name without the Config. prefix.

local requireItem = exports['oxide-radio']:GetSetting('RequireItem')
local ok = exports['oxide-radio']:SetSetting('RequireItem', false)

GetSetting

Parameters: key (string) — the setting name (e.g. 'Item', 'MinFrequency', 'JobChannels').

Returns: any — the current database-backed value.

SetSetting

Parameters: key (string), value (any) — the setting name and its new value.

Returns: boolean — updates the setting live: applies it on the server immediately, saves it to the database, and syncs it to clients. The change persists across restarts. (Editing the database row directly instead needs a resource restart to take effect.)


Events the resource consumes

oxide-radio does not expose custom events for other resources to trigger — use the exports above. For context, it listens to these o-link lifecycle and death events to keep channel membership and the roster correct:

  • olink:server:playerDropped, olink:server:playerUnload — clean up a leaving player
  • olink:server:jobChanged, olink:server:dutyChanged — re-check the player's restricted-channel access
  • olink:server:gangChanged — re-check the player's restricted-channel access and tune them to a gang default channel (fired by oxide-gangs; other gang systems are picked up by the periodic sweep)
  • olink:server:playerDied, olink:server:playerDowned — flag the player as down on the roster
  • olink:server:playerRevived, olink:server:playerRespawned — clear the down flag

Because these come from o-link, they work with whatever framework and death system you run. You don't fire these yourself.

Integration Examples

Register a department channel and auto-join officers

This is the pattern a job resource uses. Register the channel when your resource starts, and join officers when they clock on. Use o-link for the framework-side pieces (who's police, who's on duty).

local RADIO = 'oxide-radio'
local FIRE_DISPATCH = 9

-- Register our channel once, on start.
CreateThread(function()
    if GetResourceState(RADIO) ~= 'started' then return end
    exports[RADIO]:RegisterChannel(FIRE_DISPATCH, {
        label = 'Fire Dispatch',
        jobs = { 'fire', 'lsfd' },
    })
end)

-- When a firefighter clocks on, put them on the channel and show their unit number.
RegisterNetEvent('my-fire:server:clockOn', function()
    local src = source
    if GetResourceState(RADIO) ~= 'started' then return end
    exports[RADIO]:JoinChannel(src, FIRE_DISPATCH)
    exports[RADIO]:SetMemberTag(src, getUnitNumber(src))  -- your own lookup
end)

Flag a downed player from a custom death system

Only needed if your death system isn't relayed by o-link (most are).

RegisterNetEvent('my-medical:server:playerDown', function()
    if GetResourceState('oxide-radio') ~= 'started' then return end
    exports['oxide-radio']:SetRadioDead(source, true)
end)

Read who is on a channel

-- Which frequency is this player on?
local freq = exports['oxide-radio']:GetPlayerChannel(source)
if freq ~= 0 then
    print(('player %d is on frequency %d'):format(source, freq))
end

Guard your calls. oxide-radio is an optional dependency for most integrators. Check GetResourceState('oxide-radio') == 'started' before calling its exports so your resource still works when the radio isn't installed.

Next Steps