> ## Documentation Index
> Fetch the complete documentation index at: https://docs.redutzu.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Server

> Server functions your framework adapter must provide.

The MDT supports ESX, QBCore, and Qbox out of the box. For anything else, write an adapter: a set of functions that tell the MDT how to read your players, jobs, characters, and vehicles.

## Setup

**1. Copy the standalone adapter**

Copy the `standalone` folders and name them `custom`. Copy `qb-core` or `esx` instead if your framework is based on one.

```text theme={null}
server/custom/framework/standalone/  →  server/custom/framework/custom/
client/custom/framework/standalone/  →  client/custom/framework/custom/
```

**2. Rename the provider**

Change `'standalone'` to `'custom'` in the first lines of `core.lua`, `players.lua`, and `vehicles.lua`:

```lua theme={null}
if Bridge.Framework.Provider ~= 'custom' then return end -- core.lua
Bridge.Framework.Players.Register('custom')              -- players.lua
Bridge.Framework.Vehicles.Register('custom')             -- vehicles.lua
```

**3. Select it**

```lua config/config.lua theme={null}
Config.Framework = 'custom'
```

**4. Fill in the functions**

Replace each function body with calls to your framework. Each function below shows what it must return.

> [!TIP]
>
> A function you leave out of `players.lua` or `vehicles.lua` counts as unsupported. The MDT treats it as an empty result and, with `Config.Debug = true`, warns once. You can build the adapter step by step.

## Players

File: `core.lua`.

### GetOnlineSources

```lua theme={null}
---@return number[]
function Bridge.Framework.GetOnlineSources()
```

Returns the server ids of every loaded player.

### GetPlayerIdentifier

```lua theme={null}
---@param source number
---@return string?
function Bridge.Framework.GetPlayerIdentifier(source)
```

Returns the character's unique identifier, such as a citizen id. Returns `nil` if not loaded.

If the identifier isn't readable (like `license:abc...`), set `Bridge.Framework.ReadableIdentifier = false`. The MDT then shows the birthdate instead.

### GetSourceFromIdentifier

```lua theme={null}
---@param identifier string
---@return number?
function Bridge.Framework.GetSourceFromIdentifier(identifier)
```

Returns the server id of an online character, or `nil` if offline.

### GetCharacterName

```lua theme={null}
---@param source number
---@return string firstname
---@return string lastname
function Bridge.Framework.GetCharacterName(source)
```

Returns the first name and last name.

### GetPlayerData

```lua theme={null}
---@param source number
---@return table?
function Bridge.Framework.GetPlayerData(source)
```

Returns your framework's player object, or `nil` if not loaded.

### HasGroup

Optional.

```lua theme={null}
---@param source number
---@param groups string[]
---@return boolean
function Bridge.Framework.HasGroup(source, groups)
```

Returns `true` if the player is in any of `groups`. Used for `Config.AdminGroups`. Without it, the MDT checks the ACE in `Config.AdminAce`.

## Jobs and duty

File: `core.lua`.

### GetJobs

```lua theme={null}
---@return table<string, { name: string, label: string, grades: table<string, string> }>
function Bridge.Framework.GetJobs()
```

Returns every job, keyed by name. Grades are labels keyed by the level as a string:

```lua theme={null}
{ police = { name = 'police', label = 'Police', grades = { ['0'] = 'Cadet', ['1'] = 'Officer' } } }
```

### GetJobInfo

```lua theme={null}
---@param job string
---@return { name: string, label: string, grades: table<string, string> }?
function Bridge.Framework.GetJobInfo(job)
```

Returns one job in the same shape as `GetJobs`, or `nil`.

### GetPlayerJob

```lua theme={null}
---@param source number
---@return { name: string, label: string, grade: { level: number, name: string } }?
function Bridge.Framework.GetPlayerJob(source)
```

Returns the online player's job, or `nil`.

### GetPlayerJobByIdentifier

```lua theme={null}
---@param identifier string
---@return { name: string, label: string, grade: { level: number, name: string } }?
function Bridge.Framework.GetPlayerJobByIdentifier(identifier)
```

Same as `GetPlayerJob`, but works for offline characters.

### SetPlayerJob

```lua theme={null}
---@param identifier string
---@param job string
---@param grade? number
---@return boolean
function Bridge.Framework.SetPlayerJob(identifier, job, grade)
```

