2021-09-22 14:37:00 +10:00
|
|
|
package engine
|
|
|
|
|
2021-09-23 15:32:57 +10:00
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/gob"
|
|
|
|
)
|
|
|
|
|
2021-09-22 15:48:02 +10:00
|
|
|
var _ interface {
|
|
|
|
Registrar
|
|
|
|
Scanner
|
2021-09-23 15:32:57 +10:00
|
|
|
gob.GobDecoder
|
|
|
|
gob.GobEncoder
|
2021-09-22 15:48:02 +10:00
|
|
|
} = &Container{}
|
|
|
|
|
2021-09-23 15:32:57 +10:00
|
|
|
func init() {
|
|
|
|
gob.Register(&Container{})
|
|
|
|
}
|
|
|
|
|
2021-09-22 15:55:38 +10:00
|
|
|
// Container contains many components, in order.
|
2021-09-23 14:54:45 +10:00
|
|
|
type Container struct {
|
2021-09-23 15:32:57 +10:00
|
|
|
items []interface{}
|
2021-09-23 14:54:45 +10:00
|
|
|
free map[int]struct{}
|
|
|
|
reverse map[interface{}]int
|
|
|
|
}
|
|
|
|
|
2021-09-23 16:32:38 +10:00
|
|
|
// MakeContainer puts the items into a new Container.
|
2021-09-23 14:54:45 +10:00
|
|
|
func MakeContainer(items ...interface{}) *Container {
|
2021-09-23 15:32:57 +10:00
|
|
|
c := &Container{items: items}
|
2021-09-23 14:54:45 +10:00
|
|
|
c.Prepare(nil)
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
2021-09-23 16:32:38 +10:00
|
|
|
// GobDecode decodes a byte slice as though it were a slice of items.
|
2021-09-23 15:32:57 +10:00
|
|
|
func (c *Container) GobDecode(in []byte) error {
|
2021-09-23 16:32:38 +10:00
|
|
|
if err := gob.NewDecoder(bytes.NewReader(in)).Decode(&c.items); err != nil {
|
2021-09-23 15:32:57 +10:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
c.free, c.reverse = nil, nil
|
|
|
|
return c.Prepare(nil)
|
|
|
|
}
|
|
|
|
|
2021-09-23 16:32:38 +10:00
|
|
|
// GobEncode encodes c as the slice of items.
|
2021-09-23 15:32:57 +10:00
|
|
|
func (c *Container) GobEncode() ([]byte, error) {
|
2021-09-23 16:32:38 +10:00
|
|
|
c.compact()
|
2021-09-23 15:32:57 +10:00
|
|
|
var buf bytes.Buffer
|
|
|
|
if err := gob.NewEncoder(&buf).Encode(c.items); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return buf.Bytes(), nil
|
|
|
|
}
|
|
|
|
|
2021-09-23 16:32:38 +10:00
|
|
|
// Prepare ensures the helper data structures are present.
|
2021-09-23 14:54:45 +10:00
|
|
|
func (c *Container) Prepare(*Game) error {
|
|
|
|
if c.reverse == nil {
|
2021-09-23 15:32:57 +10:00
|
|
|
c.reverse = make(map[interface{}]int, len(c.items))
|
|
|
|
for i, x := range c.items {
|
2021-09-23 14:54:45 +10:00
|
|
|
c.reverse[x] = i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if c.free == nil {
|
|
|
|
c.free = make(map[int]struct{})
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2021-09-22 14:37:00 +10:00
|
|
|
|
2021-09-23 16:32:38 +10:00
|
|
|
// Scan visits every non-nil component in the container.
|
2021-09-23 14:54:45 +10:00
|
|
|
func (c *Container) Scan(visit func(interface{}) error) error {
|
2021-09-23 15:32:57 +10:00
|
|
|
for _, x := range c.items {
|
2021-09-23 16:32:38 +10:00
|
|
|
if x != nil {
|
|
|
|
if err := visit(x); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-09-22 15:48:02 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2021-09-22 14:37:00 +10:00
|
|
|
|
2021-09-23 16:32:38 +10:00
|
|
|
// Element returns the item at index i, or nil for a free slot.
|
|
|
|
func (c *Container) Element(i int) interface{} { return c.items[i] }
|
|
|
|
|
|
|
|
// Len returns the number of items plus the number of free slots in the container.
|
2021-09-23 15:32:57 +10:00
|
|
|
func (c *Container) Len() int { return len(c.items) }
|
2021-09-23 14:54:45 +10:00
|
|
|
|
2021-09-23 16:32:38 +10:00
|
|
|
// Swap swaps any two items, free slots, or a combination.
|
2021-09-23 14:54:45 +10:00
|
|
|
func (c *Container) Swap(i, j int) {
|
|
|
|
if i == j {
|
|
|
|
return
|
|
|
|
}
|
2021-09-23 15:32:57 +10:00
|
|
|
ifree := c.items[i] == nil
|
|
|
|
jfree := c.items[j] == nil
|
2021-09-23 14:54:45 +10:00
|
|
|
switch {
|
|
|
|
case ifree && jfree:
|
|
|
|
return
|
|
|
|
case ifree:
|
2021-09-23 15:32:57 +10:00
|
|
|
c.items[i] = c.items[j]
|
|
|
|
c.reverse[c.items[i]] = i
|
2021-09-23 14:54:45 +10:00
|
|
|
c.free[j] = struct{}{}
|
|
|
|
delete(c.free, i)
|
|
|
|
case jfree:
|
2021-09-23 15:32:57 +10:00
|
|
|
c.items[j] = c.items[i]
|
|
|
|
c.reverse[c.items[j]] = j
|
2021-09-23 14:54:45 +10:00
|
|
|
c.free[i] = struct{}{}
|
|
|
|
delete(c.free, j)
|
|
|
|
default:
|
2021-09-23 15:32:57 +10:00
|
|
|
c.items[i], c.items[j] = c.items[j], c.items[i]
|
|
|
|
c.reverse[c.items[i]] = i
|
|
|
|
c.reverse[c.items[j]] = j
|
2021-09-23 14:54:45 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-23 14:17:18 +10:00
|
|
|
func (c Container) String() string { return "Container" }
|
|
|
|
|
2021-09-23 15:32:57 +10:00
|
|
|
// Register records component into the slice, if parent is this container. It
|
2021-09-23 14:54:45 +10:00
|
|
|
// writes the component to an arbitrary free index in the slice, or appends if
|
|
|
|
// there are none free.
|
2021-09-22 14:37:00 +10:00
|
|
|
func (c *Container) Register(component, parent interface{}) error {
|
2021-09-23 14:54:45 +10:00
|
|
|
if parent != c {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if len(c.free) == 0 {
|
2021-09-23 15:32:57 +10:00
|
|
|
c.reverse[component] = len(c.items)
|
|
|
|
c.items = append(c.items, component)
|
2021-09-23 14:54:45 +10:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
for i := range c.free {
|
|
|
|
c.reverse[component] = i
|
2021-09-23 15:32:57 +10:00
|
|
|
c.items[i] = component
|
2021-09-23 14:54:45 +10:00
|
|
|
delete(c.free, i)
|
|
|
|
return nil
|
2021-09-22 14:37:00 +10:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unregister searches the slice for the component, and removes it by setting
|
|
|
|
// to nil. If the number of nil items is greater than half the slice, the slice
|
|
|
|
// is compacted.
|
|
|
|
func (c *Container) Unregister(component interface{}) {
|
2021-09-23 16:47:43 +10:00
|
|
|
i, found := c.reverse[component]
|
|
|
|
if !found {
|
|
|
|
return
|
2021-09-22 14:37:00 +10:00
|
|
|
}
|
2021-09-23 16:47:43 +10:00
|
|
|
c.items[i] = nil
|
|
|
|
c.free[i] = struct{}{}
|
|
|
|
delete(c.reverse, i)
|
2021-09-23 15:32:57 +10:00
|
|
|
if len(c.free) > len(c.items)/2 {
|
2021-09-22 14:37:00 +10:00
|
|
|
c.compact()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-23 16:32:38 +10:00
|
|
|
// compact moves all the items to the front of the items slice, removing any
|
|
|
|
// free slots, and empties the free map.
|
2021-09-22 14:37:00 +10:00
|
|
|
func (c *Container) compact() {
|
|
|
|
i := 0
|
2021-09-23 15:32:57 +10:00
|
|
|
for _, x := range c.items {
|
2021-09-22 14:37:00 +10:00
|
|
|
if x != nil {
|
2021-09-23 15:32:57 +10:00
|
|
|
c.items[i] = x
|
2021-09-23 14:54:45 +10:00
|
|
|
c.reverse[x] = i
|
2021-09-22 14:37:00 +10:00
|
|
|
i++
|
|
|
|
}
|
|
|
|
}
|
2021-09-23 15:32:57 +10:00
|
|
|
c.items = c.items[:i]
|
2021-09-23 14:54:45 +10:00
|
|
|
c.free = make(map[int]struct{})
|
2021-09-22 14:37:00 +10:00
|
|
|
}
|