es_extended

if GetResourceState('es_extended') ~= 'started' then return end

ESX = exports.es_extended:getSharedObject()
Framework = {

  tables = {
    players = 'users',
    licences = 'licenses',
    user_licences = 'user_licenses',
  },

  columns = {
    players = {
      identifier = 'identifier',
      fullname = "CONCAT(p.`firstname`, ' ', p.`lastname`)",
      licences = "CONCAT('{', GROUP_CONCAT(JSON_QUOTE(licences.`type`), ': ',CASE WHEN user_licences.`type` IS NOT NULL THEN 'true' ELSE 'false' ENDSEPARATOR ', '), '}')"
    }
  },

  --[[
  IdentityStats Configuration:

  `IdentityStats` is an array that defines how certain identity information is retrieved from the database.
  Each entry in the array represents a different identity category and contains:

  - `table`: The database table where the identity data is found.
  - `column`: Specifies which column to fetch. Use '*' for all columns.
  - `identifier`: The column used to match a specific row (e.g., fetching data for a particular user).
  - `value`: Specifies the keys or columns you want to retrieve. This is especially useful when fetching from JSON columns.
  - `label`: Defines how the data is presented. Placeholders (like {nationality}) get replaced by the corresponding values from the database.
  - `enabled`: Determines if this category is active or not.

  Example:
    For the configuration:
    table = "players",
    column = "charinfo",
    identifier = "citizenid",
    value = { "nationality", "birthdate" },
    label = { "Nationality: {nationality}", "Birthdate: {birthdate}" },

    This would fetch the `nationality` and `birthdate` from the `charinfo` column in the `players` table where `citizenid` matches the given identifier.
    It then labels them as "Nationality: <value>" and "Birthdate: <value>".
  ]]
  IdentityStats = {
    {
      table = "users",
      column = "*",
      identifier = "identifier",
      value = { "dateofbirth", "sex", "height", "phone_number" },
      label = { "Date of birth: {dateofbirth}", "Gender: {sex}", "Height: {height}", "Phonenumber: {phone_number}" },
      enabled = true
    },
    {
      table = "drx_mdt_player_link",
      column = "points",
      identifier = "identifier",
      value = nil,
      label = "Points",
      enabled = true
    },
  },

  VehicleStats = {
    {
      table = "owned_vehicles",
      column = "type",
      identifier = "plate",
      value = nil,
      label = "Type: {type}",
      enabled = true
    },
    {
      table = "owned_vehicles",
      column = "vehicle",
      identifier = "plate",
      value = { "modTurbo", "modXenon", "modExhaust" },
      label = { "Turbo: {modTurbo}", "Xenon: {modXenon}", "Aftermarket exhaust: {modExhaust}" },
      enabled = true
    }
  },

  ---@param src number The player server id
  getPlayerBySource = function(src)
    local player = {}
    local xPlayer = ESX.GetPlayerFromId(src)

    if not xPlayer then return nil end

    player.source = xPlayer.source
    player.identifier = xPlayer.identifier
    player.fullname = xPlayer.name

    return player
  end,

  ---@param identifier string The player framework identifier
  getPlayerByIdentifier = function(identifier)
    local player = {}
    local xPlayer = ESX.GetPlayerFromIdentifier(identifier)

    if not xPlayer then return nil end

    player.source = xPlayer.source
    player.identifier = xPlayer.identifier
    player.fullname = xPlayer.name

    return player
  end,

  ---@param src number The player server id
  getPlayerIdentifier = function(src)
    local xPlayer = ESX.GetPlayerFromId(src)
    if not xPlayer then return nil end

    return xPlayer.identifier
  end,

  ---@param src number The player server id (officer)
  ---@param identifier string The player framework identifier (criminal)
  ---@param fine number The fine amount
  finePlayer = function(source, identifier, fine)
    local xTarget = ESX.GetPlayerFromIdentifier(identifier)
    if not xTarget then return end

    fine = ESX.Math.Round(fine)
    MySQL.insert('INSERT INTO billing (`identifier`, `sender`, `target_type`, `target`, `label`, `amount`) VALUES (?, ?, ?, ?, ?, ?)',
      { xTarget.identifier, 'MDT', 'society', 'society_police', 'Issued fine', fine }, function(insertId)
        xTarget.showNotification('You have just ~r~received a fine')
      end)
  end,

  ---@param src number The player server id (officer)
  ---@param identifier string The player framework identifier (criminal)
  ---@param jailtime number The jailtime in minutes
  jailPlayer = function(source, identifier, jailtime)
    if GetResourceState('esx_jail') ~= 'started' then return end

    local xTarget = ESX.GetPlayerFromIdentifier(identifier)
    if not xTarget then return end

    exports.esx_jail:sendToJail(xTarget.source, jailtime)
  end,

  ---@param identifier string The player framework identifier
  ---@param job string The job name
  ---@param grade number The job grade level
  setJob = function(identifier, job, grade)
    local xPlayer = ESX.GetPlayerFromIdentifier(identifier)
    if not xPlayer then
      return MySQL.update.await('UPDATE `users` u SET u.`job` = ?, u.`job_grade` = ? WHERE identifier = ?', { job, grade, identifier })
    end
    xPlayer.setJob(job, grade)

    return 1
  end,

  ---Returns a list of all job names
  ---@return table<number, string>
  getJobs = function()
    local jobs = ESX.GetJobs()
    local jobNames = {}

    for job, _ in pairs(jobs) do
      jobNames[job] = true
    end

    Logger.debug('Jobs: %s', json.encode(jobNames))

    return jobNames
  end,

  ---@param stateid string The players state identifier
  getCitizen = function(stateid)
    local query = [[
      SELECT
        player_link.stateid as identifier,
        p.%s as framework_identifier,
        %s as fullname,
        CONCAT(j.label, ' (', j.name, ')') as job
      FROM
        `drx_mdt_player_link` player_link
      JOIN
        `users` p ON player_link.`identifier` = p.identifier
      JOIN
        jobs j ON p.job = j.name
      WHERE
        player_link.stateid = ?
    ]]

    query = query:format(Framework.columns.players.identifier, Framework.columns.players.fullname)

    local result = MySQL.single.await(query, { stateid })

    if not result then return {} end

    return {
      identifier = result.identifier,
      framework_identifier = result.framework_identifier,
      fullname = result.fullname,
      job = { result.job }
    }
  end,

  ---@param term string The search term
  ---@param excludeStaff boolean Whether to exclude staff or not
  searchPlayers = function(term, excludeStaff)
    local query = [[
      SELECT
        link.`stateid` as identifier,
        p.%s as framework_identifier,
        %s as fullname,
        CONCAT(j.label, ' (', j.name, ')') as job,
        link.`image`,
        link.`tags`
      FROM
        `drx_mdt_player_link` link
      JOIN
        %s p ON p.%s = link.`identifier`
      JOIN
        jobs j ON p.job = j.name
      WHERE
        (%s LIKE ?
        OR link.`stateid` = ?)
        %s
      GROUP BY
        link.`stateid`, p.identifier
      LIMIT 20
    ]]

    local excludedWhere = excludeStaff and "AND NOT EXISTS (SELECT 1 FROM `drx_mdt_officers` o WHERE o.identifier = link.`stateid`)" or ""

    query = query:format(
      Framework.columns.players.identifier,
      Framework.columns.players.fullname,
      Framework.tables.players,
      Framework.columns.players.identifier,
      Framework.columns.players.fullname,
      excludedWhere,
      "")

    return MySQL.query.await(query, { '%' .. term .. '%', term })
  end,
}

RegisterNetEvent('esx:playerLoaded', function(player, xPlayer, isNew)
  LoadOfficer(player, xPlayer.identifier)
end)

RegisterNetEvent('esx:playerDropped', function(playerId, reason)
  UnloadOfficer(playerId)

  local clockStatus = exports.drx_mdt:GetOfficerClockedIn(playerId)
  if clockStatus then
    exports.drx_mdt:ToggleClock(playerId)
  end
end)

AddEventHandler('onResourceStart', function(resource)
  if resource ~= GetCurrentResourceName() then return end

  local xPlayers = ESX.GetExtendedPlayers()
  for _, xPlayer in pairs(xPlayers) do
    LoadOfficer(xPlayer.source, xPlayer.identifier)
  end
end)

Last updated