• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

excaliburjs / Excalibur / 14804036802

02 May 2025 09:58PM UTC coverage: 5.927% (-83.4%) from 89.28%
14804036802

Pull #3404

github

web-flow
Merge 5c103d7f8 into 0f2ccaeb2
Pull Request #3404: feat: added Graph module to Math

234 of 8383 branches covered (2.79%)

229 of 246 new or added lines in 1 file covered. (93.09%)

13145 existing lines in 208 files now uncovered.

934 of 15759 relevant lines covered (5.93%)

4.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/engine/EntityComponentSystem/QueryManager.ts
1
import { Entity } from './Entity';
2
import { Query } from './Query';
3
import { Component, ComponentCtor } from './Component';
4
import { World } from './World';
5
import { TagQuery } from './TagQuery';
6

7
/**
8
 * The query manager is responsible for updating all queries when entities/components change
9
 */
10
export class QueryManager {
UNCOV
11
  private _queries = new Map<string, Query<any>>();
×
UNCOV
12
  private _addComponentHandlers = new Map<Entity, (c: Component) => any>();
×
UNCOV
13
  private _removeComponentHandlers = new Map<Entity, (c: Component) => any>();
×
UNCOV
14
  private _componentToQueriesIndex = new Map<ComponentCtor<any>, Query<any>[]>();
×
15

UNCOV
16
  private _tagQueries = new Map<string, TagQuery<any>>();
×
UNCOV
17
  private _addTagHandlers = new Map<Entity, (tag: string) => any>();
×
UNCOV
18
  private _removeTagHandlers = new Map<Entity, (tag: string) => any>();
×
UNCOV
19
  private _tagToQueriesIndex = new Map<string, TagQuery<any>[]>();
×
20

UNCOV
21
  constructor(private _world: World) {}
×
22

23
  public createQuery<TKnownComponentCtors extends ComponentCtor<Component>>(
24
    requiredComponents: TKnownComponentCtors[]
25
  ): Query<TKnownComponentCtors> {
UNCOV
26
    const id = Query.createId(requiredComponents);
×
UNCOV
27
    if (this._queries.has(id)) {
×
28
      // short circuit if query is already created
UNCOV
29
      return this._queries.get(id) as Query<TKnownComponentCtors>;
×
30
    }
31

UNCOV
32
    const query = new Query(requiredComponents);
×
33

UNCOV
34
    this._queries.set(query.id, query);
×
35

36
    // index maintenance
UNCOV
37
    for (const component of requiredComponents) {
×
UNCOV
38
      const queries = this._componentToQueriesIndex.get(component);
×
UNCOV
39
      if (!queries) {
×
UNCOV
40
        this._componentToQueriesIndex.set(component, [query]);
×
41
      } else {
UNCOV
42
        queries.push(query);
×
43
      }
44
    }
45

UNCOV
46
    for (const entity of this._world.entities) {
×
UNCOV
47
      this.addEntity(entity);
×
48
    }
49

UNCOV
50
    return query;
×
51
  }
52

53
  public createTagQuery<TKnownTags extends string>(requiredTags: TKnownTags[]): TagQuery<TKnownTags> {
UNCOV
54
    const id = TagQuery.createId(requiredTags);
×
UNCOV
55
    if (this._tagQueries.has(id)) {
×
56
      // short circuit if query is already created
57
      return this._tagQueries.get(id) as TagQuery<TKnownTags>;
×
58
    }
59

UNCOV
60
    const query = new TagQuery(requiredTags);
×
61

UNCOV
62
    this._tagQueries.set(query.id, query);
×
63

64
    // index maintenance
UNCOV
65
    for (const tag of requiredTags) {
×
UNCOV
66
      const queries = this._tagToQueriesIndex.get(tag);
×
UNCOV
67
      if (!queries) {
×
UNCOV
68
        this._tagToQueriesIndex.set(tag, [query]);
×
69
      } else {
70
        queries.push(query);
×
71
      }
72
    }
73

UNCOV
74
    for (const entity of this._world.entities) {
×
75
      this.addEntity(entity);
×
76
    }
77

UNCOV
78
    return query;
×
79
  }
80

UNCOV
81
  private _createAddComponentHandler = (entity: Entity) => (c: Component) => {
×
UNCOV
82
    this.addComponent(entity, c);
×
83
  };
84

UNCOV
85
  private _createRemoveComponentHandler = (entity: Entity) => (c: Component) => {
×
UNCOV
86
    this.removeComponent(entity, c);
×
87
  };
88

UNCOV
89
  private _createAddTagHandler = (entity: Entity) => (tag: string) => {
×
UNCOV
90
    this.addTag(entity, tag);
×
91
  };
92

UNCOV
93
  private _createRemoveTagHandler = (entity: Entity) => (tag: string) => {
×
UNCOV
94
    this.removeTag(entity, tag);
×
95
  };
96

97
  /**
98
   * Scans queries and locates any that need this entity added
99
   * @param entity
100
   */
101
  addEntity(entity: Entity) {
UNCOV
102
    const maybeAddComponent = this._addComponentHandlers.get(entity);
×
UNCOV
103
    const maybeRemoveComponent = this._removeComponentHandlers.get(entity);
×
UNCOV
104
    const addComponent = maybeAddComponent ?? this._createAddComponentHandler(entity);
×
UNCOV
105
    const removeComponent = maybeRemoveComponent ?? this._createRemoveComponentHandler(entity);
×
UNCOV
106
    this._addComponentHandlers.set(entity, addComponent);
×
UNCOV
107
    this._removeComponentHandlers.set(entity, removeComponent);
×
108

UNCOV
109
    const maybeAddTag = this._addTagHandlers.get(entity);
×
UNCOV
110
    const maybeRemoveTag = this._removeTagHandlers.get(entity);
×
UNCOV
111
    const addTag = maybeAddTag ?? this._createAddTagHandler(entity);
×
UNCOV
112
    const removeTag = maybeRemoveTag ?? this._createRemoveTagHandler(entity);
×
UNCOV
113
    this._addTagHandlers.set(entity, addTag);
×
UNCOV
114
    this._removeTagHandlers.set(entity, removeTag);
×
115

UNCOV
116
    for (const query of this._queries.values()) {
×
UNCOV
117
      query.checkAndAdd(entity);
×
118
    }
UNCOV
119
    for (const tagQuery of this._tagQueries.values()) {
×
UNCOV
120
      tagQuery.checkAndAdd(entity);
×
121
    }
UNCOV
122
    entity.componentAdded$.subscribe(addComponent);
×
UNCOV
123
    entity.componentRemoved$.subscribe(removeComponent);
×
UNCOV
124
    entity.tagAdded$.subscribe(addTag);
×
UNCOV
125
    entity.tagRemoved$.subscribe(removeTag);
×
126
  }
127

128
  /**
129
   * Scans queries and locates any that need this entity removed
130
   * @param entity
131
   */
132
  removeEntity(entity: Entity) {
133
    // Handle components
UNCOV
134
    const addComponent = this._addComponentHandlers.get(entity);
×
UNCOV
135
    const removeComponent = this._removeComponentHandlers.get(entity);
×
UNCOV
136
    for (const query of this._queries.values()) {
×
UNCOV
137
      query.removeEntity(entity);
×
138
    }
UNCOV
139
    if (addComponent) {
×
UNCOV
140
      entity.componentAdded$.unsubscribe(addComponent);
×
141
    }
UNCOV
142
    if (removeComponent) {
×
UNCOV
143
      entity.componentRemoved$.unsubscribe(removeComponent);
×
144
    }
145

146
    // Handle tags
UNCOV
147
    const addTag = this._addTagHandlers.get(entity);
×
UNCOV
148
    const removeTag = this._removeTagHandlers.get(entity);
×
UNCOV
149
    for (const tagQuery of this._tagQueries.values()) {
×
UNCOV
150
      tagQuery.removeEntity(entity);
×
151
    }
152

UNCOV
153
    if (addTag) {
×
UNCOV
154
      entity.tagAdded$.unsubscribe(addTag);
×
155
    }
UNCOV
156
    if (removeTag) {
×
UNCOV
157
      entity.tagRemoved$.unsubscribe(removeTag);
×
158
    }
159
  }
160

161
  /**
162
   * Updates any queries when a component is added to an entity
163
   * @param entity
164
   * @param component
165
   */
166
  addComponent(entity: Entity, component: Component) {
UNCOV
167
    const queries = this._componentToQueriesIndex.get(component.constructor as ComponentCtor<any>) ?? [];
×
UNCOV
168
    for (const query of queries) {
×
UNCOV
169
      query.checkAndAdd(entity);
×
170
    }
171
  }
172

173
  /**
174
   * Updates any queries when a component is removed from an entity
175
   * @param entity
176
   * @param component
177
   */
178
  removeComponent(entity: Entity, component: Component) {
UNCOV
179
    const queries = this._componentToQueriesIndex.get(component.constructor as ComponentCtor<any>) ?? [];
×
UNCOV
180
    for (const query of queries) {
×
UNCOV
181
      query.removeEntity(entity);
×
182
    }
183
  }
184

185
  /**
186
   * Updates any queries when a tag is added to an entity
187
   * @param entity
188
   * @param tag
189
   */
190
  addTag(entity: Entity, tag: string) {
UNCOV
191
    const queries = this._tagToQueriesIndex.get(tag) ?? [];
×
UNCOV
192
    for (const query of queries) {
×
193
      query.checkAndAdd(entity);
×
194
    }
195
  }
196

197
  /**
198
   * Updates any queries when a component is removed from an entity
199
   * @param entity
200
   * @param tag
201
   */
202
  removeTag(entity: Entity, tag: string) {
UNCOV
203
    const queries = this._tagToQueriesIndex.get(tag) ?? [];
×
UNCOV
204
    for (const query of queries) {
×
UNCOV
205
      query.removeEntity(entity);
×
206
    }
207
  }
208
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc