Skip to content

ns-killfeed โ€‹

A two-way verified killfeed for RedM with a draggable NUI overlay, per-player visual themes, weapon-specific icons, and a spoof-resistant kill-confirmation pipeline. Drop-in friendly across VORP, RSG-Core and RedEM:RP through ns-lib.

Each player picks the look they want from the settings panel; the server never trusts a single client's word for a kill.


Table of Contents โ€‹


Highlights โ€‹

  • Four themes, swappable on the fly โ€” Telegraph Wire, Wanted Ledger (default), Wild Headline, Tally Marks. Same data, four visual takes.
  • 23 weapon-specific icons โ€” every cattleman, every shotgun, every thrown bottle gets the right silhouette. Falls back to a category SVG for environment and fist kills.
  • Spoof-resistant pairing โ€” both the victim and the killer must report, inside a configurable window. A lone victim can never fabricate a kill in strict mode; fallback mode pings the alleged killer for a one-shot ack when the killer's report is missing.
  • Per-player UI โ€” drag to position, scale 0.5ร—โ€“1.5ร—, opacity, max rows, per-row lifetime, fade-out duration, theme selection. Everything is KVP-persisted on the player's client.
  • Server-side validation โ€” death check on every suicide claim, two rate limits (general + extended suicide cooldown), source capture on every net event, ack timeouts on fallback pings.
  • Cross-framework via ns-lib โ€” no direct VORP/RSG/RedEM calls. The resource works on any of the three with no code changes.
  • Cross-resource API โ€” admin tools, deathmatch resources or duel scripts can push rows directly with exports['ns-killfeed']:Push(...).
  • Suicide rendering โ€” fall, drown, fire, bleed-out are emitted as italicised single-name rows with an environment icon.

Themes โ€‹

All four themes render the same row data; you swap the look from the settings panel without restarting anything.

ThemeVibeLayout
telegraphBrutalist mono terminal[HH:MM] KILLER <icon> ยป VICTIM
ledger (default)Editorial parchment, two-lineLine 1: <icon> KILLER
Line 2: killed VICTIM ยท clean shot
headlineOld newspaper bold serifHeadline: KILLER FELLS VICTIM
Subhead: <icon> ยท clean shot
tallyDark leather + brass, compact 32pxKILLER <icon> โ–ธ VICTIM

Each theme has its own CSS file under ui/src/themes/<theme>.css and a matching <Theme>Row.jsx component. Adding a new theme is a self-contained change: drop a fifth pair of files and append the slug to Config.AvailableThemes.


Weapon icons โ€‹

Every entry in shared/weapons.lua carries three pieces of information:

lua
W[`weapon_revolver_schofield`] = {
    label    = 'Schofield Revolver',
    category = 'revolver',          -- colour hint for the row
    icon     = 'schofield_revolver' -- maps to ui/src/assets/weapons/<icon>.png
}

The 23 icon slugs ship with the resource:

single_action_revolver   double_action_revolver   schofield_revolver
volcanic_pistol          semi_auto_pistol_1       semi_auto_pistol_2
mauser_pistol            sawed_off_shotgun        pump_action_shotgun
lever_action_rifle       bolt_action_rifle        repeater_carbine
springfield_rifle        bow                      tomahawk
hunting_knife            knuckle_knife            machete
lasso                    dynamite                 throwing_knife
throwing_hatchet         fire_bottle

Multiple weapons share the same icon when they belong to the same visual family (e.g. Cattleman, Vaquero and Navy all use single_action_revolver; all bolas use lasso). Weapons with category = 'env' or 'fist' and no icon field fall back to a vector SVG drawn in WeaponIcon.jsx, so kills from weapon_fall, weapon_drowning, weapon_unarmed etc. always render something sensible.

To swap an icon, drop a replacement PNG into ui/src/assets/weapons/ keeping the same slug, then run npm run build.


Two-way verification โ€‹

Killfeeds are a classic spoofing target โ€” a single client can claim "I just killed X" without anyone dying. ns-killfeed pairs two independent reports on the server before broadcasting.

   Victim client         Server                     Killer client
   โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€         โ”€โ”€โ”€โ”€โ”€โ”€                     โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
   I died โ†’ killer S     โ”
   weapon W              โ”‚
   โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ–บ  pending[victim] = { killer S, W, ts }
                         โ”‚
   "Killer S killed me"  โ”‚
                         โ”‚
   โ”Œโ”€ I killed victim    โ—„โ”€โ”€โ”€โ”€ (killer client sees victim die)
   โ”‚  with W
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ  pending[victim].killerReported = true
                         โ”‚      โ†“
                         โ”‚      both flags? โ†’ broadcast
                         โ”‚
   pending expires       โ”‚
   without killer ack:   โ”‚
   โ”€โ”€ strict   โ†’ drop    โ”‚
   โ”€โ”€ soft     โ†’ emit    โ”‚      (flagged unconfirmed)
   โ”€โ”€ fallback โ†’ ping โ”€โ”€โ”€โ”€โ”€โ”€โ–บ  did you kill victim S?
                         โ”‚ โ—„โ”€โ”€โ”€โ”€โ”€โ”€ yes / no
                         โ”‚      yes โ†’ broadcast, no โ†’ drop

