Server

Player

get_players()

Returns a copy of the players table. The players table contains all current active players.

Function

--- Get all players in the boii.players table.
--- @return All connected players.
local function get_players()
    return utils.tables.deep_copy(boii.players)
end

boii.get_players = get_players
exports('get_players', get_players)

Example

--- Retrieve all players.
local players = boii.get_players()
print('Players:', json.encode(players))

get_player()

Returns a specific player from the active players table.

A source ID must be provided to ensure the correct player is returned.

Function

local function get_player(_src)
    return boii.players[_src]
end

boii.get_player = get_player
exports('get_player', get_player)

Example

local player = boii.get_player(source)

--- Accessing player data.
print('Player first name:', player.data.identity.first_name)

--- Using player functions.
local identity = player.get_identity()
print('Identity:', json.encode(identity))

get_identifier()

Returns a specific player identifier.

Function

local function get_identifier(_src, id_type)
    if not id_type then DEBUG('err', 'Function: get_identifier | Note: ID Type was missing.') return end
    return GetPlayerIdentifierByType(src, id_type)
end

boii.get_identifier = get_identifier
exports('get_identifier', get_identifier)

Example

--- Retrieve identifier for player.
local license = boii.get_identifier(source, 'license')

--- Print identifier.
print('License:', license)

get_uid()

Returns the players unique id.

Unique ID's are assigned to players through boii_utils when they first connect and a user account is created for their license.

Function

local function get_uid(license)
    if not license then DEBUG('err', 'Function: get_uid | Note: Player license was missing.') return nil end
    local result = MySQL.query.await('SELECT unique_id FROM user_accounts WHERE license = @license', { ['@license'] = license })
    return result[1] and result[1].unique_id or nil
end

boii.get_uid = get_uid
exports('get_uid', get_uid)

Example

--- Retrieve players unique id by license
local license = boii.get_identifier(source, 'license')
local unique_id = boii.get_uid(license)

--- Print UID.
print('Unique ID:', unique_id)

get_data()

Gets a players server side data.

Function

local function get_data(player, data_type)
    return data_type and player.data[data_type] or player.data
end

boii.get_data = get_data
exports('get_data', get_data)

Example

--- Retrieve the player.
local player = boii.get_player(source)

--- Retreive players identity data.
local identity = boii.get_data(player, 'identity')

--- Print players indentity data.
print('Identity:', json.encode(identity))

create_character()

Add a new character for the player into the database.

All player tables have default values provided except for Unique ID and Passport, these must always be provided.

Function

