RSG Framework
Core

Player Functions

Complete guide to accessing and modifying player data in RSG Framework

Introduction

The Player Data system is the heart of RSG Framework's player management. It handles all player information including character data, jobs, gangs, money, metadata, and inventory. Understanding this system is essential for creating resources that interact with players.

Location: resources/[framework]/rsg-core/server/player.lua

Player data is automatically saved to the database at intervals defined in config (UpdateInterval), and on logout.


Core Features

Comprehensive Data Management

  • Character Info: Names, birthdate, gender, nationality
  • Money System: Multiple currency types with transaction logging
  • Jobs & Gangs: Full hierarchy system with grades and boss status
  • Metadata: Health, hunger, thirst, stress, and custom data
  • Reputation: Flexible reputation system for any activity

Real-time Synchronization

  • StateBags: Instant sync for frequently updated data
  • Events: Server and client events for data changes
  • Auto-save: Periodic database persistence

Flexible API

  • Player Functions: Comprehensive function set for data manipulation
  • Direct Access: Read player data directly from PlayerData table
  • Extension: Add custom methods and fields dynamically

Core Server Functions

RSGCore.Player.Login

  • On player login, gets their data or sets default values
function RSGCore.Player.Login(source, citizenid, newData)
    if source and source ~= '' then
        if citizenid then
            local license = RSGCore.Functions.GetIdentifier(source, 'license')
            local PlayerData = MySQL.prepare.await('SELECT * FROM players where citizenid = ?', { citizenid })
            if PlayerData and license == PlayerData.license then
                PlayerData.money = json.decode(PlayerData.money)
                PlayerData.job = json.decode(PlayerData.job)
                PlayerData.gang = json.decode(PlayerData.gang)
                PlayerData.position = json.decode(PlayerData.position)
                PlayerData.metadata = json.decode(PlayerData.metadata)
                PlayerData.charinfo = json.decode(PlayerData.charinfo)
                RSGCore.Player.CheckPlayerData(source, PlayerData)
            else
                DropPlayer(source, Lang:t('info.exploit_dropped'))
                TriggerEvent('rsg-log:server:CreateLog', 'anticheat', 'Anti-Cheat', 'white', GetPlayerName(source) .. ' Has Been Dropped For Character Joining Exploit', false)
            end
        else
            RSGCore.Player.CheckPlayerData(source, newData)
        end
        return true
    else
        RSGCore.ShowError(resourceName, 'ERROR RSGCore.PLAYER.LOGIN - NO SOURCE GIVEN!')
        return false
    end
end

RSGCore.Player.CheckPlayerData

  • Function called above on player join to gather player data (this is where you can add/remove additional player data)
function RSGCore.Player.CheckPlayerData(source, PlayerData)
    PlayerData = PlayerData or {}
    local Offline = not source

    if source then
        PlayerData.source = source
        PlayerData.license = PlayerData.license or RSGCore.Functions.GetIdentifier(source, 'license')
        PlayerData.name = GetPlayerName(source)
    end

    local validatedJob = false
    if PlayerData.job and PlayerData.job.name ~= nil and PlayerData.job.grade and PlayerData.job.grade.level ~= nil then
        local jobInfo = RSGCore.Shared.Jobs[PlayerData.job.name]

        if jobInfo then
            local jobGradeInfo = jobInfo.grades[tostring(PlayerData.job.grade.level)]
            if jobGradeInfo then
                PlayerData.job.label = jobInfo.label
                PlayerData.job.grade.name = jobGradeInfo.name
                PlayerData.job.payment = jobGradeInfo.payment
                PlayerData.job.grade.isboss = jobGradeInfo.isboss or false
                PlayerData.job.isboss = jobGradeInfo.isboss or false
                validatedJob = true
            end
        end
    end

    if validatedJob == false then
        -- set to nil, as the default job (unemployed) will be added by `applyDefaults`
        PlayerData.job = nil
    end

    local validatedGang = false
    if PlayerData.gang and PlayerData.gang.name ~= nil and PlayerData.gang.grade and PlayerData.gang.grade.level ~= nil then
        local gangInfo = RSGCore.Shared.Gangs[PlayerData.gang.name]

        if gangInfo then
            local gangGradeInfo = gangInfo.grades[tostring(PlayerData.gang.grade.level)]
            if gangGradeInfo then
                PlayerData.gang.label = gangInfo.label
                PlayerData.gang.grade.name = gangGradeInfo.name
                PlayerData.gang.payment = gangGradeInfo.payment
                PlayerData.gang.grade.isboss = gangGradeInfo.isboss or false
                PlayerData.gang.isboss = gangGradeInfo.isboss or false
                validatedGang = true
            end
        end
    end

    if validatedGang == false then
        -- set to nil, as the default gang (unemployed) will be added by `applyDefaults`
        PlayerData.gang = nil
    end

    applyDefaults(PlayerData, RSGCore.Config.Player.PlayerDefaults)

    if GetResourceState('rsg-inventory') ~= 'missing' then
        PlayerData.items = exports['rsg-inventory']:LoadInventory(PlayerData.source, PlayerData.citizenid)
    end

    return RSGCore.Player.CreatePlayer(PlayerData, Offline)
