warcrumb

package module
v0.0.0-...-68a0b47 Latest Latest
Warning

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

Go to latest
Published: Nov 17, 2020 License: GPL-3.0 Imports: 14 Imported by: 0

README

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.

Documentation

Overview

Code generated by gen_strings. DO NOT EDIT.

Index

Constants

View Source
const (
	EmptySlot  slotStatus = "Empty"
	ClosedSlot            = "Closed"
	UsedSlot              = "Used"
)
View Source
const (
	EasyAI   AIStrength = 0x00
	NormalAI            = 0x01
	InsaneAI            = 0x02
)
View Source
const (
	ObsOff      ObserverSetting = 0
	ObsOnDefeat                 = 2
	ObsOn                       = 3
	ObsReferees                 = 4
)
View Source
const (
	SlowSpeed = iota
	NormalSpeed
	FastSpeed
)
View Source
const (
	Unknown      GameType = 0x00
	FFAor1on1             = 0x01
	Custom                = 0x09
	Singleplayer          = 0x1D
	LadderTeam            = 0x20 // (AT or RT, 2on2/3on3/4on4)
)

Variables

View Source
var (
	Red       = Color{color.RGBA{255, 4, 2, 255}, "Red"}
	Blue      = Color{color.RGBA{0, 66, 255, 255}, "Blue"}
	Teal      = Color{color.RGBA{27, 230, 186, 255}, "Teal"}
	Purple    = Color{color.RGBA{85, 0, 129, 255}, "Purple"}
	Yellow    = Color{color.RGBA{255, 252, 0, 255}, "Yellow"}
	Orange    = Color{color.RGBA{255, 138, 13, 255}, "Orange"}
	Green     = Color{color.RGBA{32, 191, 0, 255}, "Green"}
	Pink      = Color{color.RGBA{227, 91, 175, 255}, "Pink"}
	Grey      = Color{color.RGBA{148, 150, 151, 255}, "Grey"}
	LightBlue = Color{color.RGBA{126, 191, 241, 255}, "LightBlue"}
	DarkGreen = Color{color.RGBA{16, 98, 71, 255}, "DarkGreen"}
	Brown     = Color{color.RGBA{79, 43, 5, 255}, "Brown"}
	Maroon    = Color{color.RGBA{156, 0, 0, 255}, "Maroon"}
	Navy      = Color{color.RGBA{0, 0, 194, 255}, "Navy"}
	Turquoise = Color{color.RGBA{0, 235, 255, 255}, "Turquoise"}
	Violet    = Color{color.RGBA{189, 0, 255, 255}, "Violet"}
	Wheat     = Color{color.RGBA{236, 204, 134, 255}, "Wheat"}
	Peach     = Color{color.RGBA{247, 164, 139, 255}, "Peach"}
	Mint      = Color{color.RGBA{191, 255, 128, 255}, "Mint"}
	Lavender  = Color{color.RGBA{219, 184, 236, 255}, "Lavender"}
	Coal      = Color{color.RGBA{79, 79, 85, 255}, "Coal"}
	Snow      = Color{color.RGBA{236, 240, 255, 255}, "Snow"}
	Emerald   = Color{color.RGBA{0, 120, 30, 255}, "Emerald"}
	Peanut    = Color{color.RGBA{164, 111, 52, 255}, "Peanut"}
)

colour values from WorldEdit, names from https://gaming-tools.com/warcraft-3/patch-1-29/

View Source
var (
	Human      = Race{"Human"}
	Orc        = Race{"Orc"}
	NightElf   = Race{"Night Elf"}
	Undead     = Race{"Undead"}
	RandomRace = Race{"Random"}
)
View Source
var WC3Strings = map[string]StringsEntity{} /* 2370 elements not displayed */

Functions

This section is empty.

Types

type AIStrength

type AIStrength byte

func (AIStrength) MarshalText

func (a AIStrength) MarshalText() ([]byte, error)

func (AIStrength) String

func (a AIStrength) String() string

type Ability

type Ability interface {
	fmt.Stringer
	APMChange() int
}

type Action

type Action struct {
	Ability Ability
	Time    time.Duration
	Player  *Player
}

func (Action) MarshalText

func (a Action) MarshalText() (text []byte, err error)

func (Action) String

func (a Action) String() string
func (a Action) MarshalJSON() ([]byte, error) {
	// FIXME: temporary until data is more structured
	// i.e. by default the action string representation won't be encoded
	return json.Marshal(struct {
		Time time.Duration
		Player *Player
		Data interface{}
	}{a.Time, a.Player, a.Ability})
}

type BasicAbility

type BasicAbility struct {
	AbilityFlags uint16
	ItemId       ItemId
}

func (BasicAbility) APMChange

func (a BasicAbility) APMChange() int

func (BasicAbility) String

func (a BasicAbility) String() string

type BattleNet2Account

type BattleNet2Account struct {
	PlayerId  int
	Avatar    string
	Username  string
	Clan      string
	ExtraData []byte
}

type ChatMessage

type ChatMessage struct {
	Timestamp   time.Duration
	Author      Slot
	Body        string
	Destination MsgDestination
}

type Color

type Color struct {
	color.Color
	// contains filtered or unexported fields
}

func (Color) MarshalText

func (c Color) MarshalText() ([]byte, error)

func (Color) String

func (c Color) String() string

type Expac

type Expac int
const (
	ReignOfChaos Expac = iota
	TheFrozenThrone
)

type GameOptions

type GameOptions struct {
	MapName               string
	CreatorName           string
	TeamsTogether         bool
	LockTeams             bool
	FullSharedUnitControl bool
	RandomHero            bool
	RandomRaces           bool
	Speed                 GameSpeed
	Visibility            Visibility
	ObserverSetting       ObserverSetting
	GameName              string
}

type GameSpeed

type GameSpeed int

type GameType

type GameType uint16

type GiveOrDropItem

type GiveOrDropItem struct {
	ObjectTargetedAbility
	ItemObjectId1, ItemObjectId2 ObjectId
}

func (GiveOrDropItem) String

func (g GiveOrDropItem) String() string

type ItemId

type ItemId [4]byte

func (ItemId) String

func (a ItemId) String() string

type MsgDestination

type MsgDestination interface {
	fmt.Stringer
	// contains filtered or unexported methods
}

type MsgToAllies

type MsgToAllies struct{}

func (MsgToAllies) String

func (MsgToAllies) String() string

type MsgToEveryone

type MsgToEveryone struct{}

func (MsgToEveryone) String

func (MsgToEveryone) String() string

type MsgToObservers

type MsgToObservers struct{}

func (MsgToObservers) String

func (MsgToObservers) String() string

type MsgToPlayer

type MsgToPlayer struct{ Target Slot }

func (MsgToPlayer) String

func (m MsgToPlayer) String() string

type ObjectId

type ObjectId uint32

func (ObjectId) IsGround

func (o ObjectId) IsGround() bool

func (ObjectId) String

func (o ObjectId) String() string

type ObjectTargetedAbility

type ObjectTargetedAbility struct {
	TargetedAbility
	TargetObjectId1, TargetObjectId2 ObjectId
}

func (ObjectTargetedAbility) String

func (a ObjectTargetedAbility) String() string

func (ObjectTargetedAbility) TargetsGround

func (a ObjectTargetedAbility) TargetsGround() bool

type ObserverSetting

type ObserverSetting int

type Player

type Player struct {
	Name   string
	Id     int
	SlotId int

	BattleNet *BattleNet2Account
	// contains filtered or unexported fields
}

func (Player) String

func (p Player) String() string

type PointF

type PointF struct {
	X float32
	Y float32
}

func (PointF) String

func (p PointF) String() string

type Race

type Race struct {
	// contains filtered or unexported fields
}

func (Race) MarshalText

func (r Race) MarshalText() ([]byte, error)

func (Race) ShortName

func (r Race) ShortName() string

func (Race) String

func (r Race) String() string

type Replay

type Replay struct {
	Duration      time.Duration
	Version       int
	BuildNumber   int
	Expac         Expac
	IsMultiplayer bool

	GameType    GameType
	PrivateGame bool
	GameOptions GameOptions
	Players     map[int]*Player
	Slots       []Slot
	RandomSeed  uint32

	ChatMessages []ChatMessage
	Saver        *Player
	WinnerTeam   int // -1 represents a draw
	Actions      []Action
	// contains filtered or unexported fields
}

func ParseReplay

func ParseReplay(file io.Reader) (rep Replay, err error)

ParseReplay parses an opened .w3g file.

func ParseReplayDebug

func ParseReplayDebug(file io.Reader) (rep Replay, err error)

ParseReplayDebug is the same as ParseReplay but dumps binaries and prints to stdout too

type Slot

type Slot struct {
	Id     int
	Player *Player
	IsCPU  bool
	Race   Race

	SlotStatus         slotStatus
	TeamNumber         int
	Color              Color
	AIStrength         AIStrength
	Handicap           int
	MapDownloadPercent byte
	// contains filtered or unexported fields
}

func (Slot) String

func (s Slot) String() string

String returns the text you'd see in-game as the name of that slot.

type StringsEntity

type StringsEntity struct {
	// Name is the full name of the entity, e.g. "Peasant"
	Name string
	// Code is the 4-char string it is stored under, e.g. "hpea"
	Code string
	// Tip is the imperative form, e.g. "Train Peasant"
	Tip string
}

StringsEntity represents a definition from the WC3 *strings.txt files, which tools/gen_strings uses to generate mappings. Replay files only contain short codes, e.g. "hpea", from which we want to get "Peasant" (and possibly other data later).

type TargetedAbility

type TargetedAbility struct {
	BasicAbility
	Target PointF
}

func (TargetedAbility) String

func (a TargetedAbility) String() string

type TwoTargetTwoItemAbility

type TwoTargetTwoItemAbility struct {
	TargetedAbility
	ItemId2 ItemId
	Target2 PointF
}

func (TwoTargetTwoItemAbility) String

func (t TwoTargetTwoItemAbility) String() string

type UnexpectedValueError

type UnexpectedValueError struct {
	// contains filtered or unexported fields
}

func (UnexpectedValueError) Error

func (u UnexpectedValueError) Error() string

type Visibility

type Visibility int
const (
	Default Visibility = iota
	AlwaysVisible
	MapExplored
	HideTerrain
)

Directories

Path Synopsis
examples
tools
gen_strings
This program generates strings.go from a directory containing *strings.txt extracted from the WC3 mpq/casc
This program generates strings.go from a directory containing *strings.txt extracted from the WC3 mpq/casc

Jump to

Keyboard shortcuts

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