# Configuration > Configuration reference for ML Radio Category: RADIO · Source: https://miciomods.it/docs/ml-radio-configuration · Last updated: 2026-07-28 ## 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** ```lua Config.Debug = false Config.Locale = 'en' Config.Mode = 'item' -- 'item' | 'command' Config.disconnectOnDeath = true Config.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** ```lua Config.Items.radios = { { name = 'radio', label = 'Basic Radio', rangeBonus = 0, batteryMax = 100, batteryDrain = 0.5 }, { name = 'radio_military', label = 'Military Radio', rangeBonus = 2000, batteryMax = 150, batteryDrain = 0.3 }, } ``` - `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** ```lua Config.Items.consumables = { battery = 'batteria_stilo', repairKit = 'electronic_kit' } ``` - `battery`: Item consumed to recharge a handset. - `repairKit`: Default repair item shown in interaction menus. ## Jammer Items **config.lua** ```lua Config.Items.jammers = { { name = 'radio_jammer', label = 'Basic Jammer', model = 'sm_prop_smug_jammer', rangeMin = 10.0, rangeMax = 100.0, rangeDefault = 50.0, powerMax = 100, powerConsumption = 10, healthMax = 100, maxPerPlayer = 2 }, { name = 'radio_jammer_advanced', label = 'Advanced Jammer', model = 'sm_prop_smug_jammer', rangeMin = 10.0, rangeMax = 200.0, rangeDefault = 100.0, powerMax = 150, powerConsumption = 8, healthMax = 150, maxPerPlayer = 1 }, } ``` - `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** ```lua Config.Items.antennas = { { name = 'radio_antenna_small', label = 'Portable Antenna', model = 'dgm_antenna', signalRange = 500.0, healthMax = 100, weatherResistance = 0, maxPerPlayer = 3, repairItems = { ['electronic_kit'] = { addHealth = 25, duration = 5000 } }, dui = { enabled = true, renderDistance = 15.0, scale = 0.08, offset = vec3(0.0, 0.0, 0.5) } }, -- radio_antenna_medium: 1500m, 200hp, 40 weather resistance, max 2 -- radio_antenna_large: 3000m, 300hp, 80 weather resistance, max 1 } ``` - `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. > **WARNING:** 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** ```lua Config.Signal = { enabled = true, baseSignalWithoutAntenna = 0, baseRange = 5000.0, updateInterval = 5000, degradation = { distance = 0.02, buildings = 15, underground = 50, weather = { rain = 10, thunder = 25 } }, thresholds = { excellent = 80, good = 60, poor = 40, weak = 20 } } ``` - `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** ```lua Config.Antenna = { enabled = true, maxPerPlayer = 5, spawnDistance = 1.0, interactionDistance = 3.0, saveHealthToItem = true, decay = { enabled = false, rate = 1, tickInterval = 60000 }, hack = { enabled = true, defaultItem = 'hacking_device', duration = 15000, successChance = 0.7, damageOnSuccess = 50, removeItemOnFail = false }, permission = { place = {}, configure = {}, remove = {} } } ``` - `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** ```lua Config.FixedAntennas = { enabled = true, forceOnline = true, locations = { { id = 'shores', label = 'Shores Tower', model = 'dgm_antenna', coords = vec3(2296.64, 2954.15, 45.54), heading = 91.1, signalRange = 3500.0, infiniteHealth = true }, -- villa, vinewood, southside, chilliad ship pre-configured } } ``` - `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** ```lua Config.Jammer = { enabled = true, permission = { place = {}, configure = {}, remove = {} }, defaultJammers = {}, durability = { enabled = true, decayRate = 2, destroyable = true } } ``` - `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** ```lua Config.Broadcast = { enabled = true, streamingProvider = 'xsound', -- 'xsound' | 'none' stations = { { id = 'police_dispatch', label = 'LSPD Dispatch', coords = vec3(392.44, -832.0, 29.29), frequency = 1.0, permission = { 'police' }, speakerRange = 0, allowStreaming = true }, -- hospital_pa (freq 2.0, ambulance), radio_station (88.4, dj/media + autoplay) }, ducking = { enabled = true, voiceVolume = 0.1 } } ``` - `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** ```lua autoplay = { enabled = true, shuffle = false, defaultDuration = 1800, tracks = { { url = 'https://ice1.somafm.com/groovesalad-128-mp3', title = 'Groove Salad' }, } } ``` - 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** ```lua Config.SpeakerZones = { enabled = true, zones = { { id = 'prison_yard', label = 'Bolingbroke Yard Speakers', coords = vec3(285.59, -901.16, 28.81), radius = 50.0, frequency = 1.0, volume = 0.5 }, } } ``` - 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** ```lua Config.StereoProps = { enabled = true, items = { { name = 'boombox', label = 'Boombox', model = 'prop_ghettoblast_01', maxDistance = 25.0, volumeDefault = 0.6, maxPerPlayer = 10 }, }, interaction = { distance = 2.0, icon = 'fa-radio' }, presets = { { label = 'Radio Los Santos', freq = 88.4 } }, publicControl = false, battery = { enabled = false, startLevel = 100, rechargeItem = 'batteria_stilo', rechargeAmount = 50, drainPerMinute = 0.5 } } ``` - `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** ```lua Config.Battery = { enabled = true, tickInterval = 60000, drainPerMinute = 0.5, chargeAmount = 50, lowThreshold = 20 } ``` - `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** ```lua Config.Channels = { names = { ['1'] = 'LSPD CH#1', ['1.%'] = 'LSPD Private', ['2'] = 'EMS Dispatch', ['2.%'] = 'EMS Private', ['88.4'] = 'Radio Los Santos' }, restrictions = { [1] = { type = 'job', names = { 'police', 'lspd' }, requireDuty = true }, [2] = { type = 'job', names = { 'ambulance', 'ems' }, requireDuty = true } } } ``` - `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** ```lua Config.UI = { keybind = 'F5', overlay = 'default', theme = 'military', allowMovement = true, channelUpKey = 'PERIOD', channelDownKey = 'COMMA', scaleFactor = 0.85, defaultPosition = 'bottom-right', enableMicClicks = true, enablePlayerMute = true } ``` - `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** ```lua Config.Logger = 'discord' -- 'discord' | 'ox_lib' Config.DiscordWebhook = { ChannelJoin = 'INSERT_WEBHOOK_LINK_HERE', ChannelLeave = 'INSERT_WEBHOOK_LINK_HERE', JammerPlaced = 'INSERT_WEBHOOK_LINK_HERE', ExtenderPlaced = 'INSERT_WEBHOOK_LINK_HERE', BroadcastStart = 'INSERT_WEBHOOK_LINK_HERE', ExploitDetected = 'INSERT_WEBHOOK_LINK_HERE', HackSuccess = 'INSERT_WEBHOOK_LINK_HERE', } Config.AllowedExportResources = {} Config.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.