end

RSGCore.Player.Logout

  • Saves player on logout and removes them from active players table
function RSGCore.Player.Logout(source)
    TriggerClientEvent('RSGCore:Client:OnPlayerUnload', source)
    TriggerEvent('RSGCore:Server:OnPlayerUnload', source)
    TriggerClientEvent('RSGCore:Player:UpdatePlayerData', source)
    Wait(200)
    RSGCore.Players[source] = nil
end

RSGCore.Player.CreatePlayer

  • Creates a new character and sets default data (any function inside the self table can be called on the player after using the GetPlayer function)
function RSGCore.Player.CreatePlayer(PlayerData, Offline)
    local self = {}
    self.Functions = {}
    self.PlayerData = PlayerData
    self.Offline = Offline

    function self.Functions.UpdatePlayerData()
        if self.Offline then return end
        TriggerEvent('RSGCore:Player:SetPlayerData', self.PlayerData)
        TriggerClientEvent('RSGCore:Player:SetPlayerData', self.PlayerData.source, self.PlayerData)
    end

    function self.Functions.SetJob(job, grade)
        job = job:lower()
        grade = grade or '0'
        if not RSGCore.Shared.Jobs[job] then return false end
        self.PlayerData.job = {
            name = job,
            label = RSGCore.Shared.Jobs[job].label,
            onduty = RSGCore.Shared.Jobs[job].defaultDuty,
            type = RSGCore.Shared.Jobs[job].type or 'none',
            grade = {
                name = 'No Grades',
                level = 0,
                payment = 30,
                isboss = false
            }
        }
        local gradeKey = tostring(grade)
        local jobGradeInfo = RSGCore.Shared.Jobs[job].grades[gradeKey]
        if jobGradeInfo then
            self.PlayerData.job.grade.name = jobGradeInfo.name
            self.PlayerData.job.grade.level = tonumber(gradeKey)
            self.PlayerData.job.grade.payment = jobGradeInfo.payment
            self.PlayerData.job.grade.isboss = jobGradeInfo.isboss or false
            self.PlayerData.job.isboss = jobGradeInfo.isboss or false
        end

        if not self.Offline then
            self.Functions.UpdatePlayerData()
            TriggerEvent('RSGCore:Server:OnJobUpdate', self.PlayerData.source, self.PlayerData.job)
            TriggerClientEvent('RSGCore:Client:OnJobUpdate', self.PlayerData.source, self.PlayerData.job)
        end

        return true
    end

    function self.Functions.SetGang(gang, grade)
        gang = gang:lower()
        grade = grade or '0'
        if not RSGCore.Shared.Gangs[gang] then return false end
        self.PlayerData.gang = {
            name = gang,
            label = RSGCore.Shared.Gangs[gang].label,
            grade = {
                name = 'No Grades',
                level = 0,
                isboss = false
            }
        }
        local gradeKey = tostring(grade)
        local gangGradeInfo = RSGCore.Shared.Gangs[gang].grades[gradeKey]
        if gangGradeInfo then
            self.PlayerData.gang.grade.name = gangGradeInfo.name
            self.PlayerData.gang.grade.level = tonumber(gradeKey)
            self.PlayerData.gang.grade.isboss = gangGradeInfo.isboss or false
            self.PlayerData.gang.isboss = gangGradeInfo.isboss or false
        end

        if not self.Offline then
            self.Functions.UpdatePlayerData()
            TriggerEvent('RSGCore:Server:OnGangUpdate', self.PlayerData.source, self.PlayerData.gang)
            TriggerClientEvent('RSGCore:Client:OnGangUpdate', self.PlayerData.source, self.PlayerData.gang)
        end

        return true
    end

    function self.Functions.HasItem(items, amount)
        return RSGCore.Functions.HasItem(self.PlayerData.source, items, amount)
    end

    function self.Functions.SetJobDuty(onDuty)
        self.PlayerData.job.onduty = not not onDuty
        TriggerEvent('RSGCore:Server:OnJobUpdate', self.PlayerData.source, self.PlayerData.job)
        TriggerClientEvent('RSGCore:Client:OnJobUpdate', self.PlayerData.source, self.PlayerData.job)
        self.Functions.UpdatePlayerData()
    end

    function self.Functions.SetPlayerData(key, val)
        if not key or type(key) ~= 'string' then return end
        self.PlayerData[key] = val
        self.Functions.UpdatePlayerData()
    end

    function self.Functions.SetMetaData(meta, val)
        if not meta or type(meta) ~= 'string' then return end
        if meta == 'hunger' or meta == 'thirst' or meta == 'cleanliness' then
            val = val > 100 and 100 or val
        end
        self.PlayerData.metadata[meta] = val
        self.Functions.UpdatePlayerData()
    end

    function self.Functions.GetMetaData(meta)
        if not meta or type(meta) ~= 'string' then return end
        return self.PlayerData.metadata[meta]
    end

    function self.Functions.AddRep(rep, amount)
        if not rep or not amount then return end
        local addAmount = tonumber(amount)
        local currentRep = self.PlayerData.metadata['rep'][rep] or 0
        self.PlayerData.metadata['rep'][rep] = currentRep + addAmount
        self.Functions.UpdatePlayerData()
    end

    function self.Functions.RemoveRep(rep, amount)
        if not rep or not amount then return end
        local removeAmount = tonumber(amount)
        local currentRep = self.PlayerData.metadata['rep'][rep] or 0
        if currentRep - removeAmount < 0 then
            self.PlayerData.metadata['rep'][rep] = 0
        else
            self.PlayerData.metadata['rep'][rep] = currentRep - removeAmount
        end
        self.Functions.UpdatePlayerData()
    end

    function self.Functions.GetRep(rep)
        if not rep then return end
        return self.PlayerData.metadata['rep'][rep] or 0
    end

    function self.Functions.AddMoney(moneytype, amount, reason)
        reason = reason or 'unknown'
        moneytype = moneytype:lower()
        amount = tonumber(amount)
        if amount < 0 then return end
        if not self.PlayerData.money[moneytype] then return false end
        self.PlayerData.money[moneytype] = self.PlayerData.money[moneytype] + amount

        if not self.Offline then
            self.Functions.UpdatePlayerData()
            if amount > 100000 then
                TriggerEvent('rsg-log:server:CreateLog', 'playermoney', 'AddMoney', 'lightgreen', '**' .. GetPlayerName(self.PlayerData.source) .. ' (citizenid: ' .. self.PlayerData.citizenid .. ' | id: ' .. self.PlayerData.source .. ')** $' .. amount .. ' (' .. moneytype .. ') added, new ' .. moneytype .. ' balance: ' .. self.PlayerData.money[moneytype] .. ' reason: ' .. reason, true)
            else
                TriggerEvent('rsg-log:server:CreateLog', 'playermoney', 'AddMoney', 'lightgreen', '**' .. GetPlayerName(self.PlayerData.source) .. ' (citizenid: ' .. self.PlayerData.citizenid .. ' | id: ' .. self.PlayerData.source .. ')** $' .. amount .. ' (' .. moneytype .. ') added, new ' .. moneytype .. ' balance: ' .. self.PlayerData.money[moneytype] .. ' reason: ' .. reason)
            end
            TriggerClientEvent('hud:client:OnMoneyChange', self.PlayerData.source, moneytype, amount, false)
            TriggerClientEvent('RSGCore:Client:OnMoneyChange', self.PlayerData.source, moneytype, amount, 'add', reason)
            TriggerEvent('RSGCore:Server:OnMoneyChange', self.PlayerData.source, moneytype, amount, 'add', reason)
        end

        return true
    end

    function self.Functions.RemoveMoney(moneytype, amount, reason)
        reason = reason or 'unknown'
        moneytype = moneytype:lower()
        amount = tonumber(amount)
        if amount < 0 then return end
        if not self.PlayerData.money[moneytype] then return false end
        for _, mtype in pairs(RSGCore.Config.Money.DontAllowMinus) do
            if mtype == moneytype then
                if (self.PlayerData.money[moneytype] - amount) < 0 then
                    return false
                end
            end
        end
        self.PlayerData.money[moneytype] = self.PlayerData.money[moneytype] - amount

        if not self.Offline then
            self.Functions.UpdatePlayerData()
            if amount > 100000 then
                TriggerEvent('rsg-log:server:CreateLog', 'playermoney', 'RemoveMoney', 'red', '**' .. GetPlayerName(self.PlayerData.source) .. ' (citizenid: ' .. self.PlayerData.citizenid .. ' | id: ' .. self.PlayerData.source .. ')** $' .. amount .. ' (' .. moneytype .. ') removed, new ' .. moneytype .. ' balance: ' .. self.PlayerData.money[moneytype] .. ' reason: ' .. reason, true)
            else
                TriggerEvent('rsg-log:server:CreateLog', 'playermoney', 'RemoveMoney', 'red', '**' .. GetPlayerName(self.PlayerData.source) .. ' (citizenid: ' .. self.PlayerData.citizenid .. ' | id: ' .. self.PlayerData.source .. ')** $' .. amount .. ' (' .. moneytype .. ') removed, new ' .. moneytype .. ' balance: ' .. self.PlayerData.money[moneytype] .. ' reason: ' .. reason)
            end
            TriggerClientEvent('hud:client:OnMoneyChange', self.PlayerData.source, moneytype, amount, true)
            if moneytype == 'bank' then
                TriggerClientEvent('rsg-phone:client:RemoveBankMoney', self.PlayerData.source, amount)
            end
            TriggerClientEvent('RSGCore:Client:OnMoneyChange', self.PlayerData.source, moneytype, amount, 'remove', reason)
            TriggerEvent('RSGCore:Server:OnMoneyChange', self.PlayerData.source, moneytype, amount, 'remove', reason)
        end

        return true
    end

    function self.Functions.SetMoney(moneytype, amount, reason)
        reason = reason or 'unknown'
        moneytype = moneytype:lower()
        amount = tonumber(amount)
        if amount < 0 then return false end
        if not self.PlayerData.money[moneytype] then return false end
        local difference = amount - self.PlayerData.money[moneytype]
        self.PlayerData.money[moneytype] = amount

        if not self.Offline then
            self.Functions.UpdatePlayerData()
            TriggerEvent('rsg-log:server:CreateLog', 'playermoney', 'SetMoney', 'green', '**' .. GetPlayerName(self.PlayerData.source) .. ' (citizenid: ' .. self.PlayerData.citizenid .. ' | id: ' .. self.PlayerData.source .. ')** $' .. amount .. ' (' .. moneytype .. ') set, new ' .. moneytype .. ' balance: ' .. self.PlayerData.money[moneytype] .. ' reason: ' .. reason)
            TriggerClientEvent('hud:client:OnMoneyChange', self.PlayerData.source, moneytype, math.abs(difference), difference < 0)
            TriggerClientEvent('RSGCore:Client:OnMoneyChange', self.PlayerData.source, moneytype, amount, 'set', reason)
            TriggerEvent('RSGCore:Server:OnMoneyChange', self.PlayerData.source, moneytype, amount, 'set', reason)
        end

        return true
    end

    function self.Functions.GetMoney(moneytype)
        if not moneytype then return false end
        moneytype = moneytype:lower()
        return self.PlayerData.money[moneytype]
    end

    function self.Functions.Save()
        if self.Offline then
            RSGCore.Player.SaveOffline(self.PlayerData)
        else
            RSGCore.Player.Save(self.PlayerData.source)
        end
    end

    function self.Functions.Logout()
        if self.Offline then return end
        RSGCore.Player.Logout(self.PlayerData.source)
    end

    function self.Functions.AddMethod(methodName, handler)
        self.Functions[methodName] = handler
    end

    function self.Functions.AddField(fieldName, data)
        self[fieldName] = data
    end

    if self.Offline then
        return self
    else
        RSGCore.Players[self.PlayerData.source] = self
        RSGCore.Player.Save(self.PlayerData.source)
        TriggerEvent('RSGCore:Server:PlayerLoaded', self)
        self.Functions.UpdatePlayerData()
    end
