Configuration

10 min readUpdated 2 weeks ago

Overview

  • shared/config.lua: persistence behavior, world streaming, world-vehicle claiming, decay, cleanup, GPS items, admin panel
  • server/config_server.lua: Discord webhooks and log provider
  • open/server.lua: ownership, garage and impound hooks

The admin panel stores overrides for the settings shown on its Settings page. Other values remain in shared/config.lua.

General

shared/config.lua
1Config.Debug = false
2Config.locale = 'en'
3Config.PersistThroughRestart = true
4Config.MaxVehiclesPerPlayer = 5
5Config.PersistDestroyed = false
  • Debug: Prints gate decisions and lifecycle events to the server console
  • locale: Active language. Files live in locales/
  • PersistThroughRestart: Respawn every persisted vehicle in its saved spot after a server restart
  • MaxVehiclesPerPlayer: Maximum parked vehicles persisted per owner. At the limit, a new vehicle is not saved; existing saved vehicles remain. Protected vehicles and claimed vehicles with countTowardLimit set to false do not count. 0 = unlimited.
  • PersistDestroyed: true brings a wreck back as a wreck (burnt body, dead engine, burst tyres) on respawn and after a restart. false drops a destroyed vehicle from persistence for good

World Streaming

shared/config.lua
1Config.SpawnDistance = 250.0
2Config.ScanInterval = 5
3Config.SpawnStaggerMs = 150

The script keeps saved vehicle records in memory, spawns the vehicles when a player comes close and despawns them when players leave the area. Distant saved vehicles do not need an active world entity.

  • SpawnDistance: Distance in meters at which a saved vehicle is spawned. Despawn happens 60 meters beyond this
  • ScanInterval: Seconds between world scans. Lower is snappier for spawns and position updates, higher is lighter on the server
  • SpawnStaggerMs: Delay in milliseconds between two vehicle spawns inside a scan, to spread the load

External Deletes

shared/config.lua
1Config.RespawnOnExternalDelete = false
  • RespawnOnExternalDelete: Controls what happens when another resource deletes a persisted vehicle (an admin /dv, a garage, a cleanup script). false removes the saved record, including when an admin uses /dv. true respawns the vehicle at its saved position unless the deletion uses the delete hook or PurgeVehicle export.
Using true

With true, intentional deletes only stick if the deleting resource is routed through the script. Add shared_script '@ml_vehicle_persistence/deletehook.lua' as the first script line in the fxmanifest.lua of every resource that deletes vehicles (admin menu, garage), or call the PurgeVehicle export. Otherwise every /dv respawns the vehicle.

Vehicle Types

shared/config.lua
1Config.PersistTypes = {
2    automobile            = true,
3    bike                  = true,
4    bicycle               = true,
5    quadbike              = true,
6    amphibious_automobile = true,
7    amphibious_quadbike   = true,
8    submarinecar          = true,
9    boat                  = true,
10    heli                  = true,
11    blimp                 = true,
12    plane                 = true,
13    submarine             = false,
14    trailer               = true,
15}
  • Only categories set to true are persisted. The key is the server-side vehicle archetype
shared/config.lua
1Config.BlacklistedModels = {
2    -- 'police', 'ambulance',
3}
4
5Config.BlacklistedPlates = {
6    -- 'MECH', 'RENT',
7}
  • BlacklistedModels: Spawn names that never persist, even when player-owned
  • BlacklistedPlates: Plate fragments that never persist. Partial match, case-insensitive, made for job and rental prefixes

Notifications

shared/config.lua
1Config.Notifications = {
2    onParked       = false,
3    onLimitWarning = true,
4    onLimitReached = true,
5}
  • onParked: Popup on every park confirming the vehicle will stay
  • onLimitWarning: Tell the owner how many slots are in use once they reach the last free slot and again once they are full. The same count is never repeated twice in a row
  • onLimitReached: Warn the owner when a new vehicle cannot be saved because the limit is full.

World Vehicles

shared/config.lua
1Config.WorldVehicles = {
2    enabled          = true,
3    countTowardLimit = true,
4    notifyOnClaim    = true,
5}
  • enabled: An eligible unowned vehicle can be claimed by its first driver. Vehicle type, blacklist, exclusion, CanClaim and limit checks still apply.
  • countTowardLimit: Claimed vehicles count toward Config.MaxVehiclesPerPlayer. When the limit is full, the new claim is refused. Set this to false to exempt claimed vehicles from that limit.
  • notifyOnClaim: Tell the player the vehicle is now theirs
Opt a vehicle out

Vehicles spawned by other scripts can be claimed if they pass the normal checks. Mark mission, rental, staff or temporary spawns with exports.ml_vehicle_persistence:DoNotSave(plateOrNetId, reason) on the server after spawning them. The exclusion clears when that entity is deleted.