Sets the character's job. Returns `true` on success. Must work for offline characters.

### GetJobPlayers

```lua theme={null}
---@param job string
---@param checkOnDuty? boolean
---@return number[] sources
---@return number count
function Bridge.Framework.GetJobPlayers(job, checkOnDuty)
```

Returns the server ids of online players with `job`, and their count. With `checkOnDuty`, only those on duty.

### GetPlayerDuty

```lua theme={null}
---@param source number
---@return boolean
function Bridge.Framework.GetPlayerDuty(source)
```

Returns `true` if the player is on duty.

### SetPlayerDuty

```lua theme={null}
---@param source number
---@param duty boolean
---@return boolean
function Bridge.Framework.SetPlayerDuty(source, duty)
```

Sets duty. Returns `true` on success.

## Callsign and badge

File: `core.lua`. Store these wherever your framework keeps character metadata.

### GetPlayerMetadata

```lua theme={null}
---@param identifier string
---@param key string
---@return any
function Bridge.Framework.GetPlayerMetadata(identifier, key)
```

Returns the stored value for `key`, or `nil`.

### SetPlayerMetadata

```lua theme={null}
---@param identifier string
---@param key string
---@param value any
---@return boolean
function Bridge.Framework.SetPlayerMetadata(identifier, key, value)
```

Stores a value. Returns `true` on success.

### GetPlayerCallsign

```lua theme={null}
---@param source number
---@return string?
function Bridge.Framework.GetPlayerCallsign(source)
```

Returns the online player's callsign, or `nil`.

### SetPlayerCallsign

```lua theme={null}
---@param identifier string
---@param callsign string
---@return boolean
function Bridge.Framework.SetPlayerCallsign(identifier, callsign)
```

Saves the callsign. Returns `true` on success.

### SetPlayerBadge

```lua theme={null}
---@param identifier string
---@param badge string
---@return boolean
function Bridge.Framework.SetPlayerBadge(identifier, badge)
```

Saves the badge number. Returns `true` on success.

## Money

File: `core.lua`. `type` is the account name, such as `'cash'` or `'bank'`.

### GetMoney

```lua theme={null}
---@param source number
---@param type string
---@return number
function Bridge.Framework.GetMoney(source, type)
```

Returns the balance, or `0`.

### AddMoney

```lua theme={null}
---@param source number
---@param type string
---@param amount number
---@param reason string
---@return boolean
function Bridge.Framework.AddMoney(source, type, amount, reason)
```

Returns `true` if the money was added.

### RemoveMoney

```lua theme={null}
---@param identifier string
---@param amount number
---@param type string
---@param reason string
---@param allowDebt boolean
---@return boolean
function Bridge.Framework.RemoveMoney(identifier, amount, type, reason, allowDebt)
```

Returns `true` if the money was removed. Must work for offline characters. With `allowDebt`, the balance may go negative.

### GetBankAccount

Optional. Needed for `Config.ShowCitizenBank`.

```lua theme={null}
---@param identifier string
---@return { account: string?, balance: number }?
function Bridge.Framework.GetBankAccount(identifier)
```

Returns the account number and balance, or `nil`.

## Items and weapons

File: `core.lua`.

### CreateUseableItem

```lua theme={null}
---@param name string
---@param callback fun(source: number)
function Bridge.Framework.CreateUseableItem(name, callback)
```

Registers a usable item, such as the tablet. Returns nothing.

### GetItems

```lua theme={null}
---@return { name: string, label: string, image?: string }[]
function Bridge.Framework.GetItems()
```

Returns every item, for the item pickers in the Admin panel.

### GetWeapons

```lua theme={null}
---@return { model: string, label: string, group?: string }[]
function Bridge.Framework.GetWeapons()
```

Returns the weapons officers can register.

### GetWeaponLabel

Optional.

```lua theme={null}
---@param hash number
---@return string?
function Bridge.Framework.GetWeaponLabel(hash)
```

Returns a weapon's label from its hash, for dispatch calls.

### VehicleModels

Optional. A table of vehicle models keyed by spawn name, like `QBCore.Shared.Vehicles`. The MDT uses it for vehicle names and classes.

```lua theme={null}
Bridge.Framework.VehicleModels = { sultan = { name = 'Sultan', category = 'sports' } }
```

## Characters