end

RSGCore.Player.Save

  • Saves the player's info to the database
function RSGCore.Player.Save(source)
    local ped = GetPlayerPed(source)
    local pcoords = GetEntityCoords(ped)
    local PlayerData = RSGCore.Players[source].PlayerData
    if PlayerData then
        MySQL.insert('INSERT INTO players (citizenid, cid, license, name, money, charinfo, job, gang, position, metadata) VALUES (:citizenid, :cid, :license, :name, :money, :charinfo, :job, :gang, :position, :metadata) ON DUPLICATE KEY UPDATE cid = :cid, name = :name, money = :money, charinfo = :charinfo, job = :job, gang = :gang, position = :position, metadata = :metadata', {
            citizenid = PlayerData.citizenid,
            cid = tonumber(PlayerData.cid),
            license = PlayerData.license,
            name = PlayerData.name,
            money = json.encode(PlayerData.money),
            charinfo = json.encode(PlayerData.charinfo),
            job = json.encode(PlayerData.job),
            gang = json.encode(PlayerData.gang),
            position = json.encode(pcoords),
            metadata = json.encode(PlayerData.metadata)
        })
        if GetResourceState('rsg-inventory') ~= 'missing' then exports['rsg-inventory']:SaveInventory(source) end
        RSGCore.ShowSuccess(resourceName, PlayerData.name .. ' PLAYER SAVED!')
    else
        RSGCore.ShowError(resourceName, 'ERROR RSGCore.PLAYER.SAVE - PLAYERDATA IS EMPTY!')
    end
