a new era for Containe
This commit is contained in:
parent
8516d1cb91
commit
8ec3ef2a58
4 changed files with 95 additions and 105 deletions
|
@ -3,10 +3,11 @@ package engine
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ interface {
|
var _ interface {
|
||||||
Registrar
|
Prepper
|
||||||
Scanner
|
Scanner
|
||||||
gob.GobDecoder
|
gob.GobDecoder
|
||||||
gob.GobEncoder
|
gob.GobEncoder
|
||||||
|
@ -16,10 +17,11 @@ func init() {
|
||||||
gob.Register(&Container{})
|
gob.Register(&Container{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Container contains many components, in order.
|
// Container is a component that contains many other components, in order.
|
||||||
|
// It can be used as both a component in its own right, or as a ordered set.
|
||||||
|
// A nil *Container contains no items and modifications will panic (like a map).
|
||||||
type Container struct {
|
type Container struct {
|
||||||
items []interface{}
|
items []interface{}
|
||||||
free map[int]struct{}
|
|
||||||
reverse map[interface{}]int
|
reverse map[interface{}]int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +37,7 @@ func (c *Container) GobDecode(in []byte) error {
|
||||||
if err := gob.NewDecoder(bytes.NewReader(in)).Decode(&c.items); err != nil {
|
if err := gob.NewDecoder(bytes.NewReader(in)).Decode(&c.items); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.free, c.reverse = nil, nil
|
c.reverse = nil
|
||||||
return c.Prepare(nil)
|
return c.Prepare(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,14 +59,14 @@ func (c *Container) Prepare(*Game) error {
|
||||||
c.reverse[x] = i
|
c.reverse[x] = i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.free == nil {
|
|
||||||
c.free = make(map[int]struct{})
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan visits every non-nil component in the container.
|
// Scan visits every non-nil component in the container.
|
||||||
func (c *Container) Scan(visit VisitFunc) error {
|
func (c *Container) Scan(visit VisitFunc) error {
|
||||||
|
if c == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
for _, x := range c.items {
|
for _, x := range c.items {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
if err := visit(x); err != nil {
|
if err := visit(x); err != nil {
|
||||||
|
@ -75,6 +77,55 @@ func (c *Container) Scan(visit VisitFunc) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add adds an item to the end of the container, if not already present.
|
||||||
|
func (c *Container) Add(component interface{}) {
|
||||||
|
if c.Contains(component) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.reverse[component] = len(c.items)
|
||||||
|
c.items = append(c.items, component)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove replaces an item with nil. If the number of nil items is greater than
|
||||||
|
// half the slice, the slice is compacted (indexes of items will change).
|
||||||
|
func (c *Container) Remove(component interface{}) {
|
||||||
|
i, found := c.reverse[component]
|
||||||
|
if !found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.items[i] = nil
|
||||||
|
delete(c.reverse, i)
|
||||||
|
if len(c.reverse) < len(c.items)/2 {
|
||||||
|
c.compact()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains reports if an item exists in the container.
|
||||||
|
func (c *Container) Contains(component interface{}) bool {
|
||||||
|
if c == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, found := c.reverse[component]
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexOf reports if an item exists in the container and returns the index if
|
||||||
|
// present.
|
||||||
|
func (c *Container) IndexOf(component interface{}) (int, bool) {
|
||||||
|
if c == nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
i, found := c.reverse[component]
|
||||||
|
return i, found
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) ItemCount() int {
|
||||||
|
if c == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return len(c.reverse)
|
||||||
|
}
|
||||||
|
|
||||||
// Element returns the item at index i, or nil for a free slot.
|
// Element returns the item at index i, or nil for a free slot.
|
||||||
func (c *Container) Element(i int) interface{} { return c.items[i] }
|
func (c *Container) Element(i int) interface{} { return c.items[i] }
|
||||||
|
|
||||||
|
@ -83,72 +134,21 @@ func (c *Container) Len() int { return len(c.items) }
|
||||||
|
|
||||||
// Swap swaps any two items, free slots, or a combination.
|
// Swap swaps any two items, free slots, or a combination.
|
||||||
func (c *Container) Swap(i, j int) {
|
func (c *Container) Swap(i, j int) {
|
||||||
if i == j {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ifree := c.items[i] == nil
|
|
||||||
jfree := c.items[j] == nil
|
|
||||||
switch {
|
|
||||||
case ifree && jfree:
|
|
||||||
return
|
|
||||||
case ifree:
|
|
||||||
c.items[i] = c.items[j]
|
|
||||||
c.reverse[c.items[i]] = i
|
|
||||||
c.free[j] = struct{}{}
|
|
||||||
delete(c.free, i)
|
|
||||||
case jfree:
|
|
||||||
c.items[j] = c.items[i]
|
|
||||||
c.reverse[c.items[j]] = j
|
|
||||||
c.free[i] = struct{}{}
|
|
||||||
delete(c.free, j)
|
|
||||||
default:
|
|
||||||
c.items[i], c.items[j] = c.items[j], c.items[i]
|
c.items[i], c.items[j] = c.items[j], c.items[i]
|
||||||
|
if c.items[i] != nil {
|
||||||
c.reverse[c.items[i]] = i
|
c.reverse[c.items[i]] = i
|
||||||
|
}
|
||||||
|
if c.items[j] != nil {
|
||||||
c.reverse[c.items[j]] = j
|
c.reverse[c.items[j]] = j
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Container) String() string { return "Container" }
|
func (c *Container) String() string {
|
||||||
|
return "Container" + fmt.Sprint(c.items)
|
||||||
// Register records component into the slice, if parent is this container. It
|
|
||||||
// writes the component to an arbitrary free index in the slice, or appends if
|
|
||||||
// there are none free.
|
|
||||||
func (c *Container) Register(component, parent interface{}) error {
|
|
||||||
if parent != c {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if len(c.free) == 0 {
|
|
||||||
c.reverse[component] = len(c.items)
|
|
||||||
c.items = append(c.items, component)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for i := range c.free {
|
|
||||||
c.reverse[component] = i
|
|
||||||
c.items[i] = component
|
|
||||||
delete(c.free, i)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
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{}) {
|
|
||||||
i, found := c.reverse[component]
|
|
||||||
if !found {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.items[i] = nil
|
|
||||||
c.free[i] = struct{}{}
|
|
||||||
delete(c.reverse, i)
|
|
||||||
if len(c.free) > len(c.items)/2 {
|
|
||||||
c.compact()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// compact moves all the items to the front of the items slice, removing any
|
// compact moves all the items to the front of the items slice, removing any
|
||||||
// free slots, and empties the free map.
|
// free slots, and resets the free counter.
|
||||||
func (c *Container) compact() {
|
func (c *Container) compact() {
|
||||||
i := 0
|
i := 0
|
||||||
for _, x := range c.items {
|
for _, x := range c.items {
|
||||||
|
@ -159,5 +159,4 @@ func (c *Container) compact() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.items = c.items[:i]
|
c.items = c.items[:i]
|
||||||
c.free = make(map[int]struct{})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,6 @@ func TestMakeContainer(t *testing.T) {
|
||||||
if want := []interface{}{69, 420}; !cmp.Equal(c.items, want) {
|
if want := []interface{}{69, 420}; !cmp.Equal(c.items, want) {
|
||||||
t.Errorf("c.items = %v, want %v", c.items, want)
|
t.Errorf("c.items = %v, want %v", c.items, want)
|
||||||
}
|
}
|
||||||
if want := make(map[int]struct{}); !cmp.Equal(c.free, want) {
|
|
||||||
t.Errorf("c.free = %v, want %v", c.free, want)
|
|
||||||
}
|
|
||||||
if want := map[interface{}]int{69: 0, 420: 1}; !cmp.Equal(c.reverse, want) {
|
if want := map[interface{}]int{69: 0, 420: 1}; !cmp.Equal(c.reverse, want) {
|
||||||
t.Errorf("c.reverse = %v, want %v", c.reverse, want)
|
t.Errorf("c.reverse = %v, want %v", c.reverse, want)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -46,9 +45,9 @@ type Game struct {
|
||||||
|
|
||||||
dbmu sync.RWMutex
|
dbmu sync.RWMutex
|
||||||
byID map[string]Identifier // Named components by ID
|
byID map[string]Identifier // Named components by ID
|
||||||
byAB map[abKey]ComponentSet // paths matching interface
|
byAB map[abKey]*Container // paths matching interface
|
||||||
parent map[interface{}]interface{} // parent[x] is parent of x
|
parent map[interface{}]interface{} // parent[x] is parent of x
|
||||||
children map[interface{}]ComponentSet // children[x] are chilren of x
|
children map[interface{}]*Container // children[x] are chilren of x
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw draws everything.
|
// Draw draws everything.
|
||||||
|
@ -101,7 +100,7 @@ func (g *Game) Parent(c interface{}) interface{} {
|
||||||
|
|
||||||
// Children returns the direct subcomponents of the given component, or nil if
|
// Children returns the direct subcomponents of the given component, or nil if
|
||||||
// there are none. This only returns sensible values for registered components.
|
// there are none. This only returns sensible values for registered components.
|
||||||
func (g *Game) Children(c interface{}) ComponentSet {
|
func (g *Game) Children(c interface{}) *Container {
|
||||||
g.dbmu.RLock()
|
g.dbmu.RLock()
|
||||||
defer g.dbmu.RUnlock()
|
defer g.dbmu.RUnlock()
|
||||||
return g.children[c]
|
return g.children[c]
|
||||||
|
@ -179,13 +178,16 @@ func (g *Game) Query(ancestor interface{}, behaviour reflect.Type, visitPre, vis
|
||||||
g.dbmu.RLock()
|
g.dbmu.RLock()
|
||||||
q := g.byAB[abKey{ancestor, behaviour}]
|
q := g.byAB[abKey{ancestor, behaviour}]
|
||||||
g.dbmu.RUnlock()
|
g.dbmu.RUnlock()
|
||||||
for x := range q {
|
if err := q.Scan(func(x interface{}) error {
|
||||||
if err := g.Query(x, behaviour, visitPre, visitPost); err != nil {
|
if err := g.Query(x, behaviour, visitPre, visitPost); err != nil {
|
||||||
if errors.Is(err, Skip) {
|
if errors.Is(err, Skip) {
|
||||||
continue
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
if pi && visitPost != nil {
|
if pi && visitPost != nil {
|
||||||
return visitPost(ancestor)
|
return visitPost(ancestor)
|
||||||
|
@ -262,9 +264,9 @@ func (g *Game) build() error {
|
||||||
g.dbmu.Lock()
|
g.dbmu.Lock()
|
||||||
defer g.dbmu.Unlock()
|
defer g.dbmu.Unlock()
|
||||||
g.byID = make(map[string]Identifier)
|
g.byID = make(map[string]Identifier)
|
||||||
g.byAB = make(map[abKey]ComponentSet)
|
g.byAB = make(map[abKey]*Container)
|
||||||
g.parent = make(map[interface{}]interface{})
|
g.parent = make(map[interface{}]interface{})
|
||||||
g.children = make(map[interface{}]ComponentSet)
|
g.children = make(map[interface{}]*Container)
|
||||||
return g.registerRecursive(g, nil)
|
return g.registerRecursive(g, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,9 +313,10 @@ func (g *Game) registerOne(component, parent interface{}) error {
|
||||||
// register in g.parent and g.children
|
// register in g.parent and g.children
|
||||||
g.parent[component] = parent
|
g.parent[component] = parent
|
||||||
if g.children[parent] == nil {
|
if g.children[parent] == nil {
|
||||||
g.children[parent] = make(ComponentSet)
|
g.children[parent] = MakeContainer(component)
|
||||||
|
} else {
|
||||||
|
g.children[parent].Add(component)
|
||||||
}
|
}
|
||||||
g.children[parent][component] = struct{}{}
|
|
||||||
|
|
||||||
// register in g.byAB
|
// register in g.byAB
|
||||||
ct := reflect.TypeOf(component)
|
ct := reflect.TypeOf(component)
|
||||||
|
@ -324,13 +327,13 @@ func (g *Game) registerOne(component, parent interface{}) error {
|
||||||
for c, p := component, g.parent[component]; p != nil; c, p = p, g.parent[p] {
|
for c, p := component, g.parent[component]; p != nil; c, p = p, g.parent[p] {
|
||||||
k := abKey{p, b}
|
k := abKey{p, b}
|
||||||
if g.byAB[k] == nil {
|
if g.byAB[k] == nil {
|
||||||
g.byAB[k] = ComponentSet{c: {}}
|
g.byAB[k] = MakeContainer(c)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, exists := g.byAB[k][c]; exists {
|
if g.byAB[k].Contains(c) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
g.byAB[k][c] = struct{}{}
|
g.byAB[k].Add(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -349,30 +352,33 @@ func (g *Game) Unregister(component interface{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) unregisterRecursive(component interface{}) {
|
func (g *Game) unregisterRecursive(component interface{}) {
|
||||||
for x := range g.children[component] {
|
g.children[component].Scan(func(x interface{}) error {
|
||||||
g.unregisterRecursive(x)
|
g.unregisterRecursive(x)
|
||||||
}
|
return nil
|
||||||
|
})
|
||||||
g.unregisterOne(component)
|
g.unregisterOne(component)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) unregisterOne(component interface{}) {
|
func (g *Game) unregisterOne(component interface{}) {
|
||||||
|
parent := g.parent[component]
|
||||||
|
|
||||||
// unregister from g.byAB
|
// unregister from g.byAB
|
||||||
ct := reflect.TypeOf(component)
|
ct := reflect.TypeOf(component)
|
||||||
for _, b := range Behaviours {
|
for _, b := range Behaviours {
|
||||||
if !ct.Implements(b) {
|
if !ct.Implements(b) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for c, p := component, g.parent[component]; p != nil; c, p = p, g.parent[p] {
|
for c, p := component, parent; p != nil; c, p = p, g.parent[p] {
|
||||||
k := abKey{p, b}
|
k := abKey{p, b}
|
||||||
delete(g.byAB[k], c)
|
g.byAB[k].Remove(c)
|
||||||
if len(g.byAB[k]) > 0 {
|
if g.byAB[k].ItemCount() > 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// unregister from g.parent and g.children
|
// unregister from g.parent and g.children
|
||||||
delete(g.children[g.parent[component]], component)
|
g.children[parent].Remove(component)
|
||||||
delete(g.parent, component)
|
delete(g.parent, component)
|
||||||
|
|
||||||
// unregister from g.byID if needed
|
// unregister from g.byID if needed
|
||||||
|
@ -385,19 +391,6 @@ func (g *Game) String() string { return "Game" }
|
||||||
|
|
||||||
// --------- Helper stuff ---------
|
// --------- Helper stuff ---------
|
||||||
|
|
||||||
// ComponentSet is a set of components.
|
|
||||||
type ComponentSet map[interface{}]struct{}
|
|
||||||
|
|
||||||
func (c ComponentSet) String() string {
|
|
||||||
var b strings.Builder
|
|
||||||
b.WriteString("{")
|
|
||||||
for x := range c {
|
|
||||||
fmt.Fprint(&b, " ", x)
|
|
||||||
}
|
|
||||||
b.WriteString(" }")
|
|
||||||
return b.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// abKey is the key type for game.byAB.
|
// abKey is the key type for game.byAB.
|
||||||
type abKey struct {
|
type abKey struct {
|
||||||
parent interface{}
|
parent interface{}
|
||||||
|
|
|
@ -102,9 +102,10 @@ func (g *Game) printTreeRecursive(dst io.Writer, depth int, c interface{}) {
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(dst, "%s%v\n", indent, c)
|
fmt.Fprintf(dst, "%s%v\n", indent, c)
|
||||||
}
|
}
|
||||||
for x := range g.Children(c) {
|
g.Children(c).Scan(func(x interface{}) error {
|
||||||
g.printTreeRecursive(dst, depth+1, x)
|
g.printTreeRecursive(dst, depth+1, x)
|
||||||
}
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) cmdQuery(dst io.Writer, argv []string) {
|
func (g *Game) cmdQuery(dst io.Writer, argv []string) {
|
||||||
|
|
Loading…
Reference in a new issue