A blazing fast, feature complete and production ready Go library for parsing and analysing of Counter-Strike 2 and Counter-Strike: Global Offensive (CS:GO) demos (aka replays).
You can use Discord or GitHub Discussions to ask questions and discuss ideas about this project.
For business inquiries please use the contact information found on the GitHub profile.
go get -u github.com/markus-wa/demoinfocs-golang/v4/pkg/demoinfocs
go get -u github.com/markus-wa/demoinfocs-golang/v3/pkg/demoinfocs
This library requires at least go 1.20
to run.
You can download the latest version of Go here.
Download and install the latest version of Go from golang.org or via your favourite package manager
Create a new Go Modules project
mkdir my-project
cd my-project
go mod init my-project
go get -u github.com/markus-wa/demoinfocs-golang/v4/pkg/demoinfocs
Create a main.go
file with the example below
Run go run main.go
This is a simple example on how to handle game events using this library.
It prints all kills in a given demo (killer, weapon, victim, was it a wallbang/headshot?) by registering a handler for events.Kill
.
Check out the godoc of the events
package for some information about the other available events and their purpose.
package main
import (
"fmt"
"log"
"os"
dem "github.com/markus-wa/demoinfocs-golang/v4/pkg/demoinfocs"
events "github.com/markus-wa/demoinfocs-golang/v4/pkg/demoinfocs/events"
)
func main() {
f, err := os.Open("/path/to/demo.dem")
if err != nil {
log.Panic("failed to open demo file: ", err)
}
defer f.Close()
p := dem.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)
}
}
Running the code above will print something like this:
xms <AK-47 (HS)> crisby
tiziaN <USP-S (HS)> Ex6TenZ
tiziaN <USP-S> mistou
tiziaN <USP-S (HS)> ALEX
xms <Glock-18 (HS)> tiziaN
...
keev <AWP (HS) (WB)> to1nou
...
Check out the examples folder for more examples, like how to generate heatmaps like this one:
The full API documentation is available here on pkg.go.dev.
ParserConfig.NetMessageDecryptionKey
needs to be set - see also MatchInfoDecryptionKey()
.Two of the top priorities of this parser are performance and concurrency.
Here are some benchmark results from a system with an Intel i7 6700k CPU and a SSD disk running Windows 10 and a demo with 85'000 frames.
Benchmark | Description | Average Duration | Speed |
---|---|---|---|
BenchmarkConcurrent |
Read and parse 8 demos concurrently | 2.06 s (per 8 demos) | ~330'000 ticks / s |
BenchmarkDemoInfoCs |
Read demo from drive and parse | 0.89 s | ~95'000 ticks / s |
BenchmarkInMemory |
Read demo from memory and parse | 0.88 s | ~96'000 ticks / s |
That's almost 1.5 hours of gameplay per second when parsing in parallel (recorded at 64 ticks per second) - or 25 minues per second when only parsing a single demo at a time.
$ go test -run _NONE_ -bench . -benchtime 30s -benchmem -concurrentdemos 8
goos: windows
goarch: amd64
pkg: github.com/markus-wa/demoinfocs-golang
BenchmarkDemoInfoCs-8 50 894500010 ns/op 257610127 B/op 914355 allocs/op
BenchmarkInMemory-8 50 876279984 ns/op 257457271 B/op 914143 allocs/op
BenchmarkConcurrent-8 20 2058303680 ns/op 2059386582 B/op 7313145 allocs/op
--- BENCH: BenchmarkConcurrent-8
demoinfocs_test.go:315: Running concurrency benchmark with 8 demos
demoinfocs_test.go:315: Running concurrency benchmark with 8 demos
PASS
ok github.com/markus-wa/demoinfocs-golang 134.244s
We use SemVer for versioning. For the versions available, see the tags on this repository. There is one caveat however: Beta features - which are marked as such via comments and in release notes - may change in minor releases.
If your project is using this library feel free to submit a PR or send a message via Discord to be included in the list.
You can use the build tag debugdemoinfocs
to print out debugging information - such as game events or unhandled demo-messages - during the parsing process.
e.g.
go run -tags debugdemoinfocs examples/print-events/print_events.go -demo example.dem
Side-note: The tag isn't called debug
to avoid naming conflicts with other libs (and underscores in tags don't work, apparently).
To change the default debugging behavior, Go's ldflags
parameter can be used. Example for additionally printing out all server-classes with their properties: -ldflags="-X 'github.com/markus-wa/demoinfocs-golang/v4/pkg/demoinfocs.debugServerClasses=YES'"
.
e.g.
go run -tags debugdemoinfocs -ldflags="-X 'github.com/markus-wa/demoinfocs-golang/v2/pkg/demoinfocs.debugServerClasses=YES'" examples/print-events/print_events.go -demo example.dem
Check out debug_on.go
for any other settings that can be changed.
For any new features, Test Driven Development should be practiced where possible. However, due to some design flaws in some parts of the code it's currently not always practical to do so.
Running unit tests:
scripts/unit-tests.sh
# or (identical)
go test -short ./...
For the full regression suite you will need to download the test demo-set.
Prerequisites:
7z
must be in your PATH
environment variable (p7zip
or p7zip-full
package on most Linux distros)Downloading demos + running regression tests:
scripts/regression-tests.sh
default.golden
FileThe file test/default.golden
file contains a serialized output of all expected game events in test/cs-demos/default.dem
.
If there is a change to game events (new fields etc.) it is necessary to update this file so the regression tests pass. To update it you can run the following command:
go test -run TestDemoInfoCs -update
Please don't update the .golden
file if you are not sure it's required. Maybe the failing CI is just pointing out a regression.
We generate interfaces such as GameState
from structs to make it easier to keep docs in synch over structs and interfaces.
For this we use @vburenin's ifacemaker
tool.
You can download the latest version here.
After adding it to your PATH
you can use scripts/generate-interfaces.sh
to update interfaces.
Should you need to re-generate the protobuf generated code in the msg
package, you will need the following tools:
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
Make sure both are inside your PATH
variable.
After installing these use go generate ./msg
to generate the protobuf code. If you're on Windows you'll need to run go generate from CMD, not Bash.
To install some (optional, but quite handy) pre-commit
and pre-push
hooks, you can run the following script.
scripts/git-hooks/link-git-hooks.sh
pre-commit
:pre-push
:This library was originally based on Valve's demoinfogo and SatsHelix's demoinfo (although today it shares little resemblence with these two).
For Counter-Strike 2, dotabuff/manta was an amazing resource for how to parse Source 2 demos and CS2 support would not have been possible without it.
I would also like to specifically thank @akiver & @LaihoE for their brilliant help with CS2.
And a very special thanks goes out to all the ⭐contributors⭐️, be it in the form of PRs, issues or anything else.
Further shoutouts go to:
This project is licensed under the MIT license.