Koja Scripts

Events

Server and client events koja-dispatcher fires for other resources, the phone, dispatch-script and koja-mdt events it listens to, and the internal events you must not trigger.

Events

GroupSideDirection
Dispatcher eventsserverkoja-dispatcher → your resource
Events you can triggerserver / clientyour resource → koja-dispatcher
koja-mdt eventsserverkoja-mdt → koja-dispatcher
Client eventsclientkoja-dispatcher → the player's client
Internal eventsbothdo not use

Dispatcher events

Every after hook is also fired as a server-local event koja-dispatcher:<hook>, with the same payload, right after the hook functions ran.

server.lua
AddEventHandler('koja-dispatcher:incidentEnded', function(incident)
    if incident.status == 'resolved' then
        print(('incident #%d closed: %s'):format(incident.id, incident.outcome))
    end
end)
EventPayload
koja-dispatcher:callCreatedpublic call
koja-dispatcher:callAnswered{ id, kind, handler }
koja-dispatcher:callEnded{ id, kind, incident, handler }
koja-dispatcher:callMissed{ id, caller, scenario, label, waited }
koja-dispatcher:callDropped{ id, caller, scenario, label, handler }
koja-dispatcher:incidentCreatedpublic incident
koja-dispatcher:incidentUpdatedpublic incident
koja-dispatcher:incidentEndedpublic incident (status, outcome, endedAt)
koja-dispatcher:unitAssigned{ unit, src, callsign, incident }
koja-dispatcher:unitStatus{ unit, src, callsign, status, previous }
koja-dispatcher:unitAddedpublic unit
koja-dispatcher:unitRemoved{ unit, src, callsign }
koja-dispatcher:shiftStarted{ source, name, console }
koja-dispatcher:shiftEnded{ source, name, minutes, stats, earned, rating }
koja-dispatcher:settingChanged{ key, value, previous }
koja-dispatcher:adminAction{ source, name, text }
koja-dispatcher:mciStarted{ incident, patients, x, y }
koja-dispatcher:mciEnded{ incident, transported, treated, seconds, status }
koja-dispatcher:pursuitStarted{ incident, x, y, category }
koja-dispatcher:pursuitEnded{ incident, outcome, seconds }
koja-dispatcher:voiceConnected{ call, channel, provider, caller, handler }
koja-dispatcher:voiceEnded{ call, channel, caller, handler, handlerKind, reason, duration }
koja-dispatcher:boloCreatedinternal BOLO (event only, no hook)

Payload details are on the Hooks page.

koja-dispatcher:boloCreated

Fires when a BOLO is created in the dispatcher's own list (mdt_tools.boloSource = 'internal', or auto while koja-mdt is not running), from the CAD or the CreateBolo export. BOLOs created in koja-mdt fire koja-mdt's own events instead.

{
    id = 'i12', source = 'internal', type = 'vehicle',
    title = 'Black Sultan, armed', description = nil,
    plate = 'ABC123', model = 'Sultan', colour = 'Black', priority = 1, image = nil,
    x = 215.8, y = -810.1, label = 'Legion Square', incident = 1004,
    createdBy = 'Jane Doe', createdAt = 1730000000,
}
server.lua
AddEventHandler('koja-dispatcher:boloCreated', function(bolo)
    if bolo.plate then exports['my-alpr']:Watch(bolo.plate) end
end)

Events you can trigger

koja-dispatcher:phone:call (net, client → server)

Generic 911 entry point for any phone resource. The text is handled like a call to the service number: it goes through /911 logic, follow-up texts join the open call and dispatcher replies come back to the player (as phone messages when a supported phone is running, otherwise as normal notifications).

ArgumentTypeDescription
textstringMessage (control characters removed, max 250).
client.lua
TriggerServerEvent('koja-dispatcher:phone:call', 'My neighbour is screaming for help')

Only phones.enabled must be on; no supported phone is needed. The same player can call once every 2 seconds (Config.Phones.cooldown). When nobody can take the call, the player gets a "no dispatchers" message.

koja-dispatcher:compat:alert (net, client → server)

Feeds a client-side alert in the format of another dispatch script.

ArgumentTypeDescription
namestring'ps-dispatch', 'cd_dispatch', 'rcore_dispatch', 'qs-dispatch', 'core_dispatch' or 'linden_outlawalert'.
datatableThe alert in that script's format (see CompatAlert).

Rate limited to 8 alerts per 10 seconds per player (Config.Compat.rate), coordinates more than 600 m from the player's ped are replaced by the ped position (Config.Compat.maxDistance), and identical alerts within 20 s are dropped.

client.lua
TriggerServerEvent('koja-dispatcher:compat:alert', 'ps-dispatch', {
    message = 'Store robbery', code = '10-90', priority = 1,
    coords = GetEntityCoords(PlayerPedId()), jobs = { 'police' },
})

Dispatch-script events

While compat.enabled is on, koja-dispatcher answers the events of other dispatch scripts only when that script is not running (a shim from compat/ counts as not running). Existing robbery and crime scripts keep working without edits.

