Developer

6 min readUpdated 2 weeks ago

Overview

Integration happens through exports and vehicle state bags. Fuel is server-authoritative: clients can read it anywhere, but writes go through the server exports. The open/ folder is not escrowed and is there for your own code.

Vehicle State Bags

Every vehicle carries its fuel state in replicated state bags. Read them from any resource, client or server:

lua
1local state = Entity(vehicle).state
2
3state.fuel              -- number, 0-100 percent
4state.fuelType          -- 'fuel' | 'diesel' | 'kerosene' | ...
5state.fuelTypeConverted -- true after a conversion kit or SetFuelType
6state.tankSize          -- tank capacity in liters (with DynamicTankSize)
7state.tankBonus         -- extra liters from installed tank upgrades
8state.engineOn          -- mirrored engine state, used by the idle drain

fuel is a percentage. Convert with tankSize or the client exports FuelPercentToLiters / FuelLitersToPercent. While driving, the driver client updates the level every tick and replicates it every Config.Consumption.replicationInterval ticks.

lua
1GlobalState['ml_fuel:mode']     -- 'advanced' | 'simple'
2GlobalState['ml_fuel:stations'] -- active scavenge stations: { { idx, coords, charges, maxCharges }, ... }

Server Exports

Vehicle Fuel

  • GetFuel(netId): fuel level 0-100, or nil for an invalid vehicle
  • SetFuel(netId, fuel): set the level (clamped 0-100). Returns boolean
  • GetFuelType(netId): current fuel type string
  • SetFuelType(netId, fuelType): set the type (must exist in Config.FuelTypes) and mark the vehicle as converted. Returns boolean
  • GetVehicleFuel(netId) / SetVehicleFuel(netId, fuel) / GetVehicleFuelType(netId) / SetVehicleFuelType(netId, fuelType): aliases of the four above
  • GetFuelTypes(): the Config.FuelTypes table (key -> { label, color, consumptionRate })
  • GetVehicleFuelInfo(netId): fuel level, type and tank data of a spawned vehicle, or nil

Conversions and Upgrades

  • GetVehicleConversion(plate): converted fuel type for a plate, or nil
  • SetVehicleConversion(plate, fuelType, license?): write a conversion to the database
  • DeleteVehicleConversion(plate): remove a conversion
  • GetVehicleTankBonus(plate): total bonus liters installed on a plate
  • GetVehicleUpgrades(plate): { tankBonus, upgrades } for a plate
  • CanInstallUpgrade(plate, upgradeItem): false when the upgrade hit its maxStacks
  • GrantTankUpgrade(plate, upgradeItem, playerLicense?): installs an upgrade on a plate from code
  • RemoveTankUpgrade(plate, upgradeItem): removes it
  • GetTankUpgradeItems(): the configured upgrade items
  • GetVehicleFuelSummary(plate): { fuelType, converted, tankBonus, upgrades } of a plate
  • ClearVehicleConversion(plate): removes the conversion of a plate and returns true on success

Scavenge Stations

  • GetActiveStations(): array of { idx, coords, charges, maxCharges }
  • IsStationActiveAtCoords(coords): true when the coordinate falls inside an active station zone
  • IsStationActive(stationIdx) / GetStationCharges(stationIdx): state of one station by index
  • GetStationLitres(stationIdx): litres left in the tank of one station
  • RefreshActiveStations(): force a station rotation now
  • GetSiphonItemName(): the configured siphon tool item name

Database

  • WaitForDatabase() / IsDatabaseReady(): block until / check whether the table init finished

Client Exports

Reading Fuel

  • GetVehicleFuel(vehicle): fuel level 0-100 (entity handle, not netId)
  • GetVehicleFuelType(vehicle): current fuel type
  • GetFuel(vehicle): alias of GetVehicleFuel
  • IsFuelCompatible(vehicle, fuelType): true when the vehicle runs the given type
  • GetVehicleTankSize(vehicle): tank capacity in liters (includes upgrade bonus)
  • FuelPercentToLiters(vehicle, percent) / FuelLitersToPercent(vehicle, liters): unit conversion against the vehicle tank
  • GetVehicleHandlingData(vehicle): cached handling values used by the consumption model
  • GetVehicleBaseTankSize(vehicle): tank capacity in liters before upgrades
  • IsVehicleFuelConverted(vehicle): true when the vehicle runs a converted fuel type
  • GetVehicleFuelInfo(vehicle): fuel level, type and tank data of a vehicle
No client-side writes

There are no SetFuel exports on the client. Modify fuel from the server exports only.

