DEV Community

Tack k
Tack k

Posted on

Two Police Scripts That Made Our QBCore RP Server Feel More Real

The gap between RP and reality

In most QBCore servers, police work feels incomplete in two specific ways.

First: when you arrest someone and search them, you can remove items — but there's no system for what happens to those items. They just disappear. No evidence bag, no chain of custody, no way to retrieve seized goods later.

Second: communication between officers happens in voice chat or external Discord. There's no in-game system for issuing wanted notices, alerting other units when a major crime wraps up, or signaling that officers are available for large-scale operations.

I built two scripts to fix both: gg_oushu for evidence seizure, and gg_police_notice for police communications.


gg_oushu — Evidence seizure and packaging

The concept

When a police officer uses the /seize command near a suspect, the script scans that player's inventory for any items on the seizeable list — weapons, drugs, stolen goods, heist tools, and more. All matching items are removed from the suspect and bundled into a single seized_package item in the officer's inventory.

That package persists. It can be opened later, items can be extracted individually, and the whole thing is tracked in the database.

How the package works

Each package gets a unique key generated at seizure time. The items inside are stored in MySQL — not just in the inventory metadata. This matters because ox_inventory metadata can get stale when items are moved or dropped, but the database record stays accurate.

local key = "pkg_" .. os.time() .. "_" .. math.random(1000, 9999)

exports.oxmysql:execute(
    'INSERT INTO seized_packages (id, player, items) VALUES (?, ?, ?)',
    { key, GetPlayerName(src), json.encode(seizedItems) }
)
Enter fullscreen mode Exit fullscreen mode

When an officer opens the package, the server fetches the current state directly from the database — not from the cached metadata. This ensures what you see in the UI matches what's actually stored, even if the package has been partially emptied.

Extracting items

Inside the package UI, each seized item is listed individually. The officer can extract items one at a time. When an item is extracted, the database record is updated immediately. When the last item is extracted, the package item is removed from inventory and the database record is deleted.

There's also a force-delete option for disposing of an entire package at once — useful for clearing evidence that doesn't need to be processed item by item.

Race safety

Stock deduction and package deletion use database-level operations to prevent the same item from being extracted twice if two operations happen simultaneously. The server is the single source of truth — the client never decides what's in a package.

Configuring seizeable items

The list of seizeable items is defined in config — weapons, drugs, heist tools, stolen valuables. Easy to extend for any custom items on your server:

Config.SeizableItems = {
    "weapon_pistol",
    "drug_meth",
    "drill",
    "lockpick",
    -- add your server's custom items here
}

Config.SeizeDistance = 5.0  -- max distance for seizure
Enter fullscreen mode Exit fullscreen mode

gg_police_notice — In-game police communications

Three notification types

The script handles three distinct communication scenarios:

Wanted notices — issue a formal wanted notice for a suspect, with name, charges, vehicle plate, and expiry time. Sends to Discord with formatting.

Closure alerts — notify all relevant units when a major crime event ends. These go both to Discord and as an in-game overlay notification to every online player in a configured list of jobs (police, EMS, etc.).

Large operation alerts — signal that officers are available and ready for large-scale crime response.

The closure overlay

The most interesting piece is the closure alert's in-game overlay. When a closure is sent, every player in the target job list sees an overlay appear on their screen — not just a text notification, but a visible UI element with a sound cue.

The overlay appears without stealing focus (players can still control their character). If they want to interact with it, they press a configurable key to bring focus to the overlay, then close it manually.

-- Server broadcasts to all target jobs
for _, id in pairs(QBCore.Functions.GetPlayers()) do
    local Player = QBCore.Functions.GetPlayer(id)
    if Player and Config.CLOSURE_TARGET_JOBS[Player.PlayerData.job.name] then
        TriggerClientEvent("gg_police_notice:showClosureNUI", id, {
            crime = selectedCrime
        })
    end
end
Enter fullscreen mode Exit fullscreen mode

Permission check

All three notification types check server-side that the sender is a police officer before doing anything. Client-side restriction (hiding the menu from non-police) is for UX — the server-side check is what actually enforces it.

if not Player or Player.PlayerData.job.name ~= "police" then
    print("Non-police attempted to send notice: " .. GetPlayerName(src))
    return
end
Enter fullscreen mode Exit fullscreen mode

Discord webhook configuration

Each notification type has its own webhook URL. This lets you route wanted notices, closure alerts, and large operation signals to different Discord channels:

-- Set your own webhook URLs in config
Config.WANTED_WEBHOOK   = "https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN"
Config.CLOSURE_WEBHOOK  = "https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN"
Config.LARGEALERT_WEBHOOK = "https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN"
Enter fullscreen mode Exit fullscreen mode

Why these two scripts belong together

Both scripts solve the same underlying problem: the gap between what real police work looks like and what most FiveM servers actually provide.

Evidence seizure makes arrests feel meaningful — there's a record, a physical item, a process. Police notices make inter-unit communication feel real — wanted suspects get formal notices, major events get proper closure, large operations get coordinated.

Neither script is technically complex. But both make the server feel more like a functioning world and less like a game with police-shaped NPCs.


Built with Claude Opus as my coding partner. More scripts from 2+ years of server operation coming soon.

Top comments (0)