https://github.com/efskap/warcrumb
⚔️ Warcraft 3 Replay Parser in Go (supports Reforged; WIP... much like Reforged)
https://github.com/efskap/warcrumb
parser-library warcraft warcraft3
Last synced: about 1 month ago
JSON representation
⚔️ Warcraft 3 Replay Parser in Go (supports Reforged; WIP... much like Reforged)
- Host: GitHub
- URL: https://github.com/efskap/warcrumb
- Owner: efskap
- License: gpl-3.0
- Created: 2020-02-09T00:10:27.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2020-11-17T23:23:01.000Z (over 5 years ago)
- Last Synced: 2025-12-17T14:56:37.706Z (2 months ago)
- Topics: parser-library, warcraft, warcraft3
- Language: Go
- Homepage:
- Size: 7.41 MB
- Stars: 6
- Watchers: 1
- Forks: 1
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# warcrumb
## Warcraft 3 Replay Parser in Go

A work in progress, much like WC3 Reforged.
Pulls out metadata, chat, and game events (to some extent).
Supports all versions of the game, including Reforged.
Based on http://w3g.deepnode.de/files/w3g_format.txt, with my own research into the Reforged format (e.g. Battle.net 2.0 integration).
## A word on what is encoded
Please note that a WC3 replay does not record what _happened_, but rather what inputs the human players sent. The game (inc. AI) is deterministic, so it can be recreated from this.
This means you can't really know:
- what the AI did
- combat stuff like which units died (at best you can infer this from selections), resources at a given moment, etc
- what _actually happened_ (e.g. you can have a "build tower" ability encoded, even if it was cancelled thereafter)
## Loading a replay
```go
f, err := os.Open("LastReplay.w3g")
if err != nil {
log.Fatal(err)
}
replay, err := warcrumb.ParseReplay(f)
if err != nil {
log.Fatal("error parsing replay: ", err)
}
```
### Example: Actions
```go
for _, action := range replay.Actions {
if ability, ok := action.Ability.(warcrumb.BasicAbility); ok {
fmt.Println(fmtTimestamp(action.Time), action.Player, ability.ItemId.String())
} else if ability, ok := action.Ability.(warcrumb.TargetedAbility); ok {
fmt.Println(fmtTimestamp(action.Time), action.Player, ability.ItemId.String(), ability.Target)
}
}
// ---
func fmtTimestamp(duration time.Duration) string {
mins := duration.Truncate(time.Minute)
secs := (duration - mins).Truncate(time.Second).Seconds()
return fmt.Sprintf("%02d:%02d", int(mins.Minutes()), int(secs))
}
```
Prints stuff like:
```
13:34 LeoLaporte Train Raider
13:38 Ghostridah. Build Beastiary (-6592.0,-2240.0)
13:40 Ghostridah. Train Wind Rider
```
### Example: Sportsmanship Chat Analyzer
```go
sportsmanTerms := []string{"gg", "glhf"}
isSportsmanlike := make(map[*warcrumb.Player]bool)
for _, msg := range replay.ChatMessages {
for _, term := range sportsmanTerms {
if strings.Contains(strings.ToLower(msg.Body), term) {
isSportsmanlike[msg.Author.Player] = true
}
}
}
for _, player := range replay.Players {
if isSportsmanlike[player] {
fmt.Println(player, "has demonstrated sportsmanship! Well done!")
} else {
fmt.Println(player, "has NOT been sportsmanlike this game. BOOOOO!")
fmt.Printf("Typical %s player...\n", replay.Slots[player.SlotId].Race)
}
}
```
See [`/examples`](examples) for the full programs and other examples.