UI and Menus

  • OpenRefuelMenu(vehicle): open the Transfer UI against a vehicle with the best compatible container
  • OpenJerryCanMenu(slot, itemName): open the Transfer UI for a specific jerry can slot
  • OpenTransferUI(jerrycanData, vehicleData, mode): low-level open. mode = 'refuel' | 'siphon'
  • OpenTransferUIAdvanced(jerrycanData, vehicleData, options): open with mode switching (options.canRefuel, options.canSiphon)
  • OpenGeneratorTransfer(propId, genData): open the Transfer UI against a generator. genData = { fuel, fuelTank, fuelItem }
  • CloseTransferUI() / IsTransferUIOpen()
  • ShowFuelUI() / HideFuelUI() / UpdateFuelDisplay(data): control the gauge HUD
  • ShowFuelGauge() / HideFuelGauge(): show or hide the gauge from another HUD resource
  • InspectVehicleFuel(vehicle): open the fuel system inspection of a vehicle

Scavenging and State

  • StartScavenging(pumpCoords, pumpEntity?): start the pump scavenge flow
  • TryScavengeNearbyWreck() / GetNearbyWreck() / SearchNearestWreck(): wreck scavenge helpers
  • IsPumpAtActiveStation(coords) / FindClosestPump(coords, radius)
  • IsInNoLootZone(): true when the player stands inside a no-loot zone
  • IsFueling() / SetFueling(value) / GetLastVehicle()
  • GetFuelCapPosition(vehicle) / IsNearFuelCap(vehicle, maxDistance?)
  • GetJerryCanItems() / HasRefuelItem(): configured jerry can names and whether the player carries any refuel container

Jerry Can Metadata Contract

Jerry cans are the exchange format between ML Fuel and other resources. A jerry can is a normal inventory item with four metadata keys:

lua
1metadata = {
2    fuelType      = 'diesel',   -- raw fuel type key, nil when empty
3    fuelTypeLabel = 'Diesel',   -- localized label for the tooltip
4    fuelAmount    = 18.5,       -- current liters, rounded to 2 decimals
5    maxCapacity   = 25,         -- capacity from Config.JerryCans[item].capacity
6}
  • The item is refillable. A consumer empties the metadata, it never removes the item
  • An empty can has fuelAmount = 0 and fuelType = nil, and can then be filled with any type in its acceptsFuel list
  • Fuel types never mix: a can holding diesel refuses gasoline until emptied
  • Always re-read the slot server-side before consuming, never trust amounts sent by a client

The read-and-consume pattern for accepting ML Fuel jerry cans in another resource:

your_script/server.lua
1local ACCEPTED = { fuel = true, diesel = true }
2
3function DrawFuelFromJerryCan(src, slot, litersWanted)
4    local item = Bridge.GetSlot(src, slot)
5    if not item then return 0 end
6
7    local meta = item.metadata or {}
8    local amount = meta.fuelAmount or 0
9    local fuelType = meta.fuelType
10
11    if amount <= 0 or not ACCEPTED[fuelType] then return 0 end
12
13    local drawn = math.min(litersWanted, amount)
14    local left = math.floor((amount - drawn) * 100 + 0.5) / 100
15
16    Bridge.SetItemMeta(src, slot, {
17        fuelAmount    = left,
18        fuelType      = left > 0 and fuelType or nil,
19        fuelTypeLabel = left > 0 and meta.fuelTypeLabel or nil,
20        maxCapacity   = meta.maxCapacity,
21    })
22
23    return drawn
24end

ml_recycler machines consume jerry cans through this contract.

Commands

  • /mlfuel setfuel|settype|info|resetcooldowns: admin utilities on the current or last vehicle; resetcooldowns forces a station rotation
  • /showpumps: admin. Temporary blips on the active scavenge stations. Name and blip settings in Config.PumpScavenging.staffCommand
  • /mlwreckreset: admin or server console. Clears all wreck cooldowns
  • ml_fuel_interact: key mapping (default E) for pump/wreck interaction in textui mode
  • ml_fuel_stop_charge: key mapping (default BACKSPACE) to stop an EV charging session

Examples

your_script/client.lua
1if cache.vehicle then
2    local fuel = exports.ml_fuel:GetVehicleFuel(cache.vehicle)
3    local fuelType = exports.ml_fuel:GetVehicleFuelType(cache.vehicle)
4    local liters = exports.ml_fuel:FuelPercentToLiters(cache.vehicle, fuel)
5    print(('%.1f%% (%.1fL) of %s'):format(fuel, liters, fuelType))
6end
your_script/server.lua
1local netId = NetworkGetNetworkIdFromEntity(vehicle)
2exports.ml_fuel:SetFuel(netId, 100.0)
3exports.ml_fuel:SetFuelType(netId, 'diesel')
your_script/client.lua
1AddStateBagChangeHandler('fuel', nil, function(bagName, _, value)
2    local entity = GetEntityFromStateBagName(bagName)
3    if entity == 0 or entity ~= cache.vehicle then return end
4    if value and value < 10 then
5        -- low fuel warning
6    end
7end)