123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053 |
- package etdd
- import (
- "encoding/binary"
- "errors"
- "fmt"
- "math"
- "os"
- "regexp"
- "strconv"
- "strings"
- )
- const (
- maxMessageLength int = 32768
- maxStringChars int = 1024
- maxConfigStrings int = 1024
- bigInfoString int = 8192
- gEntityNumBits int = 10
- maxParseEntities int = 2048
- maxGEntities int = 1 << uint(gEntityNumBits)
- packetBackup int = 32
- packetMask int = packetBackup - 1
- maxPlayersNum int = 64
- csServerInfo int = 0
- csLevelStartTime int = 11
- csPlayers int = 689
- svcNop byte = byte(1)
- svcGameState byte = byte(2)
- svcConfigString byte = byte(3)
- svcBaseLine byte = byte(4)
- svcServerCommand byte = byte(5)
- svcSnapshot byte = byte(7)
- svcEof byte = byte(8)
- Chat string = "c"
- TeamChat string = "tc"
- )
- // Player events are not that interesting. We track them, but not actually using them.
- const (
- psEventSequence int = 27
- psEventNum int = 4
- psEventParams int = 4
- psClientNum int = psEventSequence + psEventNum + psEventParams + 1
- )
- // Event fields beyond esEventSequence seem to be just movement related.
- const (
- esType int = 0
- )
- // Event entity related stuff. Not sure how the field positioning works.
- const (
- eeMod int = 1
- eeVictim int = 20
- eeAttacker int = 21
- eeObituary int = 71
- )
- const (
- etEvents int = 60
- )
- type snapshot struct {
- valid bool
- deltaNum int
- messageNum int
- serverTime int
- flags int
- parseEntitiesNum int
- entitiesNum int
- ps PlayerState
- }
- type entityState struct {
- number int
- fields [71]int
- }
- type PlayerState struct {
- Num int
- Name string
- Team int
- fields [77]int
- }
- type Demo struct {
- file *os.File
- commandSequence int
- messageSequence int
- parseEntities [maxParseEntities]entityState
- baselines [maxGEntities]entityState
- snapshots [packetBackup]snapshot
- players [maxPlayersNum]PlayerState
- snapshot snapshot
- parseEntitiesNum int
- startTime int
- levelTime int
- bloc int // We need to maintain this in our thread.
- initialized bool
- illegible int
- }
- type message struct {
- oob bool
- data []byte
- size int
- read int
- bit int
- bloc *int
- }
- // crafted from netField_t entityStateFields[]
- var entityFieldBits = []int{
- 8, 24, 8, 32, 32, 0, 0, 0, 0, 0, 0, 8, 32, 32, 0, 0, 0, 0, 0, 0, 32, 32, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10, 8, 32, 32, 9, 9, 16, 8, 24, 10, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 16, 8, 10, 10, 10, 32, 32, 32, 8, 8, 32, 32, 32, 4, 2,
- }
- // netField_t playerStateFields[]
- var playerStateFieldBits = []int{
- 32, 8, 8, 16, -16, 0, 0, 0, 0, 0, 0, -16, -16, -16, 16, 0, 16, 16, 16, 16, 10, 16, 16,
- 10, 10, 8, 24, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 32, 32, 7, 4, 10, 0, 0, 0, 8, 8, 8, 8, 8,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 16, 8, 8, 32, 8, 8, 8, 8, 32, 8, 8, 2,
- }
- func Open(name string) (Demo, error) {
- d := Demo{}
- for i := 0; i < maxPlayersNum; i++ {
- d.players[i].Num = i
- }
- file, err := os.Open(name)
- if err == nil {
- d.file = file
- }
- return d, err
- }
- func (d *Demo) Read(handler *Logger) {
- for {
- if d.file == nil {
- break
- } else {
- // Sequence number.
- seq, err := d.readLittleLong()
- if err != nil {
- handler.Error(err)
- break
- }
- // Needed by delta parser.
- d.messageSequence = seq
- // Message length.
- size, err := d.readLittleLong()
- if err != nil {
- handler.Error(err)
- break
- }
- // Graceful end.
- if size == math.MaxUint32 {
- break
- }
- if size > maxMessageLength {
- handler.Error(errors.New("message size overflow"))
- break
- }
- // Initialize the message.
- m := message{false, make([]byte, size), size, 0, 0, &d.bloc}
- r, err := d.readBuffer(m.data)
- if err != nil {
- if r < size {
- handler.Error(errors.New(err.Error() + "; demo file was truncated"))
- } else {
- handler.Error(err)
- }
- break
- }
- // Parse the message.
- m.Parse(handler, d)
- }
- }
- handler.End()
- }
- func (d *Demo) readLittleLong() (int, error) {
- s, err := d.readBytes(4)
- if err != nil {
- d.Close()
- return -1, err
- }
- return int(binary.LittleEndian.Uint32(s)), nil
- }
- func (d *Demo) readBytes(n int) ([]byte, error) {
- s := make([]byte, n)
- _, err := d.readBuffer(s)
- return s, err
- }
- func (d *Demo) readBuffer(buffer []byte) (int, error) {
- r, err := d.file.Read(buffer)
- if err != nil {
- d.Close()
- }
- if r != len(buffer) && err == nil {
- err = errors.New("incorrect number of bytes read")
- }
- return r, err
- }
- func (d *Demo) Close() {
- if d.file != nil {
- _ = d.file.Close()
- d.file = nil
- }
- }
- func (d *Demo) checkEvents(handler *Logger) {
- // Entities event detection.
- for i := 0; i < d.snapshot.entitiesNum; i++ {
- entity := &d.parseEntities[(d.snapshot.parseEntitiesNum+i)&(maxParseEntities-1)]
- // We are only interested in event entities.
- if entity.fields[esType] > etEvents {
- switch (entity.fields[esType] - etEvents) & (^768) {
- case eeObituary:
- d.obituary(handler, entity.fields[eeVictim], entity.fields[eeAttacker], entity.fields[eeMod])
- break
- }
- // Excludes from subsequent event processing.
- entity.fields[esType] = 0
- }
- }
- }
- func (d *Demo) obituary(handler *Logger, victimNum int, attackerNum int, mod int) {
- var victim *PlayerState
- var attacker *PlayerState
- if victimNum < 0 || victimNum >= maxPlayersNum {
- return
- }
- victim = &d.players[victimNum]
- if attackerNum >= 0 && attackerNum < maxPlayersNum {
- attacker = &d.players[attackerNum]
- }
- handler.Obituary(d.levelTime, victim, attacker, mod)
- }
- func (m *message) Parse(handler *Logger, demo *Demo) {
- // Acknowledge number.
- m.readLong()
- for {
- if demo.file == nil {
- break
- }
- if m.read > m.size {
- handler.Error(errors.New("read past end of server message"))
- break
- }
- command := m.readByte()
- if command == svcEof || demo.file == nil {
- break
- }
- switch command {
- case svcNop:
- break
- case svcGameState:
- m.parseGameState(handler, demo)
- break
- case svcServerCommand:
- m.parseCommandString(handler, demo)
- break
- case svcSnapshot:
- m.parseSnapshot(handler, demo)
- break
- default:
- demo.illegible++
- handler.Error(errors.New(fmt.Sprintf("illegible server message %d", int(command))))
- }
- if demo.illegible == 10 {
- demo.file = nil
- break
- }
- }
- m.parseBinaryMessage(handler, demo)
- // Is this the best time to do it?
- demo.checkEvents(handler)
- }
- func (m *message) parseGameState(handler *Logger, demo *Demo) {
- demo.commandSequence = m.readLong()
- for {
- command := m.readByte()
- if command == svcEof {
- break
- }
- switch command {
- case svcConfigString:
- i := m.readShort()
- if i < 0 || i >= maxConfigStrings {
- handler.Error(errors.New(fmt.Sprintf("config strings overflow")))
- demo.Close()
- return
- }
- s := m.readBigString()
- demo.cs(handler, i, s)
- break
- case svcBaseLine:
- nn := m.readBits(10)
- if nn < 0 || nn >= maxGEntities {
- handler.Error(errors.New(fmt.Sprintf("baseline number out of range: %d", nn)))
- demo.Close()
- return
- }
- nullState := entityState{}
- m.readDeltaEntity(demo, &nullState, &demo.baselines[nn], nn)
- break
- default:
- handler.Error(errors.New(fmt.Sprintf("bad command byte %d", int(command))))
- return
- }
- }
- m.readLong() // clc.clientNum...
- m.readLong() // ...and a checksum.
- }
- func (m *message) parseCommandString(handler *Logger, demo *Demo) {
- seq := m.readLong()
- s := m.readString()
- if demo.commandSequence >= seq {
- return
- }
- demo.commandSequence = seq
- if len(s) == 0 || s[0] == '*' {
- return
- }
- var command string
- if !word(&s, &command) {
- return
- }
- switch command {
- case "chat", "print", "cpm", "cp", "b", "sc":
- if command == "b" {
- var __ string
- word(&s, &__)
- }
- r := regexp.MustCompile(`^("|\s)*|("|\s|\^.)*$|\[lo[nf]]|" -?\d+ \d+$`)
- s = r.ReplaceAllString(s, "")
- // Line break would fuck statistics up.
- if command == "sc" {
- // I wonder what this is, huh?
- if s == "0" {
- break
- }
- lines := strings.Split(strings.TrimSpace(s), "\n")
- for _, line := range lines {
- if line != "" {
- handler.Print(demo.levelTime, line)
- }
- }
- } else {
- handler.Print(demo.levelTime, s)
- }
- break
- case Chat, TeamChat, "cs":
- var ps string
- if word(&s, &ps) {
- p, _ := strconv.Atoi(ps)
- var message string
- if quoted(&s, &message) {
- if command == "cs" {
- demo.cs(handler, p, message)
- } else {
- handler.Chat(demo.levelTime, demo.players[p], message, command)
- }
- }
- }
- break
- default:
- return
- }
- }
- func (m *message) parseSnapshot(handler *Logger, demo *Demo) {
- newSnap := snapshot{}
- newSnap.messageNum = demo.messageSequence
- newSnap.serverTime = m.readLong()
- deltaNum := m.readBits(8)
- if deltaNum == 0 {
- newSnap.deltaNum = -1
- } else {
- newSnap.deltaNum = demo.messageSequence - deltaNum
- }
- newSnap.flags = m.readBits(8)
- var old *snapshot
- if newSnap.deltaNum <= 0 {
- newSnap.valid = true
- } else {
- old = &demo.snapshots[newSnap.deltaNum&packetMask]
- if old.valid && old.messageNum == newSnap.deltaNum && demo.parseEntitiesNum-old.parseEntitiesNum <= maxParseEntities-128 {
- newSnap.valid = true
- }
- }
- areaMask := m.readBits(8)
- if areaMask > 32 {
- return
- }
- for i := 0; i < areaMask; i++ {
- m.readBits(8)
- }
- if old != nil {
- m.readDeltaPlayerState(&old.ps, &newSnap.ps)
- } else {
- m.readDeltaPlayerState(nil, &newSnap.ps)
- }
- // This is basically CL_ParsePacketEntities()
- newSnap.parseEntitiesNum = demo.parseEntitiesNum
- newSnap.entitiesNum = 0
- index, oNum := 0, 0
- var oldState *entityState = nil
- if old == nil {
- oNum = 99999
- } else {
- if index >= old.entitiesNum {
- oNum = 99999
- } else {
- oldState = &demo.parseEntities[(old.parseEntitiesNum+index)&(maxParseEntities-1)]
- oNum = oldState.number
- }
- }
- for {
- nn := m.readBits(gEntityNumBits)
- if nn == maxGEntities-1 {
- break
- }
- if m.read > m.size {
- demo.Close()
- return
- }
- for oNum < nn {
- m.deltaEntity(demo, &newSnap, oNum, oldState, true)
- index++
- if index >= old.entitiesNum {
- oNum = 99999
- } else {
- oldState = &demo.parseEntities[(old.parseEntitiesNum+index)&(maxParseEntities-1)]
- oNum = oldState.number
- }
- }
- if oNum == nn {
- m.deltaEntity(demo, &newSnap, nn, oldState, false)
- index++
- if index >= old.entitiesNum {
- oNum = 99999
- } else {
- oldState = &demo.parseEntities[(old.parseEntitiesNum+index)&(maxParseEntities-1)]
- oNum = oldState.number
- }
- continue
- }
- if oNum > nn && nn < maxGEntities {
- m.deltaEntity(demo, &newSnap, nn, &demo.baselines[nn], false)
- }
- }
- for oNum != 99999 {
- m.deltaEntity(demo, &newSnap, oNum, oldState, true)
- index++
- if index >= old.entitiesNum {
- oNum = 99999
- } else {
- oldState = &demo.parseEntities[(old.parseEntitiesNum+index)&(maxParseEntities-1)]
- oNum = oldState.number
- }
- }
- // CL_ParsePacketEntities end.
- if !newSnap.valid {
- return
- }
- oldMessageNum := demo.snapshot.messageNum + 1
- if newSnap.messageNum-oldMessageNum >= packetBackup {
- oldMessageNum = newSnap.messageNum - (packetBackup - 1)
- }
- for oldMessageNum < newSnap.messageNum {
- demo.snapshots[oldMessageNum&packetMask].valid = false
- oldMessageNum++
- }
- demo.snapshot = newSnap
- demo.snapshots[newSnap.messageNum&packetMask] = newSnap
- demo.levelTime = newSnap.serverTime - demo.startTime
- // Transfer player state updates to CG players struct.
- player := &demo.players[newSnap.ps.fields[psClientNum]]
- for i := 0; i < len(player.fields); i++ {
- player.fields[i] = newSnap.ps.fields[i]
- }
- }
- func (m *message) parseBinaryMessage(handler *Logger, demo *Demo) {
- // Switch to uncompressed reading mode.
- m.bit = (m.bit + 1) & ^7
- m.oob = true
- size := m.size - m.read
- if size <= 0 || size > maxMessageLength {
- return
- }
- // Nothing happens here?
- }
- func (m *message) deltaEntity(demo *Demo, snapshot *snapshot, num int, oldState *entityState, unchanged bool) {
- state := &demo.parseEntities[demo.parseEntitiesNum&(maxParseEntities-1)]
- if unchanged {
- *state = *oldState
- } else {
- m.readDeltaEntity(demo, oldState, state, num)
- }
- if state.number == maxGEntities-1 {
- return
- }
- demo.parseEntitiesNum++
- snapshot.entitiesNum++
- }
- func (m *message) readDeltaEntity(demo *Demo, from *entityState, to *entityState, number int) {
- if m.readBits(1) == 1 {
- *to = entityState{}
- to.number = maxGEntities - 1
- return
- }
- if m.readBits(1) == 0 {
- *to = *from
- to.number = number
- return
- }
- lc := m.readBits(8)
- if lc > len(entityFieldBits) {
- lc = len(entityFieldBits)
- }
- to.number = number
- for i := 0; i < lc; i++ {
- if m.readBits(1) == 1 {
- if entityFieldBits[i] == 0 {
- if m.readBits(1) == 1 {
- if m.readBits(1) == 0 {
- to.fields[i] = m.readBits(13)
- } else {
- to.fields[i] = m.readBits(32)
- }
- }
- } else {
- if m.readBits(1) == 1 {
- to.fields[i] = m.readBits(entityFieldBits[i])
- }
- }
- }
- }
- }
- func (m *message) readDeltaPlayerState(from *PlayerState, to *PlayerState) {
- if from == nil {
- from = &PlayerState{}
- }
- *to = *from
- lc := m.readBits(8)
- for i := 0; i < lc && i < len(playerStateFieldBits); i++ {
- if m.readBits(1) == 1 {
- if playerStateFieldBits[i] == 0 {
- if m.readBits(1) == 0 {
- to.fields[i] = m.readBits(13)
- } else {
- to.fields[i] = m.readBits(32)
- }
- } else {
- to.fields[i] = m.readBits(playerStateFieldBits[i])
- }
- }
- }
- if m.readBits(1) == 1 {
- // stats, persistent and holdable stats
- for a := 0; a < 3; a++ {
- if m.readBits(1) == 1 {
- bits := m.readShort()
- for i := 0; i < 16; i++ {
- if (bits & (1 << uint(i))) != 0 {
- m.readShort()
- }
- }
- }
- }
- // powerups
- if m.readBits(1) == 1 {
- bits := m.readShort()
- for i := 0; i < 16; i++ {
- if (bits & (1 << uint(i))) != 0 {
- m.readLong()
- }
- }
- }
- }
- // check for any ammo change (0-63)
- if m.readBits(1) == 1 {
- for j := 0; j < 4; j++ {
- if m.readBits(1) == 1 {
- bits := m.readShort()
- for i := 0; i < 16; i++ {
- if (bits & (1 << uint(i))) != 0 {
- m.readShort()
- }
- }
- }
- }
- }
- // ammo in clip
- for j := 0; j < 4; j++ {
- if m.readBits(1) == 1 {
- bits := m.readShort()
- for i := 0; i < 16; i++ {
- if (bits & (1 << uint(i))) != 0 {
- m.readShort()
- }
- }
- }
- }
- }
- func (m *message) readLong() int {
- c := m.readBits(32)
- if m.read > m.size {
- return -1
- }
- return c
- }
- func (m *message) readShort() int {
- c := m.readBits(16)
- if m.read > m.size {
- return -1
- }
- return c
- }
- func (m *message) readString() string {
- s := ""
- for l := 0; l < maxStringChars && m.read <= m.size; l++ {
- c := m.readByte()
- if c == byte(0) {
- break
- }
- if c == '%' || c > 127 {
- c = '.'
- }
- s += string(c)
- }
- return s
- }
- func (m *message) readBigString() string {
- s := ""
- for l := 0; l < bigInfoString && m.read <= m.size; l++ {
- c := m.readByte()
- if c == byte(0) {
- break
- }
- if c == '%' {
- c = '.'
- }
- s += string(c)
- }
- return s
- }
- func (m *message) readByte() byte {
- return byte(m.readBits(8))
- }
- func (m *message) readBits(bits int) int {
- value, r, s, i, n := 0, 0, false, 0, 0
- if bits < 0 {
- bits = -bits
- s = true
- }
- if m.oob {
- if bits == 8 || bits == 16 || bits == 32 {
- for i*8 < bits {
- value |= int(m.data[m.read]) << uint8(i*8)
- m.read += 1
- m.bit += bits
- i++
- }
- } else {
- panic(fmt.Sprintf("can't read %d bits", bits))
- }
- } else {
- if (bits & 7) != 0 {
- n = bits & 7
- for i = 0; i < n; i++ {
- value |= HuffGetBit(m.data, &m.bit, m.bloc) << uint(i)
- }
- bits = bits - n
- }
- for i = 0; bits != 0 && i < bits; i += 8 {
- HuffOffsetReceive(&r, m.data, &m.bit, m.bloc)
- value |= r << uint(i+n)
- }
- m.read = (m.bit >> 3) + 1
- }
- if s && (value&(1<<uint(bits-1))) != 0 {
- value |= -1 ^ ((1 << uint(bits)) - 1)
- }
- return value
- }
- func (d *Demo) cs(handler *Logger, p int, s string) {
- if p == csServerInfo && !d.initialized {
- d.initialized = true
- bits := strings.Split(s, "\\")
- vars := make(map[string]string)
- for i := 1; i+1 < len(bits); i += 2 {
- vars[bits[i]] = bits[i+1]
- }
- if hostname, ok := vars["sv_hostname"]; ok {
- if mapname, ok := vars["mapname"]; ok {
- handler.Start(hostname, mapname)
- }
- }
- return
- }
- if p == csLevelStartTime {
- d.startTime, _ = strconv.Atoi(s)
- return
- }
- if p < csPlayers || p >= csPlayers+maxPlayersNum {
- return
- }
- bits := strings.Split(s, "\\")
- state := &d.players[p-csPlayers]
- for i := 0; i < len(bits); i += 2 {
- switch bits[i] {
- case "n":
- state.Name = bits[i+1]
- break
- case "t":
- state.Team, _ = strconv.Atoi(bits[i+1])
- break
- }
- }
- }
- func word(s *string, word *string) bool {
- i := strings.Index(*s, " ")
- if i < 1 {
- return false
- }
- *word = (*s)[0:i]
- *s = (*s)[i+1:]
- return true
- }
- func quoted(s *string, quoted *string) bool {
- if (*s)[0] == '"' {
- *s = (*s)[1:]
- }
- i := strings.Index(*s, "\"")
- if i < 1 {
- return false
- }
- *quoted = (*s)[0:i]
- *s = (*s)[i+1:]
- return true
- }
|