Police, MDT & Evidence
On this page7
Three separate integrations that often live in the same script.
- Police — who cuffed, robbed, searched or escorted whom, for the proof log.
- MDT — where camera reports and witness statements are filed.
- Evidence — fingerprint and DNA matches, which grant a Lab ID.
Police jobs first
None of this works until the job names are right.
Both lists ship with 'police' only, the one police job stock QBCore, Qbox and ESX
all have. config.lua keeps a commented example line under each list for your other
departments:
Config.Jobs = {
Police = { 'police' },
-- Police = { 'police', 'sheriff', 'fib' }, -- example: add your other departments
HintJobs = { 'police' },
-- HintJobs = { 'police', 'sheriff', 'fib' }, -- example: add your other departments
AcceptTypes = { 'leo' },
}Police scripts
Config.PoliceSystem = 'auto'| System | Support | Auto order | What it adds |
|---|---|---|---|
| generic cuff state bags | Supported | always on | Cuff and uncuff proofs, on any police script that sets one of the common keys. |
| wasabi_police_v2 | Supported | 1 | State bags plus its isPlayerCuffed. |
| wasabi_police | Stub | 2 | Same, older shape. |
| ak47_qb_policejob | Stub | 3 | Its IsCuffed poll. |
| ak47_policejob | Stub | 4 | Same. |
| qbx_police | Supported | 5 | Metadata event, observed robbery. |
| qb-policejob | Supported | 6 | Observed net events. |
| esx_policejob | Supported | 7 | Observed net events plus its isHandcuffed state. |
| ox_inventory hooks | Supported | independent | Robberies, through openInventory and swapItems. |
Config.Proof.CuffStateKeys = { 'ishandcuffed', 'isHandcuffed', 'isCuffed', 'cuffed', 'cuffs' }
Config.Proof.OxInventoryHooks = trueWhen nobody is named
Some scripts cuff a player without saying who did it. Then the nearest facing player
within Config.Proof.InferActorRadius (2.5 m) is logged as the likely actor —
marked as such, never as proven. Treat it as a lead in the timeline.
MDTs
Config.MDTSystem = 'auto'| System | Support | Auto order | What we can do |
|---|---|---|---|
| lb-tablet | Supported | 1 | Statement + camera report + alert. |
| tk_mdt | Supported | 2 | Statement + camera report with image + alert. |
| redutzu-mdt | Supported | 3 | Statement + camera report, text. |
| drx_mdt | Detect only | 4 | Nothing pushed — a person BOLO needs a real state id. |
| origen_police | Stub | 5 | Alert only. |
| wasabi_mdt | Stub | 6 | Alert only. |
| ps-mdt | Detect only | 7 | No create export. Paper only. |
Auto-detect checks the exports, not just the name
A resource that runs under the right name can still be a build without the export the
bridge calls. lb-tablet 1.5.3 is one: it has no CreateMDTReport. So before an MDT is
used, the bridge checks that the export it would call is really there. It only looks
at the export and never runs it.
| MDT | Must export to be picked | Optional export (only that feature turns off) |
|---|---|---|
| lb-tablet | CreateMDTReport | AddDispatch (alert) |
| tk_mdt | createPoliceReport or registerEvidence | the other one of those two, and addCall (alert) |
| redutzu-mdt | CreateEvidence | — |
| origen_police | SendAlert | — |
| wasabi_mdt | CreateDispatch | — |
'auto'skips a running MDT whose build has none of the exports it would be picked for, prints one line with the resource, its version and the missing export, and moves on to the next MDT in the auto order. If nothing is left, it uses'none'.- A named MDT (for example
Config.MDTSystem = 'lb-tablet') gets the same check. If it has none of those exports, you get one warning and the bridge uses'none'until it has them, so pushes are not tried and then failed one by one. - A missing optional export turns off only that feature. An lb-tablet without
AddDispatchstill takes statements and camera reports, and only the dispatch alert is off. A tk_mdt withcreatePoliceReportbut noregisterEvidencestill takes statements, and only camera reports are off. This is also checked at the moment of each push, so a resource restarted into a different build is picked up without restarting 5code-recognition. - A skip is never final. Many MDTs create their exports a moment after they start, once their database is up. So a skipped MDT is checked again a few times in the first minute after the server (or that MDT) starts, and after that each time a statement or camera report is sent, at most every 10 seconds. When the export is there, the MDT is used from then on, without a restart, and the console says so once.
- Updating the MDT is enough. When the resource that was skipped starts again with a build that has the export, it is picked up, even if auto had picked another MDT lower in the list in the meantime.
[5code-recognition] MDT auto-detect skips lb-tablet (version 1.5.3) for now: it does not export CreateMDTReport (yet), which statements and camera reports need. It is checked again, so an export that turns up later is used without a restart.
[5code-recognition] MDT lb-tablet (version <its version>) has its export(s) now, so statements and camera reports go to it.The second line only appears if the export turns up later.
Per-MDT options
Case-sensitive. Only the selected MDT's entry is used.
Config.MDT = {
['lb-tablet'] = { Mdt = 'Police', TabId = 'reports', TextFieldId = 'description' },
['tk_mdt'] = { Page = 'police' },
['redutzu-mdt'] = {},
['drx_mdt'] = {},
['origen_police'] = { Job = 'police' },
['wasabi_mdt'] = {},
}| MDT | Option | Must match |
|---|---|---|
lb-tablet | Mdt, TabId, TextFieldId | lb-tablet/config/mdts.json, and your report tab's text-field id. |
tk_mdt | Page | Its page name. |
origen_police | Job | Its job name. |
Report and statement settings
| Key | Default | What it does |
|---|---|---|
Config.Report.PushToMDT | true | Send camera reports to the MDT where supported. |
Config.Report.MDTOnlyForPolice | true | Only camera owners with a police job push. |
Config.Statement.Delivery | 'both' | paper, mdt, both, or choice. |
Config.Statement.AllowAnonymous | true | Only where the MDT can actually hide the author. |
Config.Statement.MDTZones | {} | Places from which an MDT copy can be sent. Empty = anywhere. |
Config.Statement.MdtPerDay | 5 | Per author per day. |
Config.Statement.MdtMaxAgeHours | 48 | Only memories seen in the last 48 h can be sent. |
Evidence and Lab ID
Config.EvidenceSystem = 'auto'A fingerprint or DNA match produces a Lab ID — a separate record that says "the lab matched this sample to this person". It is not a memory and it is not a recognition.
| System | Support | Auto order | Notes |
|---|---|---|---|
| any script, via our export | Supported | — | GrantLabIdentification. The recommended route. |
| evidences (noobsystems) | Stub | 1 | Keep its config.citizens.synced = true. Some v1.3.0 / v1.3.1 builds need a known one-line fix; test one fingerprint analysis after installing. |
| kartik-evidence | Snippet | 2 | A snippet into its own file. |
| wasabi_evidence | Snippet | 3 | Runs on the client, so the client-side settings below apply. |
Any evidence script
exports['5code-recognition']:GrantLabIdentification(officerSrc, targetCharKey, opts)Add your resource to ServerConfig.Api.TrustedResources, and call it from your
server code after your own checks — never straight from a client event.
Lab settings
| Key | Default | What it does |
|---|---|---|
Config.Lab.Enabled | true | |
Seconds | 604800 (7 days) | How long a Lab ID lasts. |
RequireCorrectLink | true | Grant only when the police link points at the real owner. A wrong link grants nothing and warns admins. |
Recipients | 'officer' | officer, onduty, officer_mdt, or none (admin log only). |
RepeatWindowSeconds | 600 | The same lab event again within this only refreshes the timer. |
PerTargetPerDay | 3 | Per officer, per person. |
Client-side evidence scripts
Some evidence resources run the whole match on the player's own game instead of on the server. Lab IDs from those get these extra settings:
| Key | Default | What it does |
|---|---|---|
ClientTrust.RequireJob | true | Must have a police job. |
ClientTrust.RequireDuty | true | Must be on duty. |
ClientTrust.Zones | {} | Lab rooms. Empty = anywhere. |
ClientTrust.RatePerHour | 10 | Per officer. |
ClientTrust.CountsForAct | false | Whether a client-sourced lab ID also counts for "may act" and for proofs. |
Camera reports and the police cap
A hidden-camera recording does not drop a paper by itself. Once the camera's recording reaches 50 % (the HUD shows REC n%, green from 50 %), it waits in the owner's F3 menu → Camera recordings (last 24 h). The owner prints it there once, with the hidden camera in their inventory (3 s "Printing..."). Where an MDT is supported and the owner is police, the MDT copy is sent when the recording is saved. See Hidden Camera & Reports.
An officer who reads a hidden-camera report gets a memory of that person capped:
| Key | Default | What it does |
|---|---|---|
Config.Report.PoliceCap | 60 | Maximum % from reading a report. |
Config.Report.PoliceSeconds | 604800 (7 days) | How long it lasts. |
Config.Report.KeepDays | 60 | How long the paper stays readable. |
The "possible match" hint
After any scan, a player holding a matching report or description may see "Looks like someone you were told about".
| Key | Default | What it does |
|---|---|---|
Config.Describe.Hint | true | Show the hint at all. |
Config.Describe.HintMinPct | 25 | From this %. It can never reach the act line. |
Config.Describe.HintPoliceOnly | true | Only Config.Jobs.HintJobs see it. |
Config.Jobs.HintRequireDuty | false | Whether the hint job must be on duty. |
An empty Config.Jobs.HintJobs = {} turns the hint off for everyone.
Custom
Set Config.PoliceSystem, Config.MDTSystem or Config.EvidenceSystem to
'custom' and fill the branch in bridge/police_server.lua, bridge/mdt.lua or
bridge/evidence.lua.