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

178 lines
5.7 KiB
Markdown

# 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 `Server.GameDirectory`:
```csharp
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:
```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`.