cs2-gsi
English · 简体中文
Async Counter-Strike 2 Game State Integration listener for Rust.
A Rust port (and re-design) of
antonpup/CounterStrike2GSI.
use ;
async
Why this crate?
CS2 ships a Game State Integration feature that POSTs JSON documents about the live match to whichever HTTP endpoint you configure. Doing anything useful with those payloads requires four pieces:
- An HTTP listener.
- A model that mirrors the JSON CS2 actually sends (with all its quirks — numeric fields encoded as strings, casing inconsistencies, optional sub-objects).
- A diff engine that turns snapshots into actionable events
(
PlayerDied,BombPlanted,RoundConcluded,KillFeed, ...). - A way to drop the right
gamestate_integration_*.cfginto the right place so CS2 actually starts pushing.
cs2-gsi does all four — typed, async, and with a single clean API surface.
| Feature | Status |
|---|---|
| HTTP listener (hyper 1.x + tokio) | ✅ |
Strongly typed GameState model |
✅ |
40+ derived events with previous/new |
✅ |
Synthesised KillFeed events |
✅ |
Auto-write gamestate_integration_*.cfg |
✅ |
Steam library + appmanifest_730.acf discovery (Win/Linux/macOS) |
✅ |
auth { token "..." } blocks |
✅ |
| Graceful start/stop, hot handler registration | ✅ |
Install
[]
= "0.1"
= { = "1", = ["macros", "rt-multi-thread", "signal"] }
Cargo features (all enabled by default):
| Feature | Purpose |
|---|---|
cfg-writer |
GsiCfg builder + writer for gamestate_integration_*.cfg |
steam-discover |
Locate the CS2 install through Steam's libraryfolders.vdf |
Disable both for a tiny build that only contains the HTTP listener and the data model:
= { = "0.1", = false }
Architecture
┌────────────┐ POST JSON ┌──────────────────┐
│ CS2 client │───────────────▶│ GameStateListener│
└────────────┘ │ (this crate) │
│ • hyper server │
│ • parse → State│
│ • diff w/ prev │
└────────┬─────────┘
│ typed events
┌────────────────────────┼─────────────────────────┐
▼ ▼ ▼
PlayerDied{..} RoundPhaseUpdated{..} KillFeed{killer,victim,..}
PlayerGotKill{..} BombPlanted{..} ...
Every payload is parsed into a [GameState], compared against the previous
one, and turned into a stream of typed events. Handlers fire synchronously
on the listener's tokio task (matching the upstream library's behaviour) — if
your handler needs to do heavy work, tokio::spawn from inside it.
Subscribing to events
use ;
let gsl = new;
// Strongly typed callbacks.
gsl.on;
gsl.on;
gsl.on;
// Or one catch-all handler for every event:
gsl.on_any;
A non-exhaustive list of events the diff engine fires:
- Player:
PlayerUpdated,PlayerDied,PlayerRespawned,PlayerTookDamage,PlayerGotKill,PlayerHealthChanged,PlayerArmorChanged,PlayerHelmetChanged,PlayerFlashAmountChanged,PlayerSmokedAmountChanged,PlayerBurningAmountChanged,PlayerMoneyAmountChanged,PlayerEquipmentValueChanged,PlayerRoundKillsChanged,PlayerRoundHeadshotKillsChanged,PlayerRoundTotalDamageChanged,PlayerActivityChanged,PlayerTeamChanged,PlayerActiveWeaponChanged,PlayerStatsChanged - Round / Match:
RoundUpdated,RoundPhaseUpdated,RoundStarted,RoundConcluded,FreezetimeStarted,FreezetimeOver,MatchStarted,GameOver,BombStateUpdated - Map:
MapUpdated,GamemodeChanged,LevelChanged,MapPhaseChanged,TeamScoreChanged - Bomb (root):
BombUpdated,BombPlanting,BombPlanted,BombDefusing,BombDefused,BombExploded,BombDropped,BombPickedUp - Synthesised:
KillFeed(kill ↔ death pairing within one diff window) - Meta:
NewGameState,AuthUpdated,ProviderUpdated
Generating the cfg file
CS2 has to be told where to send payloads. cs2-gsi builds an integration
file identical in shape to the upstream CounterStrike2GSI library:
use GsiCfg;
// 1. Auto-discover and place into ...\Counter-Strike Global Offensive\game\csgo\cfg\
for_localhost
.with_auth
.write_to_cs2?;
// 2. Or render to a string and place it yourself.
let kv = for_localhost.render;
println!;
Output (snipped):
"MyApp Integration Configuration"
{
"uri" "http://localhost:4000/"
"timeout" "5.0"
"buffer" "0.1"
"throttle" "0.1"
"heartbeat" "10.0"
"data"
{
"allgrenades" "1"
"allplayers_id" "1"
"allplayers_match_stats" "1"
...
}
}
Examples
MSRV & platforms
- Rust 1.86 or newer. The dev-dependency
reqwest 0.12indirectly pulls inidna_adapterand theicu_*crates, whose latest versions require Rust 1.86. The library itself only needs basic 2021-edition features;cargo build(without dev-deps) on older toolchains still works. - Tested on Windows 10/11 (primary target — that's where CS2 lives).
Linux & macOS are supported for the listener / parser / Steam discovery,
using
~/.steam/steamand~/Library/Application Support/Steamrespectively.
Differences from upstream
cs2-gsi is not a 1:1 binding of the upstream C# library — it's a
re-implementation that targets idiomatic Rust:
| Upstream (C#) | This crate |
|---|---|
event / += handler |
`gsl.on( |
PascalCase event types |
PascalCase types preserved |
| 70+ events | ~45 events covering all common cases |
EventDispatcher calls handlers sync |
Same — handlers run sync on the listener task |
GenerateGSIConfigFile(name) |
GsiCfg::for_localhost(name, port).write_to_cs2() |
.NET HttpListener (single-threaded) |
hyper 1.x + tokio (multi-threaded accept) |
| Numeric fields awkward to consume | Auto-coerced from JSON strings |
If you need an upstream event that isn't yet exposed, open an issue — adding more variants is mechanical (one diff branch, one event struct).
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual-licensed as above, without any additional terms or conditions.
Acknowledgements
Inspired by and behaviour-compatible with antonpup/CounterStrike2GSI.