Skip to Content
ScriptingEvents

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:

  1. Network Graph: Use netgraph in console to see bandwidth spikes.
  2. Event Logging:
    -- Global event listener for debugging (Development only) AddEventHandler('myresource:debug', function(...) print('Event triggered:', ...) end)
  3. Resource Monitor: Check resmon for high CPU usage during events.

Performance Considerations

  • Payload Size: Keep data small. Don’t send huge tables or HTML strings.
  • Frequency: Avoid TriggerClientEvent inside Update loops (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