Configuration

9 min readUpdated Today

Overview

  • config.lua: Every gameplay setting: items, signal model, antennas, jammers, stations, zones, stereos, channels, UI.
  • server/config_server.lua: Server-only: logging provider, Discord webhooks, export allowlist, stream host allowlist.

Stations, speaker zones and fixed towers are defined in config.lua and load on resource start; changing them requires a resource restart.

Core

config.lua
1Config.Debug = false
2Config.Locale = 'en'
3Config.Mode = 'item'            -- 'item' | 'command'
4Config.disconnectOnDeath = true
5Config.MaxFrequency = 999.99
  • Debug: Development prints. Leave false in production.
  • Locale: Interface language. Translations live in the locales/ folder.
  • Mode: 'item' requires a radio item in the inventory; 'command' opens the radio without an item.
  • disconnectOnDeath: Drops the player off the air when they die.
  • MaxFrequency: Highest tunable frequency.

Radio Items

config.lua
1Config.Items.radios = {
2    { name = 'radio',          label = 'Basic Radio',    rangeBonus = 0,    batteryMax = 100, batteryDrain = 0.5 },
3    { name = 'radio_military', label = 'Military Radio', rangeBonus = 2000, batteryMax = 150, batteryDrain = 0.3 },
4}
  • name: Inventory item name.
  • rangeBonus: Meters added on top of the signal coverage for this handset.
  • batteryMax: Battery capacity in percent points.
  • batteryDrain: Percent drained per battery tick while the radio is on and tuned.
config.lua
1Config.Items.consumables = {
2    battery = 'batteria_stilo',
3    repairKit = 'electronic_kit'
4}
  • battery: Item consumed to recharge a handset.
  • repairKit: Default repair item shown in interaction menus.

Jammer Items

config.lua
1Config.Items.jammers = {
2    { name = 'radio_jammer',          label = 'Basic Jammer',    model = 'sm_prop_smug_jammer',
3      rangeMin = 10.0, rangeMax = 100.0, rangeDefault = 50.0,
4      powerMax = 100, powerConsumption = 10, healthMax = 100, maxPerPlayer = 2 },
5    { name = 'radio_jammer_advanced', label = 'Advanced Jammer', model = 'sm_prop_smug_jammer',
6      rangeMin = 10.0, rangeMax = 200.0, rangeDefault = 100.0,
7      powerMax = 150, powerConsumption = 8, healthMax = 150, maxPerPlayer = 1 },
8}
  • rangeMin / rangeMax / rangeDefault: Owner-adjustable jamming radius bounds and its starting value.
  • powerMax / powerConsumption: Power reserve and how much drains per tick while active. Batteries refill it.
  • healthMax: Durability. Jammers can be damaged and destroyed when Config.Jammer.durability.destroyable is on.
  • maxPerPlayer: Placement cap per player for this tier.

Antenna Items

config.lua
1Config.Items.antennas = {
2    { name = 'radio_antenna_small',  label = 'Portable Antenna',   model = 'dgm_antenna',
3      signalRange = 500.0,  healthMax = 100, weatherResistance = 0,  maxPerPlayer = 3,
4      repairItems = { ['electronic_kit'] = { addHealth = 25, duration = 5000 } },
5      dui = { enabled = true, renderDistance = 15.0, scale = 0.08, offset = vec3(0.0, 0.0, 0.5) } },
6    -- radio_antenna_medium: 1500m, 200hp, 40 weather resistance, max 2
7    -- radio_antenna_large:  3000m, 300hp, 80 weather resistance, max 1
8}
  • signalRange: Coverage radius projected by the antenna.
  • weatherResistance: Percent of the rain/thunder signal penalty this tier ignores.
  • repairItems: Items accepted for repairs, each with health restored and progress duration.
  • dui: The in-world status display (health, range, lockout) rendered above the prop: toggle, view distance, scale, offset.
Antenna Model

The default model = 'dgm_antenna' must exist on your server. If you do not stream that model, point model (on antenna tiers and every fixed location) at a base-game prop such as prop_radiomast01.

Signal

config.lua
1Config.Signal = {
2    enabled = true,
3    baseSignalWithoutAntenna = 0,
4    baseRange = 5000.0,
5    updateInterval = 5000,
6    degradation = {
7        distance = 0.02,
8        buildings = 15,
9        underground = 50,
10        weather = { rain = 10, thunder = 25 }
11    },
12    thresholds = { excellent = 80, good = 60, poor = 40, weak = 20 }
13}
  • baseSignalWithoutAntenna: Signal level with no antenna in range. 0 means no coverage without infrastructure.
  • baseRange: Fallback coverage radius when signal simulation applies without a specific antenna.
  • updateInterval: Milliseconds between signal recalculations per client.
  • degradation.distance: Signal points lost per meter from the antenna, scaled by range.
  • degradation.buildings / underground: Flat penalty while indoors / below ground.
  • degradation.weather: Flat penalty during rain and thunder, reduced by the antenna's weatherResistance.
  • thresholds: Quality tier boundaries shown on the handset.

