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
endRSGCore.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)
endRSGCore.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
endRSGCore.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
endRSGCore.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
endRSGCore.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
endPlayer 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 SheriffPlayer.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 EnforcerPlayer.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 outPlayer.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')
endPlayer.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!')
endPlayer.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 repReputation 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 -950Player.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 peltsfishing- Fish caught, legendary fishcrafting- Items crafted, quality of worklawman- Criminal arrests, bounties collectedoutlaw- Crimes committed, bounties escapedtrader- Goods sold, trading reputationdoctor- 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')
endPlayer.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 slotsPlayer.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.sourceStateBags 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
endReading 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.hungerUsing 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 functionsDon'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
- Batch Updates: When setting multiple metadata values, group them to reduce database writes
- Cache Player Objects: Store the Player object in a variable instead of calling GetPlayer multiple times
- Use GetRep Sparingly: If checking reputation frequently, cache the value locally
- 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'
endNext Steps
- Server Functions Reference - Complete server function list
- Configuration Guide - Configure player defaults
- Money System - Complete money system guide
- StateBags System - Deep dive into state synchronization
- Reputation System - Detailed reputation guide
Need help? Join the RSG Framework Discord!

