# 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
<anonymous>: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> = { [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<i53, ecs_graph_edge_t>
>
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<i53, ecs_id_record_t>
>
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<number, i53>,
78 | 0 | sparse_array: Map<i24, ecs_record_t>,
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<i53, Map<i53, { ecs_observer_t }>>
>
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<i53, Map<i53, { ecs_observer_t }>>,
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<Id, ComponentRecord>
2412 | 0 | export type Archetypes = { [Id]: Archetype }
>
2413 | N/A |
2414 | 0 | export type EntityIndex = {
2415 | 0 | dense_array: Map<number, Entity>,
2416 | 0 | sparse_array: Map<i24, Record>,
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 = unknown> = { __T: T }
2501 | 0 | export type Id<T = unknown> = { __T: T }
2502 | 0 | export type Pair<P, O> = Id
2503 | 0 | type ecs_id_t<T=unknown> = Id<T> | Pair<T, "Tag"> | Pair<"Tag", T>
2504 | 0 | export type Item<T...> = (self: Query<T...>) -> (Entity, T...)
2505 | 0 | export type Iter<T...> = (query: Query<T...>) -> () -> (Entity, T...)
>
2506 | N/A |
2507 | 0 | export type Query<T...> = typeof(setmetatable({}, {
2508 | 0 | __iter = (nil :: any) :: Iter<T...>,
2509 | 0 | })) & {
2510 | 0 | iter: Iter<T...>,
2511 | 0 | with: (self: Query<T...>, ...Id) -> Query<T...>,
2512 | 0 | without: (self: Query<T...>, ...Id) -> Query<T...>,
2513 | 0 | archetypes: (self: Query<T...>) -> { Archetype },
2514 | 0 | cached: (self: Query<T...>) -> Query<T...>,
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: <T>(self: World) -> Entity<T>,
>
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: <T>(self: World, id: Entity, component: Id) -> (),
>
2555 | N/A | --- Assigns a value to a component on the given entity
2556 | 0 | set: <T>(self: World, id: Entity, component: Id<T>, 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 | & (<A, B>(self: World, id: Entity, Id, Id) -> (A?, B?))
2566 | 0 | & (<A, B, C>(self: World, id: Entity, Id, Id, Id<C>) -> (A?, B?, C?))
2567 | 0 | & <A, B, C, D>(self: World, id: Entity, Id, Id, Id<C>, Id<D>) -> (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 | & (<A, B>(World, Id, Id) -> Query<A, B>)
2585 | 0 | & (<A, B, C>(World, Id, Id, Id<C>) -> Query<A, B, C>)
2586 | 0 | & (<A, B, C, D>(World, Id, Id, Id<C>, Id<D>) -> Query<A, B, C, D>)
2587 | 0 | & (<A, B, C, D, E>(World, Id, Id, Id<C>, Id<D>, Id<E>) -> Query<A, B, C, D, E>)
2588 | 0 | & (<A, B, C, D, E, F>(World, Id, Id, Id<C>, Id<D>, Id<E>, Id<F>) -> Query<A, B, C, D, E, F>)
2589 | 0 | & (<A, B, C, D, E, F, G>(World, Id, Id, Id<C>, Id<D>, Id<E>, Id<F>, Id<G>) -> Query<A, B, C, D, E, F, G>)
2590 | 0 | & (<A, B, C, D, E, F, G, H>(World, Id, Id, Id<C>, Id<D>, Id<E>, Id<F>, Id<G>, Id<H>, ...Id<any>) -> Query<A, B, C, D, E, F, G, H>)
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<string>,
2625 | 1 | Rest = EcsRest :: Entity,
>
2626 | N/A |
2627 | 0 | pair = (ECS_PAIR :: any) :: <P, O>(first: Id , second: Id<O>) -> Pair<P, O>,
>
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 | }