Match modes โ€‹

Config.MatchModeBehaviour
strictBoth reports required within MatchWindow. Drops legit kills on packet loss or DC.
softVictim report alone is enough. Not spoof-proof โ€” trusted/small servers only.
fallback (default)Try strict; if only the victim reported, server pings the alleged killer for a one-shot ack within FallbackPingTimeout. Best balance.

Suicide reports skip pairing: when killerSrc == victimSrc or killerSrc == 0 the server confirms the reporter is actually dead (IsEntityDead(GetPlayerPed(src))), enforces a 30-second per-player cooldown, and broadcasts.


Installation โ€‹

Requirements โ€‹

  • A running RedM server with one of:
    • VORP core
    • RSG-Core
    • RedEM:RP
  • ns-lib loaded before this resource (cross-framework abstraction layer)
  • Node.js + npm โ€” only required to build the NUI; not needed at runtime

Steps โ€‹

  1. Drop the resource into your resources/[Scripts]/ns-killfeed folder.
  2. Build the NUI (one-time, or when you change UI source):
    bash
    cd ns-killfeed/ui
    npm install
    npm run build
    This emits the production bundle to ns-killfeed/html/, which fxmanifest.lua serves to the game client.
  3. In your server.cfg, make sure ns-lib starts before this resource:
    cfg
    ensure ns-lib
    ensure ns-killfeed
  4. Join the server and press F10 (or run /killfeed) to open the settings panel.

Configuration โ€‹

All knobs live in config.lua. Player-tunable UI defaults are overridden per-player via KVP once the player saves settings.

Verification โ€‹

KeyDefaultMeaning
Config.MatchMode'fallback'strict | soft | fallback
Config.MatchWindow3000ms โ€” pair must arrive within this
Config.FallbackPingTimeout1500ms โ€” killer must ack the fallback ping by this

Scope โ€‹

KeyDefaultMeaning
Config.ShowPvPtruebroadcast player-vs-player kills
Config.ShowSuicidetruebroadcast self / environment deaths
Config.ShowNpcKillfalseplayer killed by an NPC
Config.ShowEnvKillfalsededicated environmental death lines (future)

Rate limit โ€‹

KeyDefaultMeaning
Config.MinReportInterval800ms between victim reports per source

UI defaults (overridable per-player) โ€‹

KeyDefaultMeaning
Config.DefaultPosition{x=78, y=4}% of viewport (top-right corner by default)
Config.DefaultScale1.00.5 .. 1.5
Config.DefaultOpacity0.920.3 .. 1.0
Config.MaxRows4visible rows
Config.FadeMs5000per-row visible lifetime
Config.FadeOutMs600fade transition duration
Config.DefaultTheme'ledger'telegraph | ledger | headline | tally
Config.AvailableThemes(all four)list shown in the settings UI picker

Keybinds & debug โ€‹

KeyDefaultMeaning
Config.SettingsKey'F10'default keybind (rebindable in-game)
Config.SettingsKeyMapping'killfeedsettings'RegisterKeyMapping ID
Config.Debugtrueenables /killfeed_test + log output

Messages (English, no i18n) โ€‹

Config.Messages holds the strings shown to the player (suicide line, settings open/saved toast, unknown-weapon label). Edit in-place if you want custom wording.


Player commands & keybinds โ€‹

CommandEffect
/killfeedOpen the settings panel (same as the keybind)
F10 (default)Open the settings panel โ€” rebindable from the in-game key map
/killfeed_testSpawn four demo rows (cattleman / sawed-off / carcano / fall). Only available while Config.Debug = true.

The keybind is registered via RegisterKeyMapping, so each player can rebind it from the RedM key-mapping menu (Settings โ†’ Key Bindings โ†’ FiveM/RedM).


Settings panel & persistence โ€‹

Open the panel with the keybind or /killfeed. While the panel is open:

  • The feed shows four preview rows so you can see what the chosen theme
    • scale + opacity actually look like.
  • The feed becomes draggable: click-and-hold anywhere on the killfeed anchor to drop it elsewhere on the screen. Live preview, no commit until you press Save.
  • Adjust theme, scale, opacity, max rows, fade duration.
  • Save commits everything to KVP and closes the panel.
  • Cancel / ESC reverts to the previous committed settings.

KVP key (single blob, JSON-encoded):

ns-killfeed:settings

To wipe a player's settings (debug): clear that KVP key from their client and rejoin.


Cross-resource API โ€‹

Other resources โ€” admin menus, deathmatch arenas, duel scripts, custom RP events โ€” can push rows directly. The server-side export skips the pairing step (the caller is trusted).