Base Building

shared/config.lua
1Config.BaseIntegration = {
2    enabled              = true,
3    protectInBase        = true,
4    blockStoreDuringRaid = true,
5    crewGps              = true,
6}
  • enabled: Link parked vehicles to an active ml_base_building flag. The integration is ignored when that resource is not running.
  • protectInBase: Linked vehicles skip cleanup, decay and orphan removal.
  • blockStoreDuringRaid: Prevent a linked vehicle from being stored while its base is under raid.
  • crewGps: Crew members with vehicle driving permission can see linked vehicles on their GPS.

Decay

shared/config.lua
1Config.Decay = {
2    enabled      = false,
3    graceDays    = 2,
4    bodyPerDay   = 40.0,
5    enginePerDay = 40.0,
6    healthFloor  = 150.0,
7}
  • enabled: Apply decay at restart restore, based on time since the vehicle was last saved. It does not tick continuously while the server runs.
  • graceDays: Days since the vehicle's last save before decay starts.
  • bodyPerDay: Body health lost per day after the grace period
  • enginePerDay: Engine health lost per day after the grace period
  • healthFloor: Decay never drops health below this value. A vehicle already saved below the floor is left alone

Orphaned Vehicles

shared/config.lua
1Config.OrphanedVehicles = {
2    enabled       = false,
3    thresholdDays = 7,
4    action        = 'impound',
5    impoundLot    = 'impound',
6    feePerDay     = 100,
7    maxFee        = 1500,
8}
  • enabled: Clean up vehicles whose owner has not been seen for thresholdDays
  • thresholdDays: Days of owner absence before a vehicle is orphaned
  • action: 'impound' routes database-owned vehicles through OpenServer.ImpoundVehicle; 'delete' removes them. With 'impound', claimed World Vehicles stay in persistence; only 'delete' removes them.

OpenServer.ImpoundVehicle updates the Qbox/QBCore player_vehicles table by default. Adapt the hook for an ESX or custom impound system.

  • impoundLot: Impound lot name passed to the hook
  • feePerDay: Fee charged per orphaned day
  • maxFee: Maximum impound fee

Cleanup

shared/config.lua
1Config.Cleanup = {
2    enabled     = false,
3    engineBelow = nil,
4    underZ      = nil,
5    clearZones = {
6        -- { coords = vector3(0.0, 0.0, 0.0), radius = 20.0 },
7    },
8    safeZones = {
9        -- { coords = vector3(0.0, 0.0, 0.0), radius = 20.0 },
10    },
11    keepPlates = {
12        -- 'STAFF',
13    },
14    keepModels = {
15        -- 'adder',
16    },
17    schedule = {
18        -- { hour = 4, minute = 0 },
19        -- { day = 3, hour = 16, minute = 0 },
20    },
21    runOnStart = false,
22}

The cleanup sweep removes saved vehicles that match your rules. It checks the saved records, including vehicles that are not currently spawned.

  • enabled: Master switch for the sweep
  • engineBelow: Prune vehicles whose engine dropped to or under this value. A dead engine sits around 0, a blown-up one goes deeply negative. nil keeps every vehicle
  • underZ: Prune vehicles that ended up under this height, for cars dropped through the map or sunk. nil to skip
  • clearZones: Any vehicle sitting inside one of these circles is pruned
  • safeZones: Vehicles inside these zones are excluded from the cleanup sweep.
  • keepPlates: Plate fragments immune to the sweep. Partial match, case-insensitive
  • keepModels: Spawn names the sweep never touches
  • schedule: When to run, on the server clock. day is optional (0 = Sunday through 6 = Saturday)
  • runOnStart: Run one pass a few seconds after the resource boots

The sweep can also be triggered from another resource with the RunCleanup export.

My Vehicles

shared/config.lua
1Config.MyVehicles = {
2    enabled = true,
3    command = 'myvehicles',
4    item    = false,
5}

A small panel where a player sees the vehicles they have parked: plate, model and, when Config.MaxVehiclesPerPlayer is set, how many of their slots are in use. It carries no positions and no waypoints on purpose, so knowing where a car sits stays the job of the GPS item and can be taken away from a player by stealing the tracker.

  • enabled: false removes the panel, the command and the item binding
  • command: Chat command that opens the panel. Rename it, or set it to false to leave players with the item only
  • item: Optional usable item that opens the same panel, for a registration papers prop. false leaves it command-only. Any item already registered in your inventory works, nothing to install

GPS Item

shared/config.lua
1Config.GpsItem = 'vehicle_gps'
  • GpsItem: Usable item that lists the owner's persisted and garaged vehicles and sets a waypoint. Set to false to disable

GPS Scanner

