Pulse Phone docs

Connecting your own garage script

The Garage app lists a character's vehicles and shows where each one is: which garage it is stored in, which lot has impounded it and what that lot charges, and whether the phone knows where a car that is out has got to. It reads this from the garage script the server runs.

Supported out of the box

The phone recognises these scripts on its own when their resource is started:

Script Provider name
qb-garages qb-garages
qbx_garages qbx_garages
jg-advancedgarages jg-advancedgarages
cd_garage cd_garage
okokGarage okokGarage
esx_garage (2.x and the older version) esx_garage
esx_advancedgarage esx_advancedgarage
none of these: the framework's own vehicle table framework-table

To force one, set it in config/config.lua:

Lua
Config.Providers = {
    garage = 'jg-advancedgarages',   -- or 'auto' (the default), or 'none' to switch the connection off
}

Valet delivery is switched off while jg-advancedgarages or cd_garage runs, because those scripts keep "parked" in a column of their own. A car delivered by the phone would still be listed in their garage, so it could be taken out twice.

Garage positions when your script has none

If your script's config does not give a garage a position, put the position in Config.Garage.garages (config/apps.lua) under the same id your script stores in the vehicle row. Put impound lots in Config.Garage.impounds. The phone uses these for the Directions button.

Writing a provider for an in-house script

Add a file to bridge/server/ (the manifest loads every .lua there), for example garage_mygarage.lua:

Lua
local RESOURCE = 'my-garage'
local P = { KnowsImpounds = true, Script = RESOURCE }

-- Is the script running? Return false and a reason otherwise; the reason shows in the console.
function P.Available()
    return GarageKit.Ready(RESOURCE)
end

function P.Describe() return 'my-garage (player_vehicles state, garage)' end

-- The character's vehicles: cb(list) or cb({}, reason).
function P.ListVehicles(src, cb)
    GarageKit.Rows(src, function(rows, reason)
        if not rows then return cb({}, reason) end
        local out = {}
        for _, r in ipairs(rows) do
            local v = GarageKit.Condition(r)          -- fuel / engine / body, 0-100, or nil
            v.plate = r.plate
            v.model = GarageKit.Model(r)
            if tonumber(r.state) == 1 then
                v.stored = true
                v.garage = r.garage
                v.garageLabel = 'Pillbox Garage'
                v.place = { name = 'Pillbox Garage', x = 213.9, y = -808.4, z = 31.0 }
            end
            out[#out + 1] = v
        end
        cb(out)
    end)
end

-- Optional: where a garage id is, for Directions. Return a place, or nil and a reason.
function P.GetGarageLocation(id)
    return nil, 'my-garage has no positions'
end

-- The number is a priority: a higher number wins when several providers are available.
Bridge.Register('garage', RESOURCE, P, 20)

GarageKit.Rows returns every column of the character's rows in the framework's vehicle table (player_vehicles for QBCore and Qbox, owned_vehicles for ESX). A column your script does not have is simply nil.

The vehicle fields

Leave out anything your script does not record. The phone never fills a gap with a guess; it says what it does not know.

Field Meaning
plate, model required
nickname the name the player gave the car
fuel, engine, body 0-100 (GarageKit.Condition(row) reads the usual columns)
stored true when it is in a garage
garage, garageLabel the garage's id and the name to show
impounded true when the police impounded it
depot true when it is out but a depot holds it (a car left out at a restart)
fee what the lot charges to release it
impoundReason, impoundBy why it was impounded and by whom, when the script records them
releaseAt when it may be collected, in milliseconds
place { name, x, y, z }: the one garage or lot the car is at
lots a list of places when any of several lots can release it; the phone picks the nearest one that takes the vehicle's class (a place may carry classes = { 0, 1, ... })
lotLabel the lot's name when its position is unknown
anyGarage true when the car can be taken out at any garage in lots
lastSeen { x, y, z }: where the script last recorded a car that is out

Helpers

Helper Returns
GarageKit.Ready(resource) true, or false and a reason (not started, no SQL, no vehicle table)
GarageKit.Rows(src, cb) the character's rows, cb(rows, reason, tableName)
GarageKit.Model(row), GarageKit.Condition(row) the model and the condition from a row
GarageKit.Point(v) { x, y, z } from a vector, a table, an array or cd_garage's x_1 form, or nil
GarageKit.Place(name, entry) a place from a config entry, looking in the usual position fields
GarageKit.Export(resource, fn, ...) an export's result, guarded, or nil and a reason
GarageKit.Config(resource, { 'config.lua' }) the script's config read in a sandbox, or nil
GarageKit.Str, GarageKit.Num, GarageKit.Fee, GarageKit.Json safe conversions from a column

Restart the phone after adding the file. The phone's startup line in the console (and the phone:diag command) names the garage provider it chose. A script that is running but could not be connected is named there with the reason.