Server Events
Complete guide to RSG Core server-side events and event handlers
Introduction
Server events are the backbone of RSG Framework's server-side communication system. They allow resources to listen for and respond to player actions, system events, and cross-resource communication.
Server events run on the server and can be triggered by clients using TriggerServerEvent or by other server-side code using TriggerEvent.
How It Works
Event Flow
Client Script → TriggerServerEvent('eventName', args) → Server Listens → Server Processes → ResponseRegistering Events
Server events are registered using RegisterNetEvent:
-- SERVER SIDE
RegisterNetEvent('eventName', function(arg1, arg2)
local src = source -- The player who triggered the event
-- Process event
end)Always validate data received from client events! Never trust client input.
Core Player Events
RSGCore:Server:OnPlayerLoaded
Triggered when a player successfully loads into the server after character selection.
-- SERVER SIDE
RegisterNetEvent('RSGCore:Server:OnPlayerLoaded', function()
local src = source
local Player = RSGCore.Functions.GetPlayer(src)
if not Player then return end
print('Player loaded:', Player.PlayerData.charinfo.firstname)
-- Your custom logic here
-- Example: Give welcome bonus
Player.Functions.AddMoney('cash', 100, 'welcome-bonus')
end)Use Cases:
- Initialize player-specific data
- Load custom player stats
- Trigger welcome messages
- Set up player blips/markers
RSGCore:Server:OnPlayerUnload
Triggered when a player logs out or returns to character selection.
-- SERVER SIDE
RegisterNetEvent('RSGCore:Server:OnPlayerUnload', function()
local src = source
print('Player', src, 'logged out')
-- Clean up player-specific data
-- Remove from custom tables
-- Save additional data if needed
end)Use Cases:
- Clean up temporary data
- Save custom player stats
- Remove player from active lists
- Stop player-specific threads
Server Management Events
RSGCore:Server:CloseServer
Closes the server to non-whitelisted players with a custom reason.
-- SERVER SIDE
RegisterNetEvent('RSGCore:Server:CloseServer', function(reason)
local src = source
-- Permission check
if not RSGCore.Functions.HasPermission(src, 'admin') then
RSGCore.Functions.Kick(src, 'You don\'t have permissions for this', nil, nil)
return
end
reason = reason or 'Server maintenance'
RSGCore.Config.Server.Closed = true
RSGCore.Config.Server.ClosedReason = reason
-- Kick all non-whitelisted players
for playerId in pairs(RSGCore.Players) do
if not RSGCore.Functions.HasPermission(playerId, RSGCore.Config.Server.WhitelistPermission) then
RSGCore.Functions.Kick(playerId, reason, nil, nil)
end
end
print('Server closed:', reason)
end)Trigger from Client:
-- CLIENT SIDE (admin only)
TriggerServerEvent('RSGCore:Server:CloseServer', 'Emergency maintenance')Use Cases:
- Emergency maintenance
- Scheduled restarts
- Server events preparation
- Testing with limited players
RSGCore:Server:OpenServer
Reopens the server to all players.
-- SERVER SIDE
RegisterNetEvent('RSGCore:Server:OpenServer', function()
local src = source
if not RSGCore.Functions.HasPermission(src, 'admin') then
RSGCore.Functions.Kick(src, 'You don\'t have permissions for this', nil, nil)
return
end
RSGCore.Config.Server.Closed = false
print('Server opened')
end)Trigger from Client:
-- CLIENT SIDE (admin only)
TriggerServerEvent('RSGCore:Server:OpenServer')Player Data Events
RSGCore:UpdatePlayer
Updates and saves player needs (hunger, thirst, cleanliness).
-- SERVER SIDE
RegisterNetEvent('RSGCore:UpdatePlayer', function()
local src = source
local Player = RSGCore.Functions.GetPlayer(src)
if not Player then return end
-- Decrease player needs
local newHunger = math.max(0, Player.PlayerData.metadata.hunger - RSGCore.Config.Player.HungerRate)
local newThirst = math.max(0, Player.PlayerData.metadata.thirst - RSGCore.Config.Player.ThirstRate)
local newCleanliness = math.max(0, Player.PlayerData.metadata.cleanliness - RSGCore.Config.Player.CleanlinessRate)
-- Update metadata
Player.Functions.SetMetaData('hunger', newHunger)
Player.Functions.SetMetaData('thirst', newThirst)
Player.Functions.SetMetaData('cleanliness', newCleanliness)
-- Notify client
TriggerClientEvent('hud:client:UpdateNeeds', src, newHunger, newThirst, newCleanliness)
-- Save player data
Player.Functions.Save()
end)This event is typically called on a timer to gradually decrease player needs over time.
Complete Example with Timer:
-- SERVER SIDE
CreateThread(function()
while true do
Wait(RSGCore.Config.Player.UpdateInterval or 300000) -- 5 minutes default
for src, Player in pairs(RSGCore.Players) do
if Player then
TriggerEvent('RSGCore:UpdatePlayer', src)
end
end
end
end)RSGCore:Server:SetMetaData
Sets a specific metadata value for a player with validation.
-- SERVER SIDE
RegisterNetEvent('RSGCore:Server:SetMetaData', function(meta, data)
local src = source
local Player = RSGCore.Functions.GetPlayer(src)
if not Player then return end
-- Validate needs (cap at 100)
if meta == 'hunger' or meta == 'thirst' or meta == 'cleanliness' then
if data > 100 then
data = 100
elseif data < 0 then
data = 0
end
end
-- Update metadata
Player.Functions.SetMetaData(meta, data)
-- Update HUD
TriggerClientEvent('hud:client:UpdateNeeds', src,
Player.PlayerData.metadata.hunger,
Player.PlayerData.metadata.thirst,
Player.PlayerData.metadata.cleanliness
)
end)Trigger from Client:
-- CLIENT SIDE
TriggerServerEvent('RSGCore:Server:SetMetaData', 'hunger', 75)Use Cases:
- Restore hunger after eating
- Restore thirst after drinking
- Restore cleanliness after bathing
- Custom metadata updates
Job & Duty Events
RSGCore:ToggleDuty
Toggles a player's on-duty status for their job.
-- SERVER SIDE
RegisterNetEvent('RSGCore:ToggleDuty', function()
local src = source
local Player = RSGCore.Functions.GetPlayer(src)
if not Player then return end
-- Check if job has duty system
if not Player.PlayerData.job.name then return end
-- Toggle duty
local newDutyStatus = not Player.PlayerData.job.onduty
Player.Functions.SetJobDuty(newDutyStatus)
-- Notify player
lib.notify(src, {
description = newDutyStatus and 'You are now on duty' or 'You are now off duty',
type = 'info'
})
-- Trigger client event for additional logic
TriggerClientEvent('RSGCore:Client:SetDuty', src, newDutyStatus)
end)Trigger from Client:
-- CLIENT SIDE
RegisterCommand('duty', function()
TriggerServerEvent('RSGCore:ToggleDuty')
end)Complete Example with Job Restrictions:
-- SERVER SIDE
local dutyJobs = {
['police'] = true,
['doctor'] = true,
['mechanic'] = true
}
RegisterNetEvent('RSGCore:ToggleDuty', function()
local src = source
local Player = RSGCore.Functions.GetPlayer(src)
if not Player then return end
-- Check if job has duty system
if not dutyJobs[Player.PlayerData.job.name] then
lib.notify(src, {
description = 'Your job doesn\'t have a duty system',
type = 'error'
})
return
end
-- Toggle duty
local newDutyStatus = not Player.PlayerData.job.onduty
Player.Functions.SetJobDuty(newDutyStatus)
-- Job-specific logic
if Player.PlayerData.job.name == 'police' then
if newDutyStatus then
-- Add police blip
TriggerClientEvent('police:client:addBlip', src)
else
-- Remove police blip
TriggerClientEvent('police:client:removeBlip', src)
end
end
lib.notify(src, {
description = newDutyStatus and 'Clocked in' or 'Clocked out',
type = 'success'
})
end)Callback Events
RSGCore:Server:TriggerCallback
Handles server-side callback requests from clients.
-- SERVER SIDE (internal - used by callback system)
RegisterNetEvent('RSGCore:Server:TriggerCallback', function(name, ...)
local src = source
if not RSGCore.ServerCallbacks[name] then
print('^1[ERROR]^7 Callback', name, 'does not exist')
return
end
RSGCore.ServerCallbacks[name](src, function(...)
TriggerClientEvent('RSGCore:Client:TriggerCallback', src, name, ...)
end, ...)
end)This is an internal event used by the callback system. Use RSGCore.Functions.CreateCallback instead of listening to this event directly.
Proper Usage:
-- SERVER SIDE - Create callback
RSGCore.Functions.CreateCallback('myCallback', function(source, cb, arg1)
local Player = RSGCore.Functions.GetPlayer(source)
if not Player then
cb(nil)
return
end
cb(Player.PlayerData.money.cash)
end)-- CLIENT SIDE - Trigger callback
RSGCore.Functions.TriggerCallback('myCallback', function(cashAmount)
print('Player has $' .. cashAmount)
end, someArgument)Permission Events
RSGCore:Server:GivePermission
Grants a permission level to a player (typically admin/god).
-- SERVER SIDE
RegisterNetEvent('RSGCore:Server:GivePermission', function(targetId, permission)
local src = source
-- Only admins can grant permissions
if not RSGCore.Functions.HasPermission(src, 'god') then
lib.notify(src, {
description = 'You don\'t have permission to do this',
type = 'error'
})
return
end
local Target = RSGCore.Functions.GetPlayer(targetId)
if not Target then
lib.notify(src, {
description = 'Player not found',
type = 'error'
})
return
end
-- Grant permission
RSGCore.Functions.AddPermission(targetId, permission)
lib.notify(src, {
description = 'Granted ' .. permission .. ' to player',
type = 'success'
})
lib.notify(targetId, {
description = 'You have been granted ' .. permission .. ' permissions',
type = 'info'
})
end)RSGCore:Server:RemovePermission
Removes a permission level from a player.
-- SERVER SIDE
RegisterNetEvent('RSGCore:Server:RemovePermission', function(targetId, permission)
local src = source
if not RSGCore.Functions.HasPermission(src, 'god') then
lib.notify(src, {
description = 'You don\'t have permission to do this',
type = 'error'
})
return
end
-- Remove permission
RSGCore.Functions.RemovePermission(targetId, permission)
lib.notify(src, {
description = 'Removed ' .. permission .. ' from player',
type = 'success'
})
end)Best Practices
1. Always Validate Player
-- GOOD
RegisterNetEvent('myEvent', function(arg)
local src = source
local Player = RSGCore.Functions.GetPlayer(src)
if not Player then return end
-- Safe to use Player
end)
-- BAD
RegisterNetEvent('myEvent', function(arg)
local src = source
local Player = RSGCore.Functions.GetPlayer(src)
-- No check - will error if player doesn't exist!
Player.Functions.AddMoney('cash', 100)
end)2. Validate Client Input
-- GOOD
RegisterNetEvent('shop:purchase', function(itemName, amount)
local src = source
local Player = RSGCore.Functions.GetPlayer(src)
if not Player then return end
-- Validate item exists
if not RSGCore.Shared.Items[itemName] then
return
end
-- Validate amount
if type(amount) ~= 'number' or amount < 1 or amount > 100 then
return
end
-- Process purchase
end)3. Use Meaningful Event Names
-- GOOD
RegisterNetEvent('banking:server:deposit', ...)
RegisterNetEvent('inventory:server:giveItem', ...)
RegisterNetEvent('police:server:handcuff', ...)
-- BAD
RegisterNetEvent('doStuff', ...)
RegisterNetEvent('event1', ...)
RegisterNetEvent('handle', ...)4. Handle Permissions Properly
-- GOOD
RegisterNetEvent('admin:giveWeapon', function(targetId, weapon)
local src = source
if not RSGCore.Functions.HasPermission(src, 'admin') then
return -- Silent fail or notify
end
-- Admin action
end)Common Patterns
Pattern 1: Transaction Event
RegisterNetEvent('banking:server:deposit', function(amount)
local src = source
local Player = RSGCore.Functions.GetPlayer(src)
if not Player then return end
-- Validate amount
if type(amount) ~= 'number' or amount < 1 then
lib.notify(src, {description = 'Invalid amount', type = 'error'})
return
end
-- Check player has cash
if Player.Functions.GetMoney('cash') < amount then
lib.notify(src, {description = 'Not enough cash', type = 'error'})
return
end
-- Process deposit
Player.Functions.RemoveMoney('cash', amount, 'bank-deposit')
Player.Functions.AddMoney('bank', amount, 'bank-deposit')
lib.notify(src, {
description = 'Deposited $' .. amount,
type = 'success'
})
end)Pattern 2: Item Give/Take Event
RegisterNetEvent('admin:giveItem', function(targetId, itemName, amount)
local src = source
-- Permission check
if not RSGCore.Functions.HasPermission(src, 'admin') then
return
end
-- Validate inputs
local Target = RSGCore.Functions.GetPlayer(targetId)
if not Target then return end
if not RSGCore.Shared.Items[itemName] then return end
amount = tonumber(amount) or 1
-- Give item
exports['rsg-inventory']:AddItem(targetId, itemName, amount, nil, nil, 'admin-give')
lib.notify(src, {
description = 'Gave item to player',
type = 'success'
})
end)Pattern 3: Proximity Check Event
RegisterNetEvent('shop:server:purchase', function(shopId, itemName)
local src = source
local Player = RSGCore.Functions.GetPlayer(src)
if not Player then return end
-- Get shop location
local shopCoords = Config.Shops[shopId].coords
-- Check if player is near
local ped = GetPlayerPed(src)
local playerCoords = GetEntityCoords(ped)
local distance = #(playerCoords - shopCoords)
if distance > 5.0 then
lib.notify(src, {description = 'Too far from shop', type = 'error'})
return
end
-- Process purchase
end)Troubleshooting
Event Not Triggering
Make sure the event name matches exactly between client and server
Use RegisterNetEvent for networked events triggered across client/server
Always validate source exists before using it
Player Not Found
-- Common issue
RegisterNetEvent('myEvent', function()
local src = source
local Player = RSGCore.Functions.GetPlayer(src)
if not Player then
print('Player not loaded yet or disconnected')
return
end
end)Permission Denied
-- Always check permissions
if not RSGCore.Functions.HasPermission(src, 'admin') then
print('Player', src, 'tried to use admin command')
return
endSummary
| Event | Purpose | Permission Required |
|---|---|---|
RSGCore:Server:OnPlayerLoaded | Player login | None |
RSGCore:Server:OnPlayerUnload | Player logout | None |
RSGCore:Server:CloseServer | Close server | Admin |
RSGCore:Server:OpenServer | Open server | Admin |
RSGCore:UpdatePlayer | Update needs | None |
RSGCore:Server:SetMetaData | Set player metadata | None |
RSGCore:ToggleDuty | Toggle job duty | None |
RSGCore:Server:GivePermission | Grant permissions | God |
RSGCore:Server:RemovePermission | Remove permissions | God |
Next Steps
- Client Events - Client-side event reference
- Server Functions - Server function reference
- Callbacks - Learn about the callback system
Need more help? Join the RSG Framework Discord!

