You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

222 lines
6.6 KiB
Go

package ent
import (
"fmt"
"strconv"
"github.com/golang/protobuf/proto"
"github.com/jordanorelli/hyperstone/bit"
"github.com/jordanorelli/hyperstone/dota"
"github.com/jordanorelli/hyperstone/stbl"
)
// https://developer.valvesoftware.com/wiki/Entity_limit
//
// There can be up to 4096 entities. This total is split into two groups of 2048:
// Non-networked entities, which exist only on the client or server (e.g.
// death ragdolls on client, logicals on server).
// Entities with associated edicts, which can cross the client/server divide.
//
// If the game tries to assign a 2049th edict it will exit with an error
// message, but if it tries to create a 2049th non-networked entity it will
// merely refuse and print a warning to the console. The logic behind this
// may be that an entity spawned dynamically (i.e. not present in the map)
// but not assigned an edict probably isn't too important.
const e_limit = 2048
// 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 []*Entity
hidx map[int]*Entity // handle index
br *bit.BufReader
sr *selectionReader
// a reference to our string table of entity baseline data. For whatever
// reason, the first set of baselines sometimes come in before the classes
// are defined.
base *stbl.Table
}
func NewDict(sd *stbl.Dict) *Dict {
d := &Dict{
Namespace: new(Namespace),
entities: make([]*Entity, e_limit),
br: new(bit.BufReader),
sr: new(selectionReader),
base: sd.TableForName("instancebaseline"),
}
sd.WatchTable("instancebaseline", d.updateBaselines)
return d
}
// 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)
}
serial := int(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)
}
e := class.New(serial, false)
d.entities[id] = e
Debug.Printf("create entity id: %d serial: %d classId: %d className: %v class: %v\n", id, serial, classId, className, class)
if err := fillSlots(e, class.Name(), d.sr, d.br); err != nil {
return fmt.Errorf("failed to create entity %d (%s): %v", id, className, err)
}
return nil
}
func (d *Dict) getEntity(id int) *Entity {
if id < 0 || id >= e_limit {
Debug.Printf("edict refused getEntity request for invalid id %d", id)
return nil
}
return d.entities[id]
}
func (d *Dict) updateEntity(id int) error {
Debug.Printf("update entity id: %d\n", id)
e := d.getEntity(id)
if e == nil {
return fmt.Errorf("update entity %d refused: no such entity", id)
}
if err := fillSlots(e, e.Class.String(), d.sr, d.br); err != nil {
return fmt.Errorf("failed to update entity %d (%s): %v", id, e.Class.Name(), err)
}
return nil
}
func (d *Dict) deleteEntity(id int) error {
Debug.Printf("delete entity id: %d\n", id)
if id < 0 || id >= e_limit {
// we're probably reading corrupt shit if we get here
return fmt.Errorf("delete entity %d refused: id is out of bounds", id)
}
if d.entities[id] == nil {
// this one's probably ok to let fly, but it's probably a sign that
// something is wrong elsewhere.
Debug.Printf("delete entity %d refused: no such entity", id)
return nil
}
d.entities[id] = nil
return nil
}
func (d *Dict) leaveEntity(id int) error {
Debug.Printf("leave entity id: %d\n", id)
// what the shit does this do?
return nil
}
func (d *Dict) Handle(m proto.Message) {
switch v := m.(type) {
case *dota.CDemoSendTables:
d.mergeSendTables(v)
d.syncBaselines()
case *dota.CDemoClassInfo:
d.mergeClassInfo(v)
d.syncBaselines()
case *dota.CSVCMsg_PacketEntities:
if err := d.mergeEntities(v); err != nil {
Info.Fatalf("merge entities error: %v", err)
}
}
}
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++ {
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("error on entity %d: %v", id, err)
}
}
return nil
}
func (d *Dict) updateBaselines(t *stbl.Table) {
d.base = t
d.syncBaselines()
}
func (d *Dict) syncBaselines() error {
if !d.hasClassinfo() {
Debug.Printf("syncBaselines skip: no classInfo yet")
return nil
}
Debug.Printf("syncBaselines start")
if d.base == nil {
return fmt.Errorf("syncBaselines failed: reference to baseline string table is nil")
}
for _, e := range d.base.Entries() {
id, err := strconv.Atoi(e.Key)
if err != nil {
Debug.Printf("syncBaselines skipping entry with key %s: key failed to parse to integer: %v", e.Key, err)
continue
}
c := d.ClassByNetId(id)
if c == nil {
Debug.Printf("syncBaselines skipping entry with key %s: no such class", e.Key)
continue
}
if c.baseline == nil {
c.baseline = c.New(-1, true)
}
if e.Value == nil || len(e.Value) == 0 {
Debug.Printf("syncBaselines skipping entry with key %s: value is empty", e.Key)
continue
}
d.br.SetSource(e.Value)
Debug.Printf("syncBaselines has new baseline for class %v", c)
if err := fillSlots(c.baseline, c.Name(), d.sr, d.br); err != nil {
return fmt.Errorf("syncBaselines failed to fill a baseline: %v", err)
}
}
return nil
}