Game State Integration is how Valve pushes live match data out of the game client to a service you run yourself. The client posts JSON to an HTTP endpoint you choose and your service reads it. Tournament overlays, stat trackers, lighting rigs, and stream tools all start from this. The important detail is that GSI is an official channel: you do not read game memory, so you do not trip anti-cheat.
Where the configuration lives
The client looks for files named gamestate_integration_<name>.cfg in its cfg directory. Valve's documentation gives the CS:GO path as game/csgo/cfg, and CS2 uses the same file naming and the same mechanism. One file per service, and the client relays data to all of them on the next launch. The file must not carry a UTF-8 byte order mark, or the client will not load it.
What the file contains
Code: Select all
"Console Sample v.1"
{
"uri" "http://127.0.0.1:3000"
"timeout" "5.0"
"buffer" "0.1"
"throttle" "0.5"
"heartbeat" "60.0"
"auth"
{
"token" "CCWJu64ZV3JHDT8hZc"
}
"output"
{
"precision_time" "3"
"precision_position" "1"
"precision_vector" "3"
}
"data"
{
"provider" "1"
"map" "1"
"round" "1"
"player_id" "1"
"player_state" "1"
"player_weapons" "1"
"player_match_stats" "1"
}
}
- uri is where the game posts. It expects an HTTP 2XX response, and it will not send the next payload while a previous one is still in flight. If your service stops returning 2XX, you stop receiving the delta blocks.
- timeout is how long the game waits for a response before it treats the request as failed. The default is 1.1 seconds.
- buffer collects events for that many seconds so the game can report a larger delta. For a localhost service you can set it to 0.0 and disable buffering.
- throttle is the minimum gap between requests after a successful response. It keeps a busy endpoint from being flooded. The default is 1.0 second.
- heartbeat sends a request even when nothing changed, so your service can tell an idle client from a dead one. The default is 60 seconds.
- auth is optional. When it is present, the token is sent as a JSON field so your endpoint can verify the payload came from your own game client.
- output controls decimal precision for time, position, and vector values.
Each component is subscribed separately, so a service that only needs the map name does not pay for player positions.
- provider: game name, appid, and the client's SteamID
- map: map name, game mode, match phase (warmup, intermission, gameover, live), and the current score
- round: round phase, bomb state, and round winner
- player_id: player name, clan tag, observer slot, and team
- player_state: health, armor, and kills this round
- player_weapons: the equipped weapons
- player_match_stats: kills, assists, score, deaths, and MVPs this match
- allplayers_*: the same data for every player, valid for HLTV and observers only
- phase_countdowns: seconds left on each game phase, for GOTV and spectators
- allgrenades: every grenade and inferno flame in the world, for GOTV and spectators
Getting started
1. Write the cfg file with your own service name and a uri pointing at your listener.
2. Run a small HTTP POST server. Valve's documentation ships a Node example that prints the payload to the console, which is the fastest way to see the shape of the data.
3. Subscribe to only the blocks you need, then add more once the basics work.
4. If nothing arrives, check the console output when the game starts. A loaded cfg file is confirmed there.
Valve lists example integrations for C#, NodeJS, and Python in the same document. There are also CS2-specific libraries, such as antonpup's CounterStrike2GSI, which parses the payload into C# events and raises more specific ones like BombStateUpdated and RoundConcluded.
Sources