shared/config.lua
1Config.GpsScanner = {
2    enabled     = true,
3    scannerItem = 'gps_scanner',
4    moduleItem  = 'gps_module',
5    range       = 8.0,
6    tamperTime  = 6000,
7    installTime = 5000,
8    scanSeconds = 20,
9    consumeScannerOnTamper = false,
10    screen      = true,
11    anim     = { dict = 'cellphone@', name = 'cellphone_text_read_base', weapon = 'weapon_digiscanner', bone = 28422, offset = vector3(0.0, 0.0, 0.0), rot = vector3(0.0, 0.0, 0.0) },
12    workAnim = { dict = 'anim@amb@clubhouse@tutorial@bkr_tut_ig3@', name = 'machinic_loop_mechandplayer' },
13    beep     = { name = 'IDLE_BEEP', set = 'EPSILONISM_04_SOUNDSET', range = 5.0 },
14}

A thief sweeps around a vehicle with the scanner item; the closer they get, the faster it beeps. Holding the scan rips the GPS out, and the owner's GPS item then freezes on the last known position with a tampered badge until a new module is installed on the recovered vehicle.

  • enabled: Master switch for the scanner and tamper feature
  • scannerItem: Reusable detector and remover carried by the thief
  • moduleItem: Consumed by the owner to reinstall a removed GPS
  • range: Meters at which the scanner picks a vehicle up
  • tamperTime: Milliseconds held next to the vehicle to rip the GPS out
  • installTime: Milliseconds held to install a new module
  • scanSeconds: How long one scanner use stays active
  • consumeScannerOnTamper: Also burn one scanner item per removal
  • screen: Draw the live proximity bars on the device screen
  • anim: Device held in hand while sweeping. weapon takes the device model from that weapon. bone is the attach bone
  • workAnim: Animation played while ripping the GPS out or bolting a new module in
  • beep: scanner sound, heard by anyone within beep.range meters
Reinstalling

Only the vehicle's owner can install a module. The GPS stays frozen until they do.

Admin Panel

shared/config.lua
1Config.AdminAcePermission = nil
2Config.Admin = {
3    command     = 'vpadmin',
4    theme       = 'default',
5    MinimizeKey = 'F9',
6}
  • AdminAcePermission: optional ACE that grants panel access in addition to ml_bridge admins
  • Admin.command: Command that opens the panel
  • Admin.theme: Panel theme: 'default', 'wasteland', 'cyberpunk', 'noir', 'fantasy'
  • Admin.MinimizeKey: Key that hides and restores the panel (F1-F12 or a single letter/digit)

Commands

shared/config.lua
1Config.Commands = {
2    list   = 'vpcount',
3    remove = 'vpremove',
4    clear  = 'vpclear',
5    save   = 'vpsave',
6}
  • list: print the persisted vehicle count and the first 50 vehicles in the server console
  • remove: /vpremove <plate> drops one vehicle from persistence
  • clear: /vpclear wipes every persisted vehicle. /vpclear <radius> only clears persisted vehicles within that many meters of the admin
  • save: Force-save all tracked vehicles to the database

Set a command name to false to disable it. The commands also run from the server console and RCON, except /vpclear <radius>, which needs an admin in game.

UI Theme

shared/config.lua
1Config.UITheme = {
2    preset = nil,
3    colors = {
4        primary = nil,
5        accent = nil,
6        background = nil,
7        surface = nil,
8        text = nil,
9    },
10}
  • preset: Resource UI preset, or nil to inherit the global preset.
  • colors: Optional CSS hex overrides for the listed UI colors. nil inherits the global value.

Logging

server/config_server.lua
1Config.DiscordWebhook = {
2    Default      = 'INSERT_WEBHOOK_LINK_HERE',
3    Parked       = 'INSERT_WEBHOOK_LINK_HERE',
4    Claimed      = 'INSERT_WEBHOOK_LINK_HERE',
5    Removed      = 'INSERT_WEBHOOK_LINK_HERE',
6    Destroyed    = 'INSERT_WEBHOOK_LINK_HERE',
7    Impounded    = 'INSERT_WEBHOOK_LINK_HERE',
8    Restored     = 'INSERT_WEBHOOK_LINK_HERE',
9    SpawnFailed  = 'INSERT_WEBHOOK_LINK_HERE',
10    GpsTampered  = 'INSERT_WEBHOOK_LINK_HERE',
11    GpsInstalled = 'INSERT_WEBHOOK_LINK_HERE',
12    AdminAction  = 'INSERT_WEBHOOK_LINK_HERE',
13}
14Config.LogProvider = 'discord'
  • Each action has its own webhook. Unset actions fall back to Default
  • LogProvider: 'discord', 'fivemanage', 'ox_lib', 'both', or 'all'

An action without a valid URL uses Default. With no valid URL at all, nothing is logged, whatever the provider.