end

RSGCore.Player.DeleteCharacter

  • Deletes a character and all corresponding data in the database
local playertables = { -- Add tables as needed
    { table = 'players'},
    { table = 'playeroutfit'},
    { table = 'playerskins'},
    { table = 'player_horses'},
    { table = 'player_weapons'},
    { table = 'address_book'},
    { table = 'telegrams'},
}

function RSGCore.Player.DeleteCharacter(source, citizenid)
    local license = RSGCore.Functions.GetIdentifier(source, 'license')
    local result = MySQL.scalar.await('SELECT license FROM players where citizenid = ?', { citizenid })
    if license == result then
        local query = 'DELETE FROM %s WHERE citizenid = ?'
        local tableCount = #playertables
        local queries = table.create(tableCount, 0)

        for i = 1, tableCount do
            local v = playertables[i]
            queries[i] = { query = query:format(v.table), values = { citizenid } }
        end

        MySQL.transaction(queries, function(result2)
            if result2 then
                TriggerEvent('rsg-log:server:CreateLog', 'joinleave', 'Character Deleted', 'red', '**' .. GetPlayerName(source) .. '** ' .. license .. ' deleted **' .. citizenid .. '**..')
            end
        end)
    else
        DropPlayer(source, Lang:t('info.exploit_dropped'))
        TriggerEvent('rsg-log:server:CreateLog', 'anticheat', 'Anti-Cheat', 'white', GetPlayerName(source) .. ' Has Been Dropped For Character Deletion Exploit', true)
    end
