-
Notifications
You must be signed in to change notification settings - Fork 8
/
compiled.go
115 lines (99 loc) · 2.61 KB
/
compiled.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package generic
import (
"fmt"
"reflect"
"github.com/mlange-42/arche/ecs"
)
var relationType = reflect.TypeOf((*ecs.Relation)(nil)).Elem()
// compiledQuery is a helper for compiling a generic filter into a [ecs.Filter].
type compiledQuery struct {
maskFilter ecs.MaskFilter
relationFilter ecs.RelationFilter
cachedFilter ecs.CachedFilter
filter ecs.Filter
Ids []ecs.ID
Relation ecs.ID
Target ecs.Entity
HasRelation bool
compiled bool
targetCompiled bool
locked bool
}
func newCompiledQuery() compiledQuery {
return compiledQuery{}
}
// Compile compiles a generic filter.
func (q *compiledQuery) Compile(w *ecs.World, include, optional, exclude []Comp, exclusive bool, targetType Comp, target ecs.Entity, hasTarget bool) {
if q.compiled {
return
}
q.Ids = toIds(w, include)
incl := toMaskOptional(w, q.Ids, optional)
var excl ecs.Mask
if exclusive {
excl = incl.Not()
} else {
excl = toMask(w, exclude)
}
q.maskFilter = ecs.MaskFilter{
Include: incl,
Exclude: excl,
}
noExclude := !exclusive && len(exclude) == 0
if targetType == nil {
if noExclude {
q.filter = q.maskFilter.Include
} else {
q.filter = &q.maskFilter
}
q.Relation = ecs.ID{}
q.HasRelation = false
} else {
targetID := ecs.TypeID(w, targetType)
q.Relation = targetID
q.HasRelation = true
if !q.maskFilter.Include.Get(targetID) {
panic(fmt.Sprintf("relation component %v not in filter", targetType))
}
isRelation := false
if targetType.Kind() == reflect.Struct && targetType.NumField() > 0 {
field := targetType.Field(0)
isRelation = field.Type == relationType && field.Name == relationType.Name()
}
if !isRelation {
panic(fmt.Sprintf("component type %v is not a relation", targetType))
}
if hasTarget {
q.Target = target
q.relationFilter = ecs.NewRelationFilter(&q.maskFilter, target)
q.filter = &q.relationFilter
} else {
if noExclude {
q.filter = q.maskFilter.Include
} else {
q.filter = &q.maskFilter
}
}
}
q.targetCompiled = true
q.compiled = true
}
// Reset sets the compiledQuery to not compiled.
func (q *compiledQuery) Reset() {
q.compiled = false
}
// Register the compiledQuery for caching.
func (q *compiledQuery) Register(w *ecs.World) {
q.cachedFilter = w.Cache().Register(q.filter)
q.filter = &q.cachedFilter
q.locked = true
}
// Unregister the compiledQuery from caching.
func (q *compiledQuery) Unregister(w *ecs.World) {
if cf, ok := q.filter.(*ecs.CachedFilter); ok {
q.filter = w.Cache().Unregister(cf)
} else {
panic("can't unregister a filter that is not cached")
}
q.locked = false
}