EventSideScriptArguments
ps-dispatch:server:notifyserver (net)ps-dispatchdata table
cd_dispatch:AddNotificationserver (net)cd_dispatchdata table
rcore_dispatch:server:sendAlertserver (net)rcore_dispatchdata table
qs-dispatch:server:CreateDispatchCallserver (net)qs-dispatchdata table
core_dispatch:addCallserver (net)core_dispatchcode, title, extraInfo, coords, job, cooldown, sprite, color, priority
wf-alerts:svNotifyserver (net)linden_outlawalertdata table
wf-alerts:svNotify911server (net)linden_outlawalertmessage, caller, coords
ps-dispatch:client:notifyclientps-dispatchdata table, forwarded to the server
cd_dispatch:AddNotificationclientcd_dispatchdata table, forwarded
rcore_dispatch:server:sendAlertclientrcore_dispatchdata table, forwarded
qs-dispatch:server:CreateDispatchCallclientqs-dispatchdata table, forwarded

Server-side TriggerEvent (without a player) works too. Events sent by players use the same rate limit and 600 m clamp as koja-dispatcher:compat:alert.

Phone events

With phones.enabled on, the dispatcher listens to the running phone:

PhoneListened to
lb-phonelb-phone:newCall (calls to the service number or to a company in Config.Phones.companies), lb-phone:newCompanyMessage, lb-phone:messages:messageSent (texts to the service number)
npwdexports.npwd:onCall(number) and exports.npwd:onMessage(number) for the service number
qs-smartphone-pronet events qs-smartphone:server:sendJobAlert and qs-smartphone-pro:server:sendJobAlert

These are the phones' own events. You do not trigger them yourself.


koja-mdt events

With koja-mdt started and mdt.enabled on, koja-dispatcher listens to these server events. They are accepted only when koja-mdt fires them; the same events from other resources are ignored.

EventWhat koja-dispatcher does
koja-mdt:dispatch:createRings the MDT call at the console (mdt.pullCalls = 'calls') or opens an incident ('incidents'), only while a dispatcher is on shift and mode.calls is not generated. Calls created by koja-dispatcher itself are skipped.
koja-mdt:dispatch:closeEnds the linked call and resolves the linked incident (outcome = 'mdt').
koja-mdt:dispatch:assignAssigns the officer's player unit to the linked incident (mdt.syncStatus).
koja-mdt:dispatch:detachReleases the officer's unit (mdt.syncStatus).
koja-mdt:unit:statusMirrors the MDT status (available, busy, traffic, break) to the player unit (mdt.syncStatus).
koja-mdt:officer:dutyRe-checks which players are field units.
koja-mdt:bolo:create, koja-mdt:bolo:update, koja-mdt:bolo:closeRefreshes the CAD BOLO list.
koja-mdt:anpr:hitAlerts dispatchers when the read plate has a BOLO (mdt_tools.alpr).

To create MDT calls or status changes from your own resource, use the koja-mdt exports. koja-dispatcher picks the result up through these events.


Client events

koja-dispatcher sends these net events to the player's client. Other client scripts may listen to them, for example to show their own HUD. Register the name in your resource and add a handler; never trigger them yourself.

client.lua
RegisterNetEvent('koja-dispatcher:client:fieldAssign', function(incident)
    print(('assigned to #%d: %s at %s'):format(incident.id, incident.title, incident.label or '?'))
end)
EventSent toPayload
koja-dispatcher:client:fieldAssignfield unit{ id, category, title, priority, x, y, z, label, note, scene, located }. Sent on assignment and again when the incident moves.
koja-dispatcher:client:fieldMovefield unit{ x, y } staging point from the CAD.
koja-dispatcher:client:fieldOnScenefield unit{ id } the unit arrived.
koja-dispatcher:client:fieldDonefield unit{ id, outcome } the incident ended.
koja-dispatcher:client:fieldClearfield unit{} the unit was released or recalled.
koja-dispatcher:client:fieldStatusfield unitthe new status string ('available', 'enroute', ...).
koja-dispatcher:client:fieldCodefield unit{ code, status, units, mdt } after a field code; { status = 'offline' } when the unit goes off duty.
koja-dispatcher:client:boloNoticeon-duty police units{ title, plate, priority, label } new internal BOLO (mdt_tools.notifyUnits).
koja-dispatcher:client:voiceRolecaller, dispatcher or unit in a voice callrole table { call, role, provider, elapsed, held, ... }, or false when the voice call ended.
koja-dispatcher:client:syncall playersshared client settings: { modules, consoles, cinematic, remote, fieldScenes, fieldWaypoint, clientAlerts }. modules holds every setting registered with client = true.
koja-dispatcher:client:moduleslocal (client TriggerEvent)the modules table after each sync.

Dispatcher-only events (koja-dispatcher:client:call, incident, unit, radio, roster, ...) only reach players on shift and carry CAD data; their format can change between versions, so read the server exports instead.


Internal events

These are part of koja-dispatcher's own client/server protocol. Never trigger them from other resources. Server handlers check the sender and rate limits, and their arguments change between versions.

EventWhy
koja-dispatcher:rpc:request, koja-dispatcher:rpc:response, koja-dispatcher:rpc:clientRequest, koja-dispatcher:rpc:clientResponseThe CAD's RPC channel. Use the exports.
koja-dispatcher:server:syncRequests the client settings.
koja-dispatcher:server:voiceAI caller speech upload from the dispatcher's microphone.
koja-dispatcher:server:voiceTalkingVoice call talking indicator.
koja-dispatcher:server:shots, koja-dispatcher:server:crashShots and crash sensors. Use CreateCall / CreateIncident for your own alerts.
koja-dispatcher:server:avatar, koja-dispatcher:server:stationState, koja-dispatcher:server:terrainRowsAvatar capture, station screens, 3D terrain scan.
koja-dispatcher:client:*Sent by the server only. Triggering them locally only confuses that player's UI.