lua
-- Force a row from another resource.
exports['ns-killfeed']:Push(killerSrc, victimSrc, weaponHash, 'pvp')
exports['ns-killfeed']:Push(killerSrc, victimSrc, weaponHash, 'pvp', true)  -- headshot
exports['ns-killfeed']:Push(victimSrc, victimSrc, weaponHash, 'suicide')
ArgTypeNotes
killerSrcnumberPlayer server-id, or 0 / victimSrc for suicide
victimSrcnumberPlayer server-id of the victim
weaponHashnumber (uint32)Use a backtick hash literal: `weapon_revolver_cattleman`
killType'pvp' | 'suicide'Suicide rows never render a headshot badge
isHeadshotboolean (opt)Defaults to false

Anything you push appears on every client respecting their personal theme and UI settings.


File structure โ€‹

ns-killfeed/
โ”œโ”€โ”€ fxmanifest.lua
โ”œโ”€โ”€ config.lua
โ”œโ”€โ”€ README.md
โ”œโ”€โ”€ shared/
โ”‚   โ”œโ”€โ”€ utils.lua
โ”‚   โ””โ”€โ”€ weapons.lua            โ† hash โ†’ { label, category, icon }
โ”œโ”€โ”€ client/
โ”‚   โ”œโ”€โ”€ main.lua               โ† death watcher, victim reporter
โ”‚   โ”œโ”€โ”€ nui.lua                โ† inbound row pusher, /killfeed_test
โ”‚   โ”œโ”€โ”€ settings.lua           โ† KVP load/save, settings panel bridge
โ”‚   โ””โ”€โ”€ command.lua            โ† /killfeed + RegisterKeyMapping
โ”œโ”€โ”€ server/
โ”‚   โ”œโ”€โ”€ verify.lua             โ† pairing window, fallback ping, rate limits
โ”‚   โ””โ”€โ”€ main.lua               โ† net events, Push export
โ”œโ”€โ”€ html/                      โ† built NUI (git-ignored if you prefer)
โ”‚   โ”œโ”€โ”€ index.html
โ”‚   โ””โ”€โ”€ assets/
โ”‚       โ”œโ”€โ”€ index-*.js
โ”‚       โ”œโ”€โ”€ index-*.css
โ”‚       โ”œโ”€โ”€ <weapon>-<hash>.png   (23 icons)
โ”‚       โ””โ”€โ”€ *.woff / *.woff2      (Rye, IM Fell English)
โ””โ”€โ”€ ui/                        โ† React + Vite source
    โ”œโ”€โ”€ package.json
    โ”œโ”€โ”€ vite.config.js
    โ””โ”€โ”€ src/
        โ”œโ”€โ”€ App.jsx
        โ”œโ”€โ”€ main.jsx
        โ”œโ”€โ”€ components/        โ† KillFeed, KillRow, SettingsPanel, DragAnchor
        โ”œโ”€โ”€ themes/            โ† *.css + *Row.jsx for the four themes
        โ”œโ”€โ”€ icons/             โ† WeaponIcon.jsx + SkullBadge.jsx
        โ”œโ”€โ”€ hooks/             โ† useNuiEvent
        โ”œโ”€โ”€ lib/               โ† nui.js (NUI fetch bridge)
        โ”œโ”€โ”€ styles/            โ† global.css
        โ””โ”€โ”€ assets/
            โ””โ”€โ”€ weapons/       โ† 23 source PNGs

Building the NUI โ€‹

bash
cd ui
npm install
npm run build      # โ†’ ../html/ (production bundle, Vite hashes the assets)

For UI development:

bash
npm run dev        # opens localhost:5175 in the browser

The NUI no-ops its fetch('https://ns-killfeed/...') calls when it is not running inside the RedM client (see ui/src/lib/nui.js), so theme/layout work in a normal browser is safe โ€” drag-positioning, theme picker, and live preview all work without a server.

When you change any file under ui/src/, you must npm run build again and restart the resource (or refresh; ensure ns-killfeed) for the changes to appear in-game.


Troubleshooting โ€‹

SymptomLikely cause / fix
Resource fails to startns-lib not loaded first. Add ensure ns-lib above ensure ns-killfeed.
Settings panel won't openAnother resource is grabbing F10. Rebind from the in-game key map or change Config.SettingsKey.
Kills never appearLikely strict mode + lossy network. Switch Config.MatchMode to 'fallback'.
Suicide rows spammedLower Config.MinReportInterval or check that the client isn't double-reporting. The 30-second per-player suicide cooldown is hard-coded server-side.
Wrong icon for a custom weaponAdd the hash to shared/weapons.lua with the matching icon slug.
unknown weapon labelThe hash isn't in weapons.lua. The row still renders, just with a hex placeholder label.
Icons look pixelatedobjectFit: contain keeps aspect ratio. Bump Config.DefaultScale or row size in the theme CSS.
/killfeed_test does nothingConfig.Debug = false. Set it to true and restart.
Drag handle never appearsThe drag anchor only activates while the settings panel is open. Open the panel first.

Discord โ€‹

Updates, bug reports, and other NativeScriptsDev releases: https://discord.gg/UyyngemnF8

Released under the MIT License.