File: `players.lua`. These feed the **Citizens** page and the department pages. Every character is described with this shape. Only `identifier` is required.

```lua theme={null}
{
    identifier = 'ABC12345',
    name = 'John Doe',
    image = 'https://...',
    callsign = '1-ADAM-01',
    badge = '123',
    job = 'police',
    job_label = 'Police',
    job_grade = 'Sergeant',
    grade = 3,
    birthdate = '1990-04-02',
    gender = 'male',
    phone_number = '5551234',
    nationality = 'American',
    height = 180,
    fingerprint = 'AB12CD',
    last_seen = 1735689600000 -- epoch milliseconds
}
```

### GetIdentities

Always implement this one. Officers' names come from it.

```lua theme={null}
---@param identifiers (string|number)[]
---@return table<string|number, table>
function Bridge.Framework.Players.GetIdentities(identifiers)
```

Returns a character for each identifier, keyed by identifier. Leave out characters that don't exist.

### SearchCitizens

Used when `Config.AutoPopulateData = true`.

```lua theme={null}
---@param request { query: string, offset: number, limit: number, sort: string, filters: table, known_total?: number, sql: table }
---@return { data: table[], total: number }
function Bridge.Framework.Players.SearchCitizens(request)
```

Returns one page of characters and the `total` that match, ignoring `offset` and `limit`. If `known_total` is set, return it as `total` and skip counting.

Match `query` against names and identifiers that start with it, or a phone number or fingerprint equal to it. `request.sql` holds ready-made SQL fragments for the MDT's own filters and record counts. See the comments in `players.lua` for every field.

### GetCapabilities

```lua theme={null}
---@return { nationality: boolean }
function Bridge.Framework.Players.GetCapabilities()
```

Returns which optional fields your characters have. `nationality = false` hides the nationality field and filter.

### FindByPhoneNumbers

```lua theme={null}
---@param numbers (string|number)[]
---@return table<string, string|number>
function Bridge.Framework.Players.FindByPhoneNumbers(numbers)
```

Returns the owner's identifier for each phone number, keyed by number. Leave out unknown numbers.

### GetNameMatchQuery

```lua theme={null}
---@param query string
---@return { sql: string, params: any[] }?
function Bridge.Framework.Players.GetNameMatchQuery(query)
```

Returns a `SELECT` of one column: identifiers of characters whose name starts with `query`. Return `nil` if your data isn't in SQL.

```lua theme={null}
return { sql = 'SELECT citizenid FROM players WHERE name LIKE ?', params = { query .. '%' } }
```

### SetImage

```lua theme={null}
---@param identifier string|number
---@param url string
---@return boolean
function Bridge.Framework.Players.SetImage(identifier, url)
```

Saves the character's MDT photo. `url = ''` clears it. Returns `false` only if the character doesn't exist. Return the photo as `image` in `GetIdentities`.

## Job members

File: `players.lua`. These feed the roster, employees, and analytics pages. A job member is any character with that job, online or not.

### ListJobMembers

```lua theme={null}
---@param request { job: string, query: string, offset: number, limit: number, sort: string, grades?: number[], known_total?: number, sql: table }
---@return { data: table[], total: number }
function Bridge.Framework.Players.ListJobMembers(request)
```

Returns one page of members and the `total` that match. Fill `name`, `image`, `callsign`, `badge`, `phone_number`, `grade`, and `last_seen`. Without it, the roster lists only online officers.

### GetJobMembersQuery

```lua theme={null}
---@param job string
---@return { sql: string, params: any[] }?
function Bridge.Framework.Players.GetJobMembersQuery(job)
```

Returns a `SELECT` of one column named `identifier`: every member of `job`. Return `nil` if your data isn't in SQL.

### CountJobMembers

```lua theme={null}
---@param job string
---@return { total: number, grades: table<number, number> }
function Bridge.Framework.Players.CountJobMembers(job)
```

Returns the headcount, in total and per grade level:

```lua theme={null}
return { total = 12, grades = { [0] = 5, [1] = 4, [3] = 3 } }
```

### GetCallsigns

```lua theme={null}
---@param jobs string[]
---@return table<string|number, string>
function Bridge.Framework.Players.GetCallsigns(jobs)
```

Returns the stored callsign of every member of `jobs`, keyed by identifier. Used to keep callsigns unique.

### IsBadgeTaken

