XyraLScripts
Installation guide

Dispatch

Multi-department live dispatch for police, EMS and fire: vehicle-aware unit tracking, priority calls, TAC radio channels and a provider API.

Requirements

Required

  • qbx_core or qb-core — the bridge auto-detects which you run
  • ox_lib

Optional

  • pma-voice, for joining per-incident TAC channels.
  • MDT — supported natively, no adapter needed.

Installation

  1. Drop the XS-Dispatch folder into your resources directory.
  2. Add ensure XS-Dispatch to server.cfg, after your framework and ox_lib.
  3. Grant the Integration Studio ACE if you want it:
    add_ace group.admin XS-CriminalTablet.dispatch.integrations allow
  4. Restart and open the console with F6.

Keys

  • F6 — open the dispatch console
  • F5 — panic
  • Y — respond to the highlighted call from the quick overlay
  • U — toggle the quick overlay
  • I — join the operation’s TAC channel, with pma-voice running

All of these are rebindable in GTA’s own keybind settings. The defaults deliberately avoid MDT’s F10 and F11.

Departments

Each entry in Config.Departments controls:

  • Which framework jobs belong to that department
  • Which departments it is allowed to see
  • Its GTA blip colour, and separate blip sprites per vehicle category
  • Its allowed operational statuses
  • Its interface identity and alert tone name

This is what keeps fire apparatus visually distinct from EMS ambulances and police units even when all three are on the same incident. Visibility is per department, so you can run full mutual aid or keep each service on its own board.

Calls

Calls carry a P1/P2/P3 priority, a live elapsed timer, GPS routing, a responding-unit list and a clear action. Civilians can report with /911, and the panic key raises an immediate alert.

Other server resources can create fully specified calls:

local callId, call = exports['XS-Dispatch']:CreateCall({
    type = 'fire',
    title = 'Commercial Structure Fire',
    description = 'Multiple callers report smoke from the roof.',
    priority = 1,
    departments = { 'fire', 'ems', 'police' },
    coords = vector3(215.4, -810.2, 30.7),
    street = 'San Andreas Avenue',
    caller = 'Automatic Alarm',
})

Active calls and call numbering survive a restart.

Units and radio

Units can hold multiple members with leaders, callsigns, vehicles and capability IDs such as als_medical, fire_suppression, air_support or supervisor. The createUnit, joinUnit and leaveUnit callbacks back patrols, ambulance crews and engine companies.

Every incident is given an operation ID and a TAC channel. With pma-voice running, assigned responders press I to join it. Automatic switching is off by default so the script never drags someone off the channel they are already on — enable it with Config.Radio.autoJoinOperations.

Interface management for multi-member units is the next phase of the unit system; the primitives are in place ahead of it.

Provider API

External resources integrate through providers rather than by editing Dispatch, and it never reads another resource’s database tables.

exports['XS-Dispatch']:RegisterProvider('phone', 'my-phone', {
    sendStatus = function(call, status) end,
    sendMessage = function(call, message) end,
})

MDT adapters consume XS-Dispatch:provider:mdt:callCreated and callClosed. Phone adapters submit through SubmitPhoneEmergency and can listen for XS-Dispatch:provider:phone:callerUpdate to push responder messages back to the caller.

An MDT can supply responder callsigns without XS-Dispatch knowing its schema:

exports['XS-Dispatch']:RegisterIdentityProvider('my-mdt', function(source)
    return exports['my-mdt']:GetDispatchIdentity(source)
end)

Resolution order is registered MDT provider, framework metadata, saved Dispatch profile, then a generated fallback.

Integration Studio

The studio lets an admin build custom call presets in game, backed by a field registry with text, person, vehicle, patient, hazard and checklist primitives. New field types can be registered at runtime:

exports['XS-Dispatch']:RegisterFieldType('alarm_zone', {
    label = 'Alarm Zone',
    icon = 'sensor',
})

Presets persist to data/integrations.json. The studio cannot overwrite built-in call types, so an experiment can always be undone by deleting the preset.

Before going live

  • Map every dispatch-capable job into Config.Departments — a job that is in no department sees no calls.
  • Decide the visibility rules deliberately. Full mutual aid and isolated networks look identical until an incident needs both.
  • Confirm the F6/F5 defaults do not collide with anything else you run, and rebind if they do.
  • Grant XS-CriminalTablet.dispatch.integrations only to staff who should be editing call presets.
  • If you run MDT, leave its dispatch provider on auto — the two are natively aware of each other.

Updating

Replace the whole folder and restart. data/integrations.json holds your custom presets — keep it when you upgrade, or you will lose them. This is a v0.1.0 release, so read the release notes before updating rather than after.

Troubleshooting

F6 or F5 do nothing

Something else has claimed the key, or the player’s job is not mapped into any department. Check Config.Departments first, then rebind in GTA’s keybind settings — both keys are registered as rebindable rather than hard-coded.

Integration Studio will not open

It is ACE-gated. Add add_ace group.admin XS-CriminalTablet.dispatch.integrations allow and make sure the admin is actually in that group.

Units from another department are invisible

That is the per-department visibility rule doing its job. Each department lists which other departments it can see, so isolated networks are the default unless you opt into mutual aid.

Responders are not switching radio channels

Automatic joining is off by default, on purpose — it would otherwise pull players off whatever channel they were already using. Set Config.Radio.autoJoinOperations, or have responders press I to join the operation channel themselves. Either way this needs pma-voice running.

Do calls survive a server restart?

Yes. Active calls and the call numbering sequence are both recovered on restart, so a scheduled restart mid-incident does not lose the board.

Can I use this alongside MDT?

Yes — that is the intended pairing, and no adapter is needed. MDT detects a dispatch provider automatically and falls back to its own internal CAD when none is present.

Support

Found a bug? Open an issue. Need setup help? Join the Discord — check this guide first, it usually has the answer.