Placed Antennas

config.lua
1Config.Antenna = {
2    enabled = true,
3    maxPerPlayer = 5,
4    spawnDistance = 1.0,
5    interactionDistance = 3.0,
6    saveHealthToItem = true,
7    decay = { enabled = false, rate = 1, tickInterval = 60000 },
8    hack = {
9        enabled = true,
10        defaultItem = 'hacking_device',
11        duration = 15000,
12        successChance = 0.7,
13        damageOnSuccess = 50,
14        removeItemOnFail = false
15    },
16    permission = { place = {}, configure = {}, remove = {} }
17}
  • maxPerPlayer: Global cap across all antenna tiers, on top of each tier's own maxPerPlayer.
  • saveHealthToItem: Writes remaining health onto the item when an antenna is picked up, restored on re-placement.
  • decay: Optional passive health loss per tick. Off by default.
  • hack.defaultItem: Item required to attempt a hack. Individual tiers can override with hackItem, hackDuration, hackSuccessChance, hackLockDuration and hackDamageOnSuccess on the tier entry.
  • hack.duration: Hack attempt length in milliseconds.
  • hack.successChance: Roll from 0 to 1. Failures keep the item unless removeItemOnFail = true.
  • hack.damageOnSuccess: Health chipped off the antenna on a successful hack, on top of the lockout. Infinite-health towers are immune. Set 0 for lockout only.
  • permission: Job allowlists per action. Empty tables allow everyone.

Fixed Towers

config.lua
1Config.FixedAntennas = {
2    enabled = true,
3    forceOnline = true,
4    locations = {
5        { id = 'shores', label = 'Shores Tower', model = 'dgm_antenna',
6          coords = vec3(2296.64, 2954.15, 45.54), heading = 91.1,
7          signalRange = 3500.0, infiniteHealth = true },
8        -- villa, vinewood, southside, chilliad ship pre-configured
9    }
10}
  • forceOnline: Fixed towers come back online automatically on start.
  • infiniteHealth: The tower never breaks and ignores hack damage.
  • hackable = true: Per-location opt-in to allow hacking a fixed tower. Off unless set.

Jammers

config.lua
1Config.Jammer = {
2    enabled = true,
3    permission = { place = {}, configure = {}, remove = {} },
4    defaultJammers = {},
5    durability = { enabled = true, decayRate = 2, destroyable = true }
6}
  • defaultJammers: Pre-placed jammers defined in config.
  • durability.decayRate: Health lost per tick while the jammer runs.
  • durability.destroyable: Allows the prop to be damaged and destroyed by players.

Broadcast Stations

config.lua
1Config.Broadcast = {
2    enabled = true,
3    streamingProvider = 'xsound',  -- 'xsound' | 'none'
4    stations = {
5        { id = 'police_dispatch', label = 'LSPD Dispatch',
6          coords = vec3(392.44, -832.0, 29.29), frequency = 1.0,
7          permission = { 'police' }, speakerRange = 0, allowStreaming = true },
8        -- hospital_pa (freq 2.0, ambulance), radio_station (88.4, dj/media + autoplay)
9    },
10    ducking = { enabled = true, voiceVolume = 0.1 }
11}
  • streamingProvider: 'xsound' streams real audio; 'none' disables music while keeping the voice side of stations.
  • permission: Jobs allowed to operate the station console.
  • speakerRange: Meters around the station prop where audio is heard without a radio. 0 disables the local speaker.
  • allowStreaming: Whether the console accepts stream links.
  • ducking.voiceVolume: Music volume multiplier while someone talks on the station frequency.

Autoplay

config.lua
1autoplay = {
2    enabled = true,
3    shuffle = false,
4    defaultDuration = 1800,
5    tracks = {
6        { url = 'https://ice1.somafm.com/groovesalad-128-mp3', title = 'Groove Salad' },
7    }
8}
  • Per-station 24/7 playlist that keeps speaker zones and boomboxes on that frequency audible with no live DJ. A live DJ takes priority; autoplay resumes when the console is released.
  • defaultDuration: Seconds per track before rotating when the stream has no track metadata.
  • Replace the shipped example streams with your own links.

