Skip to content

efskap/warcrumb

Repository files navigation

warcrumb

Warcraft 3 Replay Parser in Go

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.

About

⚔️ Warcraft 3 Replay Parser in Go (supports Reforged; WIP... much like Reforged)

Topics

Resources

License

Stars

Watchers

Forks

Languages