XyraLScripts
Installation guide

MDT

Police, EMS and fire in one MDT: live CAD, civilian and vehicle records, warrants and BOLOs, patient care reports, fire and hazmat logs, and a live unit map.

01

Requirements

Required

  • qbx_core
  • ox_lib
  • oxmysql

Optional

  • ox_target or qb-target — right-click players in-world to open the MDT or issue a citation.

QBox only. Unlike the other XS scripts, this one lists qbx_core as a hard dependency. Standard QBCore (qb-core) is not supported.

Current release is v1.4.0.

02

Installation

  1. Place the XS-MDT folder in your resources directory, for example resources/[standalone]/XS-MDT.
  2. Add ensure XS-MDT to server.cfg.
  3. Run sql/mdt.sql against your database. It uses CREATE TABLE IF NOT EXISTS throughout, so re-running it is safe.
  4. Set Config.AuthorizedJobs to your police job names.
  5. Restart the resource.
ensure XS-MDT

Upgrading from an earlier version? Run only the commented ALTER TABLE lines at the top of sql/mdt.sql that match the version you are coming from — not the whole file's worth of migrations.

03

Configuration

All options live in config.lua.

Access

Config.AuthorizedJobs = {
    ['police']      = true,
    ['sheriff']     = true,
    ['swat']        = true,
    ['statepolice'] = true,
}

Config.MinGrade   = 0      -- 0 = every grade
Config.OpenKey    = 'F9'   -- players can rebind in GTA key bindings
Config.OnDutyOnly = true

Job names are case sensitive and must match your QBCore job names exactly.

Supervisor threshold

Config.SupervisorGrade = 3

Grade at or above this unlocks the supervisor-only features listed below.

Fines

Config.FineDeduction = {
    Enabled              = true,
    AutoDeductArrests    = true,   -- deduct as soon as the arrest is logged
    AutoDeductCitations  = false,  -- false = pay-later model (recommended)
    AutoDeductOnMarkPaid = true,   -- deduct when a supervisor marks it paid
}

Jail, audit and target

-- 'qb-prison' | 'ps-prison' | false
Config.JailResource = false

-- Leave empty to disable Discord audit posting
Config.AuditWebhook = ''

Config.Target = { Enabled = true, MaxDistance = 3.0 }

Config.BodyCam = { Enabled = true, RetentionDays = 30 }  -- 0 = keep forever

Licences

Qbox tracks whether someone holds a licence. It has no idea whether that licence is currently any good, which is what an officer wants to know at a traffic stop. This adds the missing half.

Config.Licences = {
    Enabled = true,

    -- Suspending or revoking also flips the licence off in Qbox, so anything
    -- else that checks it agrees with the MDT. It only ever touches licences
    -- the character actually holds.
    WriteBackToMetadata = true,

    -- Revoking is supervisor-only. Suspending is not, because it happens
    -- roadside and can be undone.
    RevokeRequiresSupervisor = true,

    Types = {
        { key = 'driver',   label = 'Driver',   icon = '🚗' },
        { key = 'weapon',   label = 'Weapon',   icon = '🔫' },
        { key = 'business', label = 'Business', icon = '💼' },
    },
}
04

Supervisor features

Officers at or above Config.SupervisorGrade unlock:

  • Department statistics — dashboard: weekly arrests, citations, fines and a top-officer leaderboard
  • Department bulletins — post notices with Normal, Urgent or Critical priority, pinning and auto-expiry
  • Audit log viewer — dashboard, with action, officer and date filters
  • Body camera log viewer — Officers panel, per officer
  • Edit penal codes — Penal Codes panel
  • Mark citations paid — Citations panel
  • Delete incidents — Incidents panel
05

Dispatch integration

Gunshots, crashes and fights are auto-detected. To send a call from another resource, use one of these.

Server export (recommended)

exports['XS-MDT']:CreateDispatchCall({
    callType    = 'BANK_ROBBERY',
    description = 'Silent alarm triggered at Maze Bank.',
    coords      = { x = 148.0, y = -1044.0, z = 29.0 },
    street      = 'Alta Street',   -- optional, auto-detected if omitted
    callerName  = '911 Caller',    -- optional
})

Client event

