DashKart docs

Integration API

Exports and events for phones and other scripts: duty, stores and the tablet.

DashKart exposes a small API so other resources can work with the rider job without editing DashKart’s code. The exports keep working when DashKart is delivered through the Cfx.re asset escrow.

The typical use is a phone app with Clock in / Clock out buttons. Any phone works (lb-phone, qb-phone, NPWD, yseries, a custom one): the phone’s app calls DashKart’s client exports from its own client script.


1. Allow clocking in / out away from the store

By default riders clock in and out at the store’s duty point. To allow it from a phone, turn it on in config/rider.lua:

remoteDuty = {
    clockIn = true,   -- clock in at a chosen store from anywhere
    clockOut = true,  -- clock out from anywhere
},

The server still decides everything:

  • Clocking in earns nothing by itself. The rider still rents the scooter / van at the store (and returns it there), and every order still starts with picking at the store.
  • A remote clock out is refused while an order is active (rider.finish_order_first) or a rental is out (rider.return_rental_first). The rental is never forfeited from a phone.
  • Staff-only stores, double clock-ins and the rate limit apply exactly as at the counter.
  • With remoteDuty off, the exports still work for a player standing at the duty point; anywhere else they get rider.duty_at_store.

A first clock-in from the phone gives the free DashKart tablet exactly like the counter (Config.Tablet.giveOnClockIn: straight into the inventory, once per character per day, only without one). With a full inventory the player is told to make room and clock in again. Servers that want the tablet without any item set Config.Tablet.requireItem = false; OpenTablet() then works for everyone.

The player always sees DashKart’s own notification for the result (clocked in, or why not), so your app only needs to refresh its buttons.


2. Client exports

Export Returns What it does
exports.dashkart:ClockIn(storeId?) ok, err Clocks in at storeId (a Config.Stores[].id), or at the nearest store when nil.
exports.dashkart:ClockOut() ok, err Clocks out.
exports.dashkart:IsOnDuty() boolean On duty right now.
exports.dashkart:GetDuty() table|nil { storeId, storeLabel, streak, deliveries, rental = { kind, plate }|nil, order = { code, state, customerName }|nil }, nil when off duty.
exports.dashkart:GetStores() table[] Every store, nearest first: { id, label, coords = { x, y, z }, distance, ownable }. Use it for a store picker.
exports.dashkart:CanUseRemoteDuty() table { clockIn = boolean, clockOut = boolean } from Config.Rider.remoteDuty, to show or hide your buttons.
exports.dashkart:OpenTablet() - Opens the DashKart tablet (same item check as F6).

err is a DashKart locale key, e.g. rider.already_on_duty, rider.staff_only, rider.finish_order_first, rider.return_rental_first, rider.duty_at_store.

ClockIn, ClockOut and OpenTablet wait for the server, so call them from a thread. NUI callbacks, command handlers and CreateThread bodies already are one. Called outside a thread they still run (in a new thread) but return nil, 'async'.

Event

-- fires on every clock in / out: phone app, store counter, logout
AddEventHandler('dashkart:client:dutyChanged', function(onDuty, storeId)
    -- refresh your app's state here
end)

3. Server exports

Export Returns
exports.dashkart:IsOnDuty(source) boolean
exports.dashkart:GetDuty(source) { storeId, storeLabel, since, deliveries, streak, rental = { kind, plate }|nil, order = { id, code, state }|nil } or nil

Read-only: they never change a rider’s state or move money.


4. Examples

Any phone: NUI callbacks in your app’s client script

Most phone app systems let your app’s web page call NUI callbacks of your own resource. Wire the buttons to DashKart there:

-- client.lua of your phone app resource
RegisterNUICallback('dashkart:state', function(_, cb)
    cb({
        onDuty = exports.dashkart:IsOnDuty(),
        duty = exports.dashkart:GetDuty(),
        stores = exports.dashkart:GetStores(),
        remote = exports.dashkart:CanUseRemoteDuty(),
    })
end)

RegisterNUICallback('dashkart:clockIn', function(data, cb)
    local ok, err = exports.dashkart:ClockIn(data and data.storeId) -- nil = nearest store
    cb({ ok = ok == true, err = err })
end)

RegisterNUICallback('dashkart:clockOut', function(_, cb)
    local ok, err = exports.dashkart:ClockOut()
    cb({ ok = ok == true, err = err })
end)

RegisterNUICallback('dashkart:openTablet', function(_, cb)
    exports.dashkart:OpenTablet()
    cb({})
end)

-- push duty changes to your app (use your phone's own "send to app" function here)
AddEventHandler('dashkart:client:dutyChanged', function(onDuty, storeId)
    SendNUIMessage({ action = 'dashkart:duty', onDuty = onDuty, storeId = storeId })
end)

How the app’s page reaches these callbacks, and how you register the app on the home screen, depends on your phone resource (for lb-phone: its custom app template and AddCustomApp; for qb-phone / NPWD: their app docs). DashKart’s part is only the exports above.

Simple test command (no phone needed)

RegisterCommand('dkduty', function()
    if exports.dashkart:IsOnDuty() then
        exports.dashkart:ClockOut()
    else
        exports.dashkart:ClockIn() -- nearest store
    end
end, false)

Server side: only paid for on duty, HUD badge, …

local duty = exports.dashkart:GetDuty(source)
if duty then
    print(('%s is riding for %s (%d deliveries)'):format(GetPlayerName(source), duty.storeLabel, duty.deliveries))
end