Diagnose FiveM game-event payloads
Log the smallest useful, bounded observation before writing policy. A game event can represent an incoming request, a client-side observation or a completed server transition. These are not interchangeable. Never convert “I saw this event” into an automatic ban, payment or inventory grant without corroborating server state.
Use client contracts and server contracts to choose the listener. Low-level gameEventTriggered arrays and the structured server events are different APIs: a numeric slot in one is not a property of the other.
Fields worth distinguishing
| Event | Documented fields used for diagnosis | What they do not establish |
|---|---|---|
weaponDamageEvent(sender, data) | weaponType, hitGlobalId, hitGlobalIds, overrideDefaultDamage, weaponDamage, willKill | A network ID is not a server handle; willKill is not confirmed death; weaponDamage is not always the damage used. |
startProjectileEvent(sender, data) | projectileHash, weaponHash, ownerId, fire/initial position fields, targetEntity | A projectile request is not proof of impact, ownership entitlement or a player’s final aim. |
ptFxEvent(sender, data) | assetHash, effectHash, isOnEntity, entityNetId, position/offset/rotation fields | An attached effect’s offsets are not a world-space position. |
removeAllWeaponsEvent(sender, data) | pedId | The target ped ID is not the sender’s persistent account identity. |
vehicleComponentControlEvent(sender, data) | vehicleGlobalId, pedGlobalId, componentIndex, componentIsSeat, pedInSeat, request | Upstream explicitly leaves the meaning of request uncertain; it is not a safe “accepted” boolean for policy. |
respawnPlayerPedEvent(player, content) | posX, posY, posZ | A respawn notification is not permission to grant a loadout again. |
The generated server reference lists the full structured payloads. Retain unknown f*/unk* names as unknown instead of inventing meanings. vehicleComponentControlEvent and respawnPlayerPedEvent are separately documented.
A bounded server-side damage observation
Add this as server.lua in a temporary resource with server_script 'server.lua'. It is disabled unless the server owner explicitly enables its convar. It prints a count, not player identifiers, arbitrary strings, coordinates or an entire payload.
local observed, selectedWeapon = 0, nil
RegisterCommand('fd_damage_watch', function(source, args)
if source ~= 0 then return end -- Server console only.
selectedWeapon = args[1] and joaat(args[1]) or nil
observed = 0
print('[Damage lab] Observation counter reset; no requests will be cancelled.')
end, true)
AddEventHandler('weaponDamageEvent', function(sender, data)
if GetConvarInt('fd_damage_observe', 0) ~= 1 then return end
if type(data) ~= 'table' or type(data.weaponType) ~= 'number' then return end
if selectedWeapon and data.weaponType ~= selectedWeapon then return end
observed = observed + 1
if observed <= 5 then
print(('[Damage lab] sample=%d target-id-present=%s override=%s'):format(
observed, tostring(type(data.hitGlobalId) == 'number'),
tostring(data.overrideDefaultDamage == true)))
end
end)In the server console, run set fd_damage_observe 1, then optionally fd_damage_watch WEAPON_PISTOL. Trigger one controlled hit against a remotely owned test entity. After five observations, the handler keeps only the count. Finish with set fd_damage_observe 0 and stop the resource. It neither blocks damage nor broadcasts evidence to clients.
Do not use this diagnostic flag on a public server indefinitely. For a bug report, record the artifact, Legacy/Enhanced track, game build, which client owned the target, the action taken and whether server-observed health changed. This makes a reproduction more useful than a large unlabelled JSON dump.
Handle network IDs without confusing them with handles
A server-native operation needs an entity handle. For a named payload field documented as a network ID, resolve it with NetworkGetEntityFromNetworkId, check it is nonzero, and verify the entity still exists before reading state. Recheck after an asynchronous boundary. A target that vanished between dispatch and inspection is an expected race, not evidence of cheating.
Conversely, the handle given to entityCreating is already a server entity handle. Passing it through a network-ID conversion changes its meaning. For player references, distinguish server session IDs, client player indices and permanent identifiers; see network and local IDs.
Cancellation must have a documented effect
The server contract explicitly permits rejecting weaponDamageEvent. It does not follow that every event in the table has the same supported veto. A server-side gameplay rule should first be stated independently of the transport, validated against current state, and tested for false positives on normal resources.
For low-level client arrays, preserve a build-specific decoder behind a version check and a fixture captured from that exact build. An unrecognized shape should disable that diagnostic feature rather than guess an index. Prefer named native queries or a structured documented event when available. These reference pages intentionally do not publish speculative offset lists.
Related guides
Secure custom events, OneSync and ownership, profiler captures, and client diagnostics cover the surrounding workflow. These examples have not been used to certify FiveM engine behavior; they are scoped diagnostic recipes, not an anti-cheat product.