```lua theme={null}
---@param job string
---@param badge string
---@param except string|number
---@return boolean
function Bridge.Framework.Players.IsBadgeTaken(job, badge, except)
```

Returns `true` if another member of `job` already has `badge`. Ignore the character `except`.

## Vehicles

File: `vehicles.lua`. These feed the **Vehicles** page. Every vehicle is described with this shape:

```lua theme={null}
{
    plate = 'ABC123',
    model = 'sultan',             -- spawn name or hash as a string
    owner_identifier = 'ABC12345', -- nil for job or unowned vehicles
    impounded = false,
    image = 'https://...',         -- optional
    color = 0,                     -- optional, primary color id
    secondaryColor = 0             -- optional
}
```

### IsAvailable

```lua theme={null}
---@return boolean
function Bridge.Framework.Vehicles.IsAvailable()
```

Returns `true` if this adapter reads real vehicles. Return `false` to use only vehicles officers register in the MDT.

### SearchVehicles

Used when `Config.AutoPopulateData = true`.

```lua theme={null}
---@param request { query: string, models_matching_query?: string[], offset: number, limit: number, sort: string, filters: table, known_total?: number, sql: table }
---@return { data: table[], total: number }
function Bridge.Framework.Vehicles.SearchVehicles(request)
```

Returns one page of vehicles (`plate`, `model`, `owner_identifier`, `impounded`) and the `total` that match. See the comments in `vehicles.lua` for every field.

### GetVehicle

```lua theme={null}
---@param plate string
---@return table?
function Bridge.Framework.Vehicles.GetVehicle(plate)
```

Returns one vehicle, or `nil` if no vehicle has this plate.

### GetVehicles

```lua theme={null}
---@param plates string[]
---@param with_color boolean
---@return table<string, table>
function Bridge.Framework.Vehicles.GetVehicles(plates, with_color)
```

Returns a vehicle for each plate, keyed by plate. Skip colors when `with_color` is `false`.

### GetOwnedVehicles

```lua theme={null}
---@param identifier string
---@param limit? number
---@return table[]
function Bridge.Framework.Vehicles.GetOwnedVehicles(identifier, limit)
```

Returns the vehicles a character owns, at most `limit`.

### GetOwner

```lua theme={null}
---@param plate string
---@return string?
function Bridge.Framework.Vehicles.GetOwner(plate)
```

Returns the owner's identifier, or `nil`. Called on every radar hit, so keep it fast.

### GetOwnersQuery

```lua theme={null}
---@return string?
function Bridge.Framework.Vehicles.GetOwnersQuery()
```

Returns a `SELECT` of every identifier that owns a vehicle, such as `SELECT DISTINCT owner FROM owned_vehicles`. Return `nil` to hide the "owns a vehicle" filter.

### GetImpoundedQuery

```lua theme={null}
---@return string?
function Bridge.Framework.Vehicles.GetImpoundedQuery()
```

Returns a `SELECT` of every impounded plate, such as `SELECT plate FROM owned_vehicles WHERE stored = 2`. Return `nil` if your data isn't in SQL.

### SetImage

```lua theme={null}
---@param plate string
---@param url string
---@return boolean
function Bridge.Framework.Vehicles.SetImage(plate, url)
```

Saves the vehicle's photo. `url = ''` clears it. Returns `false` only if the vehicle doesn't exist.

## Events

Trigger these from your framework's events so the MDT stays in sync:

```lua server/custom/framework/custom/core.lua theme={null}
AddEventHandler('myframework:playerLoaded', function(source)
    TriggerEvent('redutzu-mdt:internal:player-loaded', source)
    TriggerClientEvent('redutzu-mdt:client:on-player-load', source)
end)

AddEventHandler('myframework:playerLogout', function(source)
    TriggerEvent('redutzu-mdt:internal:player-unloaded', source)
    TriggerClientEvent('redutzu-mdt:client:on-player-unload', source)
end)

AddEventHandler('myframework:jobChanged', function(source, job, grade)
    TriggerEvent('redutzu-mdt:internal:job-changed', source, job, grade)
    TriggerClientEvent('redutzu-mdt:client:on-job-change', source)
end)

AddEventHandler('myframework:dutyChanged', function(source, onDuty)
    TriggerEvent('redutzu-mdt:internal:duty-changed', source, onDuty)
end)
```

Also trigger `player-unloaded` on `playerDropped`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.