TriggerEvent('XS-MDT:client:dispatch:custom',
    'ROBBERY',
    'Armed robbery in progress.',
    vector3(24.8, -1347.3, 29.5)
)

Server net event

TriggerServerEvent('XS-MDT:server:autoDispatch', {
    callType    = 'SHOTS_FIRED',
    description = 'Gunshots reported in the area.',
    street      = 'Forum Drive',
    coords      = { x = 80.0, y = -1283.0, z = 29.0 },
})

Built-in call type keys

SHOTS_FIRED     Shots Fired
FIGHT           Fight / Assault
VEHICLE_CRASH   Vehicle Crash
ROBBERY         Robbery in Progress
BANK_ROBBERY    Bank Robbery
STORE_ROBBERY   Store Robbery
TRAFFIC_STOP    Traffic Stop
MEDICAL         Medical Emergency
FIRE            Structure Fire
SUSPICIOUS      Suspicious Activity

Any other key is displayed as the raw string. Keys are upper snake case — SHOTS_FIRED, not Shots Fired.

Auto-dispatch is rate limited to one call per player per call type every 60 seconds. If a test call does not appear, wait a minute before trying again.

2.0

Search, evidence and stats

Report search lives at the top of the reports tab. It searches case numbers, titles, narratives and officer names. Drafts stay private — another officer's unfinished report never shows up.

Evidence is logged from a report's detail view. Three types: photo (a direct image link, same as mugshots), item, or note. Every entry records who logged it and when, and shows on the printed report. You can remove your own entries for 15 minutes; after that it takes a supervisor — that's deliberate, an evidence log anyone can quietly edit isn't worth keeping.

Department stats sit on the dashboard for every police officer: arrests, citations, reports, active warrants and fines, with a daily chart and a 7/30 day toggle. The top-officers list only appears for supervisors, and the server decides that, not the panel.

Upgrading from 1.x: run the v1.6 → v2.0 block at the top of sql/mdt.sql. One new table.

06

Jailing

  1. Point Config.JailResource at your jail resource — 'qb-prison' or 'ps-prison'.
  2. Log the arrest through ox_target on a live player. The modal receives that player's server ID, and filling in a jail time jails them on submit.

Arrests filed from the civilian search panel carry no source ID, so jailing does not fire. That is deliberate — the suspect may not be online or present. With Config.JailResource = false the script fires XS-MDT:client:jailPlayer instead so you can handle it yourself.

07

Server exports

-- Does this player have MDT access?
exports['XS-MDT']:IsAuthorized(src)   -- boolean

-- Officer info for a player
exports['XS-MDT']:GetOfficerInfo(src)
-- { citizenid, name, job, grade, gradeLabel, onduty }

-- Write to the audit log (also fires the Discord webhook if configured)
exports['XS-MDT']:AuditLog('Action Name', 'Officer Name', 'Details string')

-- Create a dispatch call
exports['XS-MDT']:CreateDispatchCall({ callType, description, coords })
08

Troubleshooting

The MDT will not open

  • Config.AuthorizedJobs must contain the job name exactly, including case.
  • With Config.OnDutyOnly = true the player has to be clocked on.
  • The player's grade must be at or above Config.MinGrade.

Dispatch alerts are not appearing

  • Check the call type key format — SHOTS_FIRED, not shots_fired.
  • The 60 second per-player, per-type rate limit may be in effect.
  • Confirm the resource is running with status XS-MDT.

SQL errors on start

Run sql/mdt.sql fully before first start. If upgrading, run only the ALTER TABLE blocks for your previous version.

Fines are not deducted

  • Config.FineDeduction.Enabled must be true.
  • The player has to be online for an immediate deduction.
  • Citations default to pay-later. Set AutoDeductCitations = true to deduct on creation.

Jailing does nothing

Config.JailResource must match your jail resource name exactly, and the arrest must be filed through ox_target on an online player.

09

Updating

  1. Back up the database. This resource owns records, incidents, warrants and audit history.
  2. Keep a copy of your edited config.lua.
  3. Run only the ALTER TABLE blocks in sql/mdt.sql for the version you are upgrading from.
  4. Replace the resource folder, merge your config back, restart.
10

Support

Include your qbx_core version, the job and grade you tested with, the full console error and the steps that caused it.