Engineering
Events in FiveM: how resources and players talk
FiveM events explained: local vs network events, RegisterNetEvent and AddEventHandler, TriggerEvent, TriggerServerEvent and TriggerClientEvent (and -1 for everyone), the source variable, built-in events, latent events and event security.
Overview
Events are the nervous system of a FiveM server. A client tells the server a player pressed E; the server tells every client a door unlocked; one resource tells another a job changed. The API is only a handful of functions, but the difference between a local event, a network event and a network event anyone can trigger is exactly where both bugs and exploits live.
Local and network events
| Function | Called on | Reaches |
|---|---|---|
TriggerEvent(name, ...) | Client or server | Handlers on the same side only |
TriggerServerEvent(name, ...) | Client | The server |
TriggerClientEvent(name, target, ...) | Server | One player (target = ID) or all (-1) |
TriggerLatentServerEvent(name, bps, ...) | Client | The server, rate-limited for big payloads |
TriggerLatentClientEvent(name, target, bps, ...) | Server | Client(s), rate-limited |
A local event is a message between resources on the same machine. A network event crosses from client to server or back. The receiving side must have registered the event as a network event, otherwise a network trigger is ignored — that registration is a small safety barrier.
Registering and handling
-- Short form: register as a net event and add the handler
RegisterNetEvent('bank:deposit', function(amount)
local src = source
-- validate, then act
end)
-- Long form, equivalent
RegisterNetEvent('bank:withdraw')
AddEventHandler('bank:withdraw', function(amount)
local src = source
end)
-- A purely local event between resources on the server
AddEventHandler('myjob:shiftStarted', function(playerId)
print('shift started', playerId)
end)Sending events
TriggerServerEvent('bank:deposit', 500)
RegisterNetEvent('bank:updated', function(balance)
print('new balance', balance)
end)-- To the player who asked
TriggerClientEvent('bank:updated', src, newBalance)
-- To everyone
TriggerClientEvent('weather:changed', -1, 'RAIN')Arguments can be numbers, strings, booleans and tables (serialised with msgpack). Functions and entity handles do not travel meaningfully — send network IDs for entities, as explained in entity ownership and network IDs.
Built-in events worth knowing
| Event | Side | Fires when |
|---|---|---|
playerConnecting | Server | A player starts connecting (deferrals, whitelists) |
playerJoining | Server | A player has joined and has a server ID |
playerDropped | Server | A player leaves — clean up their data here |
onResourceStart / onResourceStop | Both | Any resource starts or stops (check the name) |
onClientResourceStart | Client | A resource starts on this client |
gameEventTriggered | Client | Game events such as damage (CEventNetworkEntityDamage) |
AddEventHandler('onResourceStop', function(resourceName)
if resourceName ~= GetCurrentResourceName() then return end
-- delete spawned entities, remove blips, close NUI
end)Latent events for large payloads
Normal net events are sent immediately and are meant for small messages. Sending large tables — a whole inventory, a long list of vehicles — through them can stall the connection. Latent events split the payload and send it at the bandwidth you specify, in bytes per second:
TriggerLatentClientEvent('garage:list', src, 50000, vehicles) -- ~50 KB/sBetter still, avoid large payloads: send only what changed, or let the client ask for one page at a time. For ongoing state, state bags often replace events entirely.
Event security in one paragraph
Every event registered with RegisterNetEvent on the server can be triggered by any connected client, at any time, with any arguments — mod menus list them and fire them in a loop. So a server handler must decide for itself whether the request is allowed: is this player the right job, close enough, not on cooldown, asking for a sensible amount? Never accept an amount of money, an item or a target player ID from a client without checking it. The full checklist is in securing server events and event rate limiting.
Naming events
- Prefix with your resource:
garage:store,bank:deposit. It avoids collisions with other resources. - Use a direction or verb that makes the flow obvious:
server:/client:prefixes are also common. - Do not give net events names that describe privileged actions (
admin:giveMoney) without strict checks — they are the first thing cheaters try.
Frequently asked questions
What is the difference between TriggerEvent and TriggerServerEvent?
TriggerEvent only reaches handlers on the same side (client to client scripts, or server to server scripts). TriggerServerEvent sends from a client to the server.
How do I send an event to all players?
TriggerClientEvent('eventName', -1, ...) from the server.
What is source in FiveM?
In a server event handler, source is the server ID of the player who triggered it. Copy it to a local variable immediately, because it can change after any wait.
Why is my event not received?
The receiving side did not call RegisterNetEvent for that name, the name differs by a character, or the resource with the handler is not started.
Can players trigger my server events?
Yes. Any registered net event can be triggered by a client with arbitrary arguments. Validate every request on the server.
When should I use latent events?
For large payloads, so the transfer is spread out instead of blocking the connection.
Need this built, not just explained?
Ten years of FiveM work, from Lua to NUI
Custom resources, React NUI, ESX / QBCore / Qbox integration, OneSync performance audits and security reviews — plus the websites and SEO around your server brand.
Related guides
- EngineeringWriting server events that cannot be abusedAny client can call any event you registered with RegisterNetEvent, with any arguments. In each handler: copy source into a local, check argument types and ranges, re-derive everything from server state (prices, amounts, rewards), verify the player can do this now (distance, job, item, cooldown), and log refusals. Events only other server scripts should use are registered with AddEventHandler alone, so clients cannot trigger them.
- EngineeringState bags in FiveM: synced data without eventsA state bag is a set of key–value pairs attached to something: GlobalState (the whole server), Player(source).state (a player) or Entity(entity).state (an entity; also LocalPlayer.state on the client). Set a value on the server and it replicates to clients that can see it; clients react with AddStateBagChangeHandler. Clients can only write their own values when the server allows it, and those writes must never be trusted.
- FrameworksCallbacks: asking the server a question and getting an answerA callback registers a named handler on one side and lets the other side call it and receive its return value. With ox_lib use lib.callback.register on the server and lib.callback.await on the client (it also works server → client). ESX uses ESX.RegisterServerCallback / ESX.TriggerServerCallback, QBCore QBCore.Functions.CreateCallback / QBCore.Functions.TriggerCallback. Validate inside callbacks exactly like events.
- Getting startedClient-side vs server-side in FiveMClient scripts run on each player’s PC and handle what that player sees and does: drawing, input, markers, animations and local effects. Server scripts run once, on your server, and own everything that has value or must be trusted: money, items, jobs, the database and permissions. The client asks; the server decides. Never trust data sent by a client without checking it.