demoinfocs

package
v3.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 26, 2023 License: MIT Imports: 23 Imported by: 9

Documentation

Overview

Package demoinfocs provides a demo parser for the game Counter-Strike: Global Offensive. It is based on the official demoinfogo tool by Valve as well as Stats Helix's demoinfo.

A good entry point to using the library is the parser interface.

Demo events are documented in the events package.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrCancelled signals that parsing was cancelled via Parser.Cancel()
	ErrCancelled = errors.New("parsing was cancelled before it finished (ErrCancelled)")

	// ErrUnexpectedEndOfDemo signals that the demo is incomplete / corrupt -
	// these demos may still be useful, check how far the parser got.
	ErrUnexpectedEndOfDemo = errors.New("demo stream ended unexpectedly (ErrUnexpectedEndOfDemo)")

	// ErrInvalidFileType signals that the input isn't a valid CS:GO demo.
	ErrInvalidFileType = errors.New("invalid File-Type; expecting HL2DEMO in the first 8 bytes (ErrInvalidFileType)")
)

Parsing errors

View Source
var DefaultParserConfig = ParserConfig{
	MsgQueueBufferSize: -1,
}

DefaultParserConfig is the default Parser configuration used by NewParser().

View Source
var ErrBombsiteIndexNotFound = errors.New("bombsite index not found - see https://github.com/markus-wa/demoinfocs-golang/issues/314")

ErrBombsiteIndexNotFound indicates that a game-event occurred that contained an unknown bombsite index. This error can be disabled by setting ParserConfig.IgnoreErrBombsiteIndexNotFound = true. See https://github.com/markus-wa/demoinfocs-golang/issues/314

View Source
var ErrFailedToRetrieveGameRule = errors.New("failed to retrieve GameRule value, it's recommended to have a fallback to a default value for this scenario")

Functions

func MatchInfoDecryptionKey

func MatchInfoDecryptionKey(b []byte) ([]byte, error)

MatchInfoDecryptionKey extracts the net-message decryption key stored in `match730_*.dem.info`. Pass the whole contents of `match730_*.dem.info` to this function to get the key. See also: ParserConfig.NetMessageDecryptionKey

Types

type GameRules

type GameRules interface {
	// RoundTime returns how long rounds in the current match last for (excluding freeze time).
	// May return error if cs_gamerules_data.m_iRoundTime is not set.
	RoundTime() (time.Duration, error)
	// FreezeTime returns how long freeze time lasts for in the current match (mp_freezetime).
	// May return error if mp_freezetime cannot be converted to a time duration.
	FreezeTime() (time.Duration, error)
	// BombTime returns how long freeze time lasts for in the current match (mp_freezetime).
	// May return error if mp_c4timer cannot be converted to a time duration.
	BombTime() (time.Duration, error)
	// ConVars returns a map of CVar keys and values.
	// Not all values might be set.
	// See also: https://developer.valvesoftware.com/wiki/List_of_CS:GO_Cvars.
	ConVars() map[string]string
	// Entity returns the game's CCSGameRulesProxy entity.
	Entity() st.Entity
}

GameRules is an auto-generated interface for gameRules.

type GameState

type GameState interface {
	// IngameTick returns the latest actual tick number of the server during the game.
	//
	// Watch out, I've seen this return wonky negative numbers at the start of demos.
	IngameTick() int
	// Team returns the TeamState corresponding to team.
	// Returns nil if team != TeamTerrorists && team != TeamCounterTerrorists.
	//
	// Make sure to handle swapping sides properly if you keep the reference.
	Team(team common.Team) *common.TeamState
	// TeamCounterTerrorists returns the TeamState of the CT team.
	//
	// Make sure to handle swapping sides properly if you keep the reference.
	TeamCounterTerrorists() *common.TeamState
	// TeamTerrorists returns the TeamState of the T team.
	//
	// Make sure to handle swapping sides properly if you keep the reference.
	TeamTerrorists() *common.TeamState
	// Participants returns a struct with all currently connected players & spectators and utility functions.
	// The struct contains references to the original maps so it's always up-to-date.
	Participants() Participants
	// Rules returns the GameRules for the current match.
	// Contains information like freeze time duration etc.
	Rules() GameRules
	// Hostages returns all current hostages.
	Hostages() []*common.Hostage
	// GrenadeProjectiles returns a map from entity-IDs to all live grenade projectiles.
	//
	// Only constains projectiles currently in-flight or still active (smokes etc.),
	// i.e. have been thrown but have yet to detonate.
	GrenadeProjectiles() map[int]*common.GrenadeProjectile
	// Infernos returns a map from entity-IDs to all currently burning infernos (fires from incendiaries and Molotovs).
	Infernos() map[int]*common.Inferno
	// Weapons returns a map from entity-IDs to all weapons currently in the game.
	Weapons() map[int]*common.Equipment
	// Entities returns all currently existing entities.
	// (Almost?) everything in the game is an entity, such as weapons, players, fire etc.
	Entities() map[int]st.Entity
	// Bomb returns the current bomb state.
	Bomb() *common.Bomb
	// TotalRoundsPlayed returns the amount of total rounds played according to CCSGameRulesProxy.
	TotalRoundsPlayed() int
	// GamePhase returns the game phase of the current game state. See common/gamerules.go for more.
	GamePhase() common.GamePhase
	// IsWarmupPeriod returns whether the game is currently in warmup period according to CCSGameRulesProxy.
	IsWarmupPeriod() bool
	// IsMatchStarted returns whether the match has started according to CCSGameRulesProxy.
	IsMatchStarted() bool
	// PlayerResourceEntity returns the game's CCSPlayerResource entity.
	// Contains scoreboard information and more.
	PlayerResourceEntity() st.Entity
}

