Developer

5 min readUpdated Today

Overview

Other resources integrate through server exports and the open/server.lua hooks. Exports coordinate with garages, tow jobs, mechanics and dealerships so a vehicle is never saved or deleted twice. The open/ folder holds the ownership and garage-state logic, which the server owner adapts to their framework.

Server Exports

Coordination

  • DoNotSave(plateOrNetId, reason?): Exclude a vehicle from persistence and remove it if already tracked. Accepts a plate, a network id, or an entity handle
  • ExcludeFromPersistence(plateOrNetId, reason?): Alias of DoNotSave
  • RemoveExclusion(plate): Clear an exclusion
  • IsExcludedFromPersistence(plate): Returns true if excluded
  • LockVehicle(plate): Temporarily block persistence (during towing, mechanic work). Auto-expires after 5 minutes
  • UnlockVehicle(plate): Release a lock
  • IsVehicleLocked(plate): Returns true if locked

Vehicle Control

  • PurgeVehicle(plateOrNetId, keepInWorld?): Remove a vehicle from persistence. Despawns it unless keepInWorld is true
  • UpdateVehicle(plateOrNetId): Force a save and refresh the vehicle's stored properties from its owner client
  • ClaimVehicle(plateOrNetId, src): Register a vehicle as persistent and owned by the player src
  • TransferPersistedVehicle(plate, newOwner): Reassign a persisted vehicle to another owner identifier
  • PurgeOwner(ownerId): Remove every persisted vehicle belonging to an identifier. Returns the count removed
  • SetVehiclePlate(plateOrNetId, newPlate): Move persistence to a new plate after a plate change
  • RemovePersistedVehicle(plate): Drop a vehicle from persistence, keeping it in the world

Pause

  • PausePersistence(state): Pause or resume all saving and claiming. Returns the new state
  • IsPersistencePaused(): Returns the current pause state

Maintenance

  • RunCleanup(): Run the Config.Cleanup sweep once, outside its schedule. Returns the number of vehicles removed

Query

  • IsVehiclePersisted(plate): Returns true if the plate is persisted
  • GetWorldVehicles(): Returns every tracked vehicle
  • GetVehicleStatus(plate): Returns { isPersisted, isExcluded, isLocked, exclusion, lock, vehicle }
  • GetVehicleCoords(plate): Returns the live or saved coordinates of a persisted vehicle. Always the real position, even when the owner's GPS is tampered
  • GetVehiclesCoords(plates): Returns a map of plate to coordinates

Client Exports

  • PurgeVehicle(plateOrNetId): Drop a vehicle from persistence and delete it from the client side. Used by resources that delete vehicles on the client. Returns true when the request was accepted

Deleting Vehicles From Another Resource

With Config.RespawnOnExternalDelete = false (the default) nothing is needed: a plain DeleteEntity or an admin /dv drops the vehicle from persistence like every other script expects.

With Config.RespawnOnExternalDelete = true every delete is undone by the respawn engine unless it is routed through the script. Either call the PurgeVehicle export, or add the shipped hook as the first script line of the deleting resource:

fxmanifest.lua
1shared_script '@ml_vehicle_persistence/deletehook.lua'
First line

The hook replaces the DeleteEntity and DeleteVehicle natives. A resource that caches them in a local before the hook loads bypasses it, so the shared_script line must come before every other script entry.

The request is validated server-side: a tracked vehicle is only removed by an admin or by its own owner, and a locked vehicle is never touched.

Server Handlers

Located in open/server.lua.

CanPersist

open/server.lua
1function OpenServer.CanPersist(src, vehicle, plate, owner)
2    return nil
3end

Ownership gate before a vehicle becomes persistent. Return false to block, true to force-allow for custom ownership systems, nil for the default owned-vehicles check.

CanClaim

open/server.lua
1function OpenServer.CanClaim(src, vehicle, plate)
2    return nil
3end

Gate before a world vehicle is claimed by its first driver. Return false to block the claim, for example to require a hotwire item.

FetchOwnedPlates

open/server.lua
1function OpenServer.FetchOwnedPlates()
2    return {}
3end

Returns an array of plate strings from the ownership database. Claiming never touches these plates. The default reads player_vehicles or owned_vehicles.

IsPlateOwned

open/server.lua
1function OpenServer.IsPlateOwned(plate)
2    return false
3end

Authoritative single-plate check, consulted when a plate is missing from the cached registry, such as a car bought seconds earlier. Return true if the plate belongs to a player.

FetchGaragedVehicles

open/server.lua
1function OpenServer.FetchGaragedVehicles(src, owner)
2    return {}
3end

Vehicles the player owns that are sitting in a garage right now, so the GPS item shows them as stored instead of a dead waypoint. Return an array of { plate, model?, garage? }. The garage value is shown to the player as-is, so raw ids can be mapped to friendly names here. The default reads player_vehicles with state = 1.

OnVehicleRestored

open/server.lua
1function OpenServer.OnVehicleRestored(plate, owner)
2end

Fires for every vehicle put back into the world after a restart. The default marks the vehicle as out of the garage so garage scripts stay consistent.

ImpoundVehicle

open/server.lua
1function OpenServer.ImpoundVehicle(plate, owner, fee, impoundLot)
2end

Orphan cleanup with action = 'impound' routes here. Adapt it to your impound system.

Lifecycle Hooks

open/server.lua
1function OpenServer.OnVehiclePersisted(plate, owner) end
2function OpenServer.OnVehicleClaimed(plate, owner, src) end
3function OpenServer.OnVehicleRemoved(plate, owner, reason) end
4function OpenServer.OnGpsTampered(plate, owner, src) end
5function OpenServer.OnGpsInstalled(plate, owner, src) end

OnVehicleRemoved fires with reason one of: stored, excluded, destroyed, limit, entity_removed, impounded, orphan_deleted, owner_purged, cleanup, admin_remove, admin_clear, deleted_by_client_hook, removed_by_<resource>, deleted_by_<resource>.

OnGpsTampered fires when a thief rips a vehicle's GPS out with the scanner item; OnGpsInstalled fires when the owner restores live tracking with a module.

Client Handlers

Located in open/client.lua. These listen for garage store events and drop the stored vehicle from persistence. Add your garage's event here:

open/client.lua
1function OpenClient.NotifyStored(plate)
2    TriggerServerEvent('ml_vehicle_persistence:server:vehicleStored', plate)
3end

Examples

lua
1local netId = NetworkGetNetworkIdFromEntity(vehicle)
2exports.ml_vehicle_persistence:DoNotSave(netId, 'mission')
lua
1exports.ml_vehicle_persistence:UpdateVehicle(plate)
lua
1exports.ml_vehicle_persistence:ClaimVehicle(netId, src)