Speaker Zones

config.lua
1Config.SpeakerZones = {
2    enabled = true,
3    zones = {
4        { id = 'prison_yard', label = 'Bolingbroke Yard Speakers',
5          coords = vec3(285.59, -901.16, 28.81), radius = 50.0,
6          frequency = 1.0, volume = 0.5 },
7    }
8}
  • Anyone inside the radius hears whatever plays on the zone's frequency, no radio item needed. Volume falls off toward the edge.

Placeable Stereos

config.lua
1Config.StereoProps = {
2    enabled = true,
3    items = {
4        { name = 'boombox', label = 'Boombox', model = 'prop_ghettoblast_01',
5          maxDistance = 25.0, volumeDefault = 0.6, maxPerPlayer = 10 },
6    },
7    interaction = { distance = 2.0, icon = 'fa-radio' },
8    presets = { { label = 'Radio Los Santos', freq = 88.4 } },
9    publicControl = false,
10    battery = { enabled = false, startLevel = 100, rechargeItem = 'batteria_stilo',
11                rechargeAmount = 50, drainPerMinute = 0.5 }
12}
  • presets: Quick-pick channels in the stereo menu; free tuning stays available.
  • publicControl: false restricts power, tuning and volume to the owner.
  • battery: Optional drainable battery for placed stereos. Off by default.

Handset Battery

config.lua
1Config.Battery = {
2    enabled = true,
3    tickInterval = 60000,
4    drainPerMinute = 0.5,
5    chargeAmount = 50,
6    lowThreshold = 20
7}
  • tickInterval: Milliseconds between server-side drain passes.
  • chargeAmount: Percent restored per battery item consumed.
  • lowThreshold: Percentage that fires the low-battery warning once.

Channels

config.lua
1Config.Channels = {
2    names = {
3        ['1']   = 'LSPD CH#1',
4        ['1.%'] = 'LSPD Private',
5        ['2']   = 'EMS Dispatch',
6        ['2.%'] = 'EMS Private',
7        ['88.4'] = 'Radio Los Santos'
8    },
9    restrictions = {
10        [1] = { type = 'job', names = { 'police', 'lspd' }, requireDuty = true },
11        [2] = { type = 'job', names = { 'ambulance', 'ems' }, requireDuty = true }
12    }
13}
  • names: Display labels. The .% pattern names every sub-channel of a band (1.1, 1.2, ...).
  • restrictions: Job locks per whole-number frequency. requireDuty also checks on-duty state. Players without the job hear the channel as listen-only.

Interface

config.lua
1Config.UI = {
2    keybind = 'F5',
3    overlay = 'default',
4    theme = 'military',
5    allowMovement = true,
6    channelUpKey = 'PERIOD',
7    channelDownKey = 'COMMA',
8    scaleFactor = 0.85,
9    defaultPosition = 'bottom-right',
10    enableMicClicks = true,
11    enablePlayerMute = true
12}
  • keybind: Opens the radio. Players can rebind it in FiveM settings.
  • theme: Interface skin. Shipped themes live in the web/ build.
  • allowMovement: Lets the player walk while the radio is open.
  • scaleFactor / defaultPosition: Starting size and placement; players can drag and resize, saved per player. /resetradioui restores defaults.
  • enableMicClicks: Push-to-talk click sounds.
  • enablePlayerMute: Per-player mute entries in the channel member list.

Server Config

server/config_server.lua
1Config.Logger = 'discord' -- 'discord' | 'ox_lib'
2
3Config.DiscordWebhook = {
4    ChannelJoin     = 'INSERT_WEBHOOK_LINK_HERE',
5    ChannelLeave    = 'INSERT_WEBHOOK_LINK_HERE',
6    JammerPlaced    = 'INSERT_WEBHOOK_LINK_HERE',
7    ExtenderPlaced  = 'INSERT_WEBHOOK_LINK_HERE',
8    BroadcastStart  = 'INSERT_WEBHOOK_LINK_HERE',
9    ExploitDetected = 'INSERT_WEBHOOK_LINK_HERE',
10    HackSuccess     = 'INSERT_WEBHOOK_LINK_HERE',
11}
12
13Config.AllowedExportResources = {}
14Config.AllowedStreamHosts = {}
  • Logger: Where log entries go.
  • DiscordWebhook: One webhook per event category. Set any webhook to false to disable that log entirely.
  • AllowedExportResources: Non-ml_* resources allowed to call mutating exports. ml_* resources are allowed automatically.
  • AllowedStreamHosts: Restrict DJ stream links to specific hosts. Empty allows any public host; private addresses are always blocked.