GameState is an auto-generated interface for gameState. gameState contains all game-state relevant information.

type NetMessageCreator

type NetMessageCreator func() proto.Message

NetMessageCreator creates additional net-messages to be dispatched to net-message handlers.

See also: ParserConfig.AdditionalNetMessageCreators & Parser.RegisterNetMessageHandler()

type Parser

type Parser interface {
	// ServerClasses returns the server-classes of this demo.
	// These are available after events.DataTablesParsed has been fired.
	ServerClasses() st.ServerClasses
	// Header returns the DemoHeader which contains the demo's metadata.
	// Only possible after ParserHeader() has been called.
	Header() common.DemoHeader
	// GameState returns the current game-state.
	// It contains most of the relevant information about the game such as players, teams, scores, grenades etc.
	GameState() GameState
	// CurrentFrame return the number of the current frame, aka. 'demo-tick' (Since demos often have a different tick-rate than the game).
	// Starts with frame 0, should go up to DemoHeader.PlaybackFrames but might not be the case (usually it's just close to it).
	CurrentFrame() int
	// CurrentTime returns the time elapsed since the start of the demo
	CurrentTime() time.Duration
	// TickRate returns the tick-rate the server ran on during the game.
	//
	// Returns tick rate based on CSVCMsg_ServerInfo if possible.
	// Otherwise returns tick rate based on demo header or -1 if the header info isn't available.
	TickRate() float64
	// TickTime returns the time a single tick takes in seconds.
	//
	// Returns tick time based on CSVCMsg_ServerInfo if possible.
	// Otherwise returns tick time based on demo header or -1 if the header info isn't available.
	TickTime() time.Duration
	// Progress returns the parsing progress from 0 to 1.
	// Where 0 means nothing has been parsed yet and 1 means the demo has been parsed to the end.
	//
	// Might not be 100% correct since it's just based on the reported tick count of the header.
	// May always return 0 if the demo header is corrupt.
	Progress() float32
	/*
	   RegisterEventHandler registers a handler for game events.

	   The handler must be of type func(<EventType>) where EventType is the kind of event to be handled.
	   To catch all events func(any) can be used.

	   Example:

	   	parser.RegisterEventHandler(func(e events.WeaponFired) {
	   		fmt.Printf("%s fired his %s\n", e.Shooter.Name, e.Weapon.Type)
	   	})

	   Parameter handler has to be of type any because Go generics only work on functions, not methods.

	   Returns an identifier with which the handler can be removed via UnregisterEventHandler().
	*/
	RegisterEventHandler(handler any) dp.HandlerIdentifier
	// UnregisterEventHandler removes a game event handler via identifier.
	//
	// The identifier is returned at registration by RegisterEventHandler().
	UnregisterEventHandler(identifier dp.HandlerIdentifier)
	/*
	   RegisterNetMessageHandler registers a handler for net-messages.

	   The handler must be of type func(*<MessageType>) where MessageType is the kind of net-message to be handled.

	   Parameter handler has to be of type any because Go generics only work on functions, not methods.

	   Returns an identifier with which the handler can be removed via UnregisterNetMessageHandler().

	   See also: RegisterEventHandler()
	*/
	RegisterNetMessageHandler(handler any) dp.HandlerIdentifier
	// UnregisterNetMessageHandler removes a net-message handler via identifier.
	//
	// The identifier is returned at registration by RegisterNetMessageHandler().
	UnregisterNetMessageHandler(identifier dp.HandlerIdentifier)
	// Close closes any open resources used by the Parser (go routines, file handles).
	// This must be called before discarding the Parser to avoid memory leaks.
	// Returns an error if closing of underlying resources fails.
	Close() error
	// ParseHeader attempts to parse the header of the demo and returns it.
	// If not done manually this will be called by Parser.ParseNextFrame() or Parser.ParseToEnd().
	//
	// Returns ErrInvalidFileType if the filestamp (first 8 bytes) doesn't match HL2DEMO.
	ParseHeader() (common.DemoHeader, error)
	// ParseToEnd attempts to parse the demo until the end.
	// Aborts and returns ErrCancelled if Cancel() is called before the end.
	//
	// See also: ParseNextFrame() for other possible errors.
	ParseToEnd() (err error)
	// Cancel aborts ParseToEnd() and drains the internal event queues.
	// No further events will be sent to event or message handlers after this.
	Cancel()
	/*
	   ParseNextFrame attempts to parse the next frame / demo-tick (not ingame tick).

	   Returns true unless the demo command 'stop' or an error was encountered.

	   May return ErrUnexpectedEndOfDemo for incomplete / corrupt demos.
	   May panic if the demo is corrupt in some way.

	   See also: ParseToEnd() for parsing the complete demo in one go (faster).
	*/
	ParseNextFrame() (moreFrames bool, err error)
}

