allow blank Ident

This commit is contained in:
Josh Deprez 2021-08-30 15:49:51 +10:00
parent 91523f4769
commit 5a840950a0
3 changed files with 45 additions and 20 deletions

View file

@ -35,6 +35,7 @@ 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]map[interface{}]struct{} // Ancestor/behaviour index byAB map[abKey]map[interface{}]struct{} // Ancestor/behaviour index
par map[interface{}]interface{} // par[x] is parent of x
} }
type abKey struct { type abKey struct {
@ -67,13 +68,23 @@ func (g *Game) Update() error {
func (g *Game) Ident() string { return "__GAME__" } func (g *Game) Ident() string { return "__GAME__" }
// Component returns the component with a given ID, or nil if there is none. // Component returns the component with a given ID, or nil if there is none.
// This only returns sensible values after LoadAndPrepare. // This only returns sensible values for registered components (e.g. after
// LoadAndPrepare).
func (g *Game) Component(id string) Identifier { func (g *Game) Component(id string) Identifier {
g.dbmu.RLock() g.dbmu.RLock()
defer g.dbmu.RUnlock() defer g.dbmu.RUnlock()
return g.byID[id] return g.byID[id]
} }
// Parent returns the parent of a given component, or nil if there is none.
// This only returns sensible values for registered components (e.g. after
// LoadAndPrepare).
func (g *Game) Parent(c interface{}) interface{} {
g.dbmu.RLock()
defer g.dbmu.RUnlock()
return g.par[c]
}
// Query looks for components having both a given ancestor and implementing // Query looks for components having both a given ancestor and implementing
// a given behaviour (see Behaviors in interface.go). This only returns sensible // a given behaviour (see Behaviors in interface.go). This only returns sensible
// values after LoadAndPrepare. Note that every component is its own ancestor. // values after LoadAndPrepare. Note that every component is its own ancestor.
@ -128,6 +139,7 @@ func (g *Game) LoadAndPrepare(assets fs.FS) error {
g.dbmu.Lock() g.dbmu.Lock()
g.byID = make(map[string]Identifier) g.byID = make(map[string]Identifier)
g.byAB = make(map[abKey]map[interface{}]struct{}) g.byAB = make(map[abKey]map[interface{}]struct{})
g.par = make(map[interface{}]interface{})
if err := Walk(g, g.registerComponent); err != nil { if err := Walk(g, g.registerComponent); err != nil {
return err return err
} }
@ -143,16 +155,21 @@ func (g *Game) LoadAndPrepare(assets fs.FS) error {
} }
func (g *Game) registerComponent(c interface{}, path []interface{}) error { func (g *Game) registerComponent(c interface{}, path []interface{}) error {
// register in g.par
if l := len(path); l > 0 {
g.par[c] = path[l-1]
}
// register in g.byAB // register in g.byAB
ct := reflect.TypeOf(c) ct := reflect.TypeOf(c)
for _, b := range Behaviours { for _, b := range Behaviours {
if !ct.Implements(b) { if !ct.Implements(b) {
continue continue
} }
// TODO: sub-quadratic? // TODO: this is quadratic - do better?
for _, p := range append(path, c) { for _, p := range append(path, c) {
i, ok := p.(Identifier) i, ok := p.(Identifier)
if !ok || i.Ident() == "" { if !ok {
continue continue
} }
k := abKey{i.Ident(), b} k := abKey{i.Ident(), b}
@ -163,9 +180,9 @@ func (g *Game) registerComponent(c interface{}, path []interface{}) error {
} }
} }
// register in g.byID // register in g.byID if needed
i, ok := c.(Identifier) i, ok := c.(Identifier)
if !ok || i.Ident() == "" { if !ok {
return nil return nil
} }
id := i.Ident() id := i.Ident()
@ -176,16 +193,23 @@ func (g *Game) registerComponent(c interface{}, path []interface{}) error {
return nil return nil
} }
func (g *Game) unregisterComponent(c interface{}, path []interface{}) { // UnregisterComponent removes the component from the component database.
// unregister from g.byAB func (g *Game) UnregisterComponent(c interface{}) {
g.dbmu.Lock()
g.unregisterComponent(c)
g.dbmu.Unlock()
}
func (g *Game) unregisterComponent(c interface{}) {
// unregister from g.byAB, using g.par to trace the path
ct := reflect.TypeOf(c) ct := reflect.TypeOf(c)
for _, b := range Behaviours { for _, b := range Behaviours {
if !ct.Implements(b) { if !ct.Implements(b) {
continue continue
} }
for _, p := range append(path, c) { for p := c; p != nil; p = g.par[p] {
i, ok := p.(Identifier) i, ok := p.(Identifier)
if !ok || i.Ident() == "" { if !ok {
continue continue
} }
k := abKey{i.Ident(), b} k := abKey{i.Ident(), b}
@ -196,9 +220,12 @@ func (g *Game) unregisterComponent(c interface{}, path []interface{}) {
} }
} }
// unregister from g.byID // unregister from g.par
delete(g.par, c)
// unregister from g.byID if needed
i, ok := c.(Identifier) i, ok := c.(Identifier)
if !ok || i.Ident() == "" { if !ok {
return return
} }
delete(g.byID, i.Ident()) delete(g.byID, i.Ident())

View file

@ -95,7 +95,7 @@ func (g *Game) cmdTree(dst io.Writer, argv []string) {
} }
i, ok := c.(Identifier) i, ok := c.(Identifier)
if ok { if ok {
fmt.Fprintf(dst, "%s%T %s\n", indent, c, i.Ident()) fmt.Fprintf(dst, "%s%T %q\n", indent, c, i.Ident())
} else { } else {
fmt.Fprintf(dst, "%s%T\n", indent, c) fmt.Fprintf(dst, "%s%T\n", indent, c)
} }
@ -113,22 +113,22 @@ func (g *Game) cmdQuery(dst io.Writer, argv []string) {
return return
} }
var behav reflect.Type var behaviour reflect.Type
for _, b := range Behaviours { for _, b := range Behaviours {
if b.Name() == argv[1] { if b.Name() == argv[1] {
behav = b behaviour = b
} }
} }
if behav == nil { if behaviour == nil {
fmt.Fprintf(dst, "Unknown behaviour %q\n", argv[1]) fmt.Fprintf(dst, "Unknown behaviour %q\n", argv[1])
} }
ances := g.Ident() ancestor := g.Ident()
if len(argv) == 3 { if len(argv) == 3 {
ances = argv[2] ancestor = argv[2]
} }
x := g.Query(ances, behav) x := g.Query(ancestor, behaviour)
if len(x) == 0 { if len(x) == 0 {
fmt.Fprintln(dst, "No results") fmt.Fprintln(dst, "No results")
return return

View file

@ -9,7 +9,6 @@ import (
// Ensure Sprite satisfies interfaces. // Ensure Sprite satisfies interfaces.
var _ interface { var _ interface {
Identifier
Drawer Drawer
Scanner Scanner
Updater Updater
@ -21,7 +20,6 @@ func init() {
// Sprite combines an Actor with the ability to Draw from a single spritesheet. // Sprite combines an Actor with the ability to Draw from a single spritesheet.
type Sprite struct { type Sprite struct {
ID
Actor Actor
FrameOffset image.Point FrameOffset image.Point
Hidden Hidden