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
|
package ent
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"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 {
|
type array struct {
|
||||||
slots []interface{}
|
t *array_t
|
||||||
_slotType string
|
slots []value
|
||||||
decoder
|
}
|
||||||
|
|
||||||
|
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 (s *string_v) String() string {
|
||||||
func (a *array) slotValue(slot int) interface{} { return a.slots[slot] }
|
return string(s.valid)
|
||||||
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) 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
|
package ent
|
||||||
|
|
||||||
type Entity struct {
|
import (
|
||||||
*Class
|
"fmt"
|
||||||
serial int
|
"github.com/jordanorelli/hyperstone/bit"
|
||||||
slots []interface{}
|
)
|
||||||
isBaseline bool
|
|
||||||
|
type entity struct {
|
||||||
|
class *class
|
||||||
|
slots []value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Entity) slotName(n int) string { return e.Class.Fields[n].name.String() }
|
func (e *entity) read(r bit.Reader) error {
|
||||||
func (e *Entity) slotType(n int) string { return e.Class.Fields[n]._type.String() }
|
bit.ReadBool(r) // ???
|
||||||
func (e *Entity) slotValue(n int) interface{} {
|
return nil
|
||||||
v := e.slots[n]
|
}
|
||||||
if v != nil {
|
|
||||||
return v
|
func (e *entity) className() string {
|
||||||
|
if e.class != nil {
|
||||||
|
return e.class.name
|
||||||
}
|
}
|
||||||
if !e.isBaseline && e.Class.baseline != nil {
|
return "<None>"
|
||||||
return e.Class.baseline.slotValue(n)
|
}
|
||||||
|
|
||||||
|
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) slotName(i int) string { return e.class.fields[i].name }
|
||||||
func (e *Entity) setSlotValue(n int, v interface{}) { e.slots[n] = v }
|
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
|
package ent
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/jordanorelli/hyperstone/bit"
|
|
||||||
)
|
|
||||||
|
|
||||||
type slotted interface {
|
type slotted interface {
|
||||||
|
value
|
||||||
|
slotType(int) tÿpe
|
||||||
slotName(int) string
|
slotName(int) string
|
||||||
slotValue(int) interface{}
|
setSlotValue(int, value)
|
||||||
slotType(int) string
|
getSlotValue(int) value
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,10 @@
|
|||||||
package ent
|
package ent
|
||||||
|
|
||||||
// the internal representation of table data refers to all labels as
|
type symbol struct {
|
||||||
// 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 {
|
|
||||||
id int
|
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
|
package ent
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/jordanorelli/hyperstone/bit"
|
"github.com/jordanorelli/hyperstone/bit"
|
||||||
"github.com/jordanorelli/hyperstone/dota"
|
"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
|
type typeLiteral struct {
|
||||||
// entity type, since there are necessarily primitives, and above that, arrays
|
name string
|
||||||
// and generics.
|
newFn func() value
|
||||||
type Type interface {
|
}
|
||||||
// creates a new value of the given type.
|
|
||||||
New(...interface{}) interface{}
|
|
||||||
|
|
||||||
// name is primarily of interest for debugging
|
func (t typeLiteral) nü() value { return t.newFn() }
|
||||||
Name() string
|
func (t typeLiteral) typeName() string { return t.name }
|
||||||
|
|
||||||
// whether or not the produced values are expected to be slotted.
|
type typeParseFn func(*typeSpec, *Env) tÿpe
|
||||||
Slotted() bool
|
|
||||||
|
|
||||||
// reads a value of this type off of the bit reader
|
func parseFieldType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe {
|
||||||
Read(bit.Reader, *Dict) (interface{}, error)
|
spec := new(typeSpec)
|
||||||
|
spec.fromProto(flat, env)
|
||||||
|
return parseTypeSpec(spec, env)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseType(n *Namespace, flat *dota.ProtoFlattenedSerializerFieldT) (Type, error) {
|
func parseTypeSpec(spec *typeSpec, env *Env) tÿpe {
|
||||||
Debug.Printf("parseType: %s", prettyFlatField(n, flat))
|
Debug.Printf(" parse spec: %v", spec)
|
||||||
type_name := n.Symbol(int(flat.GetVarTypeSym())).String()
|
coalesce := func(fns ...typeParseFn) tÿpe {
|
||||||
|
for _, fn := range fns {
|
||||||
if prim, ok := primitives[type_name]; ok {
|
if t := fn(spec, env); t != nil {
|
||||||
Debug.Printf(" parseType: found primitive with name %s", type_name)
|
return t
|
||||||
return &prim, nil
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
return coalesce(arrayType, atomType, floatType, handleType, qAngleType,
|
||||||
|
hSeqType, genericType, vectorType, classType, unknownType)
|
||||||
|
}
|
||||||
|
|
||||||
if n.HasClass(type_name) {
|
type unknown_t string
|
||||||
Debug.Printf(" parseType: found class with name %s", type_name)
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch type_name {
|
func (t unknown_t) typeName() string { return string(t) }
|
||||||
case "float32", "CNetworkedQuantizedFloat":
|
func (t *unknown_t) nü() value {
|
||||||
Debug.Printf(" parseType: parsing as float type")
|
return &unknown_v{t: t}
|
||||||
return parseFloatType(n, flat)
|
}
|
||||||
case "CGameSceneNodeHandle":
|
|
||||||
return &Handle{name: "CGameSceneNodeHandle"}, nil
|
|
||||||
case "QAngle":
|
|
||||||
return parseQAngleType(n, flat)
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.Printf(" parseType: failed")
|
type unknown_v struct {
|
||||||
// type ProtoFlattenedSerializerFieldT struct {
|
t tÿpe
|
||||||
// VarTypeSym *int32
|
v uint64
|
||||||
// VarNameSym *int32
|
|
||||||
// VarEncoderSym *int32
|
|
||||||
// FieldSerializerNameSym *int32
|
|
||||||
// FieldSerializerVersion *int32
|
|
||||||
// BitCount *int32
|
|
||||||
// LowValue *float32
|
|
||||||
// HighValue *float32
|
|
||||||
// EncodeFlags *int32
|
|
||||||
// SendNodeSym *int32
|
|
||||||
// }
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func prettyFlatField(n *Namespace, ff *dota.ProtoFlattenedSerializerFieldT) string {
|
func (v unknown_v) tÿpe() tÿpe { return v.t }
|
||||||
var buf bytes.Buffer
|
func (v *unknown_v) read(r bit.Reader) error {
|
||||||
fmt.Fprintf(&buf, "{type: %s", n.Symbol(int(ff.GetVarTypeSym())))
|
v.v = bit.ReadVarInt(r)
|
||||||
fmt.Fprintf(&buf, " name: %s", n.Symbol(int(ff.GetVarNameSym())))
|
return r.Err()
|
||||||
if ff.BitCount != nil {
|
}
|
||||||
fmt.Fprintf(&buf, " bits: %d", ff.GetBitCount())
|
|
||||||
}
|
func (v unknown_v) String() string {
|
||||||
if ff.LowValue != nil {
|
return fmt.Sprintf("%s(unknown):%d", v.t.typeName(), v.v)
|
||||||
fmt.Fprintf(&buf, " low: %f", ff.GetLowValue())
|
}
|
||||||
}
|
|
||||||
if ff.HighValue != nil {
|
func unknownType(spec *typeSpec, env *Env) tÿpe {
|
||||||
fmt.Fprintf(&buf, " high: %f", ff.GetHighValue())
|
Debug.Printf("Unknown Type: %v", spec)
|
||||||
}
|
t := unknown_t(spec.typeName)
|
||||||
if ff.EncodeFlags != nil {
|
return &t
|
||||||
fmt.Fprintf(&buf, " flags: %d", ff.GetEncodeFlags())
|
}
|
||||||
}
|
|
||||||
if ff.FieldSerializerNameSym != nil {
|
// a type error is both an error and a type. It represents a type that we were
|
||||||
fmt.Fprintf(&buf, " serializer: %s", n.Symbol(int(ff.GetFieldSerializerNameSym())))
|
// 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 {
|
s.bits = uint(flat.GetBitCount())
|
||||||
fmt.Fprintf(&buf, " version: %d", ff.GetFieldSerializerVersion())
|
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 {
|
s.serializerV = int(flat.GetFieldSerializerVersion())
|
||||||
fmt.Fprintf(&buf, " send: %s", n.Symbol(int(ff.GetSendNodeSym())))
|
if flat.SendNodeSym != nil {
|
||||||
|
s.send = env.symbol(int(*flat.SendNodeSym))
|
||||||
}
|
}
|
||||||
if ff.VarEncoderSym != nil {
|
if flat.VarEncoderSym != nil {
|
||||||
fmt.Fprintf(&buf, " encoder: %s", n.Symbol(int(ff.GetVarEncoderSym())))
|
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
|
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 {
|
type vector struct {
|
||||||
x float32
|
t tÿpe
|
||||||
y float32
|
x, y, z value
|
||||||
z float32
|
}
|
||||||
|
|
||||||
|
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