Skip to content

NodeMod: nodemod-goldsrc

CSRetro uses a vendored, CSRetro-modified fork of nodemod-goldsrc as its TypeScript/JavaScript plugin runtime (3rdparty/nodemod/). The full source tree is committed in-tree (no submodules).

Vendored tree revision in this build: see VENDOR.md and Vendor table (VENDOR.md).

API and events (CSRetro-oriented)

NodeMod exposes a TypeScript API via @nodemod/core. Runtime layout and ./tools.sh integration are documented in NodeMod – CSRetro. Cross-component reminders (Metamod, YaPB, engine) live in Cross-component reference.

Core services (nodemodCore)

The default export from @nodemod/core groups services used by plugins:

Service Role
cmd Register client/server commands, parse arguments, client command hooks.
menu Built-in menu pages, pagination, key handling.
msg User messages and engine message helpers.
cvar Read/write engine and game CVars (wrappers around native cvars).
file Virtual filesystem and path helpers.
util Chat, printing, misc helpers.
player Player iteration, wrappers, utilities.
entity Entity wrappers, touch/use helpers.
trace Traces and collision.
events Thin helpers over nodemod.on (once, waitFor, throttling).
resource Precache / model sound resource helpers.
sound Sound helpers.

Implementation reference: 3rdparty/nodemod/packages/core/src/index.ts.

Global nodemod object

The native binding exposes nodemod.on, nodemod.cwd, nodemod.gameDir, nodemod.players, nodemod.mapname, nodemod.frametime, etc. Type definitions: 3rdparty/nodemod/packages/core/types/index.d.ts.

Events (nodemod.on)

Subscribe with nodemod.on(eventName, handler) (or nodemodCore.events.* helpers). The authoritative list of event names and callback signatures is the generated EventCallbacks interface in:

3rdparty/nodemod/packages/core/types/events.d.ts

Event families include:

Family Examples (not exhaustive)
DLL / game dllGameInit, dllSpawn, dllThink, dllClientConnect, dllClientPutInServer, dllClientCommand, dllServerActivate, dllStartFrame, dllPlayerPreThink, dllPlayerPostThink
Engine engPrecacheModel, engMessageBegin, engSetModel, … (precache, messaging, sound, strings)
Post- hooks postDllSpawn, postEngPrecacheModel, … (run after original game code)

Use setMetaResult / MRES where you need to supersede or skip original behaviour (see type definitions and native examples under packages/core/src/).

Configuration and admin files (runtime)

Path (under cstrike/addons/nodemod/) Purpose
configs/plugins.ini Which compiled plugins load at startup.
configs/users.ini Admin user entries (used by @nodemod/admin).
plugins/dist/*.plugin.js Compiled plugin output consumed at runtime.
logs/nodemod-build.log Build log from the npm pipeline (when enabled).

Vendored TypeScript sources for the admin bundle live under 3rdparty/nodemod/packages/admin/src/ (for example csr_botmenu.plugin.ts).

What is NodeMod?

NodeMod is a Metamod plugin that embeds a full Node.js runtime into the GoldSrc/Xash3D server process. This enables server plugins to be written in TypeScript or JavaScript instead of C++, with access to all engine and game DLL functions.

The original concept was created by TheEVolk (Maksim Nikiforov). The modern version used in CSRetro is a complete modernization by stevenlafl (Steven Linn), including Node.js v24, all engine/DLL bindings, npm packages, and compiled distributions.

Important: NodeMod requires Metamod to be loaded first — it is a Metamod plugin, not a standalone component.

Plugin architecture

addons/nodemod/
├── dlls/
│   └── libcsr_nodemod.so      ← Metamod plugin (embeds Node.js runtime; legacy name: libnodemod.so)
├── configs/
│   └── plugins.ini            ← NodeMod plugin list
└── plugins/                   ← TypeScript/JS plugin workspace
    ├── package.json
    ├── node_modules/
    │   └── @nodemod/
    │       ├── core/          ← Type-safe engine/DLL API
    │       └── admin/         ← Admin system (AMX Mod X port)
    ├── packages/
    │   └── admin/             ← symlink → node_modules/@nodemod/admin
    ├── src/                   ← Plugin source (.plugin.ts files)
    └── dist/                  ← Compiled plugin output (.plugin.js)

@nodemod/core

@nodemod/core provides the TypeScript type definitions and helper utilities for all engine and game DLL functions. It is the foundation every NodeMod plugin builds on.

Key modules: command system, menu system, player management, entity system, event handling, CVar management, file system, trace/ray system.

@nodemod/admin

@nodemod/admin is a TypeScript port of the classic AMX Mod X admin plugin suite, adapted for NodeMod. It provides:

  • Admin authentication (SteamID, IP, name-based)
  • Admin commands: kick, ban, slap, slay, map change, cvar control
  • Voting system (map vote, kick vote)
  • Player and map management menus
  • Multi-language support
  • Access flag system (ADMIN_KICK, ADMIN_BAN, ADMIN_MAP, etc.)

CSRetro: csr_botmenu plugin

CSRetro ships a custom csr_botmenu.plugin.ts (part of @nodemod/admin) that provides an in-game bot management menu. After build:full or install:addons it is compiled and available via the p key binding.

Writing a plugin

All plugins use the .plugin.ts extension and must be placed in plugins/src/:

// plugins/src/myplugin.plugin.ts
import nodemodCore from '@nodemod/core';
import { BasePlugin, Plugin, PluginMetadata } from '@nodemod/admin';

class MyPlugin extends BasePlugin implements Plugin {
    readonly metadata: PluginMetadata = {
        name: 'My Plugin',
        version: '1.0.0',
        author: 'Your Name',
        description: 'Example plugin'
    };

    constructor(pluginName: string) {
        super(pluginName);
        this.registerCommand('my_cmd', 0, 'Does something',
            (entity, args) => nodemodCore.serverPrint('Hello!\n'));
    }
}

export default MyPlugin;

After editing, rebuild with:

./tools.sh install:addons   # re-runs npm install + npm run build

Building / installation

NodeMod is installed automatically as part of build:full:

./tools.sh build:full          # Full pipeline: builds engine + client + NodeMod + admin npm
./tools.sh install:addons      # Incremental: re-install NodeMod + rebuild TypeScript plugins
ENABLE_NODEMOD_PIPELINE=0 ./tools.sh build:full   # Skip npm/TS step

If libcsr_nodemod.so (or legacy libnodemod.so) is missing when run:game or run:debug is launched, it is downloaded or built automatically (lazy install).

Engine shutdown and V8

Runtime: NodeMod and @nodemod/admin are not broken when you see [Admin] Loaded … plugins and amx_plugins lists entries — that output means the stack is alive.

Quit: Older teardown order skipped MultiIsolatePlatform::DisposeIsolate() after FreeIsolateData(), so V8::Dispose() could hit a V8 CHECK such as group->reference_count_.load() == 1 inside Meta_Detach. Current CSRetro NodeImpl::Stop() follows the same order as node::NodeMainInstance (free isolate data, then DisposeIsolate).

If you still see the assertion, you are likely on a prebuilt libnodemod.so tarball that predates the fix — rebuild/install NodeMod from source (libnode_fat.a present) or wait for a refreshed binary that includes this teardown.

Troubleshooting

Problem Solution
libcsr_nodemod.so / libnodemod.so missing Run ./tools.sh install:addons; check NODEMOD_SKIP_BUILD, NODEMOD_SKIP_NATIVE, NODEMOD_SKIP_RELEASE_DL
V8 assertion on exit Should be fixed in-tree (DisposeIsolate after FreeIsolateData); if it persists, replace tarball libnodemod.so with a native build — see Engine shutdown and V8
npm build fails Ensure Node.js 18+ and npm are installed; run npm install in game-test/cstrike/addons/nodemod/plugins/
Plugin not loading Check logs/nodemod-build.log; verify configs/plugins.ini entry
meta list shows NodeMod as "not loaded" Check that Metamod itself is loaded (meta version); verify plugins.ini path

Attribution

  • Original concept: TheEVolk (Maksim Nikiforov)
  • Complete modernization (Node.js v24, all bindings, npm, distributions): stevenlafl (Steven Linn)
  • C++17 modernization and build system: SNMetamorph
  • samp-node inspiration: iAmir (Amyr Aahmady)
  • @nodemod/admin TypeScript port: Steven Linn (stevenlafl), based on the original AMX Mod X admin plugins (OLO, AMX Mod X Development Team)

See credits for full attribution.