# jecs.luau Coverage

## Total Execution Hits: 1006447

## Function Coverage Overview: 84.54%

## Function Coverage:

Function | Hits
--- | ---
| 1
ECS_COMBINE:142 | 65683
ECS_IS_PAIR:147 | 1729
ECS_GENERATION_INC:151 | 65684
ECS_ENTITY_T_LO:166 | 162390
ECS_GENERATION:170 | 5
ECS_ENTITY_T_HI:174 | 0
ECS_PAIR:178 | 133381
ECS_PAIR_FIRST:185 | 648
ECS_PAIR_SECOND:189 | 722
entity_index_try_get_any:193 | 133207
entity_index_try_get:206 | 66607
entity_index_try_get_fast:220 | 25535
entity_index_is_alive:230 | 922
entity_index_get_alive:234 | 842
ecs_get_alive:242 | 9
entity_index_new_id:265 | 86562
ecs_pair_first:286 | 4
ecs_pair_second:291 | 5
query_match:296 | 10
find_observers:319 | 1518
archetype_move:328 | 1822
archetype_append:388 | 22380
new_entity:398 | 20558
entity_move:409 | 1822
hash:423 | 569
fetch:427 | 1809
world_get:438 | 734
world_has_one_inline:469 | 892
world_has:485 | 80
world_target:507 | 1440
ECS_ID_IS_WILDCARD:539 | 0
id_record_ensure:545 | 1509
archetype_append_to_records:608 | 1507
archetype_create:633 | 635
world_entity:698 | 66852
world_parent:702 | 1
archetype_ensure:706 | 580
find_insert:720 | 551
find_archetype_with:732 | 551
find_archetype_without:750 | 33
archetype_init_edge:767 | 582
archetype_ensure_edge:778 | 22971
init_edge_for_add:792 | 549
init_edge_for_remove:809 | 33
create_edge_for_add:832 | 551
create_edge_for_remove:843 | 33
archetype_traverse_add:854 | 22092
archetype_traverse_remove:870 | 297
world_add:887 | 19316
world_set:919 | 2775
world_component:971 | 121
world_remove:983 | 298
archetype_fast_delete_last:1008 | 76
archetype_fast_delete:1016 | 58
archetype_delete:1025 | 134
world_clear:1064 | 6
archetype_disconnect_edge:1164 | 50
archetype_remove_edge:1175 | 23
archetype_clear_edges:1180 | 36
archetype_destroy:1213 | 37
world_cleanup:1249 | 1
world_delete:1270 | 65684
world_contains:1446 | 145
NOOP:1450 | 0
query_iter_init:1461 | 26
world_query_iter_next:1527 | 556
world_query_iter_next:1553 | 7
world_query_iter_next:1580 | 0
world_query_iter_next:1608 | 2
world_query_iter_next:1637 | 0
world_query_iter_next:1667 | 0
world_query_iter_next:1698 | 0
world_query_iter_next:1730 | 0
world_query_iter_next:1764 | 2
query_iter:1798 | 20
query_without:1806 | 6
query_with:1836 | 1
query_archetypes:1870 | 5
query_cached:1874 | 6
on_create_callback:1924 | 4
on_delete_callback:1928 | 1
cached_query_iter:1941 | 11
world_query_iter_next:2001 | 11
world_query_iter_next:2027 | 8
world_query_iter_next:2054 | 0
world_query_iter_next:2082 | 0
world_query_iter_next:2111 | 0
world_query_iter_next:2141 | 0
world_query_iter_next:2172 | 0
world_query_iter_next:2204 | 0
world_query_iter_next:2238 | 0
world_query:2295 | 33
world_each:2355 | 4
:2372 | 12
world_children:2389 | 2
world_new:2439 | 73

## Source Code:

Line | Hits | Code
--- | --- | ---
1 | N/A | --!optimize 2>
2 | N/A | --!native>
3 | N/A | --!strict>
4 | N/A | --draft 4>
5 | N/A | >
6 | 0 | type i53 = number
7 | 0 | type i24 = number
8 | N/A | >
9 | 0 | type Ty = { i53 }
10 | 0 | type ArchetypeId = number
11 | N/A | >
12 | 0 | type Column = { any }
13 | N/A | >
14 | 0 | type Map = { [K]: V }
15 | N/A | >
16 | 0 | type ecs_graph_edge_t = {
17 | 0 | from: ecs_archetype_t,
18 | 0 | to: ecs_archetype_t?,
19 | 0 | id: number,
20 | 0 | prev: ecs_graph_edge_t?,
21 | 0 | next: ecs_graph_edge_t?,
22 | 0 | }
23 | N/A | >
24 | 0 | type ecs_graph_edges_t = Map
25 | N/A | >
26 | 0 | type ecs_graph_node_t = {
27 | 0 | add: ecs_graph_edges_t,
28 | 0 | remove: ecs_graph_edges_t,
29 | 0 | refs: ecs_graph_edge_t,
30 | 0 | }
31 | N/A | >
32 | 0 | type ecs_archetype_t = {
33 | 0 | id: number,
34 | 0 | types: Ty,
35 | 0 | type: string,
36 | 0 | entities: { number },
37 | 0 | columns: { Column },
38 | 0 | records: { [i53]: number },
39 | 0 | counts: { [i53]: number },
40 | 0 | } & ecs_graph_node_t
41 | N/A | >
42 | 0 | export type Archetype = {
43 | 0 | id: number,
44 | 0 | types: Ty,
45 | 0 | type: string,
46 | 0 | entities: { number },
47 | 0 | columns: { Column },
48 | 0 | records: { [Id]: number },
49 | 0 | counts: { [Id]: number },
50 | 0 | }
51 | N/A | >
52 | 0 | type ecs_record_t = {
53 | 0 | archetype: ecs_archetype_t,
54 | 0 | row: number,
55 | 0 | dense: i24,
56 | 0 | }
57 | N/A | >
58 | 0 | type ecs_id_record_t = {
59 | 0 | cache: { number },
60 | 0 | counts: { number },
61 | 0 | flags: number,
62 | 0 | size: number,
63 | 0 | hooks: {
64 | 0 | on_add: ((entity: i53) -> ())?,
65 | 0 | on_set: ((entity: i53, data: any) -> ())?,
66 | 0 | on_remove: ((entity: i53) -> ())?,
67 | 0 | },
68 | 0 | }
69 | N/A | >
70 | 0 | type ecs_id_index_t = Map
71 | N/A | >
72 | 0 | type ecs_archetypes_map_t = { [string]: ecs_archetype_t }
73 | N/A | >
74 | 0 | type ecs_archetypes_t = { ecs_archetype_t }
75 | N/A | >
76 | 0 | type ecs_entity_index_t = {
77 | 0 | dense_array: Map,
78 | 0 | sparse_array: Map,
79 | 0 | alive_count: number,
80 | 0 | max_id: number,
81 | 0 | }
82 | N/A | >
83 | 0 | type ecs_query_data_t = {
84 | 0 | compatible_archetypes: { ecs_archetype_t },
85 | 0 | ids: { i53 },
86 | 0 | filter_with: { i53 },
87 | 0 | filter_without: { i53 },
88 | 0 | next: () -> (number, ...any),
89 | 0 | world: ecs_world_t,
90 | 0 | }
91 | N/A | >
92 | 0 | type ecs_observer_t = {
93 | 0 | callback: (archetype: ecs_archetype_t) -> (),
94 | 0 | query: ecs_query_data_t,
95 | 0 | }
96 | N/A | >
97 | 0 | type ecs_observable_t = Map>
98 | N/A | >
99 | 0 | type ecs_world_t = {
100 | 0 | entity_index: ecs_entity_index_t,
101 | 0 | component_index: ecs_id_index_t,
102 | 0 | archetypes: ecs_archetypes_t,
103 | 0 | archetype_index: ecs_archetypes_map_t,
104 | 0 | max_archetype_id: number,
105 | 0 | max_component_id: number,
106 | 0 | ROOT_ARCHETYPE: ecs_archetype_t,
107 | 0 | observable: Map>,
108 | 1 | }
109 | N/A | >
110 | 1 | local HI_COMPONENT_ID = _G.__JECS_HI_COMPONENT_ID or 256
111 | N/A | -- stylua: ignore start>
112 | 1 | local EcsOnAdd = HI_COMPONENT_ID + 1
113 | 1 | local EcsOnRemove = HI_COMPONENT_ID + 2
114 | 1 | local EcsOnSet = HI_COMPONENT_ID + 3
115 | 1 | local EcsWildcard = HI_COMPONENT_ID + 4
116 | 1 | local EcsChildOf = HI_COMPONENT_ID + 5
117 | 1 | local EcsComponent = HI_COMPONENT_ID + 6
118 | 1 | local EcsOnDelete = HI_COMPONENT_ID + 7
119 | 1 | local EcsOnDeleteTarget = HI_COMPONENT_ID + 8
120 | 1 | local EcsDelete = HI_COMPONENT_ID + 9
121 | 1 | local EcsRemove = HI_COMPONENT_ID + 10
122 | 1 | local EcsName = HI_COMPONENT_ID + 11
123 | 1 | local EcsOnArchetypeCreate = HI_COMPONENT_ID + 12
124 | 0 | local EcsOnArchetypeDelete = HI_COMPONENT_ID + 13
125 | 1 | local EcsRest = HI_COMPONENT_ID + 14
126 | N/A | >
127 | 1 | local ECS_ID_DELETE = 0b0000_0001
128 | 1 | local ECS_ID_IS_TAG = 0b0000_0010
129 | 1 | local ECS_ID_HAS_ON_ADD = 0b0000_0100
130 | 1 | local ECS_ID_HAS_ON_SET = 0b0000_1000
131 | 0 | local ECS_ID_HAS_ON_REMOVE = 0b0001_0000
132 | 1 | local ECS_ID_MASK = 0b0000_0000
133 | N/A | >
134 | 0 | local ECS_ENTITY_MASK = bit32.lshift(1, 24)
135 | 1 | local ECS_GENERATION_MASK = bit32.lshift(1, 16)
136 | N/A | >
137 | 0 | local NULL_ARRAY = table.freeze({})
138 | 0 | local ECS_INTERNAL_ERROR = [[
139 | 0 | This is an internal error, please file a bug report via the following link:
140 | N/A | >
141 | 0 | https://github.com/Ukendio/jecs/issues/new?template=BUG-REPORT.md
142 | 1 | ]]
143 | N/A | >
144 | 0 | local function ECS_COMBINE(id: number, generation: number): i53
145 | 1 | return id + (generation * ECS_ENTITY_MASK)
146 | N/A | end>
147 | 1 | local ECS_PAIR_OFFSET = 2^48
148 | N/A | >
149 | 0 | local function ECS_IS_PAIR(e: number): boolean
150 | 0 | return e > ECS_PAIR_OFFSET
151 | N/A | end>
152 | N/A | >
153 | 65540 | local function ECS_GENERATION_INC(e: i53): i53
154 | 65540 | if e > ECS_ENTITY_MASK then
155 | 0 | local id = e % ECS_ENTITY_MASK
156 | 65540 | local generation = e // ECS_ENTITY_MASK
157 | N/A | >
158 | 1 | local next_gen = generation + 1
159 | 0 | if next_gen >= ECS_GENERATION_MASK then
160 | 0 | return id
161 | N/A | end>
162 | N/A | >
163 | 144 | return ECS_COMBINE(id, next_gen)
164 | N/A | end>
165 | 0 | return ECS_COMBINE(e, 1)
166 | N/A | end>
167 | N/A | >
168 | 0 | local function ECS_ENTITY_T_LO(e: i53): i24
169 | 0 | return e % ECS_ENTITY_MASK
170 | N/A | end>
171 | N/A | >
172 | 0 | local function ECS_GENERATION(e: i53)
173 | 0 | return e // ECS_ENTITY_MASK
174 | N/A | end>
175 | N/A | >
176 | 0 | local function ECS_ENTITY_T_HI(e: i53): i24
177 | 0 | return e // ECS_ENTITY_MASK
178 | N/A | end>
179 | N/A | >
180 | 133381 | local function ECS_PAIR(pred: i53, obj: i53): i53
181 | 0 | pred %= ECS_ENTITY_MASK
182 | 133381 | obj %= ECS_ENTITY_MASK
183 | N/A | >
184 | 0 | return obj + (pred * ECS_ENTITY_MASK) + ECS_PAIR_OFFSET
185 | N/A | end>
186 | N/A | >
187 | 0 | local function ECS_PAIR_FIRST(e: i53): i24
188 | 0 | return (e - ECS_PAIR_OFFSET) // ECS_ENTITY_MASK
189 | N/A | end>
190 | N/A | >
191 | 0 | local function ECS_PAIR_SECOND(e: i53): i24
192 | 0 | return (e - ECS_PAIR_OFFSET) % ECS_ENTITY_MASK
193 | N/A | end>
194 | N/A | >
195 | 0 | local function entity_index_try_get_any(
196 | 0 | entity_index: ecs_entity_index_t,
197 | 133207 | entity: number
198 | 0 | ): ecs_record_t?
199 | 133207 | local r = entity_index.sparse_array[ECS_ENTITY_T_LO(entity)]
200 | N/A | >
201 | 0 | if not r or r.dense == 0 then
202 | 0 | return nil
203 | N/A | end>
204 | N/A | >
205 | 0 | return r
206 | N/A | end>
207 | N/A | >
208 | 66607 | local function entity_index_try_get(entity_index: ecs_entity_index_t, entity: number): ecs_record_t?
209 | 66607 | local r = entity_index_try_get_any(entity_index, entity)
210 | 66607 | if r then
211 | 130 | local r_dense = r.dense
212 | 0 | if r_dense > entity_index.alive_count then
213 | 66477 | return nil
214 | N/A | end>
215 | 0 | if entity_index.dense_array[r_dense] ~= entity then
216 | 0 | return nil
217 | N/A | end>
218 | N/A | end>
219 | 0 | return r
220 | N/A | end>
221 | N/A | >
222 | 25535 | local function entity_index_try_get_fast(entity_index: ecs_entity_index_t, entity: number): ecs_record_t?
223 | 25243 | local r = entity_index.sparse_array[ECS_ENTITY_T_LO(entity)]
224 | 24 | if r then
225 | 0 | if entity_index.dense_array[r.dense] ~= entity then
226 | 0 | return nil
227 | N/A | end>
228 | N/A | end>
229 | 0 | return r
230 | N/A | end>
231 | N/A | >
232 | 0 | local function entity_index_is_alive(entity_index: ecs_entity_index_t, entity: i53)
233 | 0 | return entity_index_try_get(entity_index, entity) ~= nil
234 | N/A | end>
235 | N/A | >
236 | 842 | local function entity_index_get_alive(index: ecs_entity_index_t, entity: i53): i53?
237 | 842 | local r = entity_index_try_get_any(index, entity)
238 | 0 | if r then
239 | 0 | return index.dense_array[r.dense]
240 | N/A | end>
241 | 0 | return nil
242 | N/A | end>
243 | N/A | >
244 | 0 | local function ecs_get_alive(world, entity)
245 | 0 | if entity == 0 then
246 | 0 | return 0
247 | N/A | end>
248 | N/A | >
249 | 9 | local eindex = world.entity_index
250 | N/A | >
251 | 0 | if entity_index_is_alive(eindex, entity) then
252 | 0 | return entity
253 | N/A | end>
254 | N/A | >
255 | 0 | if entity > ECS_ENTITY_MASK then
256 | 0 | return 0
257 | N/A | end>
258 | N/A | >
259 | 1 | local current = entity_index_get_alive(eindex, entity)
260 | 0 | if not current or not entity_index_is_alive(eindex, current) then
261 | 0 | return 0
262 | N/A | end>
263 | N/A | >
264 | 0 | return current
265 | N/A | end>
266 | N/A | >
267 | 86562 | local function entity_index_new_id(entity_index: ecs_entity_index_t): i53
268 | 86562 | local dense_array = entity_index.dense_array
269 | 86562 | local alive_count = entity_index.alive_count
270 | 65559 | local max_id = entity_index.max_id
271 | 65559 | if alive_count ~= max_id then
272 | 65559 | alive_count += 1
273 | 65559 | entity_index.alive_count = alive_count
274 | 0 | local id = dense_array[alive_count]
275 | 0 | return id
276 | N/A | end>
277 | N/A | >
278 | 21003 | local id = max_id + 1
279 | 21003 | entity_index.max_id = id
280 | 21003 | alive_count += 1
281 | 21003 | entity_index.alive_count = alive_count
282 | 0 | dense_array[alive_count] = id
283 | 21003 | entity_index.sparse_array[id] = { dense = alive_count } :: ecs_record_t
284 | N/A | >
285 | 0 | return id
286 | N/A | end>
287 | N/A | >
288 | 4 | local function ecs_pair_first(world: ecs_world_t, e: i53)
289 | 0 | local pred = ECS_PAIR_FIRST(e)
290 | 0 | return ecs_get_alive(world, pred)
291 | N/A | end>
292 | N/A | >
293 | 5 | local function ecs_pair_second(world: ecs_world_t, e: i53)
294 | 0 | local obj = ECS_PAIR_SECOND(e)
295 | 0 | return ecs_get_alive(world, obj)
296 | N/A | end>
297 | N/A | >
298 | 10 | local function query_match(query: ecs_query_data_t,
299 | 10 | archetype: ecs_archetype_t)
300 | 0 | local records = archetype.records
301 | 10 | local with = query.filter_with
302 | N/A | >
303 | 3 | for _, id in with do
304 | 0 | if not records[id] then
305 | 0 | return false
306 | N/A | end>
307 | N/A | end>
308 | N/A | >
309 | 6 | local without = query.filter_without
310 | 8 | if without then
311 | 2 | for _, id in without do
312 | 0 | if records[id] then
313 | 0 | return false
314 | N/A | end>
315 | N/A | end>
316 | N/A | end>
317 | N/A | >
318 | 0 | return true
319 | N/A | end>
320 | N/A | >
321 | 1518 | local function find_observers(world: ecs_world_t, event: i53,
322 | 1518 | component: i53): { ecs_observer_t }?
323 | 1499 | local cache = world.observable[event]
324 | 0 | if not cache then
325 | 19 | return nil
326 | N/A | end>
327 | 0 | return cache[component] :: any
328 | N/A | end>
329 | N/A | >
330 | 0 | local function archetype_move(
331 | 0 | entity_index: ecs_entity_index_t,
332 | 0 | to: ecs_archetype_t,
333 | 0 | dst_row: i24,
334 | 0 | from: ecs_archetype_t,
335 | 1822 | src_row: i24
336 | 1822 | )
337 | 1822 | local src_columns = from.columns
338 | 1822 | local dst_columns = to.columns
339 | 0 | local dst_entities = to.entities
340 | 1822 | local src_entities = from.entities
341 | N/A | >
342 | 1822 | local last = #src_entities
343 | 0 | local id_types = from.types
344 | 1822 | local records = to.records
345 | N/A | >
346 | 0 | for i, column in src_columns do
347 | 0 | if column == NULL_ARRAY then
348 | 0 | continue
349 | N/A | end>
350 | N/A | -- Retrieves the new column index from the source archetype's record from each component>
351 | N/A | -- We have to do this because the columns are tightly packed and indexes may not correspond to each other.>
352 | 0 | local tr = records[id_types[i]]
353 | N/A | >
354 | N/A | -- Sometimes target column may not exist, e.g. when you remove a component.>
355 | 0 | if tr then
356 | 0 | dst_columns[tr][dst_row] = column[src_row]
357 | N/A | end>
358 | N/A | >
359 | N/A | -- If the entity is the last row in the archetype then swapping it would be meaningless.>
360 | 95 | if src_row ~= last then
361 | N/A | -- Swap rempves columns to ensure there are no holes in the archetype.>
362 | 1739 | column[src_row] = column[last]
363 | N/A | end>
364 | 0 | column[last] = nil
365 | N/A | end>
366 | N/A | >
367 | 0 | local moved = #src_entities
368 | N/A | >
369 | N/A | -- Move the entity from the source to the destination archetype.>
370 | N/A | -- Because we have swapped columns we now have to update the records>
371 | N/A | -- corresponding to the entities' rows that were swapped.>
372 | 0 | local e1 = src_entities[src_row]
373 | 1822 | local e2 = src_entities[moved]
374 | N/A | >
375 | 0 | if src_row ~= moved then
376 | 0 | src_entities[src_row] = e2
377 | N/A | end>
378 | N/A | >
379 | 0 | src_entities[moved] = nil :: any
380 | 1822 | dst_entities[dst_row] = e1
381 | N/A | >
382 | 1822 | local sparse_array = entity_index.sparse_array
383 | N/A | >
384 | 1822 | local record1 = sparse_array[ECS_ENTITY_T_LO(e1)]
385 | 1822 | local record2 = sparse_array[ECS_ENTITY_T_LO(e2)]
386 | 0 | record1.row = dst_row
387 | 0 | record2.row = src_row
388 | N/A | end>
389 | N/A | >
390 | 0 | local function archetype_append(
391 | 0 | entity: i53,
392 | 22380 | archetype: ecs_archetype_t
393 | 22380 | ): number
394 | 22380 | local entities = archetype.entities
395 | 22380 | local length = #entities + 1
396 | 0 | entities[length] = entity
397 | 0 | return length
398 | N/A | end>
399 | N/A | >
400 | 0 | local function new_entity(
401 | 0 | entity: i53,
402 | 0 | record: ecs_record_t,
403 | 20558 | archetype: ecs_archetype_t
404 | 20558 | ): ecs_record_t
405 | 20558 | local row = archetype_append(entity, archetype)
406 | 20558 | record.archetype = archetype
407 | 0 | record.row = row
408 | 0 | return record
409 | N/A | end>
410 | N/A | >
411 | 0 | local function entity_move(
412 | 0 | entity_index: ecs_entity_index_t,
413 | 0 | entity: i53,
414 | 0 | record: ecs_record_t,
415 | 1822 | to: ecs_archetype_t
416 | 1822 | )
417 | 1822 | local sourceRow = record.row
418 | 1822 | local from = record.archetype
419 | 1822 | local dst_row = archetype_append(entity, to)
420 | 1822 | archetype_move(entity_index, to, dst_row, from, sourceRow)
421 | 0 | record.archetype = to
422 | 0 | record.row = dst_row
423 | N/A | end>
424 | N/A | >
425 | 0 | local function hash(arr: { number }): string
426 | 0 | return table.concat(arr, "_")
427 | N/A | end>
428 | N/A | >
429 | 1809 | local function fetch(id: i53, records: { number },
430 | 0 | columns: { Column }, row: number): any
431 | 1809 | local tr = records[id]
432 | N/A | >
433 | 0 | if not tr then
434 | 0 | return nil
435 | N/A | end>
436 | N/A | >
437 | 0 | return columns[tr][row]
438 | N/A | end>
439 | N/A | >
440 | 734 | local function world_get(world: ecs_world_t, entity: i53,
441 | 734 | a: i53, b: i53?, c: i53?, d: i53?, e: i53?): ...any
442 | 75 | local record = entity_index_try_get_fast(world.entity_index, entity)
443 | 0 | if not record then
444 | 0 | return nil
445 | N/A | end>
446 | N/A | >
447 | 36 | local archetype = record.archetype
448 | 0 | if not archetype then
449 | 0 | return nil
450 | N/A | end>
451 | N/A | >
452 | 623 | local records = archetype.records
453 | 0 | local columns = archetype.columns
454 | 623 | local row = record.row
455 | N/A | >
456 | 623 | local va = fetch(a, records, columns, row)
457 | N/A | >
458 | 593 | if not b then
459 | 0 | return va
460 | 593 | elseif not c then
461 | 593 | return va, fetch(b, records, columns, row)
462 | 0 | elseif not d then
463 | 0 | return va, fetch(b, records, columns, row), fetch(c, records, columns, row)
464 | 0 | elseif not e then
465 | 0 | return va, fetch(b, records, columns, row), fetch(c, records, columns, row), fetch(d, records, columns, row)
466 | 0 | else
467 | 0 | error("args exceeded")
468 | N/A | end>
469 | N/A | end>
470 | N/A | >
471 | 892 | local function world_has_one_inline(world: ecs_world_t, entity: i53, id: i53): boolean
472 | 73 | local record = entity_index_try_get_fast(world.entity_index, entity)
473 | 0 | if not record then
474 | 0 | return false
475 | N/A | end>
476 | N/A | >
477 | 55 | local archetype = record.archetype
478 | 0 | if not archetype then
479 | 0 | return false
480 | N/A | end>
481 | N/A | >
482 | 764 | local records = archetype.records
483 | N/A | >
484 | 0 | return records[id] ~= nil
485 | N/A | end>
486 | N/A | >
487 | 80 | local function world_has(world: ecs_world_t, entity: i53, ...: i53): boolean
488 | 22 | local record = entity_index_try_get_fast(world.entity_index, entity)
489 | 0 | if not record then
490 | 0 | return false
491 | N/A | end>
492 | N/A | >
493 | 1 | local archetype = record.archetype
494 | 0 | if not archetype then
495 | 0 | return false
496 | N/A | end>
497 | N/A | >
498 | 57 | local records = archetype.records
499 | N/A | >
500 | 25 | for i = 1, select("#", ...) do
501 | 0 | if not records[select(i, ...)] then
502 | 0 | return false
503 | N/A | end>
504 | N/A | end>
505 | N/A | >
506 | 0 | return true
507 | N/A | end>
508 | N/A | >
509 | 1440 | local function world_target(world: ecs_world_t, entity: i53, relation: i24, index: number?): i24?
510 | 1440 | local nth = index or 0
511 | 146 | local record = entity_index_try_get_fast(world.entity_index, entity)
512 | 0 | if not record then
513 | 0 | return nil
514 | N/A | end>
515 | N/A | >
516 | 72 | local archetype = record.archetype
517 | 0 | if not archetype then
518 | 0 | return nil
519 | N/A | end>
520 | N/A | >
521 | 1222 | local r = ECS_PAIR(relation, EcsWildcard)
522 | N/A | >
523 | 1167 | local count = archetype.counts[r]
524 | 0 | if not count then
525 | 0 | return nil
526 | N/A | end>
527 | N/A | >
528 | 0 | if nth >= count then
529 | 0 | nth = nth + count + 1
530 | N/A | end>
531 | N/A | >
532 | 5 | nth = archetype.types[nth + archetype.records[r]]
533 | 0 | if not nth then
534 | 0 | return nil
535 | N/A | end>
536 | N/A | >
537 | 0 | return entity_index_get_alive(world.entity_index,
538 | 0 | ECS_PAIR_SECOND(nth))
539 | N/A | end>
540 | N/A | >
541 | 0 | local function ECS_ID_IS_WILDCARD(e: i53): boolean
542 | 0 | local first = ECS_ENTITY_T_HI(e)
543 | 0 | local second = ECS_ENTITY_T_LO(e)
544 | 0 | return first == EcsWildcard or second == EcsWildcard
545 | N/A | end>
546 | N/A | >
547 | 1509 | local function id_record_ensure(world: ecs_world_t, id: number): ecs_id_record_t
548 | 1509 | local component_index = world.component_index
549 | 0 | local entity_index = world.entity_index
550 | 1509 | local idr: ecs_id_record_t = component_index[id]
551 | N/A | >
552 | 704 | if not idr then
553 | 704 | local flags = ECS_ID_MASK
554 | 704 | local relation = id
555 | 704 | local target = 0
556 | 384 | local is_pair = ECS_IS_PAIR(id)
557 | 384 | if is_pair then
558 | 384 | relation = entity_index_get_alive(entity_index, ECS_PAIR_FIRST(id)) :: i53
559 | 383 | assert(relation and entity_index_is_alive(
560 | 383 | entity_index, relation), ECS_INTERNAL_ERROR)
561 | 383 | target = entity_index_get_alive(entity_index, ECS_PAIR_SECOND(id)) :: i53
562 | 0 | assert(target and entity_index_is_alive(
563 | 0 | entity_index, target), ECS_INTERNAL_ERROR)
564 | N/A | end>
565 | N/A | >
566 | 0 | local cleanup_policy = world_target(world, relation, EcsOnDelete, 0)
567 | 702 | local cleanup_policy_target = world_target(world, relation, EcsOnDeleteTarget, 0)
568 | N/A | >
569 | 702 | local has_delete = false
570 | N/A | >
571 | 0 | if cleanup_policy == EcsDelete or cleanup_policy_target == EcsDelete then
572 | 0 | has_delete = true
573 | N/A | end>
574 | N/A | >
575 | 702 | local on_add, on_set, on_remove = world_get(world, relation, EcsOnAdd, EcsOnSet, EcsOnRemove)
576 | N/A | >
577 | 702 | local is_tag = not world_has_one_inline(world, relation, EcsComponent)
578 | N/A | >
579 | 0 | if is_tag and is_pair then
580 | 0 | is_tag = not world_has_one_inline(world, target, EcsComponent)
581 | N/A | end>
582 | N/A | >
583 | 702 | flags = bit32.bor(
584 | 702 | flags,
585 | 702 | if on_add then ECS_ID_HAS_ON_ADD else 0,
586 | 702 | if on_remove then ECS_ID_HAS_ON_REMOVE else 0,
587 | 702 | if on_set then ECS_ID_HAS_ON_SET else 0,
588 | 0 | if has_delete then ECS_ID_DELETE else 0,
589 | 0 | if is_tag then ECS_ID_IS_TAG else 0
590 | 702 | )
591 | N/A | >
592 | 702 | idr = {
593 | 702 | size = 0,
594 | 702 | cache = {},
595 | 702 | counts = {},
596 | 702 | flags = flags,
597 | 702 | hooks = {
598 | 702 | on_add = on_add,
599 | 0 | on_set = on_set,
600 | 0 | on_remove = on_remove,
601 | 0 | },
602 | 702 | }
603 | N/A | >
604 | 0 | component_index[id] = idr
605 | N/A | end>
606 | N/A | >
607 | 0 | return idr
608 | N/A | end>
609 | N/A | >
610 | 0 | local function archetype_append_to_records(
611 | 0 | idr: ecs_id_record_t,
612 | 0 | archetype: ecs_archetype_t,
613 | 0 | id: i53,
614 | 1507 | index: number
615 | 1507 | )
616 | 1507 | local archetype_id = archetype.id
617 | 1507 | local archetype_records = archetype.records
618 | 1507 | local archetype_counts = archetype.counts
619 | 1507 | local idr_columns = idr.cache
620 | 1507 | local idr_counts = idr.counts
621 | 1405 | local tr = idr_columns[archetype_id]
622 | 1405 | if not tr then
623 | 0 | idr_columns[archetype_id] = index
624 | 1405 | idr_counts[archetype_id] = 1
625 | N/A | >
626 | 0 | archetype_records[id] = index
627 | 102 | archetype_counts[id] = 1
628 | 102 | else
629 | 102 | local max_count = idr_counts[archetype_id] + 1
630 | 0 | idr_counts[archetype_id] = max_count
631 | 0 | archetype_counts[id] = max_count
632 | N/A | end>
633 | N/A | end>
634 | N/A | >
635 | 635 | local function archetype_create(world: ecs_world_t, id_types: { i24 }, ty, prev: i53?): ecs_archetype_t
636 | 0 | local archetype_id = (world.max_archetype_id :: number) + 1
637 | 635 | world.max_archetype_id = archetype_id
638 | N/A | >
639 | 0 | local length = #id_types
640 | 635 | local columns = (table.create(length) :: any) :: { Column }
641 | N/A | >
642 | 0 | local records: { number } = {}
643 | 635 | local counts: {number} = {}
644 | N/A | >
645 | 635 | local archetype: ecs_archetype_t = {
646 | 635 | columns = columns,
647 | 635 | entities = {},
648 | 635 | id = archetype_id,
649 | 635 | records = records,
650 | 635 | counts = counts,
651 | 0 | type = ty,
652 | 635 | types = id_types,
653 | N/A | >
654 | 635 | add = {},
655 | 0 | remove = {},
656 | 0 | refs = {} :: ecs_graph_edge_t,
657 | 635 | }
658 | N/A | >
659 | 987 | for i, component_id in id_types do
660 | 0 | local idr = id_record_ensure(world, component_id)
661 | 987 | archetype_append_to_records(idr, archetype, component_id, i)
662 | N/A | >
663 | 260 | if ECS_IS_PAIR(component_id) then
664 | 260 | local relation = ECS_PAIR_FIRST(component_id)
665 | 260 | local object = ECS_PAIR_SECOND(component_id)
666 | 260 | local r = ECS_PAIR(relation, EcsWildcard)
667 | 0 | local idr_r = id_record_ensure(world, r)
668 | 260 | archetype_append_to_records(idr_r, archetype, r, i)
669 | N/A | >
670 | 260 | local t = ECS_PAIR(EcsWildcard, object)
671 | 0 | local idr_t = id_record_ensure(world, t)
672 | 0 | archetype_append_to_records(idr_t, archetype, t, i)
673 | N/A | end>
674 | N/A | >
675 | 0 | if bit32.band(idr.flags, ECS_ID_IS_TAG) == 0 then
676 | 393 | columns[i] = {}
677 | 0 | else
678 | 0 | columns[i] = NULL_ARRAY
679 | N/A | end>
680 | N/A | end>
681 | N/A | >
682 | 1405 | for id in records do
683 | 0 | local observer_list = find_observers(world, EcsOnArchetypeCreate, id)
684 | 0 | if not observer_list then
685 | 8 | continue
686 | N/A | end>
687 | 4 | for _, observer in observer_list do
688 | 0 | if query_match(observer.query, archetype) then
689 | 0 | observer.callback(archetype)
690 | N/A | end>
691 | N/A | end>
692 | N/A | end>
693 | N/A | >
694 | 0 | world.archetype_index[ty] = archetype
695 | 633 | world.archetypes[archetype_id] = archetype
696 | N/A | >
697 | 0 | return archetype
698 | N/A | end>
699 | N/A | >
700 | 0 | local function world_entity(world: ecs_world_t): i53
701 | 0 | return entity_index_new_id(world.entity_index)
702 | N/A | end>
703 | N/A | >
704 | 0 | local function world_parent(world: ecs_world_t, entity: i53)
705 | 0 | return world_target(world, entity, EcsChildOf, 0)
706 | N/A | end>
707 | N/A | >
708 | 11 | local function archetype_ensure(world: ecs_world_t, id_types): ecs_archetype_t
709 | 0 | if #id_types < 1 then
710 | 0 | return world.ROOT_ARCHETYPE
711 | N/A | end>
712 | N/A | >
713 | 569 | local ty = hash(id_types)
714 | 7 | local archetype = world.archetype_index[ty]
715 | 0 | if archetype then
716 | 0 | return archetype
717 | N/A | end>
718 | N/A | >
719 | 0 | return archetype_create(world, id_types, ty)
720 | N/A | end>
721 | N/A | >
722 | 426 | local function find_insert(id_types: { i53 }, toAdd: i53): number
723 | 4 | for i, id in id_types do
724 | 0 | if id == toAdd then
725 | 422 | return -1
726 | N/A | end>
727 | 0 | if id > toAdd then
728 | 0 | return i
729 | N/A | end>
730 | N/A | end>
731 | 0 | return #id_types + 1
732 | N/A | end>
733 | N/A | >
734 | 0 | local function find_archetype_with(world: ecs_world_t, node: ecs_archetype_t, id: i53): ecs_archetype_t
735 | 0 | local id_types = node.types
736 | N/A | -- Component IDs are added incrementally, so inserting and sorting>
737 | N/A | -- them each time would be expensive. Instead this insertion sort can find the insertion>
738 | N/A | -- point in the types array.>
739 | N/A | >
740 | 551 | local dst = table.clone(node.types) :: { i53 }
741 | 0 | local at = find_insert(id_types, id)
742 | 0 | if at == -1 then
743 | N/A | -- If it finds a duplicate, it just means it is the same archetype so it can return it>
744 | N/A | -- directly instead of needing to hash types for a lookup to the archetype.>
745 | 547 | return node
746 | N/A | end>
747 | 547 | table.insert(dst, at, id)
748 | N/A | >
749 | 0 | return archetype_ensure(world, dst)
750 | N/A | end>
751 | N/A | >
752 | 0 | local function find_archetype_without(
753 | 0 | world: ecs_world_t,
754 | 0 | node: ecs_archetype_t,
755 | 33 | id: i53
756 | 33 | ): ecs_archetype_t
757 | 33 | local id_types = node.types
758 | 0 | local at = table.find(id_types, id)
759 | 0 | if at == nil then
760 | 0 | return node
761 | N/A | end>
762 | N/A | >
763 | 0 | local dst = table.clone(id_types)
764 | 33 | table.remove(dst, at)
765 | N/A | >
766 | 0 | return archetype_ensure(world, dst)
767 | N/A | end>
768 | N/A | >
769 | 0 | local function archetype_init_edge(
770 | 0 | archetype: ecs_archetype_t,
771 | 0 | edge: ecs_graph_edge_t,
772 | 0 | id: i53,
773 | 582 | to: ecs_archetype_t
774 | 582 | )
775 | 582 | edge.from = archetype
776 | 0 | edge.to = to
777 | 0 | edge.id = id
778 | N/A | end>
779 | N/A | >
780 | 0 | local function archetype_ensure_edge(
781 | 0 | world: ecs_world_t,
782 | 0 | edges: ecs_graph_edges_t,
783 | 22971 | id: i53
784 | 22971 | ): ecs_graph_edge_t
785 | 584 | local edge = edges[id]
786 | 584 | if not edge then
787 | 0 | edge = {} :: ecs_graph_edge_t
788 | 0 | edges[id] = edge
789 | N/A | end>
790 | N/A | >
791 | 0 | return edge
792 | N/A | end>
793 | N/A | >
794 | 549 | local function init_edge_for_add(world, archetype: ecs_archetype_t, edge: ecs_graph_edge_t, id, to: ecs_archetype_t)
795 | 549 | archetype_init_edge(archetype, edge, id, to)
796 | 545 | archetype_ensure_edge(world, archetype.add, id)
797 | 545 | if archetype ~= to then
798 | 0 | local to_refs = to.refs
799 | 545 | local next_edge = to_refs.next
800 | N/A | >
801 | 545 | to_refs.next = edge
802 | 0 | edge.prev = to_refs
803 | 545 | edge.next = next_edge
804 | N/A | >
805 | 0 | if next_edge then
806 | 0 | next_edge.prev = edge
807 | N/A | end>
808 | N/A | end>
809 | N/A | end>
810 | N/A | >
811 | 0 | local function init_edge_for_remove(
812 | 0 | world: ecs_world_t,
813 | 0 | archetype: ecs_archetype_t,
814 | 0 | edge: ecs_graph_edge_t,
815 | 0 | id: number,
816 | 33 | to: ecs_archetype_t
817 | 33 | )
818 | 33 | archetype_init_edge(archetype, edge, id, to)
819 | 33 | archetype_ensure_edge(world, archetype.remove, id)
820 | 33 | if archetype ~= to then
821 | 0 | local to_refs = to.refs
822 | 33 | local prev_edge = to_refs.prev
823 | N/A | >
824 | 33 | to_refs.prev = edge
825 | 0 | edge.next = to_refs
826 | 33 | edge.prev = prev_edge
827 | N/A | >
828 | 0 | if prev_edge then
829 | 0 | prev_edge.next = edge
830 | N/A | end>
831 | N/A | end>
832 | N/A | end>
833 | N/A | >
834 | 0 | local function create_edge_for_add(
835 | 0 | world: ecs_world_t,
836 | 0 | node: ecs_archetype_t,
837 | 0 | edge: ecs_graph_edge_t,
838 | 551 | id: i53
839 | 549 | ): ecs_archetype_t
840 | 549 | local to = find_archetype_with(world, node, id)
841 | 0 | init_edge_for_add(world, node, edge, id, to)
842 | 0 | return to
843 | N/A | end>
844 | N/A | >
845 | 0 | local function create_edge_for_remove(
846 | 0 | world: ecs_world_t,
847 | 0 | node: ecs_archetype_t,
848 | 0 | edge: ecs_graph_edge_t,
849 | 33 | id: i53
850 | 33 | ): ecs_archetype_t
851 | 33 | local to = find_archetype_without(world, node, id)
852 | 0 | init_edge_for_remove(world, node, edge, id, to)
853 | 0 | return to
854 | N/A | end>
855 | N/A | >
856 | 0 | local function archetype_traverse_add(
857 | 0 | world: ecs_world_t,
858 | 0 | id: i53,
859 | 22092 | from: ecs_archetype_t
860 | 22092 | ): ecs_archetype_t
861 | 0 | from = from or world.ROOT_ARCHETYPE
862 | 22092 | local edge = archetype_ensure_edge(world, from.add, id)
863 | N/A | >
864 | 551 | local to = edge.to
865 | 0 | if not to then
866 | 0 | to = create_edge_for_add(world, from, edge, id)
867 | N/A | end>
868 | N/A | >
869 | 0 | return to :: ecs_archetype_t
870 | N/A | end>
871 | N/A | >
872 | 0 | local function archetype_traverse_remove(
873 | 0 | world: ecs_world_t,
874 | 0 | id: i53,
875 | 297 | from: ecs_archetype_t
876 | 0 | ): ecs_archetype_t
877 | 297 | from = from or world.ROOT_ARCHETYPE
878 | N/A | >
879 | 297 | local edge = archetype_ensure_edge(world, from.remove, id)
880 | N/A | >
881 | 33 | local to = edge.to
882 | 0 | if not to then
883 | 0 | to = create_edge_for_remove(world, from, edge, id)
884 | N/A | end>
885 | N/A | >
886 | 0 | return to :: ecs_archetype_t
887 | N/A | end>
888 | N/A | >
889 | 0 | local function world_add(
890 | 0 | world: ecs_world_t,
891 | 0 | entity: i53,
892 | 19316 | id: i53
893 | 19316 | ): ()
894 | 19316 | local entity_index = world.entity_index
895 | 0 | local record = entity_index_try_get_fast(entity_index, entity)
896 | 0 | if not record then
897 | 0 | return
898 | N/A | end>
899 | N/A | >
900 | 19315 | local from = record.archetype
901 | 3 | local to = archetype_traverse_add(world, id, from)
902 | 0 | if from == to then
903 | 19312 | return
904 | N/A | end>
905 | 0 | if from then
906 | 19170 | entity_move(entity_index, entity, record, to)
907 | 19170 | else
908 | 0 | if #to.types > 0 then
909 | 0 | new_entity(entity, record, to)
910 | N/A | end>
911 | N/A | end>
912 | N/A | >
913 | 0 | local idr = world.component_index[id]
914 | 19312 | local on_add = idr.hooks.on_add
915 | N/A | >
916 | 0 | if on_add then
917 | 0 | on_add(entity)
918 | N/A | end>
919 | N/A | end>
920 | N/A | >
921 | 2775 | local function world_set(world: ecs_world_t, entity: i53, id: i53, data: unknown): ()
922 | 2775 | local entity_index = world.entity_index
923 | 0 | local record = entity_index_try_get_fast(entity_index, entity)
924 | 0 | if not record then
925 | 0 | return
926 | N/A | end>
927 | N/A | >
928 | 2774 | local from: ecs_archetype_t = record.archetype
929 | 2774 | local to: ecs_archetype_t = archetype_traverse_add(world, id, from)
930 | 0 | local idr = world.component_index[id]
931 | 2774 | local idr_hooks = idr.hooks
932 | N/A | >
933 | 0 | if from == to then
934 | N/A | -- If the archetypes are the same it can avoid moving the entity>
935 | N/A | -- and just set the data directly.>
936 | 2 | local tr = to.records[id]
937 | 2 | local column = from.columns[tr]
938 | 2 | column[record.row] = data
939 | 0 | local on_set = idr_hooks.on_set
940 | 0 | if on_set then
941 | 0 | on_set(entity, data)
942 | N/A | end>
943 | N/A | >
944 | 0 | return
945 | N/A | end>
946 | N/A | >
947 | 1384 | if from then
948 | N/A | -- If there was a previous archetype, then the entity needs to move the archetype>
949 | 1388 | entity_move(entity_index, entity, record, to)
950 | 0 | else
951 | 1388 | if #to.types > 0 then
952 | N/A | -- When there is no previous archetype it should create the archetype>
953 | 0 | new_entity(entity, record, to)
954 | N/A | end>
955 | N/A | end>
956 | N/A | >
957 | 0 | local tr = to.records[id]
958 | 2772 | local column = to.columns[tr]
959 | N/A | >
960 | 2765 | column[record.row] = data
961 | N/A | >
962 | 0 | local on_add = idr_hooks.on_add
963 | 0 | if on_add then
964 | 0 | on_add(entity)
965 | N/A | end>
966 | N/A | >
967 | 1 | local on_set = idr_hooks.on_set
968 | 0 | if on_set then
969 | 0 | on_set(entity, data)
970 | N/A | end>
971 | N/A | end>
972 | N/A | >
973 | 121 | local function world_component(world: World): i53
974 | 0 | local id = (world.max_component_id :: number) + 1
975 | 0 | if id > HI_COMPONENT_ID then
976 | N/A | -- IDs are partitioned into ranges because component IDs are not nominal,>
977 | N/A | -- so it needs to error when IDs intersect into the entity range.>
978 | 121 | error("Too many components, consider using world:entity() instead to create components.")
979 | N/A | end>
980 | 121 | world.max_component_id = id
981 | N/A | >
982 | 0 | return id
983 | N/A | end>
984 | N/A | >
985 | 298 | local function world_remove(world: ecs_world_t, entity: i53, id: i53)
986 | 298 | local entity_index = world.entity_index
987 | 0 | local record = entity_index_try_get_fast(entity_index, entity)
988 | 0 | if not record then
989 | 298 | return
990 | N/A | end>
991 | 298 | local from = record.archetype
992 | N/A | >
993 | 0 | if not from then
994 | 0 | return
995 | N/A | end>
996 | N/A | >
997 | 296 | if from.records[id] then
998 | 296 | local idr = world.component_index[id]
999 | 3 | local on_remove = idr.hooks.on_remove
1000 | 0 | if on_remove then
1001 | 0 | on_remove(entity)
1002 | N/A | end>
1003 | N/A | >
1004 | 296 | local to = archetype_traverse_remove(world, id, record.archetype)
1005 | N/A | >
1006 | 0 | entity_move(entity_index, entity, record, to)
1007 | N/A | end>
1008 | N/A | end>
1009 | N/A | >
1010 | 153 | local function archetype_fast_delete_last(columns: { Column }, column_count: number, types: { i53 }, entity: i53)
1011 | 135 | for i, column in columns do
1012 | 0 | if column ~= NULL_ARRAY then
1013 | 0 | column[column_count] = nil
1014 | N/A | end>
1015 | N/A | end>
1016 | N/A | end>
1017 | N/A | >
1018 | 109 | local function archetype_fast_delete(columns: { Column }, column_count: number, row, types, entity)
1019 | 103 | for i, column in columns do
1020 | 103 | if column ~= NULL_ARRAY then
1021 | 0 | column[row] = column[column_count]
1022 | 0 | column[column_count] = nil
1023 | N/A | end>
1024 | N/A | end>
1025 | N/A | end>
1026 | N/A | >
1027 | 134 | local function archetype_delete(world: ecs_world_t, archetype: ecs_archetype_t, row: number)
1028 | 134 | local entity_index = world.entity_index
1029 | 134 | local component_index = world.component_index
1030 | 134 | local columns = archetype.columns
1031 | 134 | local id_types = archetype.types
1032 | 134 | local entities = archetype.entities
1033 | 134 | local column_count = #entities
1034 | 0 | local last = #entities
1035 | 134 | local move = entities[last]
1036 | N/A | -- We assume first that the entity is the last in the archetype>
1037 | 134 | local delete = move
1038 | N/A | >
1039 | 58 | if row ~= last then
1040 | 58 | local record_to_move = entity_index_try_get_any(entity_index, move)
1041 | 0 | if record_to_move then
1042 | 0 | record_to_move.row = row
1043 | N/A | end>
1044 | N/A | >
1045 | 0 | delete = entities[row]
1046 | 0 | entities[row] = move
1047 | N/A | end>
1048 | N/A | >
1049 | 262 | for _, id in id_types do
1050 | 262 | local idr = component_index[id]
1051 | 3 | local on_remove = idr.hooks.on_remove
1052 | 0 | if on_remove then
1053 | 0 | on_remove(delete)
1054 | N/A | end>
1055 | N/A | end>
1056 | N/A | >
1057 | 134 | entities[last] = nil :: any
1058 | N/A | >
1059 | 0 | if row == last then
1060 | 58 | archetype_fast_delete_last(columns, column_count, id_types, delete)
1061 | 0 | else
1062 | 0 | archetype_fast_delete(columns, column_count, row, id_types, delete)
1063 | N/A | end>
1064 | N/A | end>
1065 | N/A | >
1066 | 6 | local function world_clear(world: ecs_world_t, entity: i53)
1067 | 6 | local entity_index = world.entity_index
1068 | 6 | local component_index = world.component_index
1069 | 6 | local archetypes = world.archetypes
1070 | 6 | local tgt = ECS_PAIR(EcsWildcard, entity)
1071 | 6 | local idr_t = component_index[tgt]
1072 | 6 | local idr = component_index[entity]
1073 | 0 | local rel = ECS_PAIR(entity, EcsWildcard)
1074 | 6 | local idr_r = component_index[rel]
1075 | N/A | >
1076 | 4 | if idr then
1077 | 4 | local count = 0
1078 | 11 | local queue = {}
1079 | 11 | for archetype_id in idr.cache do
1080 | 11 | local idr_archetype = archetypes[archetype_id]
1081 | 11 | local entities = idr_archetype.entities
1082 | 11 | local n = #entities
1083 | 0 | count += n
1084 | 4 | table.move(entities, 1, n, #queue + 1, queue)
1085 | N/A | end>
1086 | 0 | for _, e in queue do
1087 | 0 | world_remove(world, e, entity)
1088 | N/A | end>
1089 | N/A | end>
1090 | N/A | >
1091 | 0 | if idr_t then
1092 | 0 | local queue
1093 | 0 | local ids
1094 | N/A | >
1095 | 0 | local count = 0
1096 | 0 | local archetype_ids = idr_t.cache
1097 | 0 | for archetype_id in archetype_ids do
1098 | 0 | local idr_t_archetype = archetypes[archetype_id]
1099 | 0 | local idr_t_types = idr_t_archetype.types
1100 | 0 | local entities = idr_t_archetype.entities
1101 | 0 | local removal_queued = false
1102 | N/A | >
1103 | 0 | for _, id in idr_t_types do
1104 | 0 | if not ECS_IS_PAIR(id) then
1105 | 0 | continue
1106 | N/A | end>
1107 | 0 | local object = entity_index_get_alive(
1108 | 0 | entity_index, ECS_PAIR_SECOND(id))
1109 | 0 | if object ~= entity then
1110 | 0 | continue
1111 | N/A | end>
1112 | 0 | if not ids then
1113 | 0 | ids = {}
1114 | N/A | end>
1115 | 0 | ids[id] = true
1116 | 0 | removal_queued = true
1117 | N/A | end>
1118 | N/A | >
1119 | 0 | if not removal_queued then
1120 | 0 | continue
1121 | N/A | end>
1122 | N/A | >
1123 | 0 | if not queue then
1124 | 0 | queue = {}
1125 | N/A | end>
1126 | N/A | >
1127 | 0 | local n = #entities
1128 | 0 | table.move(entities, 1, n, count + 1, queue)
1129 | 0 | count += n
1130 | N/A | end>
1131 | N/A | >
1132 | 0 | for id in ids do
1133 | 0 | for _, child in queue do
1134 | 0 | world_remove(world, child, id)
1135 | N/A | end>
1136 | N/A | end>
1137 | N/A | end>
1138 | N/A | >
1139 | 1 | if idr_r then
1140 | 1 | local count = 0
1141 | 1 | local archetype_ids = idr_r.cache
1142 | 1 | local ids = {}
1143 | 2 | local queue = {}
1144 | 2 | for archetype_id in archetype_ids do
1145 | 2 | local idr_r_archetype = archetypes[archetype_id]
1146 | 2 | local entities = idr_r_archetype.entities
1147 | 2 | local tr = idr_r_archetype.records[rel]
1148 | 2 | local tr_count = idr_r_archetype.counts[rel]
1149 | 2 | local types = idr_r_archetype.types
1150 | 0 | for i = tr, tr + tr_count - 1 do
1151 | 2 | ids[types[i]] = true
1152 | N/A | end>
1153 | 2 | local n = #entities
1154 | 0 | table.move(entities, 1, n, count + 1, queue)
1155 | 0 | count += n
1156 | N/A | end>
1157 | N/A | >
1158 | 3 | for _, e in queue do
1159 | 0 | for id in ids do
1160 | 0 | world_remove(world, e, id)
1161 | N/A | end>
1162 | N/A | end>
1163 | N/A | end>
1164 | N/A | end>
1165 | N/A | >
1166 | 50 | local function archetype_disconnect_edge(edge: ecs_graph_edge_t)
1167 | 50 | local edge_next = edge.next
1168 | 19 | local edge_prev = edge.prev
1169 | 0 | if edge_next then
1170 | 50 | edge_next.prev = edge_prev
1171 | N/A | end>
1172 | 0 | if edge_prev then
1173 | 0 | edge_prev.next = edge_next
1174 | N/A | end>
1175 | N/A | end>
1176 | N/A | >
1177 | 23 | local function archetype_remove_edge(edges: ecs_graph_edges_t, id: i53, edge: ecs_graph_edge_t)
1178 | 0 | archetype_disconnect_edge(edge)
1179 | 0 | edges[id] = nil :: any
1180 | N/A | end>
1181 | N/A | >
1182 | 36 | local function archetype_clear_edges(archetype: ecs_archetype_t)
1183 | 36 | local add: ecs_graph_edges_t = archetype.add
1184 | 36 | local remove: ecs_graph_edges_t = archetype.remove
1185 | 9 | local node_refs = archetype.refs
1186 | 9 | for id, edge in add do
1187 | 0 | archetype_disconnect_edge(edge)
1188 | 36 | add[id] = nil :: any
1189 | N/A | end>
1190 | 18 | for id, edge in remove do
1191 | 0 | archetype_disconnect_edge(edge)
1192 | 0 | remove[id] = nil :: any
1193 | N/A | end>
1194 | N/A | >
1195 | 22 | local cur = node_refs.next
1196 | 22 | while cur do
1197 | 22 | local edge = cur :: ecs_graph_edge_t
1198 | 22 | local next_edge = edge.next
1199 | 0 | archetype_remove_edge(edge.from.add, edge.id, edge)
1200 | 0 | cur = next_edge
1201 | N/A | end>
1202 | N/A | >
1203 | 1 | cur = node_refs.prev
1204 | 1 | while cur do
1205 | 1 | local edge: ecs_graph_edge_t = cur
1206 | 1 | local next_edge = edge.prev
1207 | 0 | archetype_remove_edge(edge.from.remove, edge.id, edge)
1208 | 0 | cur = next_edge
1209 | N/A | end>
1210 | N/A | >
1211 | 0 | node_refs.next = nil
1212 | 0 | node_refs.prev = nil
1213 | N/A | end>
1214 | N/A | >
1215 | 1 | local function archetype_destroy(world: ecs_world_t, archetype: ecs_archetype_t)
1216 | 0 | if archetype == world.ROOT_ARCHETYPE then
1217 | 0 | return
1218 | N/A | end>
1219 | N/A | >
1220 | 36 | local component_index = world.component_index
1221 | 36 | archetype_clear_edges(archetype)
1222 | 36 | local archetype_id = archetype.id
1223 | 36 | world.archetypes[archetype_id] = nil :: any
1224 | 0 | world.archetype_index[archetype.type] = nil :: any
1225 | 36 | local records = archetype.records
1226 | N/A | >
1227 | 113 | for id in records do
1228 | 0 | local observer_list = find_observers(world, EcsOnArchetypeDelete, id)
1229 | 0 | if not observer_list then
1230 | 2 | continue
1231 | N/A | end>
1232 | 1 | for _, observer in observer_list do
1233 | 0 | if query_match(observer.query, archetype) then
1234 | 0 | observer.callback(archetype)
1235 | N/A | end>
1236 | N/A | end>
1237 | N/A | end>
1238 | N/A | >
1239 | 113 | for id in records do
1240 | 113 | local idr = component_index[id]
1241 | 113 | idr.cache[archetype_id] = nil :: any
1242 | 113 | idr.counts[archetype_id] = nil
1243 | 113 | idr.size -= 1
1244 | 0 | records[id] = nil :: any
1245 | 0 | if idr.size == 0 then
1246 | 0 | component_index[id] = nil :: any
1247 | N/A | end>
1248 | N/A | end>
1249 | N/A | end>
1250 | N/A | >
1251 | 0 | local function world_cleanup(world: ecs_world_t)
1252 | 1 | local archetypes = world.archetypes
1253 | N/A | >
1254 | 4 | for _, archetype in archetypes do
1255 | 0 | if #archetype.entities == 0 then
1256 | 0 | archetype_destroy(world, archetype)
1257 | N/A | end>
1258 | N/A | end>
1259 | N/A | >
1260 | 0 | local new_archetypes = table.create(#archetypes) :: { ecs_archetype_t }
1261 | 1 | local new_archetype_map = {}
1262 | N/A | >
1263 | 6 | for index, archetype in archetypes do
1264 | 0 | new_archetypes[index] = archetype
1265 | 0 | new_archetype_map[archetype.type] = archetype
1266 | N/A | end>
1267 | N/A | >
1268 | 0 | world.archetypes = new_archetypes
1269 | 0 | world.archetype_index = new_archetype_map
1270 | N/A | end>
1271 | N/A | >
1272 | 65684 | local function world_delete(world: ecs_world_t, entity: i53)
1273 | 65684 | local entity_index = world.entity_index
1274 | 1 | local record = entity_index_try_get(entity_index, entity)
1275 | 0 | if not record then
1276 | 0 | return
1277 | N/A | end>
1278 | N/A | >
1279 | 0 | local archetype = record.archetype
1280 | 65683 | local row = record.row
1281 | N/A | >
1282 | 0 | if archetype then
1283 | N/A | -- In the future should have a destruct mode for>
1284 | N/A | -- deleting archetypes themselves. Maybe requires recycling>
1285 | 0 | archetype_delete(world, archetype, row)
1286 | N/A | end>
1287 | N/A | >
1288 | 65683 | local delete = entity
1289 | 65683 | local component_index = world.component_index
1290 | 65683 | local archetypes = world.archetypes
1291 | 0 | local tgt = ECS_PAIR(EcsWildcard, delete)
1292 | 65683 | local rel = ECS_PAIR(delete, EcsWildcard)
1293 | N/A | >
1294 | 65683 | local idr_t = component_index[tgt]
1295 | 0 | local idr = component_index[delete]
1296 | 65683 | local idr_r = component_index[rel]
1297 | N/A | >
1298 | 8 | if idr then
1299 | 1 | local flags = idr.flags
1300 | 1 | if bit32.band(flags, ECS_ID_DELETE) ~= 0 then
1301 | 0 | for archetype_id in idr.cache do
1302 | 1 | local idr_archetype = archetypes[archetype_id]
1303 | N/A | >
1304 | 1 | local entities = idr_archetype.entities
1305 | 2 | local n = #entities
1306 | 0 | for i = n, 1, -1 do
1307 | 0 | world_delete(world, entities[i])
1308 | N/A | end>
1309 | N/A | >
1310 | 0 | archetype_destroy(world, idr_archetype)
1311 | N/A | end>
1312 | 12 | else
1313 | 12 | for archetype_id in idr.cache do
1314 | 12 | local idr_archetype = archetypes[archetype_id]
1315 | 12 | local entities = idr_archetype.entities
1316 | 10 | local n = #entities
1317 | 0 | for i = n, 1, -1 do
1318 | 0 | world_remove(world, entities[i], delete)
1319 | N/A | end>
1320 | N/A | >
1321 | 0 | archetype_destroy(world, idr_archetype)
1322 | N/A | end>
1323 | N/A | end>
1324 | N/A | end>
1325 | N/A | >
1326 | 13 | if idr_t then
1327 | 0 | local children
1328 | 13 | local ids
1329 | N/A | >
1330 | 13 | local count = 0
1331 | 18 | local archetype_ids = idr_t.cache
1332 | 18 | for archetype_id in archetype_ids do
1333 | 18 | local idr_t_archetype = archetypes[archetype_id]
1334 | 18 | local idr_t_types = idr_t_archetype.types
1335 | 0 | local entities = idr_t_archetype.entities
1336 | 18 | local removal_queued = false
1337 | N/A | >
1338 | 0 | for _, id in idr_t_types do
1339 | 0 | if not ECS_IS_PAIR(id) then
1340 | 24 | continue
1341 | N/A | end>
1342 | 24 | local object = entity_index_get_alive(
1343 | 0 | entity_index, ECS_PAIR_SECOND(id))
1344 | 0 | if object ~= delete then
1345 | 20 | continue
1346 | N/A | end>
1347 | 20 | local id_record = component_index[id]
1348 | 20 | local flags = id_record.flags
1349 | 8 | local flags_delete_mask: number = bit32.band(flags, ECS_ID_DELETE)
1350 | 15 | if flags_delete_mask ~= 0 then
1351 | 15 | for i = #entities, 1, -1 do
1352 | 0 | local child = entities[i]
1353 | 8 | world_delete(world, child)
1354 | N/A | end>
1355 | 12 | break
1356 | 6 | else
1357 | 0 | if not ids then
1358 | 12 | ids = {}
1359 | N/A | end>
1360 | 0 | ids[id] = true
1361 | 0 | removal_queued = true
1362 | N/A | end>
1363 | N/A | end>
1364 | N/A | >
1365 | 0 | if not removal_queued then
1366 | 10 | continue
1367 | N/A | end>
1368 | 0 | if not children then
1369 | 10 | children = {}
1370 | N/A | end>
1371 | 10 | local n = #entities
1372 | 0 | table.move(entities, 1, n, count + 1, children)
1373 | 0 | count += n
1374 | N/A | end>
1375 | N/A | >
1376 | 17 | if ids then
1377 | 19 | for _, child in children do
1378 | 0 | for id in ids do
1379 | 0 | world_remove(world, child, id)
1380 | N/A | end>
1381 | N/A | end>
1382 | N/A | end>
1383 | N/A | >
1384 | 0 | for archetype_id in archetype_ids do
1385 | 0 | archetype_destroy(world, archetypes[archetype_id])
1386 | N/A | end>
1387 | N/A | end>
1388 | N/A | >
1389 | 0 | if idr_r then
1390 | 0 | local archetype_ids = idr_r.cache
1391 | 0 | local flags = idr_r.flags
1392 | 0 | if bit32.band(flags, ECS_ID_DELETE) ~= 0 then
1393 | 0 | for archetype_id in archetype_ids do
1394 | 0 | local idr_r_archetype = archetypes[archetype_id]
1395 | 0 | local entities = idr_r_archetype.entities
1396 | 0 | local n = #entities
1397 | 0 | for i = n, 1, -1 do
1398 | 0 | world_delete(world, entities[i])
1399 | N/A | end>
1400 | 0 | archetype_destroy(world, idr_r_archetype)
1401 | N/A | end>
1402 | 0 | else
1403 | 0 | local children = {}
1404 | 0 | local count = 0
1405 | 0 | local ids = {}
1406 | 0 | for archetype_id in archetype_ids do
1407 | 0 | local idr_r_archetype = archetypes[archetype_id]
1408 | 0 | local entities = idr_r_archetype.entities
1409 | 0 | local tr = idr_r_archetype.records[rel]
1410 | 0 | local tr_count = idr_r_archetype.counts[rel]
1411 | 0 | local types = idr_r_archetype.types
1412 | 0 | for i = tr, tr_count - 1 do
1413 | 0 | ids[types[tr]] = true
1414 | N/A | end>
1415 | 0 | local n = #entities
1416 | 0 | table.move(entities, 1, n, count + 1, children)
1417 | 0 | count += n
1418 | N/A | end>
1419 | N/A | >
1420 | 0 | for _, child in children do
1421 | 0 | for id in ids do
1422 | 0 | world_remove(world, child, id)
1423 | N/A | end>
1424 | N/A | end>
1425 | N/A | >
1426 | 0 | for archetype_id in archetype_ids do
1427 | 0 | archetype_destroy(world, archetypes[archetype_id])
1428 | N/A | end>
1429 | N/A | end>
1430 | N/A | end>
1431 | N/A | >
1432 | 65683 | local dense_array = entity_index.dense_array
1433 | 65683 | local index_of_deleted_entity = record.dense
1434 | 0 | local index_of_last_alive_entity = entity_index.alive_count
1435 | 65683 | entity_index.alive_count = index_of_last_alive_entity - 1
1436 | N/A | >
1437 | 65683 | local last_alive_entity = dense_array[index_of_last_alive_entity]
1438 | 65683 | local r_swap = entity_index_try_get_any(entity_index, last_alive_entity) :: ecs_record_t
1439 | 65683 | r_swap.dense = index_of_deleted_entity
1440 | 65683 | record.archetype = nil :: any
1441 | 0 | record.row = nil :: any
1442 | 65683 | record.dense = index_of_last_alive_entity
1443 | N/A | >
1444 | 0 | dense_array[index_of_deleted_entity] = last_alive_entity
1445 | 0 | dense_array[index_of_last_alive_entity] = ECS_GENERATION_INC(entity)
1446 | N/A | end>
1447 | N/A | >
1448 | 0 | local function world_contains(world: ecs_world_t, entity): boolean
1449 | 0 | return entity_index_is_alive(world.entity_index, entity)
1450 | N/A | end>
1451 | N/A | >
1452 | 0 | local function NOOP() end
1453 | N/A | >
1454 | 0 | export type QueryInner = {
1455 | 0 | compatible_archetypes: { Archetype },
1456 | 0 | ids: { i53 },
1457 | 0 | filter_with: { i53 },
1458 | 0 | filter_without: { i53 },
1459 | 0 | next: () -> (number, ...any),
1460 | 0 | world: World,
1461 | 1 | }
1462 | N/A | >
1463 | 0 | local function query_iter_init(query: ecs_query_data_t): () -> (number, ...any)
1464 | 26 | local world_query_iter_next
1465 | N/A | >
1466 | 26 | local compatible_archetypes = query.compatible_archetypes
1467 | 26 | local lastArchetype = 1
1468 | 4 | local archetype = compatible_archetypes[1]
1469 | 0 | if not archetype then
1470 | 22 | return NOOP :: () -> (number, ...any)
1471 | N/A | end>
1472 | 22 | local columns = archetype.columns
1473 | 22 | local entities = archetype.entities
1474 | 0 | local i = #entities
1475 | 22 | local records = archetype.records
1476 | N/A | >
1477 | 22 | local ids = query.ids
1478 | 22 | local A, B, C, D, E, F, G, H, I = unpack(ids)
1479 | 0 | local a: Column, b: Column, c: Column, d: Column
1480 | 22 | local e: Column, f: Column, g: Column, h: Column
1481 | N/A | >
1482 | 5 | if not B then
1483 | 3 | a = columns[records[A]]
1484 | 3 | elseif not C then
1485 | 2 | a = columns[records[A]]
1486 | 0 | b = columns[records[B]]
1487 | 0 | elseif not D then
1488 | 0 | a = columns[records[A]]
1489 | 2 | b = columns[records[B]]
1490 | 1 | c = columns[records[C]]
1491 | 1 | elseif not E then
1492 | 1 | a = columns[records[A]]
1493 | 1 | b = columns[records[B]]
1494 | 1 | c = columns[records[C]]
1495 | 0 | d = columns[records[D]]
1496 | 0 | elseif not F then
1497 | 0 | a = columns[records[A]]
1498 | 0 | b = columns[records[B]]
1499 | 0 | c = columns[records[C]]
1500 | 1 | d = columns[records[D]]
1501 | 0 | e = columns[records[E]]
1502 | 0 | elseif not G then
1503 | 0 | a = columns[records[A]]
1504 | 0 | b = columns[records[B]]
1505 | 0 | c = columns[records[C]]
1506 | 0 | d = columns[records[D]]
1507 | 1 | e = columns[records[E]]
1508 | 0 | f = columns[records[F]]
1509 | 0 | elseif not H then
1510 | 0 | a = columns[records[A]]
1511 | 0 | b = columns[records[B]]
1512 | 0 | c = columns[records[C]]
1513 | 0 | d = columns[records[D]]
1514 | 0 | e = columns[records[E]]
1515 | 1 | f = columns[records[F]]
1516 | 0 | g = columns[records[G]]
1517 | 0 | elseif not I then
1518 | 0 | a = columns[records[A]]
1519 | 0 | b = columns[records[B]]
1520 | 0 | c = columns[records[C]]
1521 | 0 | d = columns[records[D]]
1522 | 0 | e = columns[records[E]]
1523 | 0 | f = columns[records[F]]
1524 | 0 | g = columns[records[G]]
1525 | 0 | h = columns[records[H]]
1526 | N/A | end>
1527 | N/A | >
1528 | 556 | if not B then
1529 | 556 | function world_query_iter_next(): any
1530 | 26 | local entity = entities[i]
1531 | 26 | while entity == nil do
1532 | 26 | lastArchetype += 1
1533 | 18 | archetype = compatible_archetypes[lastArchetype]
1534 | 0 | if not archetype then
1535 | 0 | return nil
1536 | N/A | end>
1537 | N/A | >
1538 | 8 | entities = archetype.entities
1539 | 0 | i = #entities
1540 | 0 | if i == 0 then
1541 | 8 | continue
1542 | N/A | end>
1543 | 8 | entity = entities[i]
1544 | 8 | columns = archetype.columns
1545 | 0 | records = archetype.records
1546 | 0 | a = columns[records[A]]
1547 | N/A | end>
1548 | N/A | >
1549 | 0 | local row = i
1550 | 538 | i -= 1
1551 | N/A | >
1552 | 5 | return entity, a[row]
1553 | N/A | end>
1554 | 7 | elseif not C then
1555 | 7 | function world_query_iter_next(): any
1556 | 3 | local entity = entities[i]
1557 | 3 | while entity == nil do
1558 | 3 | lastArchetype += 1
1559 | 3 | archetype = compatible_archetypes[lastArchetype]
1560 | 0 | if not archetype then
1561 | 0 | return nil
1562 | N/A | end>
1563 | N/A | >
1564 | 0 | entities = archetype.entities
1565 | 0 | i = #entities
1566 | 0 | if i == 0 then
1567 | 0 | continue
1568 | N/A | end>
1569 | 0 | entity = entities[i]
1570 | 0 | columns = archetype.columns
1571 | 0 | records = archetype.records
1572 | 0 | a = columns[records[A]]
1573 | 0 | b = columns[records[B]]
1574 | N/A | end>
1575 | N/A | >
1576 | 0 | local row = i
1577 | 4 | i -= 1
1578 | N/A | >
1579 | 2 | return entity, a[row], b[row]
1580 | N/A | end>
1581 | 0 | elseif not D then
1582 | 0 | function world_query_iter_next(): any
1583 | 0 | local entity = entities[i]
1584 | 0 | while entity == nil do
1585 | 0 | lastArchetype += 1
1586 | 0 | archetype = compatible_archetypes[lastArchetype]
1587 | 0 | if not archetype then
1588 | 0 | return nil
1589 | N/A | end>
1590 | N/A | >
1591 | 0 | entities = archetype.entities
1592 | 0 | i = #entities
1593 | 0 | if i == 0 then
1594 | 0 | continue
1595 | N/A | end>
1596 | 0 | entity = entities[i]
1597 | 0 | columns = archetype.columns
1598 | 0 | records = archetype.records
1599 | 0 | a = columns[records[A]]
1600 | 0 | b = columns[records[B]]
1601 | 0 | c = columns[records[C]]
1602 | N/A | end>
1603 | N/A | >
1604 | 0 | local row = i
1605 | 0 | i -= 1
1606 | N/A | >
1607 | 2 | return entity, a[row], b[row], c[row]
1608 | N/A | end>
1609 | 2 | elseif not E then
1610 | 2 | function world_query_iter_next(): any
1611 | 1 | local entity = entities[i]
1612 | 1 | while entity == nil do
1613 | 1 | lastArchetype += 1
1614 | 1 | archetype = compatible_archetypes[lastArchetype]
1615 | 0 | if not archetype then
1616 | 0 | return nil
1617 | N/A | end>
1618 | N/A | >
1619 | 0 | entities = archetype.entities
1620 | 0 | i = #entities
1621 | 0 | if i == 0 then
1622 | 0 | continue
1623 | N/A | end>
1624 | 0 | entity = entities[i]
1625 | 0 | columns = archetype.columns
1626 | 0 | records = archetype.records
1627 | 0 | a = columns[records[A]]
1628 | 0 | b = columns[records[B]]
1629 | 0 | c = columns[records[C]]
1630 | 0 | d = columns[records[D]]
1631 | N/A | end>
1632 | N/A | >
1633 | 0 | local row = i
1634 | 1 | i -= 1
1635 | N/A | >
1636 | 1 | return entity, a[row], b[row], c[row], d[row]
1637 | N/A | end>
1638 | 0 | elseif not F then
1639 | 0 | function world_query_iter_next(): any
1640 | 0 | local entity = entities[i]
1641 | 0 | while entity == nil do
1642 | 0 | lastArchetype += 1
1643 | 0 | archetype = compatible_archetypes[lastArchetype]
1644 | 0 | if not archetype then
1645 | 0 | return nil
1646 | N/A | end>
1647 | N/A | >
1648 | 0 | entities = archetype.entities
1649 | 0 | i = #entities
1650 | 0 | if i == 0 then
1651 | 0 | continue
1652 | N/A | end>
1653 | 0 | entity = entities[i]
1654 | 0 | columns = archetype.columns
1655 | 0 | records = archetype.records
1656 | 0 | a = columns[records[A]]
1657 | 0 | b = columns[records[B]]
1658 | 0 | c = columns[records[C]]
1659 | 0 | d = columns[records[D]]
1660 | 0 | e = columns[records[E]]
1661 | N/A | end>
1662 | N/A | >
1663 | 0 | local row = i
1664 | 0 | i -= 1
1665 | N/A | >
1666 | 1 | return entity, a[row], b[row], c[row], d[row], e[row]
1667 | N/A | end>
1668 | 0 | elseif not G then
1669 | 0 | function world_query_iter_next(): any
1670 | 0 | local entity = entities[i]
1671 | 0 | while entity == nil do
1672 | 0 | lastArchetype += 1
1673 | 0 | archetype = compatible_archetypes[lastArchetype]
1674 | 0 | if not archetype then
1675 | 0 | return nil
1676 | N/A | end>
1677 | N/A | >
1678 | 0 | entities = archetype.entities
1679 | 0 | i = #entities
1680 | 0 | if i == 0 then
1681 | 0 | continue
1682 | N/A | end>
1683 | 0 | entity = entities[i]
1684 | 0 | columns = archetype.columns
1685 | 0 | records = archetype.records
1686 | 0 | a = columns[records[A]]
1687 | 0 | b = columns[records[B]]
1688 | 0 | c = columns[records[C]]
1689 | 0 | d = columns[records[D]]
1690 | 0 | e = columns[records[E]]
1691 | 0 | f = columns[records[F]]
1692 | N/A | end>
1693 | N/A | >
1694 | 0 | local row = i
1695 | 0 | i -= 1
1696 | N/A | >
1697 | 1 | return entity, a[row], b[row], c[row], d[row], e[row], f[row]
1698 | N/A | end>
1699 | 0 | elseif not H then
1700 | 0 | function world_query_iter_next(): any
1701 | 0 | local entity = entities[i]
1702 | 0 | while entity == nil do
1703 | 0 | lastArchetype += 1
1704 | 0 | archetype = compatible_archetypes[lastArchetype]
1705 | 0 | if not archetype then
1706 | 0 | return nil
1707 | N/A | end>
1708 | N/A | >
1709 | 0 | entities = archetype.entities
1710 | 0 | i = #entities
1711 | 0 | if i == 0 then
1712 | 0 | continue
1713 | N/A | end>
1714 | 0 | entity = entities[i]
1715 | 0 | columns = archetype.columns
1716 | 0 | records = archetype.records
1717 | 0 | a = columns[records[A]]
1718 | 0 | b = columns[records[B]]
1719 | 0 | c = columns[records[C]]
1720 | 0 | d = columns[records[D]]
1721 | 0 | e = columns[records[E]]
1722 | 0 | f = columns[records[F]]
1723 | 0 | g = columns[records[G]]
1724 | N/A | end>
1725 | N/A | >
1726 | 0 | local row = i
1727 | 0 | i -= 1
1728 | N/A | >
1729 | 1 | return entity, a[row], b[row], c[row], d[row], e[row], f[row], g[row]
1730 | N/A | end>
1731 | 0 | elseif not I then
1732 | 0 | function world_query_iter_next(): any
1733 | 0 | local entity = entities[i]
1734 | 0 | while entity == nil do
1735 | 0 | lastArchetype += 1
1736 | 0 | archetype = compatible_archetypes[lastArchetype]
1737 | 0 | if not archetype then
1738 | 0 | return nil
1739 | N/A | end>
1740 | N/A | >
1741 | 0 | entities = archetype.entities
1742 | 0 | i = #entities
1743 | 0 | if i == 0 then
1744 | 0 | continue
1745 | N/A | end>
1746 | 0 | entity = entities[i]
1747 | 0 | columns = archetype.columns
1748 | 0 | records = archetype.records
1749 | 0 | a = columns[records[A]]
1750 | 0 | b = columns[records[B]]
1751 | 0 | c = columns[records[C]]
1752 | 0 | d = columns[records[D]]
1753 | 0 | e = columns[records[E]]
1754 | 0 | f = columns[records[F]]
1755 | 0 | g = columns[records[G]]
1756 | 0 | h = columns[records[H]]
1757 | N/A | end>
1758 | N/A | >
1759 | 0 | local row = i
1760 | 0 | i -= 1
1761 | N/A | >
1762 | 0 | return entity, a[row], b[row], c[row], d[row], e[row], f[row], g[row], h[row]
1763 | N/A | end>
1764 | 1 | else
1765 | 2 | local output = {}
1766 | 2 | function world_query_iter_next(): any
1767 | 1 | local entity = entities[i]
1768 | 1 | while entity == nil do
1769 | 1 | lastArchetype += 1
1770 | 1 | archetype = compatible_archetypes[lastArchetype]
1771 | 0 | if not archetype then
1772 | 0 | return nil
1773 | N/A | end>
1774 | N/A | >
1775 | 0 | entities = archetype.entities
1776 | 0 | i = #entities
1777 | 0 | if i == 0 then
1778 | 0 | continue
1779 | N/A | end>
1780 | 0 | entity = entities[i]
1781 | 0 | columns = archetype.columns
1782 | 0 | records = archetype.records
1783 | N/A | end>
1784 | N/A | >
1785 | 0 | local row = i
1786 | 1 | i -= 1
1787 | N/A | >
1788 | 0 | for j, id in ids do
1789 | 0 | output[j] = columns[records[id]][row]
1790 | N/A | end>
1791 | N/A | >
1792 | 0 | return entity, unpack(output)
1793 | N/A | end>
1794 | N/A | end>
1795 | N/A | >
1796 | 0 | query.next = world_query_iter_next
1797 | 0 | return world_query_iter_next
1798 | N/A | end>
1799 | N/A | >
1800 | 20 | local function query_iter(query): () -> (number, ...any)
1801 | 19 | local query_next = query.next
1802 | 0 | if not query_next then
1803 | 20 | query_next = query_iter_init(query)
1804 | N/A | end>
1805 | 0 | return query_next
1806 | N/A | end>
1807 | N/A | >
1808 | 6 | local function query_without(query: ecs_query_data_t, ...: i53)
1809 | 6 | local without = { ... }
1810 | 6 | query.filter_without = without
1811 | 3 | local compatible_archetypes = query.compatible_archetypes
1812 | 3 | for i = #compatible_archetypes, 1, -1 do
1813 | 3 | local archetype = compatible_archetypes[i]
1814 | 0 | local records = archetype.records
1815 | 3 | local matches = true
1816 | N/A | >
1817 | 2 | for _, id in without do
1818 | 2 | if records[id] then
1819 | 0 | matches = false
1820 | 0 | break
1821 | N/A | end>
1822 | N/A | end>
1823 | N/A | >
1824 | 0 | if matches then
1825 | 0 | continue
1826 | N/A | end>
1827 | N/A | >
1828 | 0 | local last = #compatible_archetypes
1829 | 0 | if last ~= i then
1830 | 2 | compatible_archetypes[i] = compatible_archetypes[last]
1831 | N/A | end>
1832 | 0 | compatible_archetypes[last] = nil :: any
1833 | N/A | end>
1834 | N/A | >
1835 | 0 | return query :: any
1836 | N/A | end>
1837 | N/A | >
1838 | 1 | local function query_with(query: ecs_query_data_t, ...: i53)
1839 | 1 | local compatible_archetypes = query.compatible_archetypes
1840 | 0 | local with = { ... }
1841 | 1 | query.filter_with = with
1842 | N/A | >
1843 | 0 | for i = #compatible_archetypes, 1, -1 do
1844 | 0 | local archetype = compatible_archetypes[i]
1845 | 0 | local records = archetype.records
1846 | 0 | local matches = true
1847 | N/A | >
1848 | 0 | for _, id in with do
1849 | 0 | if not records[id] then
1850 | 0 | matches = false
1851 | 0 | break
1852 | N/A | end>
1853 | N/A | end>
1854 | N/A | >
1855 | 0 | if matches then
1856 | 0 | continue
1857 | N/A | end>
1858 | N/A | >
1859 | 0 | local last = #compatible_archetypes
1860 | 0 | if last ~= i then
1861 | 0 | compatible_archetypes[i] = compatible_archetypes[last]
1862 | N/A | end>
1863 | 0 | compatible_archetypes[last] = nil :: any
1864 | N/A | end>
1865 | N/A | >
1866 | 0 | return query :: any
1867 | N/A | end>
1868 | N/A | >
1869 | N/A | -- Meant for directly iterating over archetypes to minimize>
1870 | N/A | -- function call overhead. Should not be used unless iterating over>
1871 | N/A | -- hundreds of thousands of entities in bulk.>
1872 | 0 | local function query_archetypes(query)
1873 | 0 | return query.compatible_archetypes
1874 | N/A | end>
1875 | N/A | >
1876 | 6 | local function query_cached(query: ecs_query_data_t)
1877 | 6 | local with = query.filter_with
1878 | 1 | local ids = query.ids
1879 | 0 | if with then
1880 | 5 | table.move(ids, 1, #ids, #with + 1, with)
1881 | 0 | else
1882 | 0 | query.filter_with = ids
1883 | N/A | end>
1884 | N/A | >
1885 | 0 | local compatible_archetypes = query.compatible_archetypes
1886 | 6 | local lastArchetype = 1
1887 | N/A | >
1888 | 6 | local A, B, C, D, E, F, G, H, I = unpack(ids)
1889 | 0 | local a: Column, b: Column, c: Column, d: Column
1890 | 6 | local e: Column, f: Column, g: Column, h: Column
1891 | N/A | >
1892 | 6 | local world_query_iter_next
1893 | 6 | local columns: { Column }
1894 | 6 | local entities: { number }
1895 | 6 | local i: number
1896 | 6 | local archetype: ecs_archetype_t
1897 | 0 | local records: { number }
1898 | 6 | local archetypes = query.compatible_archetypes
1899 | N/A | >
1900 | 0 | local world = query.world :: { observable: ecs_observable_t }
1901 | N/A | -- Only need one observer for EcsArchetypeCreate and EcsArchetypeDelete respectively>
1902 | N/A | -- because the event will be emitted for all components of that Archetype.>
1903 | 6 | local observable = world.observable :: ecs_observable_t
1904 | 6 | local on_create_action = observable[EcsOnArchetypeCreate]
1905 | 6 | if not on_create_action then
1906 | 0 | on_create_action = {}
1907 | 6 | observable[EcsOnArchetypeCreate] = on_create_action
1908 | N/A | end>
1909 | 6 | local query_cache_on_create = on_create_action[A]
1910 | 6 | if not query_cache_on_create then
1911 | 0 | query_cache_on_create = {}
1912 | 0 | on_create_action[A] = query_cache_on_create
1913 | N/A | end>
1914 | N/A | >
1915 | 6 | local on_delete_action = observable[EcsOnArchetypeDelete]
1916 | 6 | if not on_delete_action then
1917 | 0 | on_delete_action = {}
1918 | 6 | observable[EcsOnArchetypeDelete] = on_delete_action
1919 | N/A | end>
1920 | 6 | local query_cache_on_delete = on_delete_action[A]
1921 | 6 | if not query_cache_on_delete then
1922 | 0 | query_cache_on_delete = {}
1923 | 0 | on_delete_action[A] = query_cache_on_delete
1924 | N/A | end>
1925 | N/A | >
1926 | 0 | local function on_create_callback(archetype)
1927 | 0 | table.insert(archetypes, archetype)
1928 | N/A | end>
1929 | N/A | >
1930 | 1 | local function on_delete_callback(archetype)
1931 | 1 | local i = table.find(archetypes, archetype) :: number
1932 | 1 | local n = #archetypes
1933 | 0 | archetypes[i] = archetypes[n]
1934 | 0 | archetypes[n] = nil
1935 | N/A | end>
1936 | N/A | >
1937 | 0 | local observer_for_create = { query = query, callback = on_create_callback }
1938 | 6 | local observer_for_delete = { query = query, callback = on_delete_callback }
1939 | N/A | >
1940 | 0 | table.insert(query_cache_on_create, observer_for_create)
1941 | 6 | table.insert(query_cache_on_delete, observer_for_delete)
1942 | N/A | >
1943 | 11 | local function cached_query_iter()
1944 | 11 | lastArchetype = 1
1945 | 1 | archetype = compatible_archetypes[lastArchetype]
1946 | 0 | if not archetype then
1947 | 10 | return NOOP
1948 | N/A | end>
1949 | 10 | entities = archetype.entities
1950 | 10 | i = #entities
1951 | 10 | records = archetype.records
1952 | 6 | columns = archetype.columns
1953 | 4 | if not B then
1954 | 4 | a = columns[records[A]]
1955 | 4 | elseif not C then
1956 | 0 | a = columns[records[A]]
1957 | 0 | b = columns[records[B]]
1958 | 0 | elseif not D then
1959 | 0 | a = columns[records[A]]
1960 | 0 | b = columns[records[B]]
1961 | 0 | c = columns[records[C]]
1962 | 0 | elseif not E then
1963 | 0 | a = columns[records[A]]
1964 | 0 | b = columns[records[B]]
1965 | 0 | c = columns[records[C]]
1966 | 0 | d = columns[records[D]]
1967 | 0 | elseif not F then
1968 | 0 | a = columns[records[A]]
1969 | 0 | b = columns[records[B]]
1970 | 0 | c = columns[records[C]]
1971 | 0 | d = columns[records[D]]
1972 | 0 | e = columns[records[E]]
1973 | 0 | elseif not G then
1974 | 0 | a = columns[records[A]]
1975 | 0 | b = columns[records[B]]
1976 | 0 | c = columns[records[C]]
1977 | 0 | d = columns[records[D]]
1978 | 0 | e = columns[records[E]]
1979 | 0 | f = columns[records[F]]
1980 | 0 | elseif not H then
1981 | 0 | a = columns[records[A]]
1982 | 0 | b = columns[records[B]]
1983 | 0 | c = columns[records[C]]
1984 | 0 | d = columns[records[D]]
1985 | 0 | e = columns[records[E]]
1986 | 0 | f = columns[records[F]]
1987 | 0 | g = columns[records[G]]
1988 | 0 | elseif not I then
1989 | 0 | a = columns[records[A]]
1990 | 0 | b = columns[records[B]]
1991 | 0 | c = columns[records[C]]
1992 | 0 | d = columns[records[D]]
1993 | 0 | e = columns[records[E]]
1994 | 0 | f = columns[records[F]]
1995 | 0 | g = columns[records[G]]
1996 | 0 | h = columns[records[H]]
1997 | N/A | end>
1998 | N/A | >
1999 | 0 | return world_query_iter_next
2000 | N/A | end>
2001 | N/A | >
2002 | 11 | if not B then
2003 | 11 | function world_query_iter_next(): any
2004 | 6 | local entity = entities[i]
2005 | 6 | while entity == nil do
2006 | 6 | lastArchetype += 1
2007 | 6 | archetype = compatible_archetypes[lastArchetype]
2008 | 0 | if not archetype then
2009 | 0 | return nil
2010 | N/A | end>
2011 | N/A | >
2012 | 0 | entities = archetype.entities
2013 | 0 | i = #entities
2014 | 0 | if i == 0 then
2015 | 0 | continue
2016 | N/A | end>
2017 | 0 | entity = entities[i]
2018 | 0 | columns = archetype.columns
2019 | 0 | records = archetype.records
2020 | 0 | a = columns[records[A]]
2021 | N/A | end>
2022 | N/A | >
2023 | 0 | local row = i
2024 | 5 | i -= 1
2025 | N/A | >
2026 | 1 | return entity, a[row]
2027 | N/A | end>
2028 | 8 | elseif not C then
2029 | 8 | function world_query_iter_next(): any
2030 | 4 | local entity = entities[i]
2031 | 4 | while entity == nil do
2032 | 4 | lastArchetype += 1
2033 | 4 | archetype = compatible_archetypes[lastArchetype]
2034 | 0 | if not archetype then
2035 | 0 | return nil
2036 | N/A | end>
2037 | N/A | >
2038 | 0 | entities = archetype.entities
2039 | 0 | i = #entities
2040 | 0 | if i == 0 then
2041 | 0 | continue
2042 | N/A | end>
2043 | 0 | entity = entities[i]
2044 | 0 | columns = archetype.columns
2045 | 0 | records = archetype.records
2046 | 0 | a = columns[records[A]]
2047 | 0 | b = columns[records[B]]
2048 | N/A | end>
2049 | N/A | >
2050 | 0 | local row = i
2051 | 4 | i -= 1
2052 | N/A | >
2053 | 0 | return entity, a[row], b[row]
2054 | N/A | end>
2055 | 0 | elseif not D then
2056 | 0 | function world_query_iter_next(): any
2057 | 0 | local entity = entities[i]
2058 | 0 | while entity == nil do
2059 | 0 | lastArchetype += 1
2060 | 0 | archetype = compatible_archetypes[lastArchetype]
2061 | 0 | if not archetype then
2062 | 0 | return nil
2063 | N/A | end>
2064 | N/A | >
2065 | 0 | entities = archetype.entities
2066 | 0 | i = #entities
2067 | 0 | if i == 0 then
2068 | 0 | continue
2069 | N/A | end>
2070 | 0 | entity = entities[i]
2071 | 0 | columns = archetype.columns
2072 | 0 | records = archetype.records
2073 | 0 | a = columns[records[A]]
2074 | 0 | b = columns[records[B]]
2075 | 0 | c = columns[records[C]]
2076 | N/A | end>
2077 | N/A | >
2078 | 0 | local row = i
2079 | 0 | i -= 1
2080 | N/A | >
2081 | 0 | return entity, a[row], b[row], c[row]
2082 | N/A | end>
2083 | 0 | elseif not E then
2084 | 0 | function world_query_iter_next(): any
2085 | 0 | local entity = entities[i]
2086 | 0 | while entity == nil do
2087 | 0 | lastArchetype += 1
2088 | 0 | archetype = compatible_archetypes[lastArchetype]
2089 | 0 | if not archetype then
2090 | 0 | return nil
2091 | N/A | end>
2092 | N/A | >
2093 | 0 | entities = archetype.entities
2094 | 0 | i = #entities
2095 | 0 | if i == 0 then
2096 | 0 | continue
2097 | N/A | end>
2098 | 0 | entity = entities[i]
2099 | 0 | columns = archetype.columns
2100 | 0 | records = archetype.records
2101 | 0 | a = columns[records[A]]
2102 | 0 | b = columns[records[B]]
2103 | 0 | c = columns[records[C]]
2104 | 0 | d = columns[records[D]]
2105 | N/A | end>
2106 | N/A | >
2107 | 0 | local row = i
2108 | 0 | i -= 1
2109 | N/A | >
2110 | 0 | return entity, a[row], b[row], c[row], d[row]
2111 | N/A | end>
2112 | 0 | elseif not F then
2113 | 0 | function world_query_iter_next(): any
2114 | 0 | local entity = entities[i]
2115 | 0 | while entity == nil do
2116 | 0 | lastArchetype += 1
2117 | 0 | archetype = compatible_archetypes[lastArchetype]
2118 | 0 | if not archetype then
2119 | 0 | return nil
2120 | N/A | end>
2121 | N/A | >
2122 | 0 | entities = archetype.entities
2123 | 0 | i = #entities
2124 | 0 | if i == 0 then
2125 | 0 | continue
2126 | N/A | end>
2127 | 0 | entity = entities[i]
2128 | 0 | columns = archetype.columns
2129 | 0 | records = archetype.records
2130 | 0 | a = columns[records[A]]
2131 | 0 | b = columns[records[B]]
2132 | 0 | c = columns[records[C]]
2133 | 0 | d = columns[records[D]]
2134 | 0 | e = columns[records[E]]
2135 | N/A | end>
2136 | N/A | >
2137 | 0 | local row = i
2138 | 0 | i -= 1
2139 | N/A | >
2140 | 0 | return entity, a[row], b[row], c[row], d[row], e[row]
2141 | N/A | end>
2142 | 0 | elseif not G then
2143 | 0 | function world_query_iter_next(): any
2144 | 0 | local entity = entities[i]
2145 | 0 | while entity == nil do
2146 | 0 | lastArchetype += 1
2147 | 0 | archetype = compatible_archetypes[lastArchetype]
2148 | 0 | if not archetype then
2149 | 0 | return nil
2150 | N/A | end>
2151 | N/A | >
2152 | 0 | entities = archetype.entities
2153 | 0 | i = #entities
2154 | 0 | if i == 0 then
2155 | 0 | continue
2156 | N/A | end>
2157 | 0 | entity = entities[i]
2158 | 0 | columns = archetype.columns
2159 | 0 | records = archetype.records
2160 | 0 | a = columns[records[A]]
2161 | 0 | b = columns[records[B]]
2162 | 0 | c = columns[records[C]]
2163 | 0 | d = columns[records[D]]
2164 | 0 | e = columns[records[E]]
2165 | 0 | f = columns[records[F]]
2166 | N/A | end>
2167 | N/A | >
2168 | 0 | local row = i
2169 | 0 | i -= 1
2170 | N/A | >
2171 | 0 | return entity, a[row], b[row], c[row], d[row], e[row], f[row]
2172 | N/A | end>
2173 | 0 | elseif not H then
2174 | 0 | function world_query_iter_next(): any
2175 | 0 | local entity = entities[i]
2176 | 0 | while entity == nil do
2177 | 0 | lastArchetype += 1
2178 | 0 | archetype = compatible_archetypes[lastArchetype]
2179 | 0 | if not archetype then
2180 | 0 | return nil
2181 | N/A | end>
2182 | N/A | >
2183 | 0 | entities = archetype.entities
2184 | 0 | i = #entities
2185 | 0 | if i == 0 then
2186 | 0 | continue
2187 | N/A | end>
2188 | 0 | entity = entities[i]
2189 | 0 | columns = archetype.columns
2190 | 0 | records = archetype.records
2191 | 0 | a = columns[records[A]]
2192 | 0 | b = columns[records[B]]
2193 | 0 | c = columns[records[C]]
2194 | 0 | d = columns[records[D]]
2195 | 0 | e = columns[records[E]]
2196 | 0 | f = columns[records[F]]
2197 | 0 | g = columns[records[G]]
2198 | N/A | end>
2199 | N/A | >
2200 | 0 | local row = i
2201 | 0 | i -= 1
2202 | N/A | >
2203 | 0 | return entity, a[row], b[row], c[row], d[row], e[row], f[row], g[row]
2204 | N/A | end>
2205 | 0 | elseif not I then
2206 | 0 | function world_query_iter_next(): any
2207 | 0 | local entity = entities[i]
2208 | 0 | while entity == nil do
2209 | 0 | lastArchetype += 1
2210 | 0 | archetype = compatible_archetypes[lastArchetype]
2211 | 0 | if not archetype then
2212 | 0 | return nil
2213 | N/A | end>
2214 | N/A | >
2215 | 0 | entities = archetype.entities
2216 | 0 | i = #entities
2217 | 0 | if i == 0 then
2218 | 0 | continue
2219 | N/A | end>
2220 | 0 | entity = entities[i]
2221 | 0 | columns = archetype.columns
2222 | 0 | records = archetype.records
2223 | 0 | a = columns[records[A]]
2224 | 0 | b = columns[records[B]]
2225 | 0 | c = columns[records[C]]
2226 | 0 | d = columns[records[D]]
2227 | 0 | e = columns[records[E]]
2228 | 0 | f = columns[records[F]]
2229 | 0 | g = columns[records[G]]
2230 | 0 | h = columns[records[H]]
2231 | N/A | end>
2232 | N/A | >
2233 | 0 | local row = i
2234 | 0 | i -= 1
2235 | N/A | >
2236 | 0 | return entity, a[row], b[row], c[row], d[row], e[row], f[row], g[row], h[row]
2237 | N/A | end>
2238 | 0 | else
2239 | 0 | local queryOutput = {}
2240 | 0 | function world_query_iter_next(): any
2241 | 0 | local entity = entities[i]
2242 | 0 | while entity == nil do
2243 | 0 | lastArchetype += 1
2244 | 0 | archetype = compatible_archetypes[lastArchetype]
2245 | 0 | if not archetype then
2246 | 0 | return nil
2247 | N/A | end>
2248 | N/A | >
2249 | 0 | entities = archetype.entities
2250 | 0 | i = #entities
2251 | 0 | if i == 0 then
2252 | 0 | continue
2253 | N/A | end>
2254 | 0 | entity = entities[i]
2255 | 0 | columns = archetype.columns
2256 | 0 | records = archetype.records
2257 | N/A | end>
2258 | N/A | >
2259 | 0 | local row = i
2260 | 0 | i -= 1
2261 | N/A | >
2262 | 0 | if not F then
2263 | 0 | return entity, a[row], b[row], c[row], d[row], e[row]
2264 | 0 | elseif not G then
2265 | 0 | return entity, a[row], b[row], c[row], d[row], e[row], f[row]
2266 | 0 | elseif not H then
2267 | 0 | return entity, a[row], b[row], c[row], d[row], e[row], f[row], g[row]
2268 | 0 | elseif not I then
2269 | 0 | return entity, a[row], b[row], c[row], d[row], e[row], f[row], g[row], h[row]
2270 | N/A | end>
2271 | N/A | >
2272 | 0 | for j, id in ids do
2273 | 0 | queryOutput[j] = columns[records[id]][row]
2274 | N/A | end>
2275 | N/A | >
2276 | 0 | return entity, unpack(queryOutput)
2277 | N/A | end>
2278 | N/A | end>
2279 | N/A | >
2280 | 6 | local cached_query = query :: any
2281 | 6 | cached_query.archetypes = query_archetypes
2282 | 6 | cached_query.__iter = cached_query_iter
2283 | 6 | cached_query.iter = cached_query_iter
2284 | 0 | setmetatable(cached_query, cached_query)
2285 | 0 | return cached_query
2286 | N/A | end>
2287 | N/A | >
2288 | 1 | local Query = {}
2289 | 1 | Query.__index = Query
2290 | 1 | Query.__iter = query_iter
2291 | 1 | Query.iter = query_iter_init
2292 | 1 | Query.without = query_without
2293 | 1 | Query.with = query_with
2294 | 0 | Query.archetypes = query_archetypes
2295 | 1 | Query.cached = query_cached
2296 | N/A | >
2297 | 33 | local function world_query(world: ecs_world_t, ...)
2298 | 0 | local compatible_archetypes = {}
2299 | 33 | local length = 0
2300 | N/A | >
2301 | 33 | local ids = { ... }
2302 | N/A | >
2303 | 33 | local archetypes = world.archetypes
2304 | N/A | >
2305 | 0 | local idr: ecs_id_record_t?
2306 | 33 | local component_index = world.component_index
2307 | N/A | >
2308 | 33 | local q = setmetatable({
2309 | 33 | ids = ids,
2310 | 33 | compatible_archetypes = compatible_archetypes,
2311 | 0 | world = world,
2312 | 33 | }, Query)
2313 | N/A | >
2314 | 47 | for _, id in ids do
2315 | 7 | local map = component_index[id]
2316 | 0 | if not map then
2317 | 0 | return q
2318 | N/A | end>
2319 | N/A | >
2320 | 0 | if idr == nil or map.size < idr.size then
2321 | 0 | idr = map
2322 | N/A | end>
2323 | N/A | end>
2324 | N/A | >
2325 | 0 | if not idr then
2326 | 0 | return q
2327 | N/A | end>
2328 | N/A | >
2329 | 48 | for archetype_id in idr.cache do
2330 | 0 | local compatibleArchetype = archetypes[archetype_id]
2331 | 0 | if #compatibleArchetype.entities == 0 then
2332 | 34 | continue
2333 | N/A | end>
2334 | 34 | local records = compatibleArchetype.records
2335 | N/A | >
2336 | 34 | local skip = false
2337 | N/A | >
2338 | 47 | for i, id in ids do
2339 | 0 | local tr = records[id]
2340 | 0 | if not tr then
2341 | 0 | skip = true
2342 | 0 | break
2343 | N/A | end>
2344 | N/A | end>
2345 | N/A | >
2346 | 0 | if skip then
2347 | 0 | continue
2348 | N/A | end>
2349 | N/A | >
2350 | 0 | length += 1
2351 | 0 | compatible_archetypes[length] = compatibleArchetype
2352 | N/A | end>
2353 | N/A | >
2354 | 0 | return q
2355 | N/A | end>
2356 | N/A | >
2357 | 4 | local function world_each(world: ecs_world_t, id: i53): () -> ()
2358 | 0 | local idr = world.component_index[id]
2359 | 0 | if not idr then
2360 | 0 | return NOOP
2361 | N/A | end>
2362 | N/A | >
2363 | 4 | local idr_cache = idr.cache
2364 | 4 | local archetypes = world.archetypes
2365 | 4 | local archetype_id = next(idr_cache, nil) :: number
2366 | 0 | local archetype = archetypes[archetype_id]
2367 | 0 | if not archetype then
2368 | 0 | return NOOP
2369 | N/A | end>
2370 | N/A | >
2371 | 0 | local entities = archetype.entities
2372 | 4 | local row = #entities
2373 | N/A | >
2374 | 12 | return function(): any
2375 | 11 | local entity = entities[row]
2376 | 11 | while not entity do
2377 | 4 | archetype_id = next(idr_cache, archetype_id) :: number
2378 | 0 | if not archetype_id then
2379 | 7 | return
2380 | N/A | end>
2381 | 7 | archetype = archetypes[archetype_id]
2382 | 7 | entities = archetype.entities
2383 | 0 | row = #entities
2384 | 8 | entity = entities[row]
2385 | N/A | end>
2386 | 0 | row -= 1
2387 | 0 | return entity
2388 | N/A | end>
2389 | N/A | end>
2390 | N/A | >
2391 | 0 | local function world_children(world: ecs_world_t, parent: i53)
2392 | 0 | return world_each(world, ECS_PAIR(EcsChildOf, parent))
2393 | N/A | end>
2394 | N/A | >
2395 | 0 | export type Record = {
2396 | 0 | archetype: Archetype,
2397 | 0 | row: number,
2398 | 0 | dense: i24,
2399 | 0 | }
2400 | 0 | export type ComponentRecord = {
2401 | 0 | cache: { [Id]: number },
2402 | 0 | counts: { [Id]: number },
2403 | 0 | flags: number,
2404 | 0 | size: number,
2405 | 0 | hooks: {
2406 | 0 | on_add: ((entity: Entity) -> ())?,
2407 | 0 | on_set: ((entity: Entity, data: any) -> ())?,
2408 | 0 | on_remove: ((entity: Entity) -> ())?,
2409 | 0 | },
2410 | 0 | }
2411 | 0 | export type ComponentIndex = Map
2412 | 0 | export type Archetypes = { [Id]: Archetype }
2413 | N/A | >
2414 | 0 | export type EntityIndex = {
2415 | 0 | dense_array: Map,
2416 | 0 | sparse_array: Map,
2417 | 0 | alive_count: number,
2418 | 0 | max_id: number,
2419 | 1 | }
2420 | N/A | >
2421 | 0 | local World = {}
2422 | 1 | World.__index = World
2423 | N/A | >
2424 | 1 | World.entity = world_entity
2425 | 1 | World.query = world_query
2426 | 1 | World.remove = world_remove
2427 | 1 | World.clear = world_clear
2428 | 1 | World.delete = world_delete
2429 | 1 | World.component = world_component
2430 | 1 | World.add = world_add
2431 | 1 | World.set = world_set
2432 | 1 | World.get = world_get
2433 | 1 | World.has = world_has
2434 | 1 | World.target = world_target
2435 | 1 | World.parent = world_parent
2436 | 1 | World.contains = world_contains
2437 | 1 | World.cleanup = world_cleanup
2438 | 0 | World.each = world_each
2439 | 1 | World.children = world_children
2440 | N/A | >
2441 | 73 | local function world_new()
2442 | 73 | local entity_index = {
2443 | 73 | dense_array = {},
2444 | 73 | sparse_array = {},
2445 | 0 | alive_count = 0,
2446 | 73 | max_id = 0,
2447 | 73 | } :: ecs_entity_index_t
2448 | 73 | local self = setmetatable({
2449 | 73 | archetype_index = {} :: { [string]: Archetype },
2450 | 73 | archetypes = {} :: Archetypes,
2451 | 73 | component_index = {} :: ComponentIndex,
2452 | 0 | entity_index = entity_index,
2453 | 73 | ROOT_ARCHETYPE = (nil :: any) :: Archetype,
2454 | N/A | >
2455 | 0 | max_archetype_id = 0,
2456 | 73 | max_component_id = 0,
2457 | N/A | >
2458 | 0 | observable = {} :: Observable,
2459 | 73 | }, World) :: any
2460 | N/A | >
2461 | 73 | self.ROOT_ARCHETYPE = archetype_create(self, {}, "")
2462 | N/A | >
2463 | 18688 | for i = 1, HI_COMPONENT_ID do
2464 | 0 | local e = entity_index_new_id(entity_index)
2465 | 0 | world_add(self, e, EcsComponent)
2466 | N/A | end>
2467 | N/A | >
2468 | 1022 | for i = HI_COMPONENT_ID + 1, EcsRest do
2469 | N/A | -- Initialize built-in components>
2470 | 0 | entity_index_new_id(entity_index)
2471 | N/A | end>
2472 | N/A | >
2473 | 73 | world_add(self, EcsName, EcsComponent)
2474 | 73 | world_add(self, EcsOnSet, EcsComponent)
2475 | 73 | world_add(self, EcsOnAdd, EcsComponent)
2476 | 73 | world_add(self, EcsOnRemove, EcsComponent)
2477 | 0 | world_add(self, EcsWildcard, EcsComponent)
2478 | 73 | world_add(self, EcsRest, EcsComponent)
2479 | N/A | >
2480 | 73 | world_set(self, EcsOnAdd, EcsName, "jecs.OnAdd")
2481 | 73 | world_set(self, EcsOnRemove, EcsName, "jecs.OnRemove")
2482 | 73 | world_set(self, EcsOnSet, EcsName, "jecs.OnSet")
2483 | 73 | world_set(self, EcsWildcard, EcsName, "jecs.Wildcard")
2484 | 73 | world_set(self, EcsChildOf, EcsName, "jecs.ChildOf")
2485 | 73 | world_set(self, EcsComponent, EcsName, "jecs.Component")
2486 | 73 | world_set(self, EcsOnDelete, EcsName, "jecs.OnDelete")
2487 | 73 | world_set(self, EcsOnDeleteTarget, EcsName, "jecs.OnDeleteTarget")
2488 | 73 | world_set(self, EcsDelete, EcsName, "jecs.Delete")
2489 | 73 | world_set(self, EcsRemove, EcsName, "jecs.Remove")
2490 | 0 | world_set(self, EcsName, EcsName, "jecs.Name")
2491 | 73 | world_set(self, EcsRest, EcsRest, "jecs.Rest")
2492 | N/A | >
2493 | 73 | world_add(self, EcsChildOf, ECS_PAIR(EcsOnDeleteTarget, EcsDelete))
2494 | N/A | >
2495 | 0 | return self
2496 | N/A | end>
2497 | N/A | >
2498 | 0 | World.new = world_new
2499 | N/A | >
2500 | 0 | export type Entity = { __T: T }
2501 | 0 | export type Id = { __T: T }
2502 | 0 | export type Pair = Id
2503 | 0 | type ecs_id_t = Id | Pair | Pair<"Tag", T>
2504 | 0 | export type Item = (self: Query) -> (Entity, T...)
2505 | 0 | export type Iter = (query: Query) -> () -> (Entity, T...)
2506 | N/A | >
2507 | 0 | export type Query = typeof(setmetatable({}, {
2508 | 0 | __iter = (nil :: any) :: Iter,
2509 | 0 | })) & {
2510 | 0 | iter: Iter,
2511 | 0 | with: (self: Query, ...Id) -> Query,
2512 | 0 | without: (self: Query, ...Id) -> Query,
2513 | 0 | archetypes: (self: Query) -> { Archetype },
2514 | 0 | cached: (self: Query) -> Query,
2515 | 0 | }
2516 | N/A | >
2517 | 0 | export type Observer = {
2518 | 0 | callback: (archetype: Archetype) -> (),
2519 | 0 | query: QueryInner,
2520 | 0 | }
2521 | N/A | >
2522 | 0 | export type Observable = {
2523 | 0 | [Id]: {
2524 | 0 | [Id]: {
2525 | 0 | { Observer }
2526 | 0 | }
2527 | 0 | }
2528 | 0 | }
2529 | N/A | >
2530 | 0 | export type World = {
2531 | 0 | archetype_index: { [string]: Archetype },
2532 | 0 | archetypes: Archetypes,
2533 | 0 | component_index: ComponentIndex,
2534 | 0 | entity_index: EntityIndex,
2535 | 0 | ROOT_ARCHETYPE: Archetype,
2536 | N/A | >
2537 | 0 | max_component_id: number,
2538 | 0 | max_archetype_id: number,
2539 | N/A | >
2540 | 0 | observable: any,
2541 | N/A | >
2542 | N/A | --- Creates a new entity>
2543 | 0 | entity: (self: World, id: Entity?) -> Entity,
2544 | N/A | --- Creates a new entity located in the first 256 ids.>
2545 | N/A | --- These should be used for static components for fast access.>
2546 | 0 | component: (self: World) -> Entity,
2547 | N/A | --- Gets the target of an relationship. For example, when a user calls>
2548 | N/A | --- `world:target(id, ChildOf(parent), 0)`, you will obtain the parent entity.>
2549 | 0 | target: (self: World, id: Entity, relation: Id, index: number?) -> Entity?,
2550 | N/A | --- Deletes an entity and all it's related components and relationships.>
2551 | 0 | delete: (self: World, id: Entity) -> (),
2552 | N/A | >
2553 | N/A | --- Adds a component to the entity with no value>
2554 | 0 | add: (self: World, id: Entity, component: Id) -> (),
2555 | N/A | --- Assigns a value to a component on the given entity>
2556 | 0 | set: (self: World, id: Entity, component: Id, data: T) -> (),
2557 | N/A | >
2558 | 0 | cleanup: (self: World) -> (),
2559 | N/A | -- Clears an entity from the world>
2560 | 0 | clear: (self: World, id: Entity) -> (),
2561 | N/A | --- Removes a component from the given entity>
2562 | 0 | remove: (self: World, id: Entity, component: Id) -> (),
2563 | N/A | --- Retrieves the value of up to 4 components. These values may be nil.>
2564 | 0 | get: ((self: World, id: Entity, Id) -> A?)
2565 | 0 | & ((self: World, id: Entity, Id, Id) -> (A?, B?))
2566 | 0 | & ((self: World, id: Entity, Id, Id, Id) -> (A?, B?, C?))
2567 | 0 | & (self: World, id: Entity, Id, Id, Id, Id) -> (A?, B?, C?, D?),
2568 | N/A | >
2569 | N/A | --- Returns whether the entity has the ID.>
2570 | 0 | has: (self: World, entity: Entity, ...Id) -> boolean,
2571 | N/A | >
2572 | N/A | --- Get parent (target of ChildOf relationship) for entity. If there is no ChildOf relationship pair, it will return nil.>
2573 | 0 | parent:(self: World, entity: Entity) -> Entity,
2574 | N/A | >
2575 | N/A | --- Checks if the world contains the given entity>
2576 | 0 | contains:(self: World, entity: Entity) -> boolean,
2577 | N/A | >
2578 | 0 | each: (self: World, id: Id) -> () -> Entity,
2579 | N/A | >
2580 | 0 | children: (self: World, id: Id) -> () -> Entity,
2581 | N/A | >
2582 | N/A | --- Searches the world for entities that match a given query>
2583 | 0 | query: ((World, Id) -> Query)
2584 | 0 | & ((World, Id, Id) -> Query)
2585 | 0 | & ((World, Id, Id, Id) -> Query)
2586 | 0 | & ((World, Id, Id, Id, Id) -> Query)
2587 | 0 | & ((World, Id, Id, Id, Id, Id) -> Query)
2588 | 0 | & ((World, Id, Id, Id, Id, Id, Id) -> Query)
2589 | 0 | & ((World, Id, Id, Id, Id, Id, Id, Id) -> Query)
2590 | 0 | & ((World, Id, Id, Id, Id, Id, Id, Id, Id, ...Id) -> Query)
2591 | 0 | }
2592 | N/A | -- type function ecs_id_t(entity)>
2593 | N/A | -- local ty = entity:components()[2]>
2594 | N/A | -- local __T = ty:readproperty(types.singleton("__T"))>
2595 | N/A | -- if not __T then>
2596 | N/A | -- return ty:readproperty(types.singleton("__jecs_pair_value"))>
2597 | N/A | -- end>
2598 | N/A | -- return __T>
2599 | N/A | -- end>
2600 | N/A | >
2601 | N/A | -- type function ecs_pair_t(first, second)>
2602 | N/A | -- if ecs_id_t(first):is("nil") then>
2603 | N/A | -- return second>
2604 | N/A | -- else>
2605 | N/A | -- return first>
2606 | N/A | -- end>
2607 | N/A | -- end>
2608 | N/A | >
2609 | 1 | return {
2610 | 0 | World = World :: { new: () -> World },
2611 | 1 | world = World.new :: () -> World,
2612 | N/A | >
2613 | 1 | OnAdd = EcsOnAdd :: Entity<(entity: Entity) -> ()>,
2614 | 1 | OnRemove = EcsOnRemove :: Entity<(entity: Entity) -> ()>,
2615 | 1 | OnSet = EcsOnSet :: Entity<(entity: Entity, data: any) -> ()>,
2616 | 1 | ChildOf = EcsChildOf :: Entity,
2617 | 1 | Component = EcsComponent :: Entity,
2618 | 1 | Wildcard = EcsWildcard :: Entity,
2619 | 1 | w = EcsWildcard :: Entity,
2620 | 1 | OnDelete = EcsOnDelete :: Entity,
2621 | 1 | OnDeleteTarget = EcsOnDeleteTarget :: Entity,
2622 | 1 | Delete = EcsDelete :: Entity,
2623 | 1 | Remove = EcsRemove :: Entity,
2624 | 0 | Name = EcsName :: Entity,
2625 | 1 | Rest = EcsRest :: Entity,
2626 | N/A | >
2627 | 0 | pair = (ECS_PAIR :: any) :: (first: Id , second: Id) -> Pair,
2628 | N/A | >
2629 | N/A | -- Inwards facing API for testing>
2630 | 1 | ECS_ID = ECS_ENTITY_T_LO,
2631 | 1 | ECS_GENERATION_INC = ECS_GENERATION_INC,
2632 | 0 | ECS_GENERATION = ECS_GENERATION,
2633 | 1 | ECS_ID_IS_WILDCARD = ECS_ID_IS_WILDCARD,
2634 | N/A | >
2635 | 1 | ECS_ID_DELETE = ECS_ID_DELETE,
2636 | N/A | >
2637 | 1 | IS_PAIR = ECS_IS_PAIR,
2638 | 1 | pair_first = ecs_pair_first,
2639 | 0 | pair_second = ecs_pair_second,
2640 | 1 | entity_index_get_alive = entity_index_get_alive,
2641 | N/A | >
2642 | 1 | archetype_append_to_records = archetype_append_to_records,
2643 | 1 | id_record_ensure = id_record_ensure,
2644 | 1 | archetype_create = archetype_create,
2645 | 1 | archetype_ensure = archetype_ensure,
2646 | 1 | find_insert = find_insert,
2647 | 1 | find_archetype_with = find_archetype_with,
2648 | 1 | find_archetype_without = find_archetype_without,
2649 | 1 | archetype_init_edge = archetype_init_edge,
2650 | 1 | archetype_ensure_edge = archetype_ensure_edge,
2651 | 1 | init_edge_for_add = init_edge_for_add,
2652 | 1 | init_edge_for_remove = init_edge_for_remove,
2653 | 1 | create_edge_for_add = create_edge_for_add,
2654 | 1 | create_edge_for_remove = create_edge_for_remove,
2655 | 0 | archetype_traverse_add = archetype_traverse_add,
2656 | 1 | archetype_traverse_remove = archetype_traverse_remove,
2657 | N/A | >
2658 | 1 | entity_move = entity_move,
2659 | N/A | >
2660 | 1 | entity_index_try_get = entity_index_try_get,
2661 | 1 | entity_index_try_get_any = entity_index_try_get_any,
2662 | 1 | entity_index_try_get_fast = entity_index_try_get_fast,
2663 | 0 | entity_index_is_alive = entity_index_is_alive,
2664 | 1 | entity_index_new_id = entity_index_new_id,
2665 | N/A | >
2666 | 1 | query_iter = query_iter,
2667 | 1 | query_iter_init = query_iter_init,
2668 | 1 | query_with = query_with,
2669 | 1 | query_without = query_without,
2670 | 0 | query_archetypes = query_archetypes,
2671 | 1 | query_match = query_match,
2672 | N/A | >
2673 | 0 | find_observers = find_observers,
2674 | 0 | }