package ent import ( "fmt" "github.com/golang/protobuf/proto" "github.com/jordanorelli/hyperstone/bit" "github.com/jordanorelli/hyperstone/dota" ) // Dict corresponds to the edict_t in Valve's documentation for the Source // engine. See here: https://developer.valvesoftware.com/wiki/Edict_t // // From the Valve docs: // edict_t ("entity dictionary") is an interface struct that allows entities // to cross the client/server divide: with one attached, an entity has the // same index at both ends. The edict also manages the state of the entity's // DataTable and provides a common representation across all DLLs. It cannot // be used on the client. type Dict struct { *Namespace entities map[int]Entity br *bit.BufReader } func NewDict() *Dict { return &Dict{ Namespace: new(Namespace), entities: make(map[int]Entity), br: new(bit.BufReader), } } // creates an entity with the provided id. the entity's contents data are read // off of the Dict's internal bit stream br func (d *Dict) createEntity(id int) error { classId := int(d.readClassId(d.br)) if len(d.Namespace.classes) == 0 { return fmt.Errorf("unable to create entity %d: namespace has no classes", id) } d.br.ReadBits(17) // ??? classV := int(bit.ReadVarInt(d.br)) className := d.classIds[classId] class := d.Class(className, classV) if class == nil { return fmt.Errorf("unable to create entity %d: no class found for class name %s, version %d", className, classV) } Debug.Printf("create entity id: %d classId: %d className: %v class: %v\n", id, classId, className, class) e := class.New() e.Read(d.br) return nil } func (d *Dict) updateEntity(id int) error { Debug.Printf("update entity id: %d\n", id) return nil } func (d *Dict) deleteEntity(id int) error { Debug.Printf("delete entity id: %d\n", id) return nil } func (d *Dict) leaveEntity(id int) error { Debug.Printf("leave entity id: %d\n", id) return nil } func (d *Dict) Handle(m proto.Message) { switch v := m.(type) { case *dota.CDemoSendTables: d.mergeSendTables(v) case *dota.CDemoClassInfo: d.mergeClassInfo(v) case *dota.CSVCMsg_PacketEntities: d.mergeEntities(v) } } func (d *Dict) mergeEntities(m *dota.CSVCMsg_PacketEntities) error { data := m.GetEntityData() Debug.Printf("packet header MaxEntries: %d UpdatedEntries: %v IsDelta: %t UpdateBaseline: %t Baseline: %d DeltaFrom: %d PendingFullFrame: %t ActiveSpawngroupHandle: %d", m.GetMaxEntries(), m.GetUpdatedEntries(), m.GetIsDelta(), m.GetUpdateBaseline(), m.GetBaseline(), m.GetDeltaFrom(), m.GetPendingFullFrame(), m.GetActiveSpawngroupHandle()) d.br.SetSource(data) id := -1 // for i := 0; i < int(m.GetUpdatedEntries()); i++ { for i := 0; i < 1; i++ { id++ // there may be a jump indicator, indicating how many id positions // to skip. id += int(bit.ReadUBitVar(d.br)) // next two bits encode one of four entity mutate operations var fn func(int) error switch d.br.ReadBits(2) { case 0: fn = d.updateEntity case 1: fn = d.leaveEntity case 2: fn = d.createEntity case 3: fn = d.deleteEntity } if err := fn(id); err != nil { return fmt.Errorf("entity merge error: %v", err) } } return nil }