Cipher MDT
A police MDT with live CAD, civilian records, incidents, warrants, BOLOs, citations, arrests and audit logs.
Requirements
Required
qbx_coreox_liboxmysql
Optional
ox_targetorqb-target— right-click players in-world to open the MDT or issue a citation.
QBox only. Unlike the other Cipher scripts, this one lists qbx_core as a hard dependency. Standard QBCore (qb-core) is not supported.
Current release is v1.4.0.
Installation
- Place the
cipher-mdtfolder in your resources directory, for exampleresources/[standalone]/cipher-mdt. - Add
ensure cipher-mdttoserver.cfg. - Run
sql/mdt.sqlagainst your database. It usesCREATE TABLE IF NOT EXISTSthroughout, so re-running it is safe. - Set
Config.AuthorizedJobsto your police job names. - Restart the resource.
ensure cipher-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.
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 foreverSupervisor 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
Dispatch integration
Gunshots, crashes and fights are auto-detected. To send a call from another resource, use one of these.
Server export (recommended)
exports['cipher-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('cipher-mdt:client:dispatch:custom',
'ROBBERY',
'Armed robbery in progress.',
vector3(24.8, -1347.3, 29.5)
)
Server net event
TriggerServerEvent('cipher-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.
Jailing
- Point
Config.JailResourceat your jail resource —'qb-prison'or'ps-prison'. - 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 cipher-mdt:client:jailPlayer instead so you can handle it yourself.
Server exports
-- Does this player have MDT access?
exports['cipher-mdt']:IsAuthorized(src) -- boolean
-- Officer info for a player
exports['cipher-mdt']:GetOfficerInfo(src)
-- { citizenid, name, job, grade, gradeLabel, onduty }
-- Write to the audit log (also fires the Discord webhook if configured)
exports['cipher-mdt']:AuditLog('Action Name', 'Officer Name', 'Details string')
-- Create a dispatch call
exports['cipher-mdt']:CreateDispatchCall({ callType, description, coords })Troubleshooting
The MDT will not open
Config.AuthorizedJobsmust contain the job name exactly, including case.- With
Config.OnDutyOnly = truethe 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, notshots_fired. - The 60 second per-player, per-type rate limit may be in effect.
- Confirm the resource is running with
status cipher-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.Enabledmust be true.- The player has to be online for an immediate deduction.
- Citations default to pay-later. Set
AutoDeductCitations = trueto 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.
Updating
- Back up the database. This resource owns records, incidents, warrants and audit history.
- Keep a copy of your edited
config.lua. - Run only the ALTER TABLE blocks in
sql/mdt.sqlfor the version you are upgrading from. - Replace the resource folder, merge your config back, restart.
Support
Include your qbx_core version, the job and grade you tested with, the full console error and the steps that caused it.