commit
53e06884b2
@ -0,0 +1,9 @@
|
||||
package bit
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
func Length(n uint) uint {
|
||||
return uint(math.Floor(math.Log2(float64(n)) + 1))
|
||||
}
|
@ -1,17 +1,149 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jordanorelli/hyperstone/bit"
|
||||
)
|
||||
|
||||
var constants = map[string]int{
|
||||
"MAX_ABILITY_DRAFT_ABILITIES": 48,
|
||||
}
|
||||
|
||||
func arrayType(spec *typeSpec, env *Env) tÿpe {
|
||||
if !strings.Contains(spec.typeName, "[") {
|
||||
return nil
|
||||
}
|
||||
elemName, count := parseArrayName(spec.typeName)
|
||||
elemSpec := *spec
|
||||
elemSpec.typeName = elemName
|
||||
elemType := parseTypeSpec(&elemSpec, env)
|
||||
if elemName == "char" {
|
||||
return string_t(count)
|
||||
}
|
||||
return &array_t{elem: elemType, count: count}
|
||||
}
|
||||
|
||||
func parseArrayName(s string) (string, uint) {
|
||||
runes := []rune(s)
|
||||
if runes[len(runes)-1] != ']' {
|
||||
panic("invalid array type name: " + s)
|
||||
}
|
||||
for i := len(runes) - 2; i >= 0; i-- {
|
||||
if runes[i] == '[' {
|
||||
ns := strings.TrimSpace(string(runes[i+1 : len(runes)-1]))
|
||||
n, err := strconv.Atoi(ns)
|
||||
if err != nil {
|
||||
n = constants[ns]
|
||||
if n <= 0 {
|
||||
panic("invalid array type name: " + err.Error())
|
||||
}
|
||||
}
|
||||
return strings.TrimSpace(string(runes[:i])), uint(n)
|
||||
}
|
||||
}
|
||||
panic("invalid array type name: " + s)
|
||||
}
|
||||
|
||||
type array_t struct {
|
||||
elem tÿpe
|
||||
count uint
|
||||
bits uint
|
||||
}
|
||||
|
||||
func (t *array_t) sizeBits() uint {
|
||||
if t.bits == 0 {
|
||||
t.bits = bit.Length(t.count)
|
||||
}
|
||||
return t.bits
|
||||
}
|
||||
|
||||
func (t *array_t) nü() value { return array{t: t, slots: make([]value, t.count)} }
|
||||
func (t array_t) typeName() string { return fmt.Sprintf("%s[%d]", t.elem.typeName(), t.count) }
|
||||
|
||||
type array struct {
|
||||
slots []interface{}
|
||||
_slotType string
|
||||
decoder
|
||||
t *array_t
|
||||
slots []value
|
||||
}
|
||||
|
||||
func (a array) tÿpe() tÿpe { return a.t }
|
||||
|
||||
func (a array) read(r bit.Reader) error {
|
||||
n := r.ReadBits(a.t.bits)
|
||||
Debug.Printf("reading %d array elements", n)
|
||||
for i := uint64(0); i < n; i++ {
|
||||
if a.slots[i] == nil {
|
||||
a.slots[i] = a.t.elem.nü()
|
||||
}
|
||||
if err := a.slots[i].read(r); err != nil {
|
||||
return wrap(err, "array read error at index %d", i)
|
||||
}
|
||||
}
|
||||
return r.Err()
|
||||
}
|
||||
|
||||
func (a array) String() string {
|
||||
if len(a.slots) > 8 {
|
||||
return fmt.Sprintf("%s(%d)%v...", a.t.typeName(), len(a.slots), a.slots[:8])
|
||||
}
|
||||
return fmt.Sprintf("%s(%d)%v", a.t.typeName(), len(a.slots), a.slots)
|
||||
}
|
||||
|
||||
func (a array) slotType(int) tÿpe { return a.t.elem }
|
||||
func (a array) slotName(n int) string { return strconv.Itoa(n) }
|
||||
func (a array) setSlotValue(slot int, v value) {
|
||||
// TODO: type check here?
|
||||
a.slots[slot] = v
|
||||
}
|
||||
func (a array) getSlotValue(slot int) value { return a.slots[slot] }
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// strings are a special case of arrays
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
type string_t int
|
||||
|
||||
func (t string_t) nü() value {
|
||||
return &string_v{t: t, buf: make([]byte, int(t))}
|
||||
}
|
||||
|
||||
func (t string_t) typeName() string { return "string" }
|
||||
|
||||
type string_v struct {
|
||||
t string_t
|
||||
buf []byte // the buffer of all possible bytes
|
||||
valid []byte // selection of current valid bytes within buf
|
||||
}
|
||||
|
||||
func (s *string_v) tÿpe() tÿpe { return s.t }
|
||||
func (s *string_v) read(r bit.Reader) error {
|
||||
for i := 0; i < int(s.t); i++ {
|
||||
b := r.ReadBits(8)
|
||||
if b == 0 {
|
||||
s.valid = s.buf[:i]
|
||||
return r.Err()
|
||||
}
|
||||
s.buf[i] = byte(b & 0xff)
|
||||
}
|
||||
s.valid = s.buf
|
||||
return r.Err()
|
||||
}
|
||||
|
||||
func (a *array) slotName(slot int) string { return strconv.Itoa(slot) }
|
||||
func (a *array) slotValue(slot int) interface{} { return a.slots[slot] }
|
||||
func (a *array) slotType(slot int) string { return a._slotType }
|
||||
func (a *array) slotDecoder(slot int) decoder { return a.decoder }
|
||||
func (a *array) setSlotValue(slot int, val interface{}) { a.slots[slot] = val }
|
||||
func (s *string_v) String() string {
|
||||
return string(s.valid)
|
||||
}
|
||||
|
||||
func (s *string_v) slotType(int) tÿpe { return char_t }
|
||||
func (s *string_v) slotName(n int) string { return strconv.Itoa(n) }
|
||||
func (s *string_v) setSlotValue(slot int, v value) {
|
||||
s.buf[slot] = byte(*v.(*char_v))
|
||||
if slot >= len(s.valid) {
|
||||
s.valid = s.buf[:slot+1]
|
||||
}
|
||||
}
|
||||
func (s *string_v) getSlotValue(slot int) value {
|
||||
v := char_v(s.buf[slot])
|
||||
return &v
|
||||
}
|
||||
|
@ -0,0 +1,390 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jordanorelli/hyperstone/bit"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// bool
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
var bool_t = &typeLiteral{
|
||||
name: "bool",
|
||||
newFn: func() value {
|
||||
return new(bool_v)
|
||||
},
|
||||
}
|
||||
|
||||
type bool_v bool
|
||||
|
||||
func (v bool_v) tÿpe() tÿpe { return bool_t }
|
||||
|
||||
func (v *bool_v) read(r bit.Reader) error {
|
||||
*v = bool_v(bit.ReadBool(r))
|
||||
return r.Err()
|
||||
}
|
||||
|
||||
func (v bool_v) String() string {
|
||||
if v {
|
||||
return "true"
|
||||
}
|
||||
return "false"
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// uint8
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
var uint8_t = &typeLiteral{
|
||||
name: "uint8",
|
||||
newFn: func() value {
|
||||
return new(uint8_v)
|
||||
},
|
||||
}
|
||||
|
||||
type uint8_v uint8
|
||||
|
||||
func (v uint8_v) tÿpe() tÿpe { return uint8_t }
|
||||
func (v *uint8_v) read(r bit.Reader) error {
|
||||
*v = uint8_v(bit.ReadVarInt32(r))
|
||||
return r.Err()
|
||||
}
|
||||
|
||||
func (v uint8_v) String() string {
|
||||
return strconv.FormatUint(uint64(v), 10)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// uint16
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
var uint16_t = &typeLiteral{
|
||||
name: "uint16",
|
||||
newFn: func() value {
|
||||
return new(uint16_v)
|
||||
},
|
||||
}
|
||||
|
||||
type uint16_v uint16
|
||||
|
||||
func (v uint16_v) tÿpe() tÿpe { return uint16_t }
|
||||
func (v *uint16_v) read(r bit.Reader) error {
|
||||
*v = uint16_v(bit.ReadVarInt32(r))
|
||||
return r.Err()
|
||||
}
|
||||
|
||||
func (v uint16_v) String() string {
|
||||
return strconv.FormatUint(uint64(v), 10)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// uint32
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
var uint32_t = &typeLiteral{
|
||||
name: "uint32",
|
||||
newFn: func() value {
|
||||
return new(uint32_v)
|
||||
},
|
||||
}
|
||||
|
||||
type uint32_v uint32
|
||||
|
||||
func (v uint32_v) tÿpe() tÿpe { return uint32_t }
|
||||
func (v *uint32_v) read(r bit.Reader) error {
|
||||
*v = uint32_v(bit.ReadVarInt32(r))
|
||||
return r.Err()
|
||||
}
|
||||
|
||||
func (v uint32_v) String() string {
|
||||
return strconv.FormatUint(uint64(v), 10)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// uint64
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
var uint64_t = &typeLiteral{
|
||||
name: "uint64",
|
||||
newFn: func() value {
|
||||
return new(uint64_v)
|
||||
},
|
||||
}
|
||||
|
||||
type uint64_v uint64
|
||||
|
||||
func (v uint64_v) tÿpe() tÿpe { return uint64_t }
|
||||
func (v *uint64_v) read(r bit.Reader) error {
|
||||
*v = uint64_v(bit.ReadVarInt32(r))
|
||||
return r.Err()
|
||||
}
|
||||
|
||||
func (v uint64_v) String() string {
|
||||
return strconv.FormatUint(uint64(v), 10)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// uint64fixed
|
||||
//
|
||||
// (a uint64 value that is always represented on the wire with 64 bits)
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
var uint64fixed_t = &typeLiteral{
|
||||
name: "uint64fixed",
|
||||
newFn: func() value {
|
||||
return new(uint64fixed_v)
|
||||
},
|
||||
}
|
||||
|
||||
type uint64fixed_v uint64
|
||||
|
||||
func (v uint64fixed_v) tÿpe() tÿpe { return uint64fixed_t }
|
||||
func (v *uint64fixed_v) read(r bit.Reader) error {
|
||||
*v = uint64fixed_v(r.ReadBits(64))
|
||||
return r.Err()
|
||||
}
|
||||
|
||||
func (v uint64fixed_v) String() string {
|
||||
return strconv.FormatUint(uint64(v), 10)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// int8
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
var int8_t = &typeLiteral{
|
||||
name: "int8",
|
||||
newFn: func() value {
|
||||
return new(int8_v)
|
||||
},
|
||||
}
|
||||
|
||||
type int8_v int8
|
||||
|
||||
func (v int8_v) tÿpe() tÿpe { return int8_t }
|
||||
func (v *int8_v) read(r bit.Reader) error {
|
||||
// TODO: bounds check here?
|
||||
*v = int8_v(bit.ReadZigZag32(r))
|
||||
return r.Err()
|
||||
}
|
||||
|
||||
func (v int8_v) String() string {
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// int16
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
var int16_t = &typeLiteral{
|
||||
name: "int16",
|
||||
newFn: func() value {
|
||||
return new(int16_v)
|
||||
},
|
||||
}
|
||||
|
||||
type int16_v int16
|
||||
|
||||
func (v int16_v) tÿpe() tÿpe { return int16_t }
|
||||
func (v *int16_v) read(r bit.Reader) error {
|
||||
// TODO: bounds check here?
|
||||
*v = int16_v(bit.ReadZigZag32(r))
|
||||
return r.Err()
|
||||
}
|
||||
|
||||
func (v int16_v) String() string {
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// int32
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
var int32_t = &typeLiteral{
|
||||
name: "int32",
|
||||
newFn: func() value {
|
||||
return new(int32_v)
|
||||
},
|
||||
}
|
||||
|
||||
type int32_v int32
|
||||
|
||||
func (v int32_v) tÿpe() tÿpe { return int32_t }
|
||||
func (v *int32_v) read(r bit.Reader) error {
|
||||
*v = int32_v(bit.ReadZigZag32(r))
|
||||
return r.Err()
|
||||
}
|
||||
|
||||
func (v int32_v) String() string {
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// int64
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
var int64_t = &typeLiteral{
|
||||
name: "int64",
|
||||
newFn: func() value {
|
||||
return new(int64_v)
|
||||
},
|
||||
}
|
||||
|
||||
type int64_v int64
|
||||
|
||||
func (v int64_v) tÿpe() tÿpe { return int64_t }
|
||||
func (v *int64_v) read(r bit.Reader) error {
|
||||
*v = int64_v(bit.ReadZigZag(r))
|
||||
return r.Err()
|
||||
}
|
||||
|
||||
func (v int64_v) String() string {
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// CUtlStringToken
|
||||
//
|
||||
// weirdly, this type isn't a string; it's actually a number. The number
|
||||
// presumably indicates some value on a symbol table.
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
var stringToken_t = &typeLiteral{
|
||||
name: "CUtlStringToken",
|
||||
newFn: func() value {
|
||||
return new(stringToken_v)
|
||||
},
|
||||
}
|
||||
|
||||
type stringToken_v uint64
|
||||
|
||||
func (v stringToken_v) tÿpe() tÿpe { return stringToken_t }
|
||||
func (v *stringToken_v) read(r bit.Reader) error {
|
||||
*v = stringToken_v(bit.ReadVarInt(r))
|
||||
return r.Err()
|
||||
}
|
||||
|
||||
func (v stringToken_v) String() string {
|
||||
return fmt.Sprintf("token:%d", v)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Color
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
var color_t = &typeLiteral{
|
||||
name: "Color",
|
||||
newFn: func() value {
|
||||
return new(color)
|
||||
},
|
||||
}
|
||||
|
||||
type color struct{ r, g, b, a uint8 }
|
||||
|
||||
func (c color) tÿpe() tÿpe { return color_t }
|
||||
func (c *color) read(r bit.Reader) error {
|
||||
u := bit.ReadVarInt(r)
|
||||
c.r = uint8(u >> 6 & 0xff)
|
||||
c.g = uint8(u >> 4 & 0xff)
|
||||
c.b = uint8(u >> 2 & 0xff)
|
||||
c.a = uint8(u >> 0 & 0xff)
|
||||
return r.Err()
|
||||
}
|
||||
|
||||
func (c color) String() string {
|
||||
return fmt.Sprintf("#%x%x%x%x", c.r, c.g, c.b, c.a)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// CUtlSymbolLarge
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
var cutl_string_t = typeLiteral{
|
||||
name: "CUtlSymbolLarge",
|
||||
newFn: func() value {
|
||||
return new(cutl_string_v)
|
||||
},
|
||||
}
|
||||
|
||||
type cutl_string_v string
|
||||
|
||||
func (v cutl_string_v) tÿpe() tÿpe { return cutl_string_t }
|
||||
func (v cutl_string_v) String() string { return string(v) }
|
||||
func (v *cutl_string_v) read(r bit.Reader) error {
|
||||
*v = cutl_string_v(bit.ReadString(r))
|
||||
return r.Err()
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// char
|
||||
//
|
||||
// this seems absolutely wrong, but it's how it's done in clarity. a char is a
|
||||
// cstring? wtf?
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
var char_t = typeLiteral{
|
||||
name: "char",
|
||||
newFn: func() value {
|
||||
return new(char_v)
|
||||
},
|
||||
}
|
||||
|
||||
type char_v byte
|
||||
|
||||
func (v char_v) tÿpe() tÿpe { return char_t }
|
||||
func (v char_v) String() string { return string(v) }
|
||||
func (v *char_v) read(r bit.Reader) error {
|
||||
*v = char_v(r.ReadByte())
|
||||
return r.Err()
|
||||
}
|
||||
|
||||
var atom_types = []tÿpe{
|
||||
bool_t,
|
||||
char_t,
|
||||
uint8_t,
|
||||
uint16_t,
|
||||
uint32_t,
|
||||
int8_t,
|
||||
int16_t,
|
||||
int32_t,
|
||||
int64_t,
|
||||
stringToken_t,
|
||||
color_t,
|
||||
cutl_string_t,
|
||||
}
|
||||
|
||||
func atomType(spec *typeSpec, env *Env) tÿpe {
|
||||
for _, t := range atom_types {
|
||||
if t.typeName() == spec.typeName {
|
||||
Debug.Printf(" atom type: %s", t.typeName())
|
||||
if spec.bits != 0 {
|
||||
return typeError("spec can't be atom type: has bit specification: %v", spec)
|
||||
}
|
||||
if spec.encoder != "" {
|
||||
return typeError("spec can't be atom type: has encoder specification: %v", spec)
|
||||
}
|
||||
if spec.flags != 0 {
|
||||
return typeError("spec can't be atom type: has flags: %v", spec)
|
||||
}
|
||||
if spec.high != 0 {
|
||||
return typeError("spec can't be atom type: has high value constraint: %v", spec)
|
||||
}
|
||||
if spec.low != 0 {
|
||||
return typeError("spec can't be atom type: has low value constraint: %v", spec)
|
||||
}
|
||||
if spec.serializer != "" {
|
||||
return typeError("spec can't be atom type: has serializer: %v", spec)
|
||||
}
|
||||
return t
|
||||
}
|
||||
}
|
||||
if spec.typeName == "uint64" {
|
||||
if spec.encoder == "fixed64" {
|
||||
return uint64fixed_t
|
||||
}
|
||||
return uint64_t
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type cutlVector struct {
|
||||
slots []interface{}
|
||||
_slotType string
|
||||
decoder
|
||||
}
|
||||
|
||||
func (v *cutlVector) slotName(slot int) string { return strconv.Itoa(slot) }
|
||||
func (v *cutlVector) slotValue(slot int) interface{} { return v.slots[slot] }
|
||||
func (v *cutlVector) slotType(slot int) string { return v._slotType }
|
||||
func (v *cutlVector) slotDecoder(slot int) decoder { return v.decoder }
|
||||
|
||||
func (v *cutlVector) setSlotValue(slot int, val interface{}) {
|
||||
v.slots[slot] = val
|
||||
}
|
@ -1,232 +0,0 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
// "strings"
|
||||
|
||||
"github.com/jordanorelli/hyperstone/bit"
|
||||
)
|
||||
|
||||
// a decoder decodes an entity value off of a bit reader
|
||||
type decoder func(bit.Reader) interface{}
|
||||
|
||||
// creates a new field decoder for the field f.
|
||||
func newFieldDecoder(n *Namespace, f *Field) decoder {
|
||||
Debug.Printf("new decoder: type: %s name: %s sendNode: %s\n\tbits: %d low: %v high: %v\n\tflags: %d serializer: %v serializerVersion: %v\n\tclass: %v encoder: %v", f._type, f.name, f.sendNode, f.bits, f.low, f.high, f.flags, f.serializer, f.serializerVersion, f.class, f.encoder)
|
||||
|
||||
typeName := f._type.String()
|
||||
|
||||
switch typeName {
|
||||
case "bool":
|
||||
return decodeBool
|
||||
case "uint8", "uint16", "uint32", "uint64":
|
||||
return decodeVarInt64
|
||||
case "Color":
|
||||
return decodeColor
|
||||
case "int8", "int16", "int32", "int64":
|
||||
return decodeZigZag
|
||||
case "CNetworkedQuantizedFloat", "float32":
|
||||
return floatDecoder(f)
|
||||
case "Vector":
|
||||
return vectorDecoder(f)
|
||||
case "QAngle":
|
||||
return qangleDecoder(f)
|
||||
case "CGameSceneNodeHandle":
|
||||
// ehhh maybe no?
|
||||
return decodeVarInt32
|
||||
case "CUtlStringToken":
|
||||
return symbolDecoder(n)
|
||||
case "CUtlSymbolLarge", "char":
|
||||
return decodeString
|
||||
}
|
||||
|
||||
// the field is itself an entity contained within the outer entity.
|
||||
if f.class != nil {
|
||||
return entityDecoder(f.class)
|
||||
}
|
||||
|
||||
// for compound types such as handles, vectors (in the c++ std::vector
|
||||
// sense), and arrays
|
||||
switch f.typeSpec.kind {
|
||||
case t_element:
|
||||
Debug.Printf("weird typespec: we shouldn't have elements here: %v", f.typeSpec)
|
||||
return func(bit.Reader) interface{} {
|
||||
Info.Fatalf("unable to decode element of type: %v", f.typeSpec.name)
|
||||
return nil
|
||||
}
|
||||
case t_object:
|
||||
return func(br bit.Reader) interface{} {
|
||||
Debug.Printf("unable to decode object of type: %v", f.typeSpec.name)
|
||||
return decodeVarInt32(br)
|
||||
}
|
||||
case t_array:
|
||||
return arrayDecoder(n, f)
|
||||
case t_template:
|
||||
return templateDecoder(f)
|
||||
case t_pointer:
|
||||
return decodeBool
|
||||
}
|
||||
|
||||
panic("fart")
|
||||
}
|
||||
|
||||
func decodeBool(br bit.Reader) interface{} { return bit.ReadBool(br) }
|
||||
func decodeVarInt32(br bit.Reader) interface{} { return bit.ReadVarInt32(br) }
|
||||
func decodeVarInt64(br bit.Reader) interface{} { return bit.ReadVarInt(br) }
|
||||
func decodeZigZag(br bit.Reader) interface{} { return bit.ReadZigZag(br) }
|
||||
func decodeString(br bit.Reader) interface{} { return bit.ReadString(br) }
|
||||
|
||||
type color struct{ r, g, b, a uint8 }
|
||||
|
||||
func decodeColor(br bit.Reader) interface{} {
|
||||
u := bit.ReadVarInt(br)
|
||||
return color{
|
||||
r: uint8(u & 0xff000000),
|
||||
g: uint8(u & 0x00ff0000),
|
||||
b: uint8(u & 0x0000ff00),
|
||||
a: uint8(u & 0x000000ff),
|
||||
}
|
||||
}
|
||||
|
||||
func entityDecoder(c *Class) decoder {
|
||||
return func(br bit.Reader) interface{} {
|
||||
bit.ReadBool(br) // what does this do
|
||||
return c.New(-1, false)
|
||||
}
|
||||
}
|
||||
|
||||
func vectorDecoder(f *Field) decoder {
|
||||
if f.encoder != nil {
|
||||
switch f.encoder.String() {
|
||||
case "normal":
|
||||
return decodeNormalVector
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
fn := floatDecoder(f)
|
||||
if fn == nil {
|
||||
return nil
|
||||
}
|
||||
return func(br bit.Reader) interface{} {
|
||||
return vector{fn(br).(float32), fn(br).(float32), fn(br).(float32)}
|
||||
}
|
||||
}
|
||||
|
||||
func decodeNormalVector(br bit.Reader) interface{} {
|
||||
var v vector
|
||||
x, y := bit.ReadBool(br), bit.ReadBool(br)
|
||||
if x {
|
||||
v.x = bit.ReadNormal(br)
|
||||
}
|
||||
if y {
|
||||
v.y = bit.ReadNormal(br)
|
||||
}
|
||||
|
||||
// here comes a shitty hack!
|
||||
bit.ReadBool(br)
|
||||
// discard this flag, it's concerned with the sign of the z value, which
|
||||
// we're skipping.
|
||||
return v
|
||||
|
||||
// ok, so. I have a suspicion that what we're interested in here is a
|
||||
// surface normal, and that it's describing something about the geometry of
|
||||
// the scene. I can't for the life of me see why in the hell we'd *ever*
|
||||
// care about this in the context of parsing a replay, so I'm just turning
|
||||
// off the z calculation. What follows is the original implementation as
|
||||
// adapted from Manta and Clarity. The reason I care to skip this is that
|
||||
// it involves a sqrt, which is a super slow operatation, and one worth
|
||||
// dispensing with if you don't need the data that it produces.
|
||||
//
|
||||
// p := v.x*v.x + v.y*v.y
|
||||
// if p < 1.0 {
|
||||
// v.z = float32(math.Sqrt(float64(1.0 - p)))
|
||||
// }
|
||||
// if bit.ReadBool(br) {
|
||||
// // we might wind up with the float representation of negative zero here,
|
||||
// // but as far as I can tell, negative zero is fine because negative
|
||||
// // zero is equivalent to positive zero. They'll print differently, but
|
||||
// // I don't think we care about that.
|
||||
// v.z = -v.z
|
||||
// }
|
||||
// return v
|
||||
}
|
||||
|
||||
// decodes a qangle, which is a representation of an euler angle. that is, it's
|
||||
// a three-angle encoding of orientation.
|
||||
// https://developer.valvesoftware.com/wiki/QAngle
|
||||
//
|
||||
// the x, y, and z in this case do not refer to positions along a set of
|
||||
// cartesian axes, but degress of rotation in an Euler angle.
|
||||
//
|
||||
// (-45,10,0) means 45° up, 10° left and 0° roll.
|
||||
func qangleDecoder(f *Field) decoder {
|
||||
if f.encoder != nil && f.encoder.String() == "qangle_pitch_yaw" {
|
||||
return nil
|
||||
}
|
||||
bits := f.bits
|
||||
if bits == 32 {
|
||||
return nil
|
||||
}
|
||||
if bits == 0 {
|
||||
return func(br bit.Reader) interface{} {
|
||||
var (
|
||||
v vector
|
||||
x = bit.ReadBool(br)
|
||||
y = bit.ReadBool(br)
|
||||
z = bit.ReadBool(br)
|
||||
)
|
||||
if x {
|
||||
v.x = bit.ReadCoord(br)
|
||||
}
|
||||
if y {
|
||||
v.y = bit.ReadCoord(br)
|
||||
}
|
||||
if z {
|
||||
v.z = bit.ReadCoord(br)
|
||||
}
|
||||
return v
|
||||
}
|
||||
}
|
||||
return func(br bit.Reader) interface{} {
|
||||
return vector{
|
||||
x: bit.ReadAngle(br, bits),
|
||||
y: bit.ReadAngle(br, bits),
|
||||
z: bit.ReadAngle(br, bits),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func arrayDecoder(n *Namespace, f *Field) decoder {
|
||||
return func(br bit.Reader) interface{} {
|
||||
Debug.Printf("dunno what this int32 val does in array decoder: %d", bit.ReadVarInt32(br))
|
||||
if f.initializer != nil {
|
||||
return f.initializer()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func templateDecoder(f *Field) decoder {
|
||||
switch f.typeSpec.template {
|
||||
case "CHandle":
|
||||
return decodeVarInt32
|
||||
case "CStrongHandle":
|
||||
return decodeVarInt64
|
||||
case "CUtlVector":
|
||||
return func(br bit.Reader) interface{} {
|
||||
v := decodeVarInt32(br)
|
||||
Debug.Printf("dunno what this varint is for in the cutlvector decoder: %v", v)
|
||||
return v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// so far a sanity check on the values I'm seeing out of this seem wrong.
|
||||
func symbolDecoder(n *Namespace) decoder {
|
||||
return func(br bit.Reader) interface{} {
|
||||
u := bit.ReadVarInt32(br)
|
||||
return n.Symbol(int(u))
|
||||
}
|
||||
}
|
@ -1,221 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,23 +1,38 @@
|
||||
package ent
|
||||
|
||||
type Entity struct {
|
||||
*Class
|
||||
serial int
|
||||
slots []interface{}
|
||||
isBaseline bool
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jordanorelli/hyperstone/bit"
|
||||
)
|
||||
|
||||
type entity struct {
|
||||
class *class
|
||||
slots []value
|
||||
}
|
||||
|
||||
func (e *Entity) slotName(n int) string { return e.Class.Fields[n].name.String() }
|
||||
func (e *Entity) slotType(n int) string { return e.Class.Fields[n]._type.String() }
|
||||
func (e *Entity) slotValue(n int) interface{} {
|
||||
v := e.slots[n]
|
||||
if v != nil {
|
||||
return v
|
||||
func (e *entity) read(r bit.Reader) error {
|
||||
bit.ReadBool(r) // ???
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *entity) className() string {
|
||||
if e.class != nil {
|
||||
return e.class.name
|
||||
}
|
||||
if !e.isBaseline && e.Class.baseline != nil {
|
||||
return e.Class.baseline.slotValue(n)
|
||||
return "<None>"
|
||||
}
|
||||
|
||||
func (e *entity) String() string {
|
||||
return fmt.Sprintf("%s{id: ?}", e.class.typeName())
|
||||
}
|
||||
|
||||
func (e *entity) tÿpe() tÿpe { return e.class }
|
||||
func (e *entity) slotType(i int) tÿpe {
|
||||
if i >= len(e.class.fields) {
|
||||
return typeError("index out of range in slotType: %d is beyond capacity %d", i, len(e.class.fields))
|
||||
}
|
||||
return nil
|
||||
return e.class.fields[i].tÿpe
|
||||
}
|
||||
func (e *Entity) slotDecoder(n int) decoder { return e.Class.Fields[n].decoder }
|
||||
func (e *Entity) setSlotValue(n int, v interface{}) { e.slots[n] = v }
|
||||
func (e *entity) slotName(i int) string { return e.class.fields[i].name }
|
||||
func (e *entity) setSlotValue(i int, v value) { e.slots[i] = v }
|
||||
func (e *entity) getSlotValue(i int) value { return e.slots[i] }
|
||||
|
@ -0,0 +1,203 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"github.com/golang/protobuf/proto"
|
||||
"strconv"
|
||||
|
||||
"github.com/jordanorelli/hyperstone/bit"
|
||||
"github.com/jordanorelli/hyperstone/dota"
|
||||
"github.com/jordanorelli/hyperstone/stbl"
|
||||
)
|
||||
|
||||
type Env struct {
|
||||
symbols symbolTable
|
||||
source bit.BufReader
|
||||
classes map[string]*classHistory
|
||||
netIds map[int]string
|
||||
fields []field
|
||||
strings *stbl.Dict
|
||||
entities map[int]*entity
|
||||
}
|
||||
|
||||
func NewEnv() *Env {
|
||||
e := &Env{
|
||||
classes: make(map[string]*classHistory),
|
||||
strings: stbl.NewDict(),
|
||||
}
|
||||
e.strings.WatchTable("instancebaseline", e.syncBaselineTable)
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Env) Handle(m proto.Message) error {
|
||||
switch v := m.(type) {
|
||||
case *dota.CSVCMsg_CreateStringTable:
|
||||
_, err := e.strings.Create(v)
|
||||
return err
|
||||
|
||||
case *dota.CSVCMsg_UpdateStringTable:
|
||||
return e.strings.Update(v)
|
||||
|
||||
case *dota.CDemoSendTables:
|
||||
if err := e.mergeSendTables(v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case *dota.CDemoClassInfo:
|
||||
e.mergeClassInfo(v)
|
||||
e.syncBaseline()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Env) setSource(buf []byte) {
|
||||
e.source.SetSource(buf)
|
||||
}
|
||||
|
||||
// merges the "sendTables" message. "sendTables" is a deeply misleading name,
|
||||
// but it's the name the structure is given in the protobufs. sendTables
|
||||
// contains the type definitions that define our entity type system.
|
||||
func (e *Env) mergeSendTables(m *dota.CDemoSendTables) error {
|
||||
Debug.Printf("merge send tables")
|
||||
|
||||
flat, err := getSerializers(m)
|
||||
if err != nil {
|
||||
return wrap(err, "unable to get serializers in sendtables")
|
||||
}
|
||||
for i, s := range flat.GetSymbols() {
|
||||
Debug.Printf("symbol %d: %s", i, s)
|
||||
}
|
||||
e.symbols = symbolTable(flat.GetSymbols())
|
||||
e.stubClasses(flat)
|
||||
if err := e.parseFields(flat); err != nil {
|
||||
return wrap(err, "unable to parse serializer fields")
|
||||
}
|
||||
e.fillClasses(flat)
|
||||
return nil
|
||||
}
|
||||
|
||||
// stubs out the classes to be created later. we do this to create empty class
|
||||
// structs that fields may point to.
|
||||
func (e *Env) stubClasses(flat *dota.CSVCMsg_FlattenedSerializer) {
|
||||
serializers := flat.GetSerializers()
|
||||
for _, s := range serializers {
|
||||
name := e.symbol(int(s.GetSerializerNameSym()))
|
||||
v := int(s.GetSerializerVersion())
|
||||
c := &class{name: name, version: v}
|
||||
Debug.Printf("new class: %s", c)
|
||||
h := e.classes[name]
|
||||
if h == nil {
|
||||
h = new(classHistory)
|
||||
e.classes[name] = h
|
||||
}
|
||||
h.add(c)
|
||||
}
|
||||
}
|
||||
|
||||
// parses the type definitions for each field. some fields have types that
|
||||
// refer to class types defined in the replay file. classes must be declared up
|
||||
// front via stubclasses prior to parseFields working correctly.
|
||||
func (e *Env) parseFields(flat *dota.CSVCMsg_FlattenedSerializer) error {
|
||||
e.fields = make([]field, len(flat.GetFields()))
|
||||
for i, ff := range flat.GetFields() {
|
||||
f := &e.fields[i]
|
||||
if err := f.fromProto(ff, e); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// associates each class with its list of fields. parseFields must be run
|
||||
// before fillClasses in order for the field definitions to exist.
|
||||
func (e *Env) fillClasses(flat *dota.CSVCMsg_FlattenedSerializer) {
|
||||
for _, s := range flat.GetSerializers() {
|
||||
name := e.symbol(int(s.GetSerializerNameSym()))
|
||||
v := int(s.GetSerializerVersion())
|
||||
class := e.classes[name].version(v)
|
||||
|
||||
class.fields = make([]field, len(s.GetFieldsIndex()))
|
||||
for i, id := range s.GetFieldsIndex() {
|
||||
Debug.Printf("class %s has field %s (%s)", name, e.fields[id].name, e.fields[id].typeName())
|
||||
class.fields[i] = e.fields[id]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Env) mergeClassInfo(m *dota.CDemoClassInfo) {
|
||||
if e.netIds == nil {
|
||||
e.netIds = make(map[int]string, len(m.GetClasses()))
|
||||
}
|
||||
for _, info := range m.GetClasses() {
|
||||
id := info.GetClassId()
|
||||
name := info.GetNetworkName()
|
||||
table := info.GetTableName()
|
||||
Debug.Printf("class info id: %d name: %s table: %s", id, name, table)
|
||||
e.netIds[int(id)] = name
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Env) symbol(id int) string { return e.symbols[id] }
|
||||
|
||||
func (e *Env) syncBaseline() {
|
||||
t := e.strings.TableForName("instancebaseline")
|
||||
if t != nil {
|
||||
e.syncBaselineTable(t)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Env) syncBaselineTable(t *stbl.Table) {
|
||||
if e.netIds == nil || len(e.netIds) == 0 {
|
||||
Debug.Printf("syncBaselines skipped: net ids are nil")
|
||||
}
|
||||
if e.classes == nil || len(e.classes) == 0 {
|
||||
Debug.Printf("syncBaselines skipped: classes are nil")
|
||||
}
|
||||
|
||||
r := new(bit.BufReader)
|
||||
sr := new(selectionReader)
|
||||
for _, entry := range t.Entries() {
|
||||
netId, err := strconv.Atoi(entry.Key)
|
||||
if err != nil {
|
||||
Debug.Printf("syncBaselines ignored bad key %s: %v", err)
|
||||
continue
|
||||
}
|
||||
className := e.netIds[netId]
|
||||
if className == "" {
|
||||
Debug.Printf("syncBaselines couldn't find class with net id %d", netId)
|
||||
continue
|
||||
}
|
||||
c := e.class(className)
|
||||
if c == nil {
|
||||
Debug.Printf("syncBaselines couldn't find class named %s", className)
|
||||
continue
|
||||
}
|
||||
Debug.Printf("syncBaselines key: %s className: %s", entry.Key, c.name)
|
||||
ent := c.nü().(*entity)
|
||||
r.SetSource(entry.Value)
|
||||
selections, err := sr.readSelections(r)
|
||||
if err != nil {
|
||||
Debug.Printf("unable to read selections for %s: %v", className, err)
|
||||
continue
|
||||
}
|
||||
Debug.Printf("selections: %v", selections)
|
||||
for _, s := range selections {
|
||||
if err := s.fillSlots(ent, r); err != nil {
|
||||
Debug.Printf("syncBaseline fill error: %v", err)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Env) class(name string) *class {
|
||||
h := e.classes[name]
|
||||
if h == nil {
|
||||
return nil
|
||||
}
|
||||
return h.newest
|
||||
}
|
||||
|
||||
func (e *Env) classVersion(name string, version int) *class {
|
||||
h := e.classes[name]
|
||||
return h.version(version)
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/jordanorelli/hyperstone/bit"
|
||||
)
|
||||
|
||||
func floatDecoder(f *Field) decoder {
|
||||
if f.encoder != nil && f.encoder.String() == "coord" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if f.bits <= 0 || f.bits >= 32 {
|
||||
return ieeeFloat32Decoder
|
||||
}
|
||||
|
||||
// a quantized field value must have some range specified, otherwise the
|
||||
// quantization makes no sense.
|
||||
if f.low == 0 && f.high == 0 {
|
||||
panic("quantization rules make no sense")
|
||||
}
|
||||
|
||||
bits := f.bits
|
||||
low := f.low
|
||||
high := f.high
|
||||
|
||||
flags := f.flags
|
||||
|
||||
// there's a flag that's -8 and i don't know what to do with it. I'm just
|
||||
// gonna mask away everything except the three least significant bits and
|
||||
// pray for the best.
|
||||
flags = flags & 7
|
||||
|
||||
// number of input steps
|
||||
steps := int(1<<f.bits - 1)
|
||||
|
||||
// keep the inverse to mult instead of divide later
|
||||
inv_steps := 1.0 / float32(steps)
|
||||
|
||||
// total range of values
|
||||
span := f.high - f.low
|
||||
|
||||
if span < 0 {
|
||||
panic("quantization span is backwards")
|
||||
}
|
||||
|
||||
// output width of each step
|
||||
step_width := span * inv_steps
|
||||
|
||||
var special *float32
|
||||
switch {
|
||||
case flags&f_min > 0:
|
||||
special = new(float32)
|
||||
*special = low
|
||||
case flags&f_max > 0:
|
||||
special = new(float32)
|
||||
*special = high
|
||||
case flags&f_center > 0:
|
||||
special = new(float32)
|
||||
middle := (high + low) * 0.5
|
||||
// if we're within a step of zero just return zero.
|
||||
if middle > 0 && middle-step_width < 0 || middle < 0 && middle+step_width > 0 {
|
||||
middle = 0
|
||||
}
|
||||
*special = middle
|
||||
}
|
||||
|
||||
return func(br bit.Reader) interface{} {
|
||||
if special != nil && bit.ReadBool(br) {
|
||||
Debug.Printf("decode float type: %s low: %f high: %f bits: %d steps: %d span: %f flags: %d special: %v", f._type.String(), low, high, bits, steps, span, flags, *special)
|
||||
return *special
|
||||
}
|
||||
u := br.ReadBits(bits)
|
||||
out := low + float32(u)*inv_steps*span
|
||||
Debug.Printf("decode float type: %s low: %f high: %f bits: %d bitVal: %d steps: %d span: %f flags: %d output: %v", f._type.String(), low, high, bits, u, steps, span, flags, out)
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
// reads an IEEE 754 binary float value off of the stream
|
||||
func ieeeFloat32Decoder(br bit.Reader) interface{} {
|
||||
return math.Float32frombits(uint32(br.ReadBits(32)))
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jordanorelli/hyperstone/bit"
|
||||
)
|
||||
|
||||
func genericType(spec *typeSpec, env *Env) tÿpe {
|
||||
if !strings.Contains(spec.typeName, "<") {
|
||||
return nil
|
||||
}
|
||||
|
||||
parts, err := genericName(spec.typeName)
|
||||
if err != nil {
|
||||
return typeError("bad generic name: %v", err)
|
||||
}
|
||||
|
||||
genericName, elemName := parts[0], parts[1]
|
||||
elemSpec := *spec
|
||||
elemSpec.typeName = elemName
|
||||
elem := parseTypeSpec(&elemSpec, env)
|
||||
|
||||
switch genericName {
|
||||
case "CHandle", "CStrongHandle":
|
||||
t := handle_t(fmt.Sprintf("%s<%s>", genericName, elem.typeName()))
|
||||
return &t
|
||||
case "CUtlVector":
|
||||
return &cutl_vector_t{elem}
|
||||
default:
|
||||
return typeError("unknown generic name: %v", parts[0])
|
||||
}
|
||||
}
|
||||
|
||||
func genericName(name string) ([2]string, error) {
|
||||
var out [2]string
|
||||
runes := []rune(strings.TrimSpace(name))
|
||||
b_start := 0
|
||||
depth := 0
|
||||
for i, r := range runes {
|
||||
if r == '<' {
|
||||
if depth == 0 {
|
||||
b_start = i
|
||||
out[0] = strings.TrimSpace(string(runes[0:i]))
|
||||
}
|
||||
depth++
|
||||
}
|
||||
if r == '>' {
|
||||
depth--
|
||||
if depth == 0 {
|
||||
if i == len(runes)-1 {
|
||||
out[1] = strings.TrimSpace(string(runes[b_start+1 : i]))
|
||||
} else {
|
||||
return out, fmt.Errorf("extra runes in generic type name")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if out[0] == "" {
|
||||
return out, fmt.Errorf("empty generic container name")
|
||||
}
|
||||
if out[1] == "" {
|
||||
return out, fmt.Errorf("empty generic element name")
|
||||
}
|
||||
Debug.Printf(" generic name in: %s out: %v", name, out)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
type cutl_vector_t struct {
|
||||
elem tÿpe
|
||||
}
|
||||
|
||||
func (t *cutl_vector_t) nü() value {
|
||||
return &cutl_vector{t: t}
|
||||
}
|
||||
|
||||
func (t cutl_vector_t) typeName() string {
|
||||
return fmt.Sprintf("CUtlVector<%s>", t.elem.typeName())
|
||||
}
|
||||
|
||||
type cutl_vector struct {
|
||||
t *cutl_vector_t
|
||||
slots []value
|
||||
}
|
||||
|
||||
func (v *cutl_vector) tÿpe() tÿpe { return v.t }
|
||||
|
||||
func (v *cutl_vector) read(r bit.Reader) error {
|
||||
bit.ReadVarInt32(r) // ??
|
||||
return r.Err()
|
||||
}
|
||||
|
||||
func (v cutl_vector) String() string {
|
||||
if len(v.slots) > 8 {
|
||||
return fmt.Sprintf("%s(%d)%v...", v.t.typeName(), len(v.slots), v.slots[:8])
|
||||
}
|
||||
return fmt.Sprintf("%s(%d)%v", v.t.typeName(), len(v.slots), v.slots)
|
||||
}
|
||||
|
||||
func (v *cutl_vector) slotType(int) tÿpe { return v.t.elem }
|
||||
func (v *cutl_vector) slotName(n int) string { return strconv.Itoa(n) }
|
||||
func (v *cutl_vector) setSlotValue(slot int, val value) {
|
||||
if slot == len(v.slots) {
|
||||
v.slots = append(v.slots, val)
|
||||
} else {
|
||||
v.slots[slot] = val
|
||||
}
|
||||
}
|
||||
|
||||
func (v *cutl_vector) getSlotValue(slot int) value {
|
||||
if slot >= len(v.slots) {
|
||||
return nil
|
||||
}
|
||||
return v.slots[slot]
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jordanorelli/hyperstone/bit"
|
||||
)
|
||||
|
||||
var hseq_t = &typeLiteral{
|
||||
name: "HSequence",
|
||||
newFn: func() value {
|
||||
return new(hseq_v)
|
||||
},
|
||||
}
|
||||
|
||||
type hseq_v uint64
|
||||
|
||||
func (v hseq_v) tÿpe() tÿpe { return hseq_t }
|
||||
func (v *hseq_v) read(r bit.Reader) error {
|
||||
*v = hseq_v(bit.ReadVarInt(r) - 1)
|
||||
return r.Err()
|
||||
}
|
||||
|
||||
func (v hseq_v) String() string {
|
||||
return fmt.Sprintf("hseq:%d", uint64(v))
|
||||
}
|
||||
|
||||
func hSeqType(spec *typeSpec, env *Env) tÿpe {
|
||||
if spec.typeName != "HSequence" {
|
||||
return nil
|
||||
}
|
||||
Debug.Printf(" hsequence type")
|
||||
return hseq_t
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
"github.com/jordanorelli/hyperstone/bit"
|
||||
"github.com/jordanorelli/hyperstone/dota"
|
||||
)
|
||||
|
||||
func getSerializers(m *dota.CDemoSendTables) (*dota.CSVCMsg_FlattenedSerializer, error) {
|
||||
data := m.GetData()
|
||||
r := bit.NewBytesReader(data)
|
||||
|
||||
size := int(bit.ReadVarInt(r))
|
||||
buf := make([]byte, size)
|
||||
|
||||
r.Read(buf)
|
||||
if r.Err() != nil {
|
||||
return nil, wrap(r.Err(), "error reading serializers body")
|
||||
}
|
||||
|
||||
flat := dota.CSVCMsg_FlattenedSerializer{}
|
||||
if err := proto.Unmarshal(buf, &flat); err != nil {
|
||||
return nil, wrap(err, "error unmarshaling serializers body")
|
||||
}
|
||||
return &flat, nil
|
||||
}
|
||||
|
||||
func prettyFlatField(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) string {
|
||||
var_name := env.symbol(int(flat.GetVarNameSym()))
|
||||
var_type := env.symbol(int(flat.GetVarTypeSym()))
|
||||
|
||||
var pretty bytes.Buffer
|
||||
fmt.Fprintf(&pretty, "{name: %s type: %s", var_name, var_type)
|
||||
if flat.BitCount != nil {
|
||||
fmt.Fprintf(&pretty, " bits: %d", flat.GetBitCount())
|
||||
}
|
||||
if flat.LowValue != nil {
|
||||
fmt.Fprintf(&pretty, " low: %f", flat.GetLowValue())
|
||||
}
|
||||
if flat.HighValue != nil {
|
||||
fmt.Fprintf(&pretty, " high: %f", flat.GetHighValue())
|
||||
}
|
||||
if flat.EncodeFlags != nil {
|
||||
fmt.Fprintf(&pretty, " flags: %d", flat.GetEncodeFlags())
|
||||
}
|
||||
if flat.FieldSerializerNameSym != nil {
|
||||
fmt.Fprintf(&pretty, " serializer: %s", env.symbol(int(flat.GetFieldSerializerNameSym())))
|
||||
}
|
||||
if flat.FieldSerializerVersion != nil {
|
||||
fmt.Fprintf(&pretty, " s_version: %d", flat.GetFieldSerializerVersion())
|
||||
}
|
||||
if flat.SendNodeSym != nil {
|
||||
fmt.Fprintf(&pretty, " send: %s", env.symbol(int(flat.GetSendNodeSym())))
|
||||
}
|
||||
if flat.VarEncoderSym != nil {
|
||||
fmt.Fprintf(&pretty, " var_enc: %s", env.symbol(int(flat.GetVarEncoderSym())))
|
||||
}
|
||||
fmt.Fprint(&pretty, "}")
|
||||
return pretty.String()
|
||||
}
|
@ -1,246 +0,0 @@
|
||||
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) hasClassinfo() bool {
|
||||
return n.classIds != nil && len(n.classIds) > 0
|
||||
}
|
||||
|
||||
// merges the send table data found in the replay protobufs. The send table
|
||||
// data contains a specification for an entity type system.
|
||||
func (n *Namespace) mergeSendTables(st *dota.CDemoSendTables) error {
|
||||
// sendtables only has one field, a binary data field.
|
||||
Debug.Printf("merge send tables")
|
||||
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 {
|
||||
return fmt.Errorf("unable to merge send tables: %v", err)
|
||||
}
|
||||
|
||||
n.SymbolTable = SymbolTable(flat.GetSymbols())
|
||||
|
||||
// the full set of fields that may appear on the classes is read first.
|
||||
// each class will have a list of fields.
|
||||
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()))
|
||||
|
||||
// each serializer in the source data generates a class.
|
||||
for _, c := range flat.GetSerializers() {
|
||||
name := n.Symbol(int(c.GetSerializerNameSym()))
|
||||
version := int(c.GetSerializerVersion())
|
||||
|
||||
Debug.Printf("new class: %s %v", name, version)
|
||||
class := Class{name: name, Version: version}
|
||||
class.fromProto(c, fields)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
for i := range fields {
|
||||
f := &fields[i]
|
||||
if f.serializer != nil {
|
||||
if f.serializerVersion != nil {
|
||||
f.class = n.classesByName[f.serializer.String()][int(*f.serializerVersion)]
|
||||
} else {
|
||||
f.class = n.NewestClass(f.serializer.String())
|
||||
}
|
||||
}
|
||||
|
||||
// for some fields, we want to initialize a zero value on the baseline
|
||||
// instance. specifically, we do this for arrays so that we can't try
|
||||
// to index into nil.
|
||||
f.typeSpec = parseTypeName(n, f._type.String())
|
||||
if f.isContainer() {
|
||||
mf := f.memberField()
|
||||
fn := newFieldDecoder(n, mf)
|
||||
if f.typeSpec.kind == t_array {
|
||||
f.initializer = func() interface{} {
|
||||
return &array{
|
||||
slots: make([]interface{}, f.typeSpec.size),
|
||||
_slotType: mf._type.String(),
|
||||
decoder: fn,
|
||||
}
|
||||
}
|
||||
} else if f.typeSpec.kind == t_template && f.typeSpec.template == "CUtlVector" {
|
||||
f.initializer = func() interface{} {
|
||||
return &cutlVector{
|
||||
slots: make([]interface{}, f.typeSpec.size),
|
||||
_slotType: mf._type.String(),
|
||||
decoder: fn,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we also wait until after we've discovered all of the classes to
|
||||
// build the field decoder functions, because some fields are
|
||||
// themselves entities.
|
||||
f.decoder = newFieldDecoder(n, f)
|
||||
}
|
||||
|
||||
for _, ff := range flat.GetFields() {
|
||||
t, err := parseType(n, ff)
|
||||
if err != nil {
|
||||
Debug.Printf(" parseType error: %v", err)
|
||||
} else {
|
||||
Debug.Printf(" parseType type: %v", t)
|
||||
}
|
||||
}
|
||||
|
||||
return br.Err()
|
||||
}
|
||||
|
||||
/*
|
||||
func (n *Namespace) newMergeSendTables(st *dota.CDemoSendTables) error {
|
||||
// sendtables only has one field, a binary data field. It's not immediately
|
||||
// clear why this message exists at all when it could simply contain
|
||||
// CSVMsg_FlattenedSerializer.
|
||||
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 {
|
||||
return fmt.Errorf("unable to merge send tables: %v", err)
|
||||
}
|
||||
return n.mergeSerializers(&flat)
|
||||
}
|
||||
|
||||
func (n *Namespace) mergeSerializers(flat *dota.CSVCMsg_FlattenedSerializer) error {
|
||||
// most of the names and associated strings are interned in a symbol table.
|
||||
// We'll need these first since they're referenced throughought the class
|
||||
// and field definitions.
|
||||
n.SymbolTable = SymbolTable(flat.GetSymbols())
|
||||
|
||||
n.classes = make(map[classId]*Class, len(flat.GetSerializers()))
|
||||
n.classesByName = make(map[string]map[int]*Class, len(flat.GetSerializers()))
|
||||
|
||||
// some fields refer to classes, but classes are collections of fields.
|
||||
// Their references are potentially cyclical. We start by creating empty
|
||||
// classes so that fields may point to them.
|
||||
for _, s := range flat.GetSerializers() {
|
||||
name_sym := n.Symbol(int(s.GetSerializerNameSym()))
|
||||
ver := int(s.GetSerializerVersion())
|
||||
class := Class{name: name_sym, Version: ver}
|
||||
n.classes[class.Id()] = &class
|
||||
if n.classesByName[class.Name()] == nil {
|
||||
n.classesByName[class.Name()] = make(map[int]*Class)
|
||||
}
|
||||
n.classesByName[class.Name()][ver] = &class
|
||||
}
|
||||
|
||||
// type ProtoFlattenedSerializerFieldT struct {
|
||||
// VarTypeSym *int32
|
||||
// VarNameSym *int32
|
||||
// VarEncoderSym *int32
|
||||
// FieldSerializerNameSym *int32
|
||||
// FieldSerializerVersion *int32
|
||||
// BitCount *int32
|
||||
// LowValue *float32
|
||||
// HighValue *float32
|
||||
// EncodeFlags *int32
|
||||
// SendNodeSym *int32
|
||||
// }
|
||||
|
||||
// next we parse all of the fields along with their type definitions
|
||||
fields := make(map[int]Field, len(flat.GetFields()))
|
||||
for id, flatField := range flat.GetFields() {
|
||||
t := parseType(n, flatField)
|
||||
fields[id].Name = n.Symbol(int(flatField.GetVarNameSym())).String()
|
||||
}
|
||||
os.Exit(1)
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
func (n *Namespace) readClassId(r bit.Reader) int {
|
||||
return int(r.ReadBits(uint(n.idBits)))
|
||||
}
|
||||
|
||||
func (n *Namespace) Class(name string, version int) *Class {
|
||||
return n.classesByName[name][version]
|
||||
}
|
||||
|
||||
// retrieves the newest version of a class, as referenced by name.
|
||||
func (n *Namespace) NewestClass(name string) *Class {
|
||||
versions, newest := n.classesByName[name], -1
|
||||
for v, _ := range versions {
|
||||
if v > newest {
|
||||
newest = v
|
||||
}
|
||||
}
|
||||
if newest == -1 {
|
||||
Info.Fatalf("class %s has no known versions in its version map", name)
|
||||
}
|
||||
return versions[newest]
|
||||
}
|
||||
|
||||
func (n *Namespace) ClassByNetId(id int) *Class {
|
||||
name, ok := n.classIds[id]
|
||||
if !ok {
|
||||
Info.Fatalf("can't find class name for net id %d", id)
|
||||
}
|
||||
return n.NewestClass(name)
|
||||
}
|
||||
|
||||
func (n *Namespace) HasClass(name string) bool {
|
||||
_, ok := n.classesByName[name]
|
||||
return ok
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"github.com/jordanorelli/hyperstone/bit"
|
||||
)
|
||||
|
||||
var primitives map[string]Primitive
|
||||
|
||||
// a Primitive type in the ent object system.
|
||||
type Primitive struct {
|
||||
name string
|
||||
read decodeFn
|
||||
alloc func() interface{}
|
||||
}
|
||||
|
||||
func (p *Primitive) New(args ...interface{}) interface{} {
|
||||
if p.alloc != nil {
|
||||
return p.alloc()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (p *Primitive) Name() string { return p.name }
|
||||
func (p *Primitive) Slotted() bool { return false }
|
||||
|
||||
func (p *Primitive) Read(br bit.Reader, d *Dict) (interface{}, error) {
|
||||
return p.read(br, d)
|
||||
}
|
||||
|
||||
func init() {
|
||||
types := []Primitive{
|
||||
{name: "bool", read: readBool},
|
||||
{name: "uint8", read: readUint8},
|
||||
{name: "uint16", read: readUint16},
|
||||
{name: "uint32", read: readUint32},
|
||||
{name: "uint64", read: readUint64},
|
||||
{name: "int8", read: readInt8},
|
||||
{name: "int16", read: readInt16},
|
||||
{name: "int32", read: readInt32},
|
||||
{name: "int64", read: readInt64},
|
||||
{name: "Color", read: readColor},
|
||||
{name: "HSequence", read: readIntMinusOne},
|
||||
}
|
||||
primitives = make(map[string]Primitive, len(types))
|
||||
for _, t := range types {
|
||||
primitives[t.name] = t
|
||||
}
|
||||
}
|
||||
|
||||
func readBool(br bit.Reader, d *Dict) (interface{}, error) {
|
||||
return bit.ReadBool(br), br.Err()
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// uints
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
func readUint8(br bit.Reader, d *Dict) (interface{}, error) {
|
||||
return uint8(br.ReadBits(8)), br.Err()
|
||||
}
|
||||
|
||||
func readUint16(br bit.Reader, d *Dict) (interface{}, error) {
|
||||
return uint16(bit.ReadVarInt(br)), br.Err()
|
||||
}
|
||||
|
||||
func readUint32(br bit.Reader, d *Dict) (interface{}, error) {
|
||||
return uint32(bit.ReadVarInt(br)), br.Err()
|
||||
}
|
||||
|
||||
func readUint64(br bit.Reader, d *Dict) (interface{}, error) {
|
||||
return bit.ReadVarInt(br), br.Err()
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ints
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
func readInt8(br bit.Reader, d *Dict) (interface{}, error) {
|
||||
return int8(bit.ReadZigZag(br)), br.Err()
|
||||
}
|
||||
|
||||
func readInt16(br bit.Reader, d *Dict) (interface{}, error) {
|
||||
return int16(bit.ReadZigZag(br)), br.Err()
|
||||
}
|
||||
|
||||
func readInt32(br bit.Reader, d *Dict) (interface{}, error) {
|
||||
return int32(bit.ReadZigZag(br)), br.Err()
|
||||
}
|
||||
|
||||
func readInt64(br bit.Reader, d *Dict) (interface{}, error) {
|
||||
return bit.ReadZigZag(br), br.Err()
|
||||
}
|
||||
|
||||
func readColor(br bit.Reader, d *Dict) (interface{}, error) {
|
||||
u := bit.ReadVarInt(br)
|
||||
return color{
|
||||
r: uint8(u & 0xff000000),
|
||||
g: uint8(u & 0x00ff0000),
|
||||
b: uint8(u & 0x0000ff00),
|
||||
a: uint8(u & 0x000000ff),
|
||||
}, br.Err()
|
||||
}
|
||||
|
||||
// what in the good fuck is this
|
||||
func readIntMinusOne(br bit.Reader, d *Dict) (interface{}, error) {
|
||||
return bit.ReadVarInt(br) - 1, br.Err()
|
||||
}
|
@ -1,28 +1,9 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jordanorelli/hyperstone/bit"
|
||||
)
|
||||
|
||||
type slotted interface {
|
||||
value
|
||||
slotType(int) tÿpe
|
||||
slotName(int) string
|
||||
slotValue(int) interface{}
|
||||
slotType(int) string
|
||||
slotDecoder(int) decoder
|
||||
setSlotValue(int, interface{})
|
||||
}
|
||||
|
||||
func fillSlots(dest slotted, displayPath string, sr *selectionReader, br bit.Reader) error {
|
||||
selections, err := sr.readSelections(br, htree)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error filling slots: %v", err)
|
||||
}
|
||||
|
||||
for _, s := range selections {
|
||||
if err := s.fill(0, displayPath, dest, br); err != nil {
|
||||
return fmt.Errorf("error filling slots: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
setSlotValue(int, value)
|
||||
getSlotValue(int) value
|
||||
}
|
||||
|
@ -1,17 +1,10 @@
|
||||
package ent
|
||||
|
||||
// the internal representation of table data refers to all labels as
|
||||
// interned strings (symbols). This array of string contains the mapping of
|
||||
// symbol ids to symbol display representations. The sample replay I have
|
||||
// at the time of writing this contains 2215 symbols in its symbol table.
|
||||
// The dota replay format uses an ordered list of symbols.
|
||||
type SymbolTable []string
|
||||
|
||||
func (t *SymbolTable) Symbol(id int) Symbol { return Symbol{id: id, table: t} }
|
||||
|
||||
type Symbol struct {
|
||||
type symbol struct {
|
||||
id int
|
||||
table *SymbolTable
|
||||
table *symbolTable
|
||||
}
|
||||
|
||||
func (s Symbol) String() string { return (*s.table)[s.id] }
|
||||
func (s symbol) String() string { return (*s.table)[s.id] }
|
||||
|
||||
type symbolTable []string
|
||||
|
@ -1,99 +1,121 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/jordanorelli/hyperstone/bit"
|
||||
"github.com/jordanorelli/hyperstone/dota"
|
||||
)
|
||||
|
||||
type decodeFn func(bit.Reader, *Dict) (interface{}, error)
|
||||
type tÿpe interface {
|
||||
nü() value
|
||||
typeName() string
|
||||
}
|
||||
|
||||
// a Type in the entity type system. Note that not every type is necessarily an
|
||||
// entity type, since there are necessarily primitives, and above that, arrays
|
||||
// and generics.
|
||||
type Type interface {
|
||||
// creates a new value of the given type.
|
||||
New(...interface{}) interface{}
|
||||
type typeLiteral struct {
|
||||
name string
|
||||
newFn func() value
|
||||
}
|
||||
|
||||
// name is primarily of interest for debugging
|
||||
Name() string
|
||||
func (t typeLiteral) nü() value { return t.newFn() }
|
||||
func (t typeLiteral) typeName() string { return t.name }
|
||||
|
||||
// whether or not the produced values are expected to be slotted.
|
||||
Slotted() bool
|
||||
type typeParseFn func(*typeSpec, *Env) tÿpe
|
||||
|
||||
// reads a value of this type off of the bit reader
|
||||
Read(bit.Reader, *Dict) (interface{}, error)
|
||||
func parseFieldType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe {
|
||||
spec := new(typeSpec)
|
||||
spec.fromProto(flat, env)
|
||||
return parseTypeSpec(spec, env)
|
||||
}
|
||||
|
||||
func parseType(n *Namespace, flat *dota.ProtoFlattenedSerializerFieldT) (Type, error) {
|
||||
Debug.Printf("parseType: %s", prettyFlatField(n, flat))
|
||||
type_name := n.Symbol(int(flat.GetVarTypeSym())).String()
|
||||
|
||||
if prim, ok := primitives[type_name]; ok {
|
||||
Debug.Printf(" parseType: found primitive with name %s", type_name)
|
||||
return &prim, nil
|
||||
func parseTypeSpec(spec *typeSpec, env *Env) tÿpe {
|
||||
Debug.Printf(" parse spec: %v", spec)
|
||||
coalesce := func(fns ...typeParseFn) tÿpe {
|
||||
for _, fn := range fns {
|
||||
if t := fn(spec, env); t != nil {
|
||||
return t
|
||||
}
|
||||
|
||||
if n.HasClass(type_name) {
|
||||
Debug.Printf(" parseType: found class with name %s", type_name)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch type_name {
|
||||
case "float32", "CNetworkedQuantizedFloat":
|
||||
Debug.Printf(" parseType: parsing as float type")
|
||||
return parseFloatType(n, flat)
|
||||
case "CGameSceneNodeHandle":
|
||||
return &Handle{name: "CGameSceneNodeHandle"}, nil
|
||||
case "QAngle":
|
||||
return parseQAngleType(n, flat)
|
||||
return nil
|
||||
}
|
||||
return coalesce(arrayType, atomType, floatType, handleType, qAngleType,
|
||||
hSeqType, genericType, vectorType, classType, unknownType)
|
||||
}
|
||||
|
||||
Debug.Printf(" parseType: failed")
|
||||
// type ProtoFlattenedSerializerFieldT struct {
|
||||
// VarTypeSym *int32
|
||||
// VarNameSym *int32
|
||||
// VarEncoderSym *int32
|
||||
// FieldSerializerNameSym *int32
|
||||
// FieldSerializerVersion *int32
|
||||
// BitCount *int32
|
||||
// LowValue *float32
|
||||
// HighValue *float32
|
||||
// EncodeFlags *int32
|
||||
// SendNodeSym *int32
|
||||
// }
|
||||
return nil, nil
|
||||
type unknown_t string
|
||||
|
||||
func (t unknown_t) typeName() string { return string(t) }
|
||||
func (t *unknown_t) nü() value {
|
||||
return &unknown_v{t: t}
|
||||
}
|
||||
|
||||
func prettyFlatField(n *Namespace, ff *dota.ProtoFlattenedSerializerFieldT) string {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "{type: %s", n.Symbol(int(ff.GetVarTypeSym())))
|
||||
fmt.Fprintf(&buf, " name: %s", n.Symbol(int(ff.GetVarNameSym())))
|
||||
if ff.BitCount != nil {
|
||||
fmt.Fprintf(&buf, " bits: %d", ff.GetBitCount())
|
||||
}
|
||||
if ff.LowValue != nil {
|
||||
fmt.Fprintf(&buf, " low: %f", ff.GetLowValue())
|
||||
}
|
||||
if ff.HighValue != nil {
|
||||
fmt.Fprintf(&buf, " high: %f", ff.GetHighValue())
|
||||
}
|
||||
if ff.EncodeFlags != nil {
|
||||
fmt.Fprintf(&buf, " flags: %d", ff.GetEncodeFlags())
|
||||
}
|
||||
if ff.FieldSerializerNameSym != nil {
|
||||
fmt.Fprintf(&buf, " serializer: %s", n.Symbol(int(ff.GetFieldSerializerNameSym())))
|
||||
type unknown_v struct {
|
||||
t tÿpe
|
||||
v uint64
|
||||
}
|
||||
|
||||
func (v unknown_v) tÿpe() tÿpe { return v.t }
|
||||
func (v *unknown_v) read(r bit.Reader) error {
|
||||
v.v = bit.ReadVarInt(r)
|
||||
return r.Err()
|
||||
}
|
||||
|
||||
func (v unknown_v) String() string {
|
||||
return fmt.Sprintf("%s(unknown):%d", v.t.typeName(), v.v)
|
||||
}
|
||||
|
||||
func unknownType(spec *typeSpec, env *Env) tÿpe {
|
||||
Debug.Printf("Unknown Type: %v", spec)
|
||||
t := unknown_t(spec.typeName)
|
||||
return &t
|
||||
}
|
||||
|
||||
// a type error is both an error and a type. It represents a type that we were
|
||||
// unable to correctly parse. It can be interpreted as an error or as a type;
|
||||
// when interpreted as a type, it errors every time it tries to read a value.
|
||||
func typeError(t string, args ...interface{}) tÿpe {
|
||||
Debug.Printf(" type error: %s", fmt.Sprintf(t, args...))
|
||||
return error_t(fmt.Sprintf(t, args...))
|
||||
}
|
||||
|
||||
type error_t string
|
||||
|
||||
func (e error_t) nü() value { panic("can't create an error val like that") }
|
||||
func (e error_t) typeName() string { return "error" }
|
||||
func (e error_t) Error() string { return string(e) }
|
||||
|
||||
type typeSpec struct {
|
||||
name string
|
||||
typeName string
|
||||
bits uint
|
||||
low float32
|
||||
high float32
|
||||
flags int
|
||||
serializer string
|
||||
serializerV int
|
||||
send string
|
||||
encoder string
|
||||
}
|
||||
|
||||
func (s *typeSpec) fromProto(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) {
|
||||
s.name = env.symbol(int(flat.GetVarNameSym()))
|
||||
s.typeName = env.symbol(int(flat.GetVarTypeSym()))
|
||||
if flat.GetBitCount() < 0 {
|
||||
// this would cause ridiculously long reads later if we let it overflow
|
||||
panic("negative bit count: data is likely corrupt")
|
||||
}
|
||||
if ff.FieldSerializerVersion != nil {
|
||||
fmt.Fprintf(&buf, " version: %d", ff.GetFieldSerializerVersion())
|
||||
s.bits = uint(flat.GetBitCount())
|
||||
s.low = flat.GetLowValue()
|
||||
s.high = flat.GetHighValue()
|
||||
s.flags = int(flat.GetEncodeFlags())
|
||||
if flat.FieldSerializerNameSym != nil {
|
||||
s.serializer = env.symbol(int(*flat.FieldSerializerNameSym))
|
||||
}
|
||||
if ff.SendNodeSym != nil {
|
||||
fmt.Fprintf(&buf, " send: %s", n.Symbol(int(ff.GetSendNodeSym())))
|
||||
s.serializerV = int(flat.GetFieldSerializerVersion())
|
||||
if flat.SendNodeSym != nil {
|
||||
s.send = env.symbol(int(*flat.SendNodeSym))
|
||||
}
|
||||
if ff.VarEncoderSym != nil {
|
||||
fmt.Fprintf(&buf, " encoder: %s", n.Symbol(int(ff.GetVarEncoderSym())))
|
||||
if flat.VarEncoderSym != nil {
|
||||
s.encoder = env.symbol(int(*flat.VarEncoderSym))
|
||||
}
|
||||
fmt.Fprintf(&buf, "}")
|
||||
return buf.String()
|
||||
}
|
||||
|
@ -1,120 +0,0 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
t_element = iota // a known element class
|
||||
t_object // c++ object type that is not a known element class
|
||||
t_array // c++ array type
|
||||
t_template // c++ template type
|
||||
t_pointer // c++ pointer
|
||||
)
|
||||
|
||||
// constant identifiers
|
||||
var cIdents = map[string]int{"MAX_ABILITY_DRAFT_ABILITIES": 48}
|
||||
|
||||
type typeSpec struct {
|
||||
name string
|
||||
kind int
|
||||
size int
|
||||
template string
|
||||
member *typeSpec
|
||||
}
|
||||
|
||||
func (t typeSpec) String() string {
|
||||
if t.member != nil {
|
||||
return fmt.Sprintf("{%v %v %v %v %v}", t.name, t.kind, t.size, t.template, *t.member)
|
||||
}
|
||||
return fmt.Sprintf("{%v %v %v %v %v}", t.name, t.kind, t.size, t.template, t.member)
|
||||
}
|
||||
|
||||
func parseTypeName(n *Namespace, s string) typeSpec {
|
||||
s = strings.TrimSpace(s)
|
||||
t := typeSpec{name: s}
|
||||
|
||||
if n.HasClass(s) {
|
||||
t.kind = t_element
|
||||
return t
|
||||
}
|
||||
|
||||
if strings.HasSuffix(s, "*") {
|
||||
t.kind = t_pointer
|
||||
t.member = new(typeSpec)
|
||||
*t.member = parseTypeName(n, s[:len(s)-2])
|
||||
return t
|
||||
}
|
||||
|
||||
// presumably this is some sort of array type
|
||||
if strings.ContainsRune(s, '[') {
|
||||
memName, count := parseArrayName(s)
|
||||
t.kind = t_array
|
||||
t.size = count
|
||||
t.member = new(typeSpec)
|
||||
*t.member = parseTypeName(n, memName)
|
||||
return t
|
||||
}
|
||||
|
||||
if strings.ContainsRune(s, '<') {
|
||||
t.kind = t_template
|
||||
template, member := parseTemplateName(s)
|
||||
t.template = template
|
||||
t.member = new(typeSpec)
|
||||
*t.member = parseTypeName(n, member)
|
||||
return t
|
||||
}
|
||||
|
||||
t.kind = t_object
|
||||
return t
|
||||
}
|
||||
|
||||
func parseArrayName(s string) (string, int) {
|
||||
runes := []rune(s)
|
||||
if runes[len(runes)-1] != ']' {
|
||||
panic("invalid array type name: " + s)
|
||||
}
|
||||
for i := len(runes) - 2; i >= 0; i-- {
|
||||
if runes[i] == '[' {
|
||||
ns := strings.TrimSpace(string(runes[i+1 : len(runes)-1]))
|
||||
n, err := strconv.Atoi(ns)
|
||||
if err != nil {
|
||||
n = cIdents[ns]
|
||||
if n <= 0 {
|
||||
panic("invalid array type name: " + err.Error())
|
||||
}
|
||||
}
|
||||
return strings.TrimSpace(string(runes[:i])), n
|
||||
}
|
||||
}
|
||||
panic("invalid array type name: " + s)
|
||||
}
|
||||
|
||||
func parseTemplateName(s string) (string, string) {
|
||||
if strings.ContainsRune(s, ',') {
|
||||
panic("can't handle templates with multiple parameters")
|
||||
}
|
||||
|
||||
runes := []rune(s)
|
||||
template := ""
|
||||
depth := 0
|
||||
bracket_start := -1
|
||||
for i, r := range runes {
|
||||
if r == '<' {
|
||||
if depth == 0 {
|
||||
bracket_start = i
|
||||
template = strings.TrimSpace(string(runes[:i]))
|
||||
}
|
||||
depth++
|
||||
}
|
||||
if r == '>' {
|
||||
depth--
|
||||
if depth == 0 {
|
||||
return template, strings.TrimSpace(string(runes[bracket_start+1 : i]))
|
||||
}
|
||||
}
|
||||
}
|
||||
panic("invalid template type definition: " + s)
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"github.com/jordanorelli/hyperstone/bit"
|
||||
)
|
||||
|
||||
type value interface {
|
||||
String() string
|
||||
tÿpe() tÿpe
|
||||
read(bit.Reader) error
|
||||
}
|
@ -1,7 +1,67 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jordanorelli/hyperstone/bit"
|
||||
)
|
||||
|
||||
func vectorType(spec *typeSpec, env *Env) tÿpe {
|
||||
if spec.encoder != "" {
|
||||
return nil
|
||||
}
|
||||
t := floatType(spec, env)
|
||||
if _, ok := t.(error); ok {
|
||||
return t
|
||||
}
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
return &vector_t{elem: t}
|
||||
}
|
||||
|
||||
type vector_t struct {
|
||||
elem tÿpe
|
||||
}
|
||||
|
||||
func (t vector_t) typeName() string {
|
||||
return fmt.Sprintf("vector<%s>", t.elem.typeName())
|
||||
}
|
||||
|
||||
func (t *vector_t) nü() value {
|
||||
return &vector{t: t}
|
||||
}
|
||||
|
||||
type vector struct {
|
||||
x float32
|
||||
y float32
|
||||
z float32
|
||||
t tÿpe
|
||||
x, y, z value
|
||||
}
|
||||
|
||||
func (v vector) tÿpe() tÿpe { return v.t }
|
||||
|
||||
func (v *vector) read(r bit.Reader) error {
|
||||
if v.x == nil {
|
||||
v.x = v.t.nü()
|
||||
}
|
||||
if v.y == nil {
|
||||
v.y = v.t.nü()
|
||||
}
|
||||
if v.z == nil {
|
||||
v.z = v.t.nü()
|
||||
}
|
||||
|
||||
type fn func(bit.Reader) error
|
||||
coalesce := func(fns ...fn) error {
|
||||
for _, f := range fns {
|
||||
if err := f(r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return coalesce(v.x.read, v.y.read, v.z.read)
|
||||
}
|
||||
|
||||
func (v vector) String() string {
|
||||
return fmt.Sprintf("vector<%s>{%s %s %s}", v.t.typeName, v.x, v.y, v.z)
|
||||
}
|
||||
|
@ -1,18 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type entity struct {
|
||||
t entityType
|
||||
size uint32
|
||||
body []byte
|
||||
}
|
||||
|
||||
func (e entity) String() string {
|
||||
if len(e.body) < 30 {
|
||||
return fmt.Sprintf("{entity type: %s size: %d data: %x}", e.t, e.size, e.body)
|
||||
}
|
||||
return fmt.Sprintf("{entity type: %s size: %d data: %x...}", e.t, e.size, e.body[:27])
|
||||
}
|
Loading…
Reference in New Issue