local function create_character(data)
    local queries = {
        { query = 'INSERT INTO players (unique_id, char_id, passport) VALUES (?, ?, ?)', params = { data.unique_id, data.char_id, data.passport }},
        { query = 'INSERT INTO player_statuses (passport) VALUES (?)', params = { data.passport }},
        { query = 'INSERT INTO player_groups (passport) VALUES (?)', params = { data.passport }},
        { query = 'INSERT INTO player_inventory (passport) VALUES (?)', params = { data.passport }},
        { query = 'INSERT INTO player_stats (passport) VALUES (?)', params = { data.passport }},
        { query = 'INSERT INTO player_achievements (passport) VALUES (?)', params = { data.passport }}
    }

    for _, account in ipairs(config.player_data.accounts) do
        queries[#queries + 1] = { query = 'INSERT INTO player_accounts (passport, account_type, amount, allow_negative, interest_rate, interest_time) VALUES (?, ?, ?, ?, ?, ?)', params = { data.passport, account.account_type, account.amount, account.allow_negative, account.interest_rate, account.interest_time }}
    end

    for _, query_data in ipairs(queries) do
        MySQL.Async.execute(query_data.query, query_data.params, function(rows_changed)
            if rows_changed <= 0 then DEBUG('err', 'Failed to create character in table: ' .. query_data.query) return false end
        end)
    end
    return true
end

boii.create_character = create_character
exports('create_character', create_character)

Example

--- Define character data.
local data = {
    unique_id = 'BOII_JZFG3',
    char_id = 1,
    passport = 'B12345678',
    identity = {
        first_name = 'John',
        last_name = 'Doe'
        --- Additional fields here.
    }
    --- Additional fields here.
}

boii.create_character(data)

Player Methods

These functions can be used directly on the players object.

Before using any player method you must first retrieve the correct player.

Get functions are created on the players object for each data type specified under PLAYER_DATA_TYPES.

--- Player data types.
local PLAYER_DATA_TYPES <const> = { 'identity', 'position', 'statuses', 'accounts', 'groups', 'inventory', 'stats', 'achievements' }

--- Loop inside player_methods function to add get functions.
for _, data_type in pairs(PLAYER_DATA_TYPES) do
    player['get_' .. data_type] = function()
        return get_data(player, data_type)
    end
end

get_data()

Get player data.

Function

function player.get_data(data_type)
    return data_type and player.data[data_type] or player.data
end

Example

--- Retrieve player.
local player = boii.get_player(source)

--- Retreive players identity data.
local identity = player.get_data('identity')

--- Print players indentity data.
print('Identity:', json.encode(identity))

get_identity()

Returns a players identity data.

Example

local player = boii.get_player(source)
local identity = player.get_identity()

print('Identity:', json.encode(identity))

get_position()

Returns a players position data.

Example

local player = boii.get_player(source)
local position = player.get_position()

print('Position:', position)

get_statuses()

Returns a players statuses data.

Example

local player = boii.get_player(source)
local statuses = player.get_statuses()

print('Statuses:', json.encode(statuses))

get_accounts()

Returns a players accounts data.

Example

local player = boii.get_player(source)
local accounts = player.get_accounts()

print('Accounts:', json.encode(accounts))

get_groups()

Returns a players groups data.

Example

local player = boii.get_player(source)
local groups = player.get_groups()

print('Groups:', json.encode(groups))

get_inventory()

Returns a players inventory data.

Example

local player = boii.get_player(source)
local inventory = player.get_inventory()

print('Inventory:', json.encode(inventory))

get_stats()

Returns a players stats data.

Example

local player = boii.get_player(source)
local stats = player.get_stats()

print('Stats:', json.encode(stats))

get_achievements()

Returns a players achievements data.

Example

local player = boii.get_player(source)
local achievements = player.get_achievements()

print('Achievements:', json.encode(achievements))

adjust_statuses()

Adjust a players status values.

Function

function player.adjust_statuses(updates)
    if not updates or type(updates) ~= 'table' then DEBUG('err', 'Function: player.adjust_statuses | Note: Invalid input, expected a table.') return false end
    for status, effect in pairs(updates) do
        if not utils.tables.table_contains(TABLE_MAP.statuses.fields, status) then DEBUG('warn', 'Function: player.adjust_statuses | Invalid status: ' .. status) return false end
        if type(player.data.statuses[status]) == 'number' then
            local current_value = player.data.statuses[status] or 0
            local new_value = current_value + (effect.add or 0) - (effect.remove or 0)
            if status == 'health' then
                new_value = math.min(200, math.max(0, new_value))
            else
                new_value = math.min(100, math.max(0, new_value))
            end
            player.data.statuses[status] = new_value
            DEBUG('info', 'Updated ' .. status .. ' for player ' .. tostring(player.source) .. ': ' .. tostring(new_value - current_value))
        elseif status == 'illnesses' or status == 'injuries' or status == 'infections' or status == 'buffs' or status == 'debuffs' then
            player.data.statuses[status] = effect
        else
            player.data.statuses[status] = effect and 1 or 0
        end
    end
    player.set_data({ 'statuses' }, true)
    DEBUG('info', 'Function: player.adjust_statuses | Statuses adjusted for player ' .. player.source)
    return true
end

Example

--- Retrieve the player.
local player = boii.get_player(source)

--- Adjust statuses.
player.adjust_statuses(player, {
    health = { add = 20 },
    hunger = { remove = 10 },
    fatigue = { add = 5 },
    is_injured = true,
    injuries = { leg_fracture = true }
})

kill()

KIll the player.

Function

function player.kill()
    local statuses = { 'health', 'armour', 'hunger', 'thirst', 'stamina', 'stress', 'hygiene', 'fatigue', 'temperature' }
    for _, status in ipairs(statuses) do
        player.data.statuses[status] = 0
    end
    player.data.statuses.is_dead = 1
    player.data.statuses.is_bleeding = 0
    player.data.statuses.is_unconscious = 0
    player.data.statuses.is_ill = 0
    player.data.statuses.is_injured = 0
    player.data.statuses.is_poisoned = 0
    player.data.statuses.is_infected = 0
    player.data.statuses.illnesses = json.encode({})
    player.data.statuses.injuries = json.encode({})
    player.data.statuses.infections = json.encode({})
    player.data.statuses.buffs = json.encode({})
    player.data.statuses.debuffs = json.encode({})
    player.set_data({ 'statuses' }, true)
    DEBUG('info', 'Function: player.kill | Player ' .. player.source .. ' has been killed.')
end

Example

--- Retrieve the player. 
local player = boii.get_player(source)

--- Kill the player.
player.kill()

revive()

Revive the player and reset statuses back to default values.

Function

function player.revive()
    player.data.statuses.health = 200
    player.data.statuses.armour = 0
    local statuses = { hunger = 100, thirst = 100, stamina = 100, stress = 0, hygiene = 100, fatigue = 0, temperature = 37 }
    for status, value in pairs(statuses) do
        player.data.statuses[status] = value
    end
    player.data.statuses.is_dead = 0
    player.data.statuses.is_bleeding = 0
    player.data.statuses.is_unconscious = 0
    player.data.statuses.is_ill = 0
    player.data.statuses.is_injured = 0
    player.data.statuses.is_poisoned = 0
    player.data.statuses.is_infected = 0
    player.data.statuses.illnesses = json.encode({})
    player.data.statuses.injuries = json.encode({})
    player.data.statuses.infections = json.encode({})
    player.data.statuses.buffs = json.encode({})
    player.data.statuses.debuffs = json.encode({})
    player.set_data({ 'statuses' }, true)
    DEBUG('info', 'Function: player.revive | Player ' .. player.source .. ' has been revived.')
end

Example

--- Retrieve the player. 
local player = boii.get_player(source)

--- Revive the player.
player.revive()

add_account()

Adds a new account entry for the player.

Function

function player.add_account(account_type, data)
    if not player.data.accounts then DEBUG('err', 'Function: player.add_account | player.data.accounts is missing for ' .. player.source) return false end
    if player.data.accounts[account_type] then DEBUG('warn', 'Function: player.add_account | Account type ' .. account_type .. ' already exists for player ' .. player.source) return false end
    player.data.accounts[account_type] = { account_type = account_type, amount = data.amount or 0, allow_negative = data.allow_negative or false, interest_rate = data.interest_rate or 0, interest_time = data.interest_time or 0, interest_accrued = 0, last_payout = utils.dates.convert_timestamp(os.time()).formatted }
    add_account(player, account_type)
    player.set_data({ 'accounts' }, false)
    DEBUG('info', 'Function: player.add_account | New account added successfully for player ' .. player.source)
    return true
end

Example

--- Retrieve the player. 
local player = boii.get_player(source)

--- Add a savings account for the player.
player.add_account('savings', { amount = 0 })

remove_account()

Removes an account entry for the player.

Function

function player.remove_account(account_type)
    if not player.data.accounts then DEBUG('err', 'Function: player.remove_account | player.data.accounts is missing for ' .. player.source) return false end
    if not player.data.accounts[account_type] then DEBUG('warn', 'Function: player.remove_account | Account type ' .. account_type .. ' not found for player ' .. player.source) return false end
    player.data.accounts[account_type] = nil
    player.set_data({ 'accounts' }, true)
    remove_account(player, account_type)
    DEBUG('info', 'Function: player.remove_account | Account removed successfully for player ' .. player.source)
    return true
end

Example

--- Retrieve the player. 
local player = boii.get_player(source)

--- Remove a players savings account.
player.remove_account('savings')

add_to_account()

Adds money to a specified player account.

Function

function player.add_to_account(account_type, amount)
    if not player.data.accounts then DEBUG('err', 'Function: player.add_to_account | Note: player.data.accounts is missing for ' .. player.source) return false end
    local account = player.data.accounts[account_type]
    if not account then DEBUG('warn', 'Function: player.add_to_account | Note: Account type ' .. account_type .. ' not found for player ' .. player.source) return false end
    account.amount += amount
    player.set_data({ 'accounts' }, true)
    DEBUG('info', 'Function: player.add_to_account | Note: Account added successfully for player ' .. player.source)
    return true
end

Example

--- Retrieve the player. 
local player = boii.get_player(source)

--- Add value to a players savings account.
player.add_to_account('savings', 1000)

remove_from_account()

Removes money from a specified player account.

Function

function player.remove_from_account(account_type, amount)
    if not player.data.accounts then DEBUG('err', 'Function: player.remove_from_account | Note: player.data.accounts is missing for ' .. player.source) return false end
    local account = player.data.accounts[account_type]
    if not account then DEBUG('warn', 'Function: player.remove_from_account | Note: Account type ' .. account_type .. ' not found for player ' .. player.source) return false end
    account.amount -= amount
    player.set_data({ 'accounts' }, true)
    DEBUG('info', 'Function: player.remove_from_account | Note: Account removed successfully for player ' .. player.source ..')')
    return true
end

Example

--- Retrieve the player. 
local player = boii.get_player(source)

--- Remove value from a players savings account.
player.remove_from_account('savings', 1000)

set_account()

Sets a new value for a specified player account.

Function

function player.set_account(account_type, amount)
    if not player.data.accounts then DEBUG('err', 'Function: player.set_account | Note: player.data.accounts is missing for ' .. player.source) return false end
    local account = player.data.accounts[account_type]
    if not account then DEBUG('warn', 'Function: player.set_account | Note: Account type ' .. account_type .. ' not found for player ' .. player.source) return false end
    account.amount = amount
    player.set_data({ 'accounts' }, true)
    DEBUG('info', 'Function: player.set_account | Note: Account set successfully for player ' .. player.source)
    return true
end

Example

--- Retreive the player.
local player = boii.get_player(source)

--- Set a players savings account value.
player.set_account('savings', 1000)

add_group()

Adds a new group for the player.

Function

function player.add_group(group_type, group, grade)
    if not player.data.groups then DEBUG('err', 'Function: player.add_group | Note: player.data.groups is missing for ' .. player.source) return false end
    player.data.groups[group_type] = { group = group, grade = grade }
    player.set_data({ 'groups' }, true)
    DEBUG('info', 'Function: player.add_group | Note: Group added/updated successfully for player ' .. player.source)
    return true
end

Example

local player = boii.get_player(source)

player.add_group('job', 'police', 0)

remove_group()

Removes a specified group from the player.

Function

function player.remove_group(group_type, group_name)
    if not player.data.groups then DEBUG('err', 'Function: player.remove_group | Note: player.data.groups is missing for ' .. player.source) return false end
    if not player.data.groups[group_type][group_name] then DEBUG('warn', 'Function: player.remove_group | Note: Group ' .. group_name .. ' not found for player ' .. player.source) return false end
    player.data.groups[group_type][group_name] = nil
    player.set_data({ 'groups' }, true)
    DEBUG('info', 'Function: player.remove_group | Note: Group removed successfully for player ' .. player.source)
    return true
end

Example

local player = boii.get_player(source)

player.remove_group('job', 'police')

change_group_grade()

Modifies a players current group grade.

Function

function player.change_group_grade(group_type, group_name, grade)
    if not player.data.groups then DEBUG('err', 'Function: player.change_group_grade | Note: player.data.groups is missing for ' .. player.source) return false end
    if not player.data.groups[group_type][group_name] then DEBUG('warn', 'Function: player.change_group_grade | Note: Group type ' .. group_type .. ' not found for player ' .. player.source) return false end
    player.data.groups[group_type][group_name].grade = grade
    player.set_data({ 'groups' }, true)
    DEBUG('info', 'Function: player.change_group_grade | Note: Group grade modified successfully for player ' .. player.source)
    return true
end

Example

local player = boii.get_player(source)

player.change_group_grade('job', 'police', 1)

increase_stat()

Increase the value of a specified player stat.

Function

function player.increase_stat(stat_category, stat_name, amount)
    if not player.data.stats then DEBUG('err', 'Function: player.increase_stat | player.data.stats is missing for ' .. player.source) return false end
    local stats = player.data.stats[stat_category]
    if not stats then DEBUG('warn', 'Function: player.increase_stat | Stat category ' .. stat_category .. ' not found for player ' .. player.source) return false end
    if not stats[stat_name] then DEBUG('warn', 'Function: player.increase_stat | Stat name ' .. stat_name .. ' not found in category ' .. stat_category .. ' for player ' .. player.source) return false end
    stats[stat_name] += amount
    DEBUG('info', 'Function: player.increase_stat | Stat ' .. stat_name .. ' increased by ' .. amount .. ' for player ' .. player.source)
    player.set_data({ 'stats' }, true)
    return true
end

Example

local player = boii.get_player(source)

player.increase_stat('general', 'total_money_earned', 1000)

decrease_stat()

Decrease the value of a specified player stat.

Function

function player.decrease_stat(stat_category, stat_name, amount)
    if not player.data.stats then DEBUG('err', 'Function: player.decrease_stat | player.data.stats is missing for ' .. player.source) return false end
    local stats = player.data.stats[stat_category]
    if not stats then DEBUG('warn', 'Function: player.decrease_stat | Stat category ' .. stat_category .. ' not found for player ' .. player.source) return false end
    if not stats[stat_name] then DEBUG('warn', 'Function: player.decrease_stat | Stat name ' .. stat_name .. ' not found in category ' .. stat_category .. ' for player ' .. player.source) return false end
    stats[stat_name] -= amount
    DEBUG('info', 'Function: player.decrease_stat | Stat ' .. stat_name .. ' decreased by ' .. amount .. ' for player ' .. player.source)
    player.set_data({ 'stats' }, true)
    return true
end

Example

local player = boii.get_player(source)

player.decrease_stat('general', 'total_money_earned', 1000)

set_stat()

Set a value for a specified player stat.

Function

function player.set_stat(stat_category, stat_name, amount)
    if not player.data.stats then DEBUG('err', 'Function: player.set_stat | player.data.stats is missing for ' .. player.source) return false end
    local stats = player.data.stats[stat_category]
    if not stats then DEBUG('warn', 'Function: player.set_stat | Stat category ' .. stat_category .. ' not found for player ' .. player.source) return false end
    if not stats[stat_name] then DEBUG('warn', 'Function: player.set_stat | Stat name ' .. stat_name .. ' not found in category ' .. stat_category .. ' for player ' .. player.source) return false end
    stats[stat_name] = amount
    DEBUG('info', 'Function: player.set_stat | Stat ' .. stat_name .. ' set to ' .. amount .. ' for player ' .. player.source)
    player.set_data({ 'stats' }, true)
    return true
end

Example

local player = boii.get_player(source)

player.set_stat('general', 'total_money_earned', 1000)

check_achievement()

Checks the current status of a specified player achievement.

Function

function player.check_achievement(achievement_name)
    if not player.data.achievements then  DEBUG('err', 'Function: player.check_achievement | Note: player.data.achievements is missing for ' .. player.source) return false end
    local achievement = player.data.achievements[achievement_name]
    return achievement ~= nil and achievement.achieved == true
end

Example

local player = boii.get_player(source)

local has_achievement = player.check_achievement('bought_first_vehicle')
print(tostring(has_achievement))

grant_achievement()

Grants an achievement to the player.

Function

function player.grant_achievement(achievement_name)
    if not player.data.achievements then DEBUG('err', 'Function: player.grant_achievement | Note: player.data.achievements is missing for ' .. player.source) return false end
    if player.data.achievements[achievement_name] and player.data.achievements[achievement_name].achieved then  DEBUG('warn', 'Function: player.grant_achievement | Note: Achievement ' .. achievement_name .. ' already granted to player ' .. player.source)  return false end
    player.data.achievements[achievement_name] = {
        achieved = true,
        timestamp = utils.dates.convert_timestamp(os.time())
    }
    player.set_data({ 'achievements' }, true)
    DEBUG('info', 'Function: player.grant_achievement | Note: Achievement ' .. achievement_name .. ' granted to player ' .. player.source)
    return true
end

Example

local player = boii.get_player(source)

player.grant_achievement('bought_first_car')

set_data()

Sets a players data.

Data updates can be optionally flagged to save to the database.

Function

function player.set_data(data_types, save_to_db)
    data_types = next(data_types) and data_types or PLAYER_DATA_TYPES
    local data_table = { passport = player.passport }
    for _, data_type in pairs(data_types) do
        if not player.data[data_type] then DEBUG('warn', 'Function: player.set_data | Note: Data type ' .. data_type .. ' not found in player data.') return end
        data_table[data_type] = player.data[data_type]
    end
    TriggerClientEvent('boii:cl:set_data', player.source, data_table)
    if save_to_db then
        player.save_data(data_types)
    end
end

Example

--- Retrieve the player.
local player = boii.get_player(source)

--- Setting all player data without saving.
player.set_data({}) 

--- Setting and saving.
player.set_data({}, true)

--- Changing the players first name.
player.data.identity.first_name = 'Bob'

--- Setting a specified data type.
player.set_data({'identity'})

save_data()

Saves player data updates to database.

Updates will only be saved to the database if flagged to do so through player.set_data(data_types, save_to_db)

Function

function player.save_data(data_types)
    local queries = {}
    for _, field in pairs(data_types) do
        local data = player.data[field]
        if data then
            local table_info = TABLE_MAP[field]
            if not table_info then DEBUG('err', 'Function: player.save_data | Note: No table defined for data type: ' .. field) return end
            local is_batch = type(data) == 'table' and #data > 0 and type(data[1]) == 'table'
            for _, record in ipairs(is_batch and data or {data}) do
                local field_updates, params = create_query(player, table_info, record)
                if #params > 0 then
                    local query = string.format('UPDATE %s SET %s WHERE %s', table_info.table, table.concat(field_updates, ', '), table.concat(table_info.where, ' = ? AND ') .. ' = ?')
                    queries[#queries + 1] = { query = query, params = params, record = record }
                end
            end
        end
    end
    for _, query_data in ipairs(queries) do
        MySQL.Async.execute(query_data.query, query_data.params, function(rows_changed)
            if rows_changed <= 0 then DEBUG('err', 'Function: player.save_data | Note: Failed to save data for query: ' .. query_data.query .. ' | Data: ' .. json.encode(query_data.record or {})) return false end
        end)
    end
end

Example

--- Retrieve the player.
local player = boii.get_player(source)

--- Save specific player data types.
player.save_data({'identity', 'accounts'})

Last updated