Parser is an auto-generated interface for Parser, intended to be used when mockability is needed. Parser can parse a CS:GO demo. Creating a new instance is done via NewParser().

To start off you may use Parser.ParseHeader() to parse the demo header (this can be skipped and will be done automatically if necessary). Further, Parser.ParseNextFrame() and Parser.ParseToEnd() can be used to parse the demo.

Use Parser.RegisterEventHandler() to receive notifications about events.

Example (without error handling):

f, _ := os.Open("/path/to/demo.dem")
p := dem.NewParser(f)
defer p.Close()
header := p.ParseHeader()
fmt.Println("Map:", header.MapName)
p.RegisterEventHandler(func(e events.BombExplode) {
	fmt.Printf(e.Site, "went BOOM!")
})
p.ParseToEnd()

Prints out '{A/B} site went BOOM!' when a bomb explodes.

Example

This will print all kills of a demo in the format '[[killer]] <[[weapon]] [(HS)] [(WB)]> [[victim]]'

noinspection GoUnhandledErrorResult

package main

import (
	"fmt"
	"log"
	"os"

	demoinfocs "github.com/markus-wa/demoinfocs-golang/v3/pkg/demoinfocs"
	events "github.com/markus-wa/demoinfocs-golang/v3/pkg/demoinfocs/events"
)

func main() {
	f, err := os.Open("../../test/cs-demos/default.dem")
	if err != nil {
		log.Panic("failed to open demo file: ", err)
	}

	defer f.Close()

	p := demoinfocs.NewParser(f)
	defer p.Close()

	// Register handler on kill events
	p.RegisterEventHandler(func(e events.Kill) {
		var hs string
		if e.IsHeadshot {
			hs = " (HS)"
		}

		var wallBang string
		if e.PenetratedObjects > 0 {
			wallBang = " (WB)"
		}

		fmt.Printf("%s <%v%s%s> %s\n", e.Killer, e.Weapon, hs, wallBang, e.Victim)
	})

	// Parse to end
	err = p.ParseToEnd()
	if err != nil {
		log.Panic("failed to parse demo: ", err)
	}
}
Output:

func NewParser

func NewParser(demostream io.Reader) Parser

NewParser creates a new Parser with the default configuration. The demostream io.Reader (e.g. os.File or bytes.Reader) must provide demo data in the '.DEM' format.

See also: NewCustomParser() & DefaultParserConfig

func NewParserWithConfig

func NewParserWithConfig(demostream io.Reader, config ParserConfig) Parser

NewParserWithConfig returns a new Parser with a custom configuration.

See also: NewParser() & ParserConfig

type ParserConfig

