FREE TOOL · GAME EVENTS
Detecting kills and damage with CEventNetworkEntityDamage
One client handler tells you every time anything near you is hurt: who hurt it, what with, and whether it died. It is the right tool for hit registration, injury systems and kill feeds — and it has thirteen arguments, four of which anybody has actually worked out. This page gives you the working script, says plainly which arguments are guesses, and explains when baseevents is the better answer.
- 13arguments
- 4reliably known
- 8observation only
- 1disputed
- 17non-weapon causes
WHAT IT IS
A game event, forwarded to your script
The engine raises it, FiveM hands it to you, and nothing about it is documented beyond the fact that it exists.
GTA’s own AI runs on an event queue, and CEventNetworkEntityDamage is the entry on it that fires whenever a networked entity takes damage — EVENT_NETWORK_DAMAGE_ENTITY in the engine’s own event enum. FiveM forwards the whole queue to scripts through gameEventTriggered, which gives you the event name and an array of integers. The official reference says exactly that much: a name, and some integers. What the integers mean is not in it.
Three facts shape every script built on it. It is client-side only — the engine raises these events on the machine the entities live on, so there is no server-side handler to register. It fires for everything you can see, not just you, which is what makes it useful for NPC and vehicle damage and also what floods your handler. And it fires on every hit, not only fatal ones, so a death handler written without a check on the fatal flag will run on a graze.
Finding out what your build sends
Before you trust any argument list, including ours, print the real thing. Drop this in a client script, get shot, and read the console.
Worth doing once per server build. The argument layout is not contractual.
AddEventHandler('gameEventTriggered', function(name, args)
if name == 'CEventNetworkEntityDamage' then
print(name, json.encode(args))
end
end)THE ARGUMENTS
All thirteen, honestly labelled
Four are solid and in daily use. Eight are one person's observations. One has two contradictory readings in circulation.
| Lua | JS / C# | Name | What it holds | Provenance |
|---|---|---|---|---|
| args[1] | args[0] | victim | The entity that took the damage. A ped, or a vehicle when the vehicle itself was hit.The one argument everybody agrees on, and the one your filter should start with: compare it against PlayerPedId() and drop everything else. | community-reconstructed |
| args[2] | args[1] | attacker | The entity that dealt the damage. −1 when nothing did — a fall, drowning, fire.A vehicle here means the car did the hitting, not its driver. Read the driver out of seat −1 if you want to blame a person. | community-reconstructed |
| args[3] | args[2] | unknown | Looks like a hash. Nobody has pinned down which one. | observation only |
| args[4] | args[3] | unknown | Reported as always 0. | observation only |
| args[5] | args[4] | unknown | Reported as always 0. | observation only |
| args[6] | args[5] | isFatal | 1 when this hit killed the entity, 0 when it only hurt it.This is the kill flag. The event fires on every scratch, so without this check a death handler runs on flesh wounds. | community-reconstructed |
| args[7] | args[6] | weaponHash | The weapon hash. Also carries the environmental causes — falling, drowning, fire, being run over.Matches the weapon table and the damage-source list below, so you can name the cause without a lookup table of your own. | community-reconstructed |
| args[8] | args[7] | unknown | Sometimes a hash, usually 0. Thought to relate to vehicle collisions. | observation only |
| args[9] | args[8] | unknown | Sometimes a hash, usually 0. Thought to relate to vehicle collisions. | observation only |
| args[10] | args[9] | unknown | Reported as always 0. | observation only |
| args[11] | args[10] | unknown | Sometimes 1, mostly 0. Guessed to involve police. | observation only |
| args[12] | args[11] | isMelee or parkedCar | Read as a melee flag in the code people copy, and as a parked-car flag in the same thread’s own argument list.The sample handler everyone pastes calls this isMelee; the list directly above it says 1 means the entity hit a parked car. Both cannot be right, so test it on your build before you branch on it. | two readings in circulation |
| args[13] | args[12] | flag | 116 after hitting a parked car, otherwise mostly 0. | observation only |
Lua indexes the array from 1, JavaScript and C# from 0, so every index here is given both ways — mixing the two up produces an attacker where you expected a victim. The meanings are community-reconstructed: the Cfx.re thread they come from says outright that the original source has been lost. We have not added names to the arguments nobody has identified, and where the one source contradicts itself — its sample handler reads args[12] as a melee flag, its own list calls it a parked-car flag — we say so rather than pick one.
THE SCRIPT
A handler with the false positives already filtered out
Four filters, in order: is it about me, was it fatal, have I already handled this death, and was there actually an attacker.
This is the whole client side. It resolves the attacker into something a server can use — a server id for a player, a model for an NPC, the driver behind the wheel when a vehicle did the hitting — translates the environmental causes into words, and fires a non-fatal event separately so an injury system can use it.
Paste it in, add the server handler below, and you have a working death pipeline.
local CAUSES = {
[`WEAPON_FALL`] = 'fell',
[`WEAPON_DROWNING`] = 'drowned',
[`WEAPON_DROWNING_IN_VEHICLE`] = 'drowned in a vehicle',
[`WEAPON_FIRE`] = 'burned',
[`WEAPON_EXPLOSION`] = 'was caught in an explosion',
[`WEAPON_RAMMED_BY_CAR`] = 'was rammed',
[`WEAPON_RUN_OVER_BY_CAR`] = 'was run over',
[`WEAPON_HELI_CRASH`] = 'was hit by a helicopter',
[`WEAPON_ELECTRIC_FENCE`] = 'was electrocuted',
[`WEAPON_BLEEDING`] = 'bled out',
[`WEAPON_EXHAUSTION`] = 'collapsed',
[`WEAPON_HIT_BY_WATER_CANNON`] = 'was hit by a water cannon',
}
local lastFatal = 0
local function describe(entity)
if not entity or entity <= 0 or not DoesEntityExist(entity) then
return nil
end
local kind = GetEntityType(entity)
if kind == 1 then
local player = NetworkGetPlayerIndexFromPed(entity)
if player ~= -1 and NetworkIsPlayerActive(player) then
return { kind = 'player', server = GetPlayerServerId(player), ped = entity }
end
return { kind = 'ped', model = GetEntityModel(entity), ped = entity }
end
if kind == 2 then
local driver = GetPedInVehicleSeat(entity, -1)
local behind = describe(driver)
return {
kind = 'vehicle',
model = GetEntityModel(entity),
plate = GetVehicleNumberPlateText(entity),
driver = behind,
}
end
return { kind = 'object', model = GetEntityModel(entity) }
end
AddEventHandler('gameEventTriggered', function(name, args)
if name ~= 'CEventNetworkEntityDamage' then
return
end
local victim = args[1]
local attacker = args[2]
local fatal = args[6] == 1
local weapon = args[7]
if victim ~= PlayerPedId() then
return
end
if not fatal then
local found, bone = GetPedLastDamageBone(victim)
TriggerEvent('damage:hurt', {
weapon = weapon,
bone = found and bone or nil,
attacker = describe(attacker),
})
return
end
local now = GetGameTimer()
if now - lastFatal < 2000 then
return
end
lastFatal = now
local killer = describe(attacker)
local selfInflicted = attacker == victim or killer == nil
TriggerServerEvent('damage:died', {
weapon = weapon,
cause = CAUSES[weapon],
selfInflicted = selfInflicted,
killerType = killer and killer.kind or nil,
killerServerId = killer and killer.server or nil,
killerNet = killer and killer.ped and NetworkGetEntityIsNetworked(killer.ped)
and NetworkGetNetworkIdFromEntity(killer.ped) or nil,
coords = GetEntityCoords(victim),
})
end)The four filters, and why each one is there
- Is it my ped? The event fires on every client that can see the damage, so one death reaches the server once per nearby player if you do not check. Comparing args[1] against PlayerPedId() makes each client responsible for itself and nothing else.
- Was it fatal? args[6] is 1 only on the hit that killed. Without it a death handler runs on every bullet that lands.
- Have I handled this death already? A death is usually several hits in quick succession, and the ped is not dead yet when the event arrives, so IsEntityDead cannot be used as the guard. A timestamp can: a second fatal event inside two seconds is the same death.
- Was there an attacker at all? args[2] is −1 for a fall, drowning or fire, and can be 0 when the attacking entity exists but is not streamed on your client. Either way there is nobody to credit, and the cause is in the weapon hash instead.
The server side
Everything above runs on a client, which means everything it reports is a claim. The server handler validates the shape, resolves the claimed killer against a real connected player, and — this is the part that matters — never takes a damage figure or a kill from the client as fact for anything that affects score or money.
RegisterNetEvent('damage:died', function(report)
local src = source
if type(report) ~= 'table' or type(report.weapon) ~= 'number' then
return
end
local killer = nil
if type(report.killerServerId) == 'number' and GetPlayerName(report.killerServerId) then
killer = report.killerServerId
end
print(('%s died · weapon %d · killer %s'):format(
GetPlayerName(src) or src,
report.weapon,
killer and GetPlayerName(killer) or (report.cause or 'unknown')
))
end)Vehicle damage
The same event covers vehicles: when the car itself is hit rather than the person in it, args[1] is the vehicle. Check the entity type and compare it against the vehicle the player is in, then read the health values from the engine rather than trying to work damage out of the event.
AddEventHandler('gameEventTriggered', function(name, args)
if name ~= 'CEventNetworkEntityDamage' then
return
end
local victim = args[1]
if GetEntityType(victim) ~= 2 then
return
end
local mine = GetVehiclePedIsIn(PlayerPedId(), false)
if victim ~= mine then
return
end
TriggerEvent('damage:vehicle', {
body = GetVehicleBodyHealth(victim),
engine = GetVehicleEngineHealth(victim),
petrol = GetVehiclePetrolTankHealth(victim),
weapon = args[7],
attacker = args[2],
})
end)THE ALTERNATIVE
baseevents, and when to use it instead
One of these is a documented resource that ships with the server. The other tells you things it cannot.
The resource that comes with cfx-server-data. It runs a thread on every frame, watches IsPedFatallyInjured on your own ped, and when that flips it reads the killer and weapon out of NetworkGetEntityKillerOfPlayer.
- Fires baseevents:onPlayerDied, onPlayerKilled and onPlayerWasted
- Triggers on the client and the server for you
- Hands you the killer’s server id, ped type, vehicle and seat
- Documented, stable, and already running on most servers
- Local player only — nothing about NPCs or vehicles
- Death only — no hits, no bones, no injuries
- Costs a thread that wakes every frame
The raw game event. One handler, no polling, and everything the engine reports about damage in your scope.
- Every hit, fatal or not, with the weapon hash
- Works on NPCs, vehicles and other players, not just you
- Pairs with GetPedLastDamageBone for hit location
- No thread — it costs nothing until something is damaged
- Most arguments are undocumented guesses
- Fires on every nearby client, so you must filter by ped
- Not reliable for fall damage on its own — see the b2060 report
Use baseevents for the death and respawn flow, because it is documented and it already works. Add the game event when you need something it cannot give you: hit registration, a wound system that cares which limb was hit, damage on an NPC, or damage to a vehicle. They do not conflict — one polls your own health, the other listens for damage in your scope — and running both costs less than making either do the other’s job.
REFERENCE
What the weapon hash says when no weapon was involved
17 entries in the game's weapon file are not weapons at all — they are causes of death, and they arrive in args[7] exactly like a pistol would.
| Cause | Unsigned | Signed | Hex | Damage type |
|---|---|---|---|---|
| WEAPON_BARBED_WIRE | 1,223,143,800 | 1,223,143,800 | 0x48E7B178 | BARBED_WIRE |
| WEAPON_BLEEDING | 2,339,582,971 | -1,955,384,325 | 0x8B7333FB | UNKNOWN |
| WEAPON_BRIEFCASE | 2,294,779,575 | -2,000,187,721 | 0x88C78EB7 | UNKNOWN |
| WEAPON_BRIEFCASE_02 | 28,811,031 | 28,811,031 | 0x01B79F17 | UNKNOWN |
| WEAPON_DROWNING | 4,284,007,675 | -10,959,621 | 0xFF58C4FB | UNKNOWN |
| WEAPON_DROWNING_IN_VEHICLE | 1,936,677,264 | 1,936,677,264 | 0x736F5990 | UNKNOWN |
| WEAPON_ELECTRIC_FENCE | 2,461,879,995 | -1,833,087,301 | 0x92BD4EBB | ELECTRIC |
| WEAPON_EXHAUSTION | 910,830,060 | 910,830,060 | 0x364A29EC | UNKNOWN |
| WEAPON_EXPLOSION | 539,292,904 | 539,292,904 | 0x2024F4E8 | EXPLOSIVE |
| WEAPON_FALL | 3,452,007,600 | -842,959,696 | 0xCDC174B0 | UNKNOWN |
| WEAPON_FIRE | 3,750,660,587 | -544,306,709 | 0xDF8E89EB | FIRE |
| WEAPON_GARBAGEBAG | 3,794,977,420 | -499,989,876 | 0xE232C28C | MELEE |
| WEAPON_HANDCUFFS | 3,494,679,629 | -800,287,667 | 0xD04C944D | MELEE |
| WEAPON_HELI_CRASH | 341,774,354 | 341,774,354 | 0x145F1012 | FALL |
| WEAPON_HIT_BY_WATER_CANNON | 3,425,972,830 | -868,994,466 | 0xCC34325E | WATER_CANNON |
| WEAPON_RAMMED_BY_CAR | 133,987,706 | 133,987,706 | 0x07FC7D7A | UNKNOWN |
| WEAPON_RUN_OVER_BY_CAR | 2,741,846,334 | -1,553,120,962 | 0xA36D413E | UNKNOWN |
Every one of these is a real CWeaponInfo entry in the game’s own weapons.meta, which is why the engine can hand you its hash as a weapon. The same values come back from GetPedCauseOfDeath. Hashes are unsigned 32-bit, so Lua prints the larger ones negative — both forms are the same bits, so a comparison matches either way. Writing the name in backticks, as the script above does, lets the Lua runtime hash it at compile time and saves you picking a form at all.
The 16 animal attacks, which arrive the same way
| Attack | Unsigned | Signed | Hex |
|---|---|---|---|
| WEAPON_ANIMAL | 4,194,021,054 | -100,946,242 | 0xF9FBAEBE |
| WEAPON_ANIMAL_RETRIEVER | 3,854,032,506 | -440,934,790 | 0xE5B7DE7A |
| WEAPON_BIRD_CRAP | 1,834,887,169 | 1,834,887,169 | 0x6D5E2801 |
| WEAPON_BOAR | 861,723,357 | 861,723,357 | 0x335CDADD |
| WEAPON_CAT | 3,799,318,422 | -495,648,874 | 0xE274FF96 |
| WEAPON_COUGAR | 148,160,082 | 148,160,082 | 0x08D4BE52 |
| WEAPON_COW | 94,548,753 | 94,548,753 | 0x05A2B311 |
| WEAPON_COYOTE | 1,161,062,353 | 1,161,062,353 | 0x453467D1 |
| WEAPON_DEER | 4,106,648,222 | -188,319,074 | 0xF4C67A9E |
| WEAPON_HAMMERHEAD_SHARK | 3,030,980,043 | -1,263,987,253 | 0xB4A915CB |
| WEAPON_HEN | 955,837,630 | 955,837,630 | 0x38F8ECBE |
| WEAPON_KILLER_WHALE | 4,198,358,245 | -96,609,051 | 0xFA3DDCE5 |
| WEAPON_PIG | 1,205,296,881 | 1,205,296,881 | 0x47D75EF1 |
| WEAPON_RABBIT | 2,793,925,639 | -1,501,041,657 | 0xA687EC07 |
| WEAPON_SMALL_DOG | 3,146,768,957 | -1,148,198,339 | 0xBB8FE23D |
| WEAPON_TIGER_SHARK | 743,550,225 | 743,550,225 | 0x2C51AD11 |
A shark, a coyote or a cougar killing a player comes through as its own weapon hash. Worth having if your kill feed is ever going to meet the wildlife.
QUESTIONS
About the damage event
Why does my death handler fire several times for one death?
Why is the attacker −1 or 0?
Should I use CEventNetworkEntityDamage or baseevents?
Is the event reliable for fall damage?
Can I handle this on the server?
MORE
Related references
The weapon hash in args[7] is the one in that table, with its damage and group alongside.
Weapon names and hashesA flat lookup when all you need is the number for a name.
All the free toolsThe map, the animation lists, the bitmask calculators and every reference table.
ALSO FREE
Tools that go with this one
Picked because they touch the same files, natives or part of the game.