end

Player Object Functions

Once you have a Player object (via RSGCore.Functions.GetPlayer(source)), you can use the following functions on that player:

Player.Functions.SetJob

  • Sets a player's job and grade
function Player.Functions.SetJob(job, grade)
    job = job:lower()
    grade = grade or '0'
    -- Sets the job and triggers events
    return true/false
end

-- Example

local Player = RSGCore.Functions.GetPlayer(source)
Player.Functions.SetJob('vallaw', '2') -- Set to Valentine Law Sheriff

Player.Functions.SetGang

  • Sets a player's gang and grade
function Player.Functions.SetGang(gang, grade)
    gang = gang:lower()
    grade = grade or '0'
    -- Sets the gang and triggers events
    return true/false
end

-- Example

local Player = RSGCore.Functions.GetPlayer(source)
Player.Functions.SetGang('odriscoll', '1') -- Set to O'Driscoll Enforcer

Player.Functions.SetJobDuty

  • Sets a player's on/off duty status for their job
function Player.Functions.SetJobDuty(onDuty)
    -- Sets duty status and triggers events
end

-- Example

local Player = RSGCore.Functions.GetPlayer(source)
Player.Functions.SetJobDuty(true) -- Clock player in
Player.Functions.SetJobDuty(false) -- Clock player out

Player.Functions.AddMoney

  • Add money to a player's account
function Player.Functions.AddMoney(moneytype, amount, reason)
    -- Adds money and triggers events/logs
    return true/false
end

-- Example

local Player = RSGCore.Functions.GetPlayer(source)
if Player.Functions.AddMoney('cash', 100, 'Sold item') then
    print('Added $100 cash to player')
end

-- Different money types: 'cash', 'bank', 'valbank', 'rhobank', 'blkbank', 'armbank', 'bloodmoney'

Player.Functions.RemoveMoney

  • Remove money from a player's account
function Player.Functions.RemoveMoney(moneytype, amount, reason)
    -- Removes money if player has enough
    return true/false
end

-- Example

local Player = RSGCore.Functions.GetPlayer(source)
if Player.Functions.RemoveMoney('cash', 50, 'Bought item') then
    print('Removed $50 cash from player')
else
    print('Player does not have enough money')
end

Player.Functions.SetMoney

  • Set a player's money to a specific amount
function Player.Functions.SetMoney(moneytype, amount, reason)
    -- Sets money to exact amount
    return true/false
end

-- Example

local Player = RSGCore.Functions.GetPlayer(source)
Player.Functions.SetMoney('bank', 5000, 'Admin set money')

Player.Functions.GetMoney

  • Get the amount of money a player has in a specific account
function Player.Functions.GetMoney(moneytype)
    return amount
end

-- Example

local Player = RSGCore.Functions.GetPlayer(source)
local cashAmount = Player.Functions.GetMoney('cash')
print('Player has $' .. cashAmount .. ' cash')

Player.Functions.SetMetaData

  • Set metadata for a player
function Player.Functions.SetMetaData(meta, val)
    -- Sets metadata (hunger, thirst, cleanliness are clamped to 0-100)
end

-- Example

local Player = RSGCore.Functions.GetPlayer(source)
Player.Functions.SetMetaData('hunger', 75) -- Set hunger to 75
Player.Functions.SetMetaData('thirst', 80) -- Set thirst to 80
Player.Functions.SetMetaData('isdead', false) -- Set player as alive
Player.Functions.SetMetaData('armor', 100) -- Set armor to 100

-- You can also pass a table to set multiple at once
Player.Functions.SetMetaData({
    hunger = 100,
    thirst = 100,
    stress = 0
})

Player.Functions.GetMetaData

  • Get metadata for a player
function Player.Functions.GetMetaData(meta)
    return value
end

-- Example

local Player = RSGCore.Functions.GetPlayer(source)
local hunger = Player.Functions.GetMetaData('hunger')
if hunger < 20 then
    print('Player is very hungry!')
end

Player.Functions.AddRep

  • Add reputation points for a specific reputation type
function Player.Functions.AddRep(rep, amount)
    -- Adds reputation points to metadata.rep[rep]
end

-- Example

local Player = RSGCore.Functions.GetPlayer(source)
Player.Functions.AddRep('hunting', 10)   -- Add 10 hunting rep
Player.Functions.AddRep('fishing', 5)    -- Add 5 fishing rep
Player.Functions.AddRep('lawman', 25)    -- Add 25 lawman rep

Reputation System: The framework includes a flexible reputation system stored in metadata.rep. You can create any reputation types you need (hunting, fishing, crafting, lawman, outlaw, etc.)

Player.Functions.RemoveRep

  • Remove reputation points for a specific reputation type
function Player.Functions.RemoveRep(rep, amount)
    -- Removes reputation points (won't go below 0)
end

-- Example

local Player = RSGCore.Functions.GetPlayer(source)
Player.Functions.RemoveRep('hunting', 5) -- Remove 5 hunting rep

-- Reputation is clamped at 0 (won't go negative)
Player.Functions.RemoveRep('lawman', 1000) -- If player has 50, it goes to 0, not -950

Player.Functions.GetRep

  • Get reputation points for a specific reputation type
function Player.Functions.GetRep(rep)
    return amount  -- Returns 0 if reputation type doesn't exist
end

-- Example

local Player = RSGCore.Functions.GetPlayer(source)
local huntingRep = Player.Functions.GetRep('hunting')
if huntingRep >= 100 then
    print('Player is a master hunter!')
    -- Give special rewards or unlock content
end

-- Check multiple reputations
local totalRep = 0
totalRep = totalRep + Player.Functions.GetRep('hunting')
totalRep = totalRep + Player.Functions.GetRep('fishing')
totalRep = totalRep + Player.Functions.GetRep('crafting')
print('Player total rep: '.. totalRep)

Common Reputation Types:

  • hunting - Tracking, animal kills, perfect pelts
  • fishing - Fish caught, legendary fish
  • crafting - Items crafted, quality of work
  • lawman - Criminal arrests, bounties collected
  • outlaw - Crimes committed, bounties escaped
  • trader - Goods sold, trading reputation
  • doctor - Patients healed, medical services
  • Custom types as needed for your server

Reputation Use Cases:

-- Grant bonuses based on reputation
RegisterNetEvent('hunting:server:sellPelt', function(peltType, quality)
    local src = source
    local Player = RSGCore.Functions.GetPlayer(src)
    if not Player then return end

    local huntingRep = Player.Functions.GetRep('hunting')
    local basePrice = 10
    local repBonus = math.floor(huntingRep / 10) -- 1% bonus per 10 rep

    local finalPrice = basePrice + (basePrice * repBonus / 100)

    Player.Functions.AddMoney('cash', finalPrice, 'pelt-sale')
    Player.Functions.AddRep('hunting', 2) -- Gain rep for selling

    TriggerClientEvent('ox_lib:notify', src, {
        description = 'Sold pelt for $'.. finalPrice ..' (+' .. repBonus ..'% rep bonus)',
        type = 'success'
    })
end)

-- Lock content behind reputation requirements
RegisterNetEvent('shop:server:buySpecialItem', function(itemName)
    local src = source
    local Player = RSGCore.Functions.GetPlayer(src)
    if not Player then return end

    local requiredRep = 50
    local playerRep = Player.Functions.GetRep('trader')

    if playerRep < requiredRep then
        TriggerClientEvent('ox_lib:notify', src, {
            description = 'Requires '.. requiredRep ..' trader reputation (you have '.. playerRep ..')',
            type = 'error'
        })
        return
    end

    -- Process purchase...
end)

Player.Functions.HasItem

  • Check if player has a specific item
function Player.Functions.HasItem(items, amount)
    return RSGCore.Functions.HasItem(source, items, amount)
end

-- Example

local Player = RSGCore.Functions.GetPlayer(source)
if Player.Functions.HasItem('bread', 1) then
    print('Player has bread')
end

Player.Functions.SetPlayerData

  • Set any player data field
function Player.Functions.SetPlayerData(key, val)
    -- Sets player data and updates
end

-- Example

local Player = RSGCore.Functions.GetPlayer(source)
Player.Functions.SetPlayerData('weight', 50000) -- Change inventory weight
Player.Functions.SetPlayerData('slots', 30) -- Change inventory slots

Player.Functions.Save

  • Manually save a player's data to database
function Player.Functions.Save()
    -- Saves player data to database
end

-- Example

local Player = RSGCore.Functions.GetPlayer(source)
-- Make changes to player data
Player.Functions.SetMoney('cash', 1000, 'test')
-- Manually save
Player.Functions.Save()

Player.Functions.Logout

  • Log a player out (removes from active players)
function Player.Functions.Logout()
    -- Logs player out and triggers events
end

-- Example

local Player = RSGCore.Functions.GetPlayer(source)
Player.Functions.Logout()

Player.Functions.UpdatePlayerData

  • Trigger an update of player data to client
function Player.Functions.UpdatePlayerData()
    -- Updates player data on client
end

-- Example

local Player = RSGCore.Functions.GetPlayer(source)
-- Make changes
Player.PlayerData.charinfo.firstname = 'John'
-- Manually trigger update
Player.Functions.UpdatePlayerData()

Accessing Player Data

Once you have a Player object, you can access their data directly:

local Player = RSGCore.Functions.GetPlayer(source)

-- Access character info
local firstname = Player.PlayerData.charinfo.firstname
local lastname = Player.PlayerData.charinfo.lastname
local birthdate = Player.PlayerData.charinfo.birthdate
local gender = Player.PlayerData.charinfo.gender
local account = Player.PlayerData.charinfo.account

-- Access money
local cash = Player.PlayerData.money.cash
local bank = Player.PlayerData.money.bank

-- Access job info
local jobName = Player.PlayerData.job.name
local jobLabel = Player.PlayerData.job.label
local jobGrade = Player.PlayerData.job.grade.level
local jobGradeName = Player.PlayerData.job.grade.name
local jobPayment = Player.PlayerData.job.payment
local onDuty = Player.PlayerData.job.onduty
local isBoss = Player.PlayerData.job.isboss

-- Access gang info
local gangName = Player.PlayerData.gang.name
local gangLabel = Player.PlayerData.gang.label
local gangGrade = Player.PlayerData.gang.grade.level
local gangIsBoss = Player.PlayerData.gang.isboss

-- Access metadata
local hunger = Player.PlayerData.metadata.hunger
local thirst = Player.PlayerData.metadata.thirst
local cleanliness = Player.PlayerData.metadata.cleanliness
local stress = Player.PlayerData.metadata.stress
local isDead = Player.PlayerData.metadata.isdead
local armor = Player.PlayerData.metadata.armor
local bloodtype = Player.PlayerData.metadata.bloodtype
local fingerprint = Player.PlayerData.metadata.fingerprint
local walletid = Player.PlayerData.metadata.walletid
local callsign = Player.PlayerData.metadata.callsign

-- Access other info
local citizenid = Player.PlayerData.citizenid
local license = Player.PlayerData.license
local name = Player.PlayerData.name
local source = Player.PlayerData.source

StateBags System

The RSG Framework uses FiveM's StateBags system to sync specific metadata between server and client in real-time. This provides better performance than constantly triggering events.

What are StateBags?

StateBags are key-value stores attached to entities (players, vehicles, etc.) that automatically sync between server and client. They're more efficient than events for frequently updated data.

Synced Metadata Keys:

  • hunger (0-100)
  • thirst (0-100)
  • cleanliness (0-100)
  • stress (0-100)
  • health (0-600)
  • isLoggedIn (boolean)

How StateBags Work

-- SERVER SIDE - Automatically synced on player load
function Player.Functions.InitializeStateBags()
    local metadata = self.PlayerData.metadata
    local keys = { "hunger", "thirst", "cleanliness", "stress", "health" }

    local state = Player(self.PlayerData.source).state
    for _, key in ipairs(keys) do
        if metadata[key] ~= nil then
            state[key] = metadata[key]  -- Automatically syncs to client
        end
    end
end

-- When player data saves, StateBags are persisted back to metadata
function Player.Functions.PersistStateBags()
    local metadata = {}
    local keys = { "hunger", "thirst", "cleanliness", "stress", "health" }

    local state = Player(self.PlayerData.source).state
    for _, key in ipairs(keys) do
        if state[key] ~= nil then
            metadata[key] = state[key]
        end
    end

    if next(metadata) then
        self.Functions.SetMetaData(metadata)
    end
end

Reading StateBags (Client-Side)

-- CLIENT SIDE
-- Read player's own state
local hunger = LocalPlayer.state.hunger
local thirst = LocalPlayer.state.thirst
local health = LocalPlayer.state.health

if hunger < 20 then
    print('You are very hungry!')
end

-- Watch for state changes
AddStateBagChangeHandler('hunger', nil, function(bagName, key, value)
    if bagName ~= ('player:%s'):format(GetPlayerServerId(PlayerId())) then return end
    print('Your hunger changed to: '.. value)
    -- Update UI, trigger effects, etc.
end)

-- Read another player's state
local targetPlayerId = GetPlayerFromServerId(targetServerId)
local targetState = Player(targetPlayerId).state
local targetHunger = targetState.hunger

Using StateBags in Resources

-- SERVER SIDE - Update state directly
local state = Player(source).state
state.hunger = 50  -- Automatically syncs to client

-- Or use the player function (recommended)
local Player = RSGCore.Functions.GetPlayer(source)
Player.Functions.SetMetaData('hunger', 50)  -- This updates StateBag automatically

-- CLIENT SIDE - React to state changes
CreateThread(function()
    while true do
        Wait(1000)
        local hunger = LocalPlayer.state.hunger or 100
        local thirst = LocalPlayer.state.thirst or 100

        if hunger < 20 then
            -- Show hunger warning
            -- Apply debuffs
        end

        if thirst < 20 then
            -- Show thirst warning
            -- Apply debuffs
        end
    end
end)

-- Listen for login state
AddStateBagChangeHandler('isLoggedIn', nil, function(bagName, key, value)
    if bagName ~= ('player:%s'):format(GetPlayerServerId(PlayerId())) then return end

    if value then
        print('Player is now logged in')
        -- Initialize client-side systems
    else
        print('Player logged out')
        -- Clean up client-side systems
    end
end)

Benefits of StateBags

Performance: No need to constantly trigger events for metadata updates

Real-time: Changes sync instantly without event delays

Clean Code: Access data directly instead of callback hell

When to Use StateBags vs Events

Use StateBags for:

  • Frequently updated data (hunger, thirst, health)
  • Data that needs instant client access
  • Simple value syncing

Use Events for:

  • Complex data updates
  • Actions that need validation
  • One-time notifications

Example: Custom HUD Using StateBags

-- CLIENT SIDE
local function UpdateHUD()
    local hunger = LocalPlayer.state.hunger or 100
    local thirst = LocalPlayer.state.thirst or 100
    local health = LocalPlayer.state.health or 600
    local stress = LocalPlayer.state.stress or 0

    -- Send to NUI
    SendNUIMessage({
        action = 'updateHud',
        hunger = hunger,
        thirst = thirst,
        health = health,
        stress = stress
    })
end

-- Update HUD every second
CreateThread(function()
    while true do
        Wait(1000)
        if LocalPlayer.state.isLoggedIn then
            UpdateHUD()
        end
    end
end)

-- Also update immediately when states change
AddStateBagChangeHandler('hunger', nil, function(bagName, key, value)
    if bagName ~= ('player:%s'):format(GetPlayerServerId(PlayerId())) then return end
    UpdateHUD()
end)

AddStateBagChangeHandler('thirst', nil, function(bagName, key, value)
    if bagName ~= ('player:%s'):format(GetPlayerServerId(PlayerId())) then return end
    UpdateHUD()
end)

Complete Metadata Structure

The full metadata table structure in PlayerData:

metadata = {
    -- Basic Stats (synced via StateBags)
    health = 600,               -- Player health (0-600, RedM scale)
    hunger = 100,               -- Hunger level (0-100)
    thirst = 100,               -- Thirst level (0-100)
    cleanliness = 100,          -- Cleanliness level (0-100)
    stress = 0,                 -- Stress level (0-100)

    -- Status Flags
    isdead = false,             -- Is player dead
    ishandcuffed = false,       -- Is player handcuffed
    armor = 0,                  -- Armor value

    -- Jail System
    injail = 0,                 -- Jail time remaining (minutes)
    jailitems = {},             -- Items confiscated during arrest

    -- Character Info
    bloodtype = 'O+',           -- Blood type (randomly assigned)
    fingerprint = 'AB123CD456', -- Unique fingerprint ID
    walletid = 'WL789XY012',    -- Wallet ID
    callsign = 'NO CALLSIGN',   -- Job callsign (for law/medic)

    -- Criminal Record
    criminalrecord = {
        hasRecord = false,      -- Has criminal record
        date = nil              -- Date of last offense
    },

    -- Reputation System
    rep = {
        hunting = 0,            -- Hunting reputation
        fishing = 0,            -- Fishing reputation
        -- Add custom reputation types as needed
    },

    -- Custom Status Effects
    status = {},                -- Custom status effects table
}

Best Practices

Tip

Always Check Player Exists: Before accessing player data, always verify the player object exists to prevent errors

local Player = RSGCore.Functions.GetPlayer(source)
if not Player then return end

-- Now safe to use Player functions

Don't Modify PlayerData Directly: Always use Player.Functions to modify data - this ensures events fire and data syncs properly

-- Bad
Player.PlayerData.money.cash = 1000

-- Good
Player.Functions.SetMoney('cash', 1000, 'admin-set')

Use StateBags for HUDs: For frequently updated display data (hunger, thirst, health), read from StateBags instead of triggering events

Performance Tips

  1. Batch Updates: When setting multiple metadata values, group them to reduce database writes
  2. Cache Player Objects: Store the Player object in a variable instead of calling GetPlayer multiple times
  3. Use GetRep Sparingly: If checking reputation frequently, cache the value locally
  4. Avoid Save Spam: Don't call Player.Functions.Save() excessively - let auto-save handle it

Common Patterns

-- Get player with validation
local function GetValidPlayer(source)
    local Player = RSGCore.Functions.GetPlayer(source)
    if not Player then
        TriggerClientEvent('ox_lib:notify', source, {
            description = 'Player data not loaded',
            type = 'error'
        })
        return nil
    end
    return Player
end

-- Check job with grade
local function HasJobWithMinGrade(source, jobName, minGrade)
    local Player = RSGCore.Functions.GetPlayer(source)
    if not Player then return false end

    return Player.PlayerData.job.name == jobName
        and Player.PlayerData.job.grade.level >= minGrade
end

-- Check any law job
local function IsLawEnforcement(source)
    local Player = RSGCore.Functions.GetPlayer(source)
    if not Player then return false end

    return Player.PlayerData.job.type == 'leo'
end

Next Steps


Need help? Join the RSG Framework Discord!

On this page