type ParserConfig struct {
	// MsgQueueBufferSize defines the size of the internal net-message queue.
	// For large demos, fast i/o and slow CPUs higher numbers are suggested and vice versa.
	// The buffer size can easily be in the hundred-thousands to low millions for the best performance.
	// A negative value will make the Parser automatically decide the buffer size during ParseHeader()
	// based on the number of ticks in the demo (nubmer of ticks = buffer size);
	// this is the default behavior for DefaultParserConfig.
	// Zero enforces sequential parsing.
	MsgQueueBufferSize int

	// AdditionalNetMessageCreators maps net-message-IDs to creators (instantiators).
	// The creators should return a new instance of the correct protobuf-message type (from the msg package).
	// Interesting net-message-IDs can easily be discovered with the build-tag 'debugdemoinfocs'; when looking for 'UnhandledMessage'.
	// Check out parsing.go to see which net-messages are already being parsed by default.
	// This is a beta feature and may be changed or replaced without notice.
	AdditionalNetMessageCreators map[int]NetMessageCreator

	// IgnoreErrBombsiteIndexNotFound tells the parser to not return an error when a bombsite-index from a game-event is not found in the demo.
	// See https://github.com/markus-wa/demoinfocs-golang/issues/314
	IgnoreErrBombsiteIndexNotFound bool

	// NetMessageDecryptionKey tells the parser how to decrypt certain encrypted net-messages.
	// See MatchInfoDecryptionKey() on how to retrieve the key from `match730_*.dem.info` files.
	NetMessageDecryptionKey []byte
}

ParserConfig contains the configuration for creating a new Parser.

type Participants

type Participants interface {
	// ByUserID returns all currently connected players in a map where the key is the user-ID.
	// The returned map is a snapshot and is not updated on changes (not a reference to the actual, underlying map).
	// Includes spectators.
	ByUserID() map[int]*common.Player
	// ByEntityID returns all currently connected players in a map where the key is the entity-ID.
	// The returned map is a snapshot and is not updated on changes (not a reference to the actual, underlying map).
	// Includes spectators.
	ByEntityID() map[int]*common.Player
	// AllByUserID returns all currently known players & spectators, including disconnected ones,
	// in a map where the key is the user-ID.
	// The returned map is a snapshot and is not updated on changes (not a reference to the actual, underlying map).
	// Includes spectators.
	AllByUserID() map[int]*common.Player
	// All returns all currently known players & spectators, including disconnected ones, of the demo.
	// The returned slice is a snapshot and is not updated on changes.
	All() []*common.Player
	// Connected returns all currently connected players & spectators.
	// The returned slice is a snapshot and is not updated on changes.
	Connected() []*common.Player
	// Playing returns all players that aren't spectating or unassigned.
	// The returned slice is a snapshot and is not updated on changes.
	Playing() []*common.Player
	// TeamMembers returns all players belonging to the requested team at this time.
	// The returned slice is a snapshot and is not updated on changes.
	TeamMembers(team common.Team) []*common.Player
	// FindByHandle attempts to find a player by his entity-handle.
	// The entity-handle is often used in entity-properties when referencing other entities such as a weapon's owner.
	//
	// Returns nil if not found or if handle == invalidEntityHandle (used when referencing no entity).
	FindByHandle(handle int) *common.Player
	// SpottersOf returns a list of all players who have spotted the passed player.
	// This is NOT "Line of Sight" / FOV - look up "CSGO TraceRay" for that.
	// May not behave as expected with multiple spotters.
	SpottersOf(spotted *common.Player) (spotters []*common.Player)
	// SpottedBy returns a list of all players that the passed player has spotted.
	// This is NOT "Line of Sight" / FOV - look up "CSGO TraceRay" for that.
	// May not behave as expected with multiple spotters.
	SpottedBy(spotter *common.Player) (spotted []*common.Player)
}

Participants is an auto-generated interface for participants. participants provides helper functions on top of the currently connected players. E.g. ByUserID(), ByEntityID(), TeamMembers(), etc.

See GameState.Participants()

Directories

Path Synopsis
Package common contains common types, constants and functions used over different demoinfocs packages.
Package common contains common types, constants and functions used over different demoinfocs packages.
Package constants contains constants that are used internally across the demoinfocs library.
Package constants contains constants that are used internally across the demoinfocs library.
Package events contains all events that can be sent out from demoinfocs.Parser.
Package events contains all events that can be sent out from demoinfocs.Parser.
Package fake provides basic mocks for Parser, GameState and Participants.
Package fake provides basic mocks for Parser, GameState and Participants.
Package msg contains the generated protobuf demo message code.
Package msg contains the generated protobuf demo message code.
Package sendtables contains code related to decoding sendtables.
Package sendtables contains code related to decoding sendtables.
fake
Package fake provides basic mocks for Entity and Property.
Package fake provides basic mocks for Entity and Property.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL