Grand opening25% off everything with FAM-261023-261025-UL4B9this weekendAdded for you at checkout Halloween Edition36 maps, limited runlimited runShop the drop

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.

client.lua — log the raw arguments

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.

LuaJS / C#NameWhat it holdsProvenance
args[1]args[0]victimThe 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]attackerThe 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]unknownLooks like a hash. Nobody has pinned down which one.observation only
args[4]args[3]unknownReported as always 0.observation only
args[5]args[4]unknownReported as always 0.observation only
args[6]args[5]isFatal1 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]weaponHashThe 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]unknownSometimes a hash, usually 0. Thought to relate to vehicle collisions.observation only
args[9]args[8]unknownSometimes a hash, usually 0. Thought to relate to vehicle collisions.observation only
args[10]args[9]unknownReported as always 0.observation only
args[11]args[10]unknownSometimes 1, mostly 0. Guessed to involve police.observation only
args[12]args[11]isMelee or parkedCarRead 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]flag116 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.

client.lua — kills, deaths and hits

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.

server.lua — receiving it safely
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.

client.lua — damage to the car you are in
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.

baseevents

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
CEventNetworkEntityDamage

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
In practice most servers want both.

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.

CauseUnsignedSignedHexDamage type
WEAPON_BARBED_WIRE1,223,143,8001,223,143,8000x48E7B178BARBED_WIRE
WEAPON_BLEEDING2,339,582,971-1,955,384,3250x8B7333FBUNKNOWN
WEAPON_BRIEFCASE2,294,779,575-2,000,187,7210x88C78EB7UNKNOWN
WEAPON_BRIEFCASE_0228,811,03128,811,0310x01B79F17UNKNOWN
WEAPON_DROWNING4,284,007,675-10,959,6210xFF58C4FBUNKNOWN
WEAPON_DROWNING_IN_VEHICLE1,936,677,2641,936,677,2640x736F5990UNKNOWN
WEAPON_ELECTRIC_FENCE2,461,879,995-1,833,087,3010x92BD4EBBELECTRIC
WEAPON_EXHAUSTION910,830,060910,830,0600x364A29ECUNKNOWN
WEAPON_EXPLOSION539,292,904539,292,9040x2024F4E8EXPLOSIVE
WEAPON_FALL3,452,007,600-842,959,6960xCDC174B0UNKNOWN
WEAPON_FIRE3,750,660,587-544,306,7090xDF8E89EBFIRE
WEAPON_GARBAGEBAG3,794,977,420-499,989,8760xE232C28CMELEE
WEAPON_HANDCUFFS3,494,679,629-800,287,6670xD04C944DMELEE
WEAPON_HELI_CRASH341,774,354341,774,3540x145F1012FALL
WEAPON_HIT_BY_WATER_CANNON3,425,972,830-868,994,4660xCC34325EWATER_CANNON
WEAPON_RAMMED_BY_CAR133,987,706133,987,7060x07FC7D7AUNKNOWN
WEAPON_RUN_OVER_BY_CAR2,741,846,334-1,553,120,9620xA36D413EUNKNOWN

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
AttackUnsignedSignedHex
WEAPON_ANIMAL4,194,021,054-100,946,2420xF9FBAEBE
WEAPON_ANIMAL_RETRIEVER3,854,032,506-440,934,7900xE5B7DE7A
WEAPON_BIRD_CRAP1,834,887,1691,834,887,1690x6D5E2801
WEAPON_BOAR861,723,357861,723,3570x335CDADD
WEAPON_CAT3,799,318,422-495,648,8740xE274FF96
WEAPON_COUGAR148,160,082148,160,0820x08D4BE52
WEAPON_COW94,548,75394,548,7530x05A2B311
WEAPON_COYOTE1,161,062,3531,161,062,3530x453467D1
WEAPON_DEER4,106,648,222-188,319,0740xF4C67A9E
WEAPON_HAMMERHEAD_SHARK3,030,980,043-1,263,987,2530xB4A915CB
WEAPON_HEN955,837,630955,837,6300x38F8ECBE
WEAPON_KILLER_WHALE4,198,358,245-96,609,0510xFA3DDCE5
WEAPON_PIG1,205,296,8811,205,296,8810x47D75EF1
WEAPON_RABBIT2,793,925,639-1,501,041,6570xA687EC07
WEAPON_SMALL_DOG3,146,768,957-1,148,198,3390xBB8FE23D
WEAPON_TIGER_SHARK743,550,225743,550,2250x2C51AD11

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?
Because the event is raised per damage event, not per death, and a death is usually several hits in quick succession — the burst that killed the ped, then the hit that landed as it fell. Check args[6] so only the fatal one gets through, and keep a timestamp so a second fatal event inside a couple of seconds is ignored. The script on this page does both. The other duplicate source is scope: the event fires on every client that can see the damage, so if you forward it to the server without checking that args[1] is your own ped, one death arrives at the server once per nearby player.
Why is the attacker −1 or 0?
Because nothing attacked. Falls, drowning, fire and bleeding out all come through this event with no attacker entity, and the cause is in the weapon hash instead — WEAPON_FALL, WEAPON_DROWNING, WEAPON_FIRE and the rest of the damage-source list on this page. A 0 can also mean the attacking entity exists but is not streamed on your client, which is why the entity handle has to be turned into a network id before it is any use to the server.
Should I use CEventNetworkEntityDamage or baseevents?
baseevents is the easier of the two and enough for most servers: it polls IsPedFatallyInjured on your own ped, reads the killer and weapon out of NetworkGetEntityKillerOfPlayer, and fires baseevents:onPlayerDied or baseevents:onPlayerKilled on both sides for you. What it cannot tell you is anything short of death — no hit registration, no bone, no damage on NPCs or vehicles — and it costs a loop that runs every frame. The game event is the opposite trade: one handler, no polling, every scratch on every entity near you, and arguments you have to verify yourself because most of them are undocumented. Use baseevents for a death and respawn flow; use the game event when you need hit detection, injury systems, or damage on something that is not the local player.
Is the event reliable for fall damage?
Not entirely, and it is worth knowing before you build a fall-injury system on it. A long-standing Cfx.re report says that on builds from b2060 the event is not raised for fall damage unless the ped was damaged by an entity first. The usual answer is to keep a cheap health watcher alongside the handler: compare GetEntityHealth against the previous frame and treat a drop with no event as environmental. Both halves are cheap — one handler plus one comparison on a timer — and the pair is far more reliable than either alone.
Can I handle this on the server?
No. gameEventTriggered is a client event, because the game events it forwards are raised by the game engine on the machine the entities live on. Anything the server needs has to be sent up with TriggerServerEvent, and that means treating it the way you would treat any other client message: validate the shape, never accept a damage number or a kill the client has decided for itself, and resolve entities through network ids rather than the client’s local handles. A client that reports who killed it is a client that can report whatever it likes.

ALSO FREE

Tools that go with this one

Picked because they touch the same files, natives or part of the game.