demoinfocs-golang

repository·master·Indexed 22 days ago

https://github.com/markus-wa/demoinfocs-golang

A high-performance Go library for parsing and analyzing Counter-Strike 2 and CS:GO demo files. It enables developers to extract game events, track game state, and access low-level network messages. Features include support for parsing live CSTV+ broadcasts, custom handlers for unhandled entity data and property updates, and a fake package for mocking the parser in unit tests.

Tokens
9.3K
Snippets
28
Records
37
Agent score
74%

What's inside demoinfocs-golang

  1. Overview of demoinfocs-golang examples

    master

    The demoinfocs-golang repository provides several example implementations demonstrating different ways to interact with CS2 demo data. These range from high-level data visualization to low-level protocol handling.

    Note: Example code is intended for demonstration purposes and may use simplified error handling. It should not be used in production systems without implementing robust error management.

  2. Core features of demoinfocs-golang

    master

    The library provides several high-level capabilities for demo analysis:

    • Game Events: Access kills, shots, round starts/ends, footsteps, chat, and console messages via the events package.
    • Game State Tracking: Monitor players, teams, grenades, and ConVars.
    • Grenade Projectiles: Track trajectories and projectile data.
    • Entity Access: Access entities, server-classes, and data-tables via sendtables.
    • Net-Messages: Access all raw net-messages.
    • Matchmaking Ranks: Retrieve official MM ranks from official matchmaking demos.
    • POV Support: Full support for Point of View demos.
    • WASM Support: Use the library in browsers or Node.js via WebAssembly.
  3. Handle unhandled entity data using handlers

    master

    You can process entity data that is not explicitly defined in the parser by registering custom handlers. This is achieved by setting up two types of handlers:

    1. Entity-creation handlers: Registered on server-classes to intercept when a new entity of a specific class is created.
    2. Property-update handlers: Registered on specific entities to intercept updates to their properties.

    This approach allows you to extract information from unhandled entity-data without modifying the core parser logic.

  4. Available usage examples and patterns

    master

    The following patterns are demonstrated across the repository's examples:

    • Heatmaps: Creating a heatmap from player shot positions.
    • Grenade Trajectories: Mapping grenade trajectories on a map overview.
    • Voice Capture: Capturing voice data from players.
    • Unhandled Entity Data: Accessing unhandled data from entities using Parser.ServerClasses().
    • Custom Net-Messages: Parsing and handling custom net-messages.
    • Event Printing: Printing kills, scores, and chat messages.
    • Unit Testing: Using the fake package to write unit tests for your code.
    • WebAssembly: Using the library from JavaScript (in the browser or Node.js) via WebAssembly.
  5. Understand the print-events output format

    master

    The print-events example outputs a stream of parsed game information. The format includes:

    • Map Name: The map being played (e.g., Map: de_cache).
    • Kill Events: Shows the killer, the victim, and the weapon used. Format: [Side]PlayerName <Weapon (Modifier)> [Side]VictimName.
      • [T] indicates Terrorist.
      • [CT] indicates Counter-Terrorist.
      • (HS) indicates a Headshot.
      • (WB) indicates a Wallbang.
    • Chat Messages: Shows the sender's side, name, and message. Format: Chat - [Side]PlayerName says: message.
    • Round Results: Shows the winner and the current score. Format: Round finished: winnerSide=SIDE ; score=X:Y.
    • Tie/No Winner: Indicates rounds that ended without a winner.
    Map: de_cache
    [T]xms*ASUS ♥ /F/ <AK-47 (HS)> [CT]crisby
    Chat - [T]to1nou * Seagate says: hf
    Round finished: winnerSide=CT ; score=1:0
  6. Parse Live CSTV Broadcasts (CSTV+)

    master

    You can parse live Counter-Strike 2 (CS2) broadcasts using the CSTV+ (Live Broadcast Parsing) functionality. This involves running a local Node.js server to handle the broadcast stream, starting a dedicated CS2 server with specific parameters, and then using the Go parser to consume the broadcast URL.

    Workflow Overview

    1. Start the stream handler: Run the provided Node.js script to listen for the broadcast.
    2. Start the CS2 Server: Launch a dedicated CS2 instance with broadcast enabled.
    3. Configure the Broadcast: Use in-game console commands to set the broadcast URL and start the stream.
    4. Run the Parser: Use the broadcasts.go tool with the generated broadcast URL to see real-time parsed data (kills, chat, round results, etc.).
    # 1. Start the Node.js stream handler
    node cstv.js
    
    # 2. Start the dedicated CS2 server
    cs2 -dedicated -port 27016 +sv_setsteamaccount 0F81C7E09971B48600F6642E21183EEC +game_type 0 +game_mode 1 +sv_hibernate_when_empty 0 +map de_overpass
    
    # 3. In the CS2 console, configure the broadcast
    tv_delay 0; tv_broadcast_url "http://localhost:8080"; tv_broadcast 1
    
    # 4. Run the Go parser with the tokenized URL
    go run broadcasts.go -url "http://localhost:8080/<token>"
  7. Generate Protobuf code in the `msg` package

    master

    If you need to re-generate the protobuf code within the msg package, ensure you have protoc-gen-go installed. Then, run the go generate command targeting the msg package. Note: On Windows, run this from CMD rather than Bash.

    go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
    go generate ./msg
  8. Use the library with WebAssembly (WASM)

    master

    To use the demoinfocs-golang library in a WebAssembly environment, you cannot use the standard library build process. Instead, you must use a specialized build process and repository designed for WASM integration.

    For a complete implementation and build instructions, refer to the dedicated WASM example repository.

    https://github.com/markus-wa/demoinfocs-wasm
  9. Install demoinfocs-golang for CS:GO

    master

    To use the library for parsing legacy CS:GO demos, use the following command to fetch the v3 package:

    go get -u github.com/markus-wa/demoinfocs-golang/v3/pkg/demoinfocs
    go get -u github.com/markus-wa/demoinfocs-golang/v3/pkg/demoinfocs
  10. Mock the parser using the fake package

    master

    To write unit tests for applications using demoinfocs-golang, you can use the fake package to mock the demoinfocs.Parser interface and other components. This allows you to simulate specific event sequences and parser behaviors (like errors) without needing a real demo file.

    Key Mocking Steps:

    1. Initialize the Mock: Create a new mocked parser using fake.NewParser().
    2. Inject Events: Use parser.MockEvents(...args) to schedule events. The arguments are passed to the mock's internal event map. The index in the slice determines the frame at which the events are emitted (e.g., parser.MockEvents(event1) for frame 0, parser.MockEvents(event2, event3) for frame 1).
    3. Define Method Behavior: Use the .On("MethodName").Return(value) pattern to define what the mock should return when specific parser methods are called (e.g., ParseToEnd).
    import (
    	"errors"
    	"testing"
    
    	assert "github.com/stretchr/testify/assert"
    	common "github.com/markus-wa/demoinfocs-golang/v5/pkg/demoinfocs/common"
    	events "github.com/markus-wa/demoinfocs-golang/v5/pkg/demoinfocs/events"
    	fake "github.com/markus-wa/demoinfocs-golang/v5/pkg/demoinfocs/fake"
    )
    
    func TestCollectKills(t *testing.T) {
    	parser := fake.NewParser()
    	kill1 := kill(common.EqAK47)
    	kill2 := kill(common.EqScout)
    	kill3 := kill(common.EqAUG)
    
    	// Mocking events at specific frames
    	parser.MockEvents(kill1)        // Frame 0
    	parser.MockEvents(kill2, kill3) // Frame 1
    
    	// Mocking method return values
    	parser.On("ParseToEnd").Return(nil)
    
    	actual, err := collectKills(parser)
    
    	assert.Nil(t, err)
    	expected := []events.Kill{kill1, kill2, kill3}
    	assert.Equal(t, expected, actual)
    }
    
    func kill(wep common.EquipmentElement) events.Kill {
    	eq := common.NewEquipment(wep)
    	return events.Kill{
    		Killer: new(common.Player),
    		Weapon: &eq,
    		Victim: new(common.Player),
    	}
    }