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
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
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
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
for the full programs and other examples.