pre/postorder walks, more complete register/unregister
This commit is contained in:
parent
0af38d6d91
commit
fd36c44701
2 changed files with 40 additions and 13 deletions
|
@ -217,14 +217,15 @@ func (g *Game) Query(ancestorID string, behaviour reflect.Type) map[interface{}]
|
||||||
// Scan implements Scanner.
|
// Scan implements Scanner.
|
||||||
func (g *Game) Scan() []interface{} { return []interface{}{g.Root} }
|
func (g *Game) Scan() []interface{} { return []interface{}{g.Root} }
|
||||||
|
|
||||||
// Walk calls visit with every component and its parent, reachable from the
|
// PreorderWalk calls visit with every component and its parent, reachable from
|
||||||
// given component via Scan, for as long as visit returns nil. The parent of
|
// the given component via Scan, for as long as visit returns nil. The parent
|
||||||
// the first component (as passed to visit) will be nil.
|
// value passed to visit when visiting component will be nil. The parent will be
|
||||||
func Walk(component interface{}, visit func(component, parent interface{}) error) error {
|
// visited before the children.
|
||||||
return walk(component, nil, visit)
|
func PreorderWalk(component interface{}, visit func(component, parent interface{}) error) error {
|
||||||
|
return preorderWalk(component, nil, visit)
|
||||||
}
|
}
|
||||||
|
|
||||||
func walk(component, parent interface{}, visit func(component, parent interface{}) error) error {
|
func preorderWalk(component, parent interface{}, visit func(component, parent interface{}) error) error {
|
||||||
if err := visit(component, parent); err != nil {
|
if err := visit(component, parent); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -233,20 +234,39 @@ func walk(component, parent interface{}, visit func(component, parent interface{
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for _, c := range sc.Scan() {
|
for _, c := range sc.Scan() {
|
||||||
if err := walk(c, component, visit); err != nil {
|
if err := preorderWalk(c, component, visit); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PostorderWalk calls visit with every component and its parent, reachable from
|
||||||
|
// the given component via Scan, for as long as visit returns nil. The parent
|
||||||
|
// value passed to visit when visiting component will be nil. The children will
|
||||||
|
// be visited before the parent.
|
||||||
|
func PostorderWalk(component interface{}, visit func(component, parent interface{}) error) error {
|
||||||
|
return preorderWalk(component, nil, visit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func postorderWalk(component, parent interface{}, visit func(component, parent interface{}) error) error {
|
||||||
|
if sc, ok := component.(Scanner); ok {
|
||||||
|
for _, c := range sc.Scan() {
|
||||||
|
if err := postorderWalk(c, component, visit); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return visit(component, parent)
|
||||||
|
}
|
||||||
|
|
||||||
// LoadAndPrepare first calls Load on all Loaders. Once loading is complete, it
|
// LoadAndPrepare first calls Load on all Loaders. Once loading is complete, it
|
||||||
// builds the component databases and then calls Prepare on every Preparer.
|
// builds the component databases and then calls Prepare on every Preparer.
|
||||||
// LoadAndPrepare must be called before any calls to Component or Query.
|
// LoadAndPrepare must be called before any calls to Component or Query.
|
||||||
func (g *Game) LoadAndPrepare(assets fs.FS) error {
|
func (g *Game) LoadAndPrepare(assets fs.FS) error {
|
||||||
// Load all the Loaders.
|
// Load all the Loaders.
|
||||||
startLoad := time.Now()
|
startLoad := time.Now()
|
||||||
if err := Walk(g, func(c, _ interface{}) error {
|
if err := PreorderWalk(g, func(c, _ interface{}) error {
|
||||||
l, ok := c.(Loader)
|
l, ok := c.(Loader)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
|
@ -263,7 +283,7 @@ func (g *Game) LoadAndPrepare(assets fs.FS) error {
|
||||||
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{})
|
g.par = make(map[interface{}]interface{})
|
||||||
if err := Walk(g, g.register); err != nil {
|
if err := PreorderWalk(g, g.register); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
g.dbmu.Unlock()
|
g.dbmu.Unlock()
|
||||||
|
@ -283,6 +303,8 @@ func (g *Game) LoadAndPrepare(assets fs.FS) error {
|
||||||
// Register registers a component into the component database (as the
|
// Register registers a component into the component database (as the
|
||||||
// child of a given parent). Passing a nil component or parent is an error.
|
// child of a given parent). Passing a nil component or parent is an error.
|
||||||
// Registering multiple components with the same ID is also an error.
|
// Registering multiple components with the same ID is also an error.
|
||||||
|
// Registering a component will recursively register all children found via
|
||||||
|
// Scan.
|
||||||
func (g *Game) Register(component, parent interface{}) error {
|
func (g *Game) Register(component, parent interface{}) error {
|
||||||
if component == nil {
|
if component == nil {
|
||||||
return errNilComponent
|
return errNilComponent
|
||||||
|
@ -292,7 +314,8 @@ func (g *Game) Register(component, parent interface{}) error {
|
||||||
}
|
}
|
||||||
g.dbmu.Lock()
|
g.dbmu.Lock()
|
||||||
defer g.dbmu.Unlock()
|
defer g.dbmu.Unlock()
|
||||||
return g.register(component, parent)
|
// walk goes in the right order for registering.
|
||||||
|
return preorderWalk(component, parent, g.register)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) register(component, parent interface{}) error {
|
func (g *Game) register(component, parent interface{}) error {
|
||||||
|
@ -340,13 +363,17 @@ func (g *Game) register(component, parent interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unregister removes the component from the component database.
|
// Unregister removes the component from the component database.
|
||||||
// Passing a nil component has no effect.
|
// Passing a nil component has no effect. Unregistering a component will
|
||||||
|
// recursively unregister child components found via Scan.
|
||||||
func (g *Game) Unregister(component interface{}) {
|
func (g *Game) Unregister(component interface{}) {
|
||||||
if component == nil {
|
if component == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
g.dbmu.Lock()
|
g.dbmu.Lock()
|
||||||
g.unregister(component)
|
postorderWalk(component, nil, func(c, _ interface{}) error {
|
||||||
|
g.unregister(c)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
g.dbmu.Unlock()
|
g.dbmu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ func (g *Game) cmdTree(dst io.Writer, argv []string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Walk(c, func(c, p interface{}) error {
|
PreorderWalk(c, func(c, p interface{}) error {
|
||||||
indent := ""
|
indent := ""
|
||||||
l := 0
|
l := 0
|
||||||
for ; p != nil; p = g.par[p] {
|
for ; p != nil; p = g.par[p] {
|
||||||
|
|
Loading…
Reference in a new issue