package wire import ( "bytes" "fmt" "strconv" "strings" "github.com/jordanorelli/astro-domu/internal/math" ) type Frame struct { RoomName string `json:"room_name"` RoomSize math.Rect `json:"room_size"` Entities map[int]Entity `json:"entities"` Players map[string]int `json:"players"` } func (Frame) NetTag() string { return "frame" } func (f Frame) Diff(next Frame) *Delta { var delta Delta if f.RoomSize != next.RoomSize { rs := next.RoomSize delta.RoomSize = &rs } for id, e := range next.Entities { if old, ok := f.Entities[id]; !ok { // a new entity delta.addEntity(e) } else { // an existing entity if e != old { delta.addEntity(e) } } } for id, _ := range f.Entities { // entity removed if _, ok := next.Entities[id]; !ok { delta.nullEntity(id) } } for name, id := range next.Players { if oldID, ok := f.Players[name]; !ok { // a new player delta.addPlayer(name, id) } else { // an existing player if oldID != id { delta.addPlayer(name, id) } } } for name, _ := range f.Players { if _, ok := next.Players[name]; !ok { delta.nullPlayer(name) } } if !delta.IsEmpty() { return &delta } return nil } type Delta struct { RoomSize *math.Rect `json:"room_size,omitempty"` Entities map[int]*Entity `json:"entities,omitempty"` Players map[string]*int `json:"players,omitempty"` } func (d Delta) String() string { first := true var buf bytes.Buffer fmt.Fprint(&buf, "Δ{") if d.RoomSize != nil { fmt.Fprintf(&buf, "size(%d,%d@%d,%d)", d.RoomSize.Origin.X, d.RoomSize.Origin.Y, d.RoomSize.Width, d.RoomSize.Height) first = false } if len(d.Entities) > 0 { if !first { buf.WriteString(",") } buf.WriteString("entities<") parts := make([]string, 0, len(d.Entities)) for id, e := range d.Entities { if e == nil { parts = append(parts, fmt.Sprintf("-%d", id)) } else { parts = append(parts, strconv.Itoa(id)) } } buf.WriteString(strings.Join(parts, ",")) buf.WriteString(">") first = false } if len(d.Players) > 0 { if !first { buf.WriteString(",") } buf.WriteString("players<") parts := make([]string, 0, len(d.Players)) for name, id := range d.Players { if id == nil { parts = append(parts, fmt.Sprintf("-%s", name)) } else { parts = append(parts, fmt.Sprintf("+%s", name)) } } buf.WriteString(strings.Join(parts, ",")) buf.WriteString(">") first = false } return buf.String() } func (Delta) NetTag() string { return "delta" } func (d *Delta) addEntity(e Entity) { if d.Entities == nil { d.Entities = make(map[int]*Entity) } d.Entities[e.ID] = &e } func (d *Delta) nullEntity(id int) { if d.Entities == nil { d.Entities = make(map[int]*Entity) } d.Entities[id] = nil } func (d *Delta) addPlayer(name string, id int) { if d.Players == nil { d.Players = make(map[string]*int) } d.Players[name] = &id } func (d *Delta) nullPlayer(name string) { if d.Players == nil { d.Players = make(map[string]*int) } d.Players[name] = nil } func (d Delta) IsEmpty() bool { return d.RoomSize == nil && len(d.Entities) == 0 && len(d.Players) == 0 } func init() { Register(func() Value { return new(Frame) }) Register(func() Value { return new(Delta) }) }