From 8b4c6662843c6e249a7e3e3c5d8f32dc06c9bbc3 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Fri, 2 Sep 2016 16:32:07 -0400 Subject: [PATCH] stop calling things send tables there is no entity or type called send tables, that's just the label for the message. it's sending a flattened representation of datatype specifications. --- class_info.go | 9 +---- dt/serializer.go | 20 ---------- dt/tables.go | 56 ---------------------------- ent/class.go | 24 ++++++++++++ ent/context.go | 28 ++++++++++++++ ent/entity.go | 8 ++++ {dt => ent}/field.go | 2 +- ent/namespace.go | 86 +++++++++++++++++++++++++++++++++++++++++++ {dt => ent}/symbol.go | 2 +- entities.go | 31 ++++++++++++++++ main.go | 5 +-- send_tables.go | 38 ------------------- 12 files changed, 181 insertions(+), 128 deletions(-) delete mode 100644 dt/serializer.go delete mode 100644 dt/tables.go create mode 100644 ent/class.go create mode 100644 ent/context.go create mode 100644 ent/entity.go rename {dt => ent}/field.go (99%) create mode 100644 ent/namespace.go rename {dt => ent}/symbol.go (97%) delete mode 100644 send_tables.go diff --git a/class_info.go b/class_info.go index 21fd0ec..397b337 100644 --- a/class_info.go +++ b/class_info.go @@ -6,13 +6,7 @@ import ( "github.com/jordanorelli/hyperstone/dota" ) -// classInfo container contains info about entity classes found in a given -// replay -type classInfo struct { - names map[int]string -} - -func (c *classInfo) handle(m proto.Message) { +func dumpClasses(m proto.Message) { v, ok := m.(*dota.CDemoClassInfo) if !ok { return @@ -20,6 +14,5 @@ func (c *classInfo) handle(m proto.Message) { for _, class := range v.GetClasses() { fmt.Printf("class-id: %d network-name: %s table-name: %s\n", class.GetClassId(), class.GetNetworkName(), class.GetTableName()) - c.names[int(class.GetClassId())] = class.GetNetworkName() } } diff --git a/dt/serializer.go b/dt/serializer.go deleted file mode 100644 index eecb042..0000000 --- a/dt/serializer.go +++ /dev/null @@ -1,20 +0,0 @@ -package dt - -import ( - "github.com/jordanorelli/hyperstone/dota" -) - -type Serializer struct { - Name Symbol - Version int - Fields []*Field -} - -func (s *Serializer) fromProto(v *dota.ProtoFlattenedSerializerT, st *SymbolTable, fields []Field) { - s.Name = st.Symbol(int(v.GetSerializerNameSym())) - s.Version = int(v.GetSerializerVersion()) - s.Fields = make([]*Field, len(v.GetFieldsIndex())) - for i, fi := range v.GetFieldsIndex() { - s.Fields[i] = &fields[fi] - } -} diff --git a/dt/tables.go b/dt/tables.go deleted file mode 100644 index b436b93..0000000 --- a/dt/tables.go +++ /dev/null @@ -1,56 +0,0 @@ -package dt - -import ( - "fmt" - "io" - - "github.com/jordanorelli/hyperstone/dota" -) - -// TableSet represents a collection of tables. -type TableSet struct { - SymbolTable - Fields []Field - Serializers []Serializer -} - -func (t *TableSet) DebugPrint(w io.Writer) { - fmt.Fprintln(w, "Symbols:") - for _, sym := range t.SymbolTable { - fmt.Fprintf(w, "\t%s\n", sym) - } - fmt.Fprintln(w, "Fields:") - for _, f := range t.Fields { - fmt.Fprintf(w, "\t%s\n", f) - } - fmt.Fprintln(w, "Serializers:") - for _, s := range t.Serializers { - fmt.Fprintf(w, "\t%s (%d):\n", s.Name, s.Version) - for _, f := range s.Fields { - fmt.Fprintf(w, "\t\t%s\n", f) - } - } -} - -// ParseFlattened parses a flattened TableSet definition, as defined by the -// Dota replay protobufs. -func ParseFlattened(m *dota.CSVCMsg_FlattenedSerializer) *TableSet { - ts := &TableSet{SymbolTable: SymbolTable(m.GetSymbols())} - ts.parseFields(m.GetFields()) - ts.parseSerializers(m.GetSerializers()) - return ts -} - -func (ts *TableSet) parseFields(flat []*dota.ProtoFlattenedSerializerFieldT) { - ts.Fields = make([]Field, len(flat)) - for i, f := range flat { - ts.Fields[i].fromProto(f, &ts.SymbolTable) - } -} - -func (ts *TableSet) parseSerializers(flat []*dota.ProtoFlattenedSerializerT) { - ts.Serializers = make([]Serializer, len(flat)) - for i, s := range flat { - ts.Serializers[i].fromProto(s, &ts.SymbolTable, ts.Fields) - } -} diff --git a/ent/class.go b/ent/class.go new file mode 100644 index 0000000..aacec5f --- /dev/null +++ b/ent/class.go @@ -0,0 +1,24 @@ +package ent + +import ( + "github.com/jordanorelli/hyperstone/dota" +) + +// Class represents a set of constraints around an Entity. +type Class struct { + Name Symbol + Version int + Fields []*Field +} + +type classId struct { + name Symbol + version int +} + +func (c *Class) fromProto(v *dota.ProtoFlattenedSerializerT, fields []Field) { + c.Fields = make([]*Field, len(v.GetFieldsIndex())) + for i, fi := range v.GetFieldsIndex() { + c.Fields[i] = &fields[fi] + } +} diff --git a/ent/context.go b/ent/context.go new file mode 100644 index 0000000..da1f7e1 --- /dev/null +++ b/ent/context.go @@ -0,0 +1,28 @@ +package ent + +import ( + "github.com/jordanorelli/hyperstone/bit" +) + +type Context struct { + Namespace + entities map[int]Entity +} + +func NewContext() *Context { + return &Context{entities: make(map[int]Entity)} +} + +func (c *Context) CreateEntity(id int, r bit.Reader) { +} + +func (c *Context) GetEntity(id int) Entity { + return Entity{} +} + +func (c *Context) DeleteEntity(id int) { +} + +func (c *Context) LeaveEntity(id int) { + +} diff --git a/ent/entity.go b/ent/entity.go new file mode 100644 index 0000000..d782eb5 --- /dev/null +++ b/ent/entity.go @@ -0,0 +1,8 @@ +package ent + +type Entity struct { +} + +func (e *Entity) Update(raw []byte) { + +} diff --git a/dt/field.go b/ent/field.go similarity index 99% rename from dt/field.go rename to ent/field.go index d560760..8aad2c2 100644 --- a/dt/field.go +++ b/ent/field.go @@ -1,4 +1,4 @@ -package dt +package ent import ( "bytes" diff --git a/ent/namespace.go b/ent/namespace.go new file mode 100644 index 0000000..12bf252 --- /dev/null +++ b/ent/namespace.go @@ -0,0 +1,86 @@ +package ent + +import ( + "fmt" + "math" + + "github.com/golang/protobuf/proto" + + "github.com/jordanorelli/hyperstone/bit" + "github.com/jordanorelli/hyperstone/dota" +) + +// Namespace registers the names of known classess, associating their ids to +// their network types. +type Namespace struct { + SymbolTable + idBits int + + // maps ClassInfo ids to class names + classIds map[int]string + + // maps classes to their {name, version} id pairs + classes map[classId]*Class + + // maps a class name to every version of the class + classesByName map[string]map[int]*Class +} + +// Merges in the ClassInfo data found in the replay protobufs +func (n *Namespace) MergeClassInfo(ci *dota.CDemoClassInfo) { + if n.classIds == nil { + n.classIds = make(map[int]string, len(ci.GetClasses())) + } + for _, class := range ci.GetClasses() { + n.classIds[int(class.GetClassId())] = class.GetNetworkName() + } + n.idBits = int(math.Floor(math.Log2(float64(len(n.classIds))))) + 1 +} + +func (n *Namespace) MergeSendTables(st *dota.CDemoSendTables) { + // sendtables only has one field, a binary data field. + data := st.GetData() + br := bit.NewBytesReader(data) + + // body is length-prefixed + size := int(bit.ReadVarInt(br)) + + buf := make([]byte, size) + br.Read(buf) + + flat := dota.CSVCMsg_FlattenedSerializer{} + if err := proto.Unmarshal(buf, &flat); err != nil { + fmt.Printf("error: %v\n", err) + return + } + + n.SymbolTable = SymbolTable(flat.GetSymbols()) + + fields := make([]Field, len(flat.GetFields())) + for i, f := range flat.GetFields() { + fields[i].fromProto(f, &n.SymbolTable) + } + + n.classes = make(map[classId]*Class, len(flat.GetSerializers())) + n.classesByName = make(map[string]map[int]*Class, len(flat.GetSerializers())) + + for _, c := range flat.GetSerializers() { + class := Class{} + class.fromProto(c, fields) + + name := n.Symbol(int(c.GetSerializerNameSym())) + version := int(c.GetSerializerVersion()) + id := classId{name: name, version: version} + n.classes[id] = &class + + if n.classesByName[name.String()] == nil { + n.classesByName[name.String()] = map[int]*Class{version: &class} + } else { + n.classesByName[name.String()][version] = &class + } + } +} + +func (n *Namespace) ReadClassId(r bit.Reader) int { + return int(r.ReadBits(uint(n.idBits))) +} diff --git a/dt/symbol.go b/ent/symbol.go similarity index 97% rename from dt/symbol.go rename to ent/symbol.go index 67e1731..726293c 100644 --- a/dt/symbol.go +++ b/ent/symbol.go @@ -1,4 +1,4 @@ -package dt +package ent // the internal representation of table data refers to all labels as // interned strings (symbols). This array of string contains the mapping of diff --git a/entities.go b/entities.go index 5d35f65..93a7b61 100644 --- a/entities.go +++ b/entities.go @@ -3,7 +3,9 @@ package main import ( "fmt" "github.com/golang/protobuf/proto" + "github.com/jordanorelli/hyperstone/bit" "github.com/jordanorelli/hyperstone/dota" + "github.com/jordanorelli/hyperstone/ent" ) // type CSVCMsg_PacketEntities struct { @@ -20,12 +22,41 @@ import ( // } func dumpEntities(m proto.Message) { + ctx := ent.NewContext() + switch v := m.(type) { + case *dota.CDemoSendTables: + ctx.MergeSendTables(v) + + case *dota.CDemoClassInfo: + ctx.MergeClassInfo(v) + case *dota.CSVCMsg_PacketEntities: data := v.GetEntityData() if len(data) > 32 { data = data[:32] } fmt.Printf("{MaxEntries: %d UpdatedEntries: %v IsDelta: %t UpdateBaseline: %t Baseline: %d DeltaFrom: %d EntityData: %x PendingFullFrame: %t ActiveSpawngroupHandle: %d}\n", v.GetMaxEntries(), v.GetUpdatedEntries(), v.GetIsDelta(), v.GetUpdateBaseline(), v.GetBaseline(), v.GetDeltaFrom(), data, v.GetPendingFullFrame(), v.GetActiveSpawngroupHandle()) + + br := bit.NewBytesReader(data) + id := -1 + for i := 0; i < int(v.GetUpdatedEntries()); i++ { + id++ + // there may be a jump indicator, indicating how many id positions + // to skip. + id += int(bit.ReadUBitVar(br)) + + // next two bits encode one of four entity mutate operations + switch br.ReadBits(2) { + case 0: + // update + case 1: + // leave + case 2: + ctx.CreateEntity(id, br) + case 3: + // delete + } + } } } diff --git a/main.go b/main.go index 20c394a..cbd5e70 100644 --- a/main.go +++ b/main.go @@ -124,14 +124,11 @@ func main() { handle = printTypes case "pretty": handle = prettyPrint - case "send-tables": - handle = sendTables case "string-tables": st := newStringTables() handle = st.handle case "class-info": - ci := new(classInfo) - handle = ci.handle + handle = dumpClasses case "entities": handle = dumpEntities default: diff --git a/send_tables.go b/send_tables.go deleted file mode 100644 index c7013ab..0000000 --- a/send_tables.go +++ /dev/null @@ -1,38 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/golang/protobuf/proto" - - "github.com/jordanorelli/hyperstone/bit" - "github.com/jordanorelli/hyperstone/dota" - "github.com/jordanorelli/hyperstone/dt" -) - -func sendTables(m proto.Message) { - v, ok := m.(*dota.CDemoSendTables) - if !ok { - return - } - - // sendtables only has one field, a binary data field. - data := v.GetData() - br := bit.NewBytesReader(data) - - // body is length-prefixed - size := int(bit.ReadVarInt(br)) - - buf := make([]byte, size) - br.Read(buf) - - serializer := dota.CSVCMsg_FlattenedSerializer{} - if err := proto.Unmarshal(buf, &serializer); err != nil { - fmt.Printf("error: %v\n", err) - return - } - - ts := dt.ParseFlattened(&serializer) - ts.DebugPrint(os.Stdout) -}