# 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: ```text 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`: ```cpp 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: ```text 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 the normal addons layout: ```csharp string addonsDirectory = Path.GetFullPath(Path.Combine(ModuleDirectory, "..", "..", "..")); string libraryPath = Path.Combine(addonsDirectory, "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: ```text 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: ```text --hl2sdk-root /home/csgo/am/hl2sdk-root --mms_path /home/csgo/am/metamod-source ``` Configure and build: ```bash mkdir -p build cd build python3 ../configure.py --enable-debug ambuild ``` The packaged addon is written to: ```text build/package/addons/sendproxy/ build/package/addons/metamod/sendproxy.vdf ``` ## Installing Copy the package into the CS2 server's `game/csgo/addons` tree: ```bash 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: ```bash 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: ```bash cd examples/CounterStrikeSharp/SendProxyWeaponVisibility dotnet build -c Release ``` Install the built CSS plugin under: ```text 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: ```text 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`.