Files
cs2-sendproxy/README.md
2026-08-10 14:23:20 +02:00

5.7 KiB

SendProxy for CS2

SendProxy is an experimental Counter-Strike 2 Metamod addon plus CounterStrikeSharp interop layer for per-recipient networked field spoofing.

The goal is similar to Source 1 SendProxy: let plugin code change the value serialized to one viewer without changing the real server entity state. For example, player A can see an entity field as one value while player B sees another value.

Current Architecture

The Metamod addon is intentionally small:

  • Hooks CServerSideClient::SendSnapshot to identify the recipient slot for the current snapshot.
  • Hooks CNetworkGameServer::PackEntity.
  • Looks up exact rules by packed entity index.
  • Temporarily writes raw bytes to the entity field.
  • Calls the original entity packer.
  • Restores the original bytes immediately.

There is no value parser or high-level rule language in Metamod. CounterStrikeSharp owns typed values, selectors, policy, and rule lifecycle.

Native rule shape:

recipient slot + entity index + schema field path = raw bytes

The native side resolves className + fieldPath to a schema offset when a rule is added. The pack hot path does not parse strings, walk entities, or query schema.

Native ABI

The addon exports a small C ABI from sendproxy.so:

extern "C" int SendProxy_SetOverride(
    int recipientSlot,
    int entityIndex,
    const char* className,
    const char* fieldPath,
    const void* value,
    int valueSize);

extern "C" bool SendProxy_RemoveOverride(int ruleId);
extern "C" void SendProxy_ClearOverrides();
extern "C" bool SendProxy_MarkDirty(int entityIndex, const char* className, const char* fieldPath);

recipientSlot may be -1 for all recipients, or 0..63 for one viewer.

SendProxy_SetOverride returns a rule ID. Keep this ID in managed code and pass it to SendProxy_RemoveOverride when the spoof is no longer wanted.

Disabled native rules are compacted on every map start.

CounterStrikeSharp Wrapper

The C# wrapper is in:

examples/CounterStrikeSharp/SendProxyNative.cs

It loads the native addon with NativeLibrary.Load and exposes byte-oriented and typed helpers. In a CSS plugin, resolve the native library from Server.GameDirectory:

string libraryPath = Path.Combine(Server.GameDirectory, "addons", "sendproxy", "bin", "linuxsteamrt64", "sendproxy.so");
var sendProxy = new SendProxyNative(libraryPath);

int ruleId = sendProxy.SetInt32(
    recipientSlot: viewer.Slot,
    entityIndex: (int)targetPawn.Index,
    className: "CBaseEntity",
    fieldPath: "m_iHealth",
    value: 42);

sendProxy.RemoveOverride(ruleId);

Use SetBytes for custom/fixed-layout fields and typed helpers such as SetInt32, SetUInt32, SetFloat, SetBool, and SetColor for common values.

Example CSS Plugin

Example source:

examples/CounterStrikeSharp/SendProxyWeaponVisibility/

It demonstrates:

  • Spoofing other players' pawn health to 42 per viewer while leaving the viewer's own health real.
  • Hiding weapon entities owned by other players with spoofed CBaseEntity::m_fEffects.
  • Coloring dropped AK-47s red for Terrorist viewers.
  • Coloring dropped M4A1-S blue for CT viewers.

This sample periodically reconciles desired rules and diffs them against active rule IDs. For production, prefer event-driven updates on player connect/disconnect/team changes/spawn/death and weapon pickup/drop/create/delete.

Building the Metamod Addon

Prerequisites:

  • AMBuild 2.2+
  • Metamod:Source
  • HL2SDK for CS2
  • funchook and generated protobuf headers as referenced by AMBuildScript

The defaults in configure.py match this workspace:

--hl2sdk-root /home/csgo/am/hl2sdk-root
--mms_path /home/csgo/am/metamod-source

Configure and build:

mkdir -p build
cd build
python3 ../configure.py --enable-debug
ambuild

The packaged addon is written to:

build/package/addons/sendproxy/
build/package/addons/metamod/sendproxy.vdf

Installing

Copy the package into the CS2 server's game/csgo/addons tree:

cp -a build/package/addons/sendproxy /home/csgo/serverfiles/game/csgo/addons/
cp -a build/package/addons/metamod/sendproxy.vdf /home/csgo/serverfiles/game/csgo/addons/metamod/

In this workspace, /home/csgo/codex/sendproxy/serverfiles is a bind-mounted target equivalent to /home/csgo/serverfiles.

Verify the native ABI exports:

nm -D /home/csgo/serverfiles/game/csgo/addons/sendproxy/bin/linuxsteamrt64/sendproxy.so \
  | rg 'SendProxy_(SetOverride|RemoveOverride|ClearOverrides|MarkDirty)'

Building the CSS Example

This container currently has the CounterStrikeSharp .NET runtime but not a .NET SDK, so the C# sample cannot be compiled here as-is.

On a machine with the .NET SDK:

cd examples/CounterStrikeSharp/SendProxyWeaponVisibility
dotnet build -c Release

Install the built CSS plugin under:

game/csgo/addons/counterstrikesharp/plugins/SendProxyWeaponVisibility/

Make sure SendProxyNative.cs is included in your CSS project or copied into your plugin source.

The example plugin expects the Metamod addon at:

game/csgo/addons/sendproxy/bin/linuxsteamrt64/sendproxy.so

Notes and Limits

  • Only fields serialized through the hooked PackEntity path are affected.
  • The real server entity state is restored immediately after packing.
  • The value byte layout must match the server schema field layout.
  • The native addon validates that valueSize is not larger than the resolved schema field size when schema size is available.
  • Rule updates should be event-driven where possible. Avoid removing and recreating many rules every tick.
  • If per-entity rule lists become large, the next native optimization is indexing by (entityIndex, recipientSlot) instead of only entityIndex.