Events
Events are the primary way resources communicate in FiveM.
Server Events
Triggered from server, received by clients:
-- Server: Trigger event
TriggerClientEvent('myresource:clientEvent', source, data)
-- Client: Listen for event
RegisterNetEvent('myresource:clientEvent')
AddEventHandler('myresource:clientEvent', function(data)
print('Received:', data)
end)Client Events
Triggered from client, received by server:
-- Client: Trigger event
TriggerServerEvent('myresource:serverEvent', data)
-- Server: Listen for event
RegisterNetEvent('myresource:serverEvent')
AddEventHandler('myresource:serverEvent', function(data)
print('Received from client:', data)
end)Net Events (Secure)
Use RegisterNetEvent for secure events:
-- Register event (server)
RegisterNetEvent('myresource:secureEvent')
-- Trigger (client)
TriggerServerEvent('myresource:secureEvent', data)Security Best Practices
1. Validate Input
Never trust data from the client. Always verify types and constraints.
RegisterNetEvent('myresource:updateMoney')
AddEventHandler('myresource:updateMoney', function(amount)
local source = source
-- Type check
if type(amount) ~= 'number' then
print(('Player %s sent invalid amount type'):format(source))
return
end
-- Sanity check
if amount < 0 or amount > 100000 then
DropPlayer(source, 'Invalid money amount')
return
end
-- Process
local xPlayer = ESX.GetPlayerFromId(source)
if xPlayer then
xPlayer.addMoney(amount)
end
end)2. Distance Checking
For interactions that happen in the world, verify the player is close enough to the target.
RegisterNetEvent('myresource:openDoor')
AddEventHandler('myresource:openDoor', function(doorId)
local source = source
local ped = GetPlayerPed(source)
local pCoords = GetEntityCoords(ped)
local dCoords = Config.Doors[doorId].coords
-- Distance check (e.g., 2.0 units)
if #(pCoords - dCoords) > 2.0 then
print(('Player %s tried to open door from too far'):format(source))
return
end
TriggerClientEvent('myresource:setDoorState', -1, doorId, true)
end)Rate Limiting Patterns
Prevent event spam (DoS attacks or buggy loops) using a bucket strategy.
local eventBuckets = {}
local RATE_LIMIT = 10 -- requests
local TIME_WINDOW = 5000 -- ms
local function isRateLimited(source, eventName)
local now = GetGameTimer()
if not eventBuckets[source] then eventBuckets[source] = {} end
if not eventBuckets[source][eventName] then
eventBuckets[source][eventName] = { count = 0, timer = now }
end
local bucket = eventBuckets[source][eventName]
-- Reset window
if now - bucket.timer > TIME_WINDOW then
bucket.count = 0
bucket.timer = now
end
bucket.count = bucket.count + 1
if bucket.count > RATE_LIMIT then
return true
end
return false
end
RegisterNetEvent('myresource:heavyTask')
AddEventHandler('myresource:heavyTask', function()
local source = source
if isRateLimited(source, 'heavyTask') then
print(('Player %s is rate limited on heavyTask'):format(source))
-- Optional: DropPlayer if severe
return
end
-- Proceed with task
end)Event Debugging
When events fail or misbehave:
- Network Graph: Use
netgraphin console to see bandwidth spikes. - Event Logging:
-- Global event listener for debugging (Development only) AddEventHandler('myresource:debug', function(...) print('Event triggered:', ...) end) - Resource Monitor: Check
resmonfor high CPU usage during events.
Performance Considerations
- Payload Size: Keep data small. Don’t send huge tables or HTML strings.
- Frequency: Avoid
TriggerClientEventinsideUpdateloops (every frame). - Targeting: Use specific targets (source) instead of
-1(broadcast) whenever possible to reduce network load. - Serialization: Lua to JSON conversion happens on every event. Complex nested tables are expensive to serialize.
Last updated on