From 2c9f3dad349a40b0ad02acfdb7f2de051c1f0de4 Mon Sep 17 00:00:00 2001 From: kurokuukyo Date: Mon, 20 Apr 2026 16:17:17 -0400 Subject: [PATCH 01/17] Implement world:targets --- src/jecs.luau | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/jecs.luau b/src/jecs.luau index 50909fc..a837efb 100755 --- a/src/jecs.luau +++ b/src/jecs.luau @@ -194,6 +194,7 @@ type world = { entity: (self: world, id: i53?) -> i53, component: (self: world) -> i53, target: (self: world, id: i53, relation: i53, index: number?) -> i53?, + targets: (self: world, id: i53, relation: i53) -> () -> i53?, delete: (self: world, id: i53) -> (), add: (self: world, id: i53, component: i53) -> (), set: (self: world, id: i53, component: i53, data: any) -> (), @@ -253,6 +254,13 @@ export type World = { index: number? ) -> Entity?, + -- Gets an iterator for all viable targets of a relationship + targets: ( + self: World, + id: Entity | number, + relation: ecs_entity_t + ) -> () -> Id, + --- Deletes an entity and all it's related components and relationships. delete: (self: World, id: Entity) -> (), @@ -3302,6 +3310,43 @@ local function world_new(DEBUG: boolean?) ECS_PAIR_SECOND(nth)) end + local function world_targets(world: world, entity: i53, relation: i53): () -> i53? + local record = entity_index_try_get_unsafe(entity) + if not record then + return NOOP :: () -> i53 + end + + local archetype = record.archetype + if not archetype then + return NOOP :: () -> i53 + end + + local r = ECS_PAIR(relation, EcsWildcard) + local idr = world.component_index[r] + + if not idr then + return NOOP :: () -> i53 + end + + local archetype_id = archetype.id + local count = idr.counts[archetype_id] + if not count then + return NOOP :: () -> i53 + end + + local nth = 0 + + return function(): i53? + if nth == count then + return nil + end + local target = entity_index_get_alive(world.entity_index, + ECS_PAIR_SECOND(nth)) + nth += 1 + return target + end + end + local function world_parent(world: world, entity: i53): i53? return world_target(world, entity, EcsChildOf, 0) end @@ -3730,6 +3775,7 @@ local function world_new(DEBUG: boolean?) world.get = world_get :: any world.has = world_has :: any world.target = world_target + world.targets = world_targets world.parent = world_parent world.contains = world_contains world.exists = world_exists From 62aefff3191c7efe20a14aa66978976d079c87ef Mon Sep 17 00:00:00 2001 From: kurokuukyo Date: Mon, 20 Apr 2026 17:03:09 -0400 Subject: [PATCH 02/17] Proper indexing in ECS_PAIR_SECOND --- src/jecs.luau | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jecs.luau b/src/jecs.luau index a837efb..08f8dca 100755 --- a/src/jecs.luau +++ b/src/jecs.luau @@ -3341,7 +3341,7 @@ local function world_new(DEBUG: boolean?) return nil end local target = entity_index_get_alive(world.entity_index, - ECS_PAIR_SECOND(nth)) + ECS_PAIR_SECOND(archetype.types[nth + idr.records[archetype_id]])) nth += 1 return target end From af57f4024f9fa154ff89520faa8411d21c6e14bc Mon Sep 17 00:00:00 2001 From: kurokuukyo Date: Mon, 20 Apr 2026 17:03:18 -0400 Subject: [PATCH 03/17] Unit tests --- test/tests.luau | 124 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/test/tests.luau b/test/tests.luau index d50e98b..760267d 100755 --- a/test/tests.luau +++ b/test/tests.luau @@ -3077,6 +3077,130 @@ TEST("world:set()", function() end end) +TEST("world:targets", function() + do CASE "can find single relation" + local world = jecs.world() + + local Alice = world:entity() + local Bob = world:entity() + local Likes = world:entity() + + world:add(Alice, jecs.pair(Likes, Bob)) + + local i = 0 + for target in world:targets(Alice, Likes) do + i += 1 + CHECK(target == Bob) + end + CHECK(i == 1) + end + do CASE "basic iteration" + local world = jecs.world() + + local ROOT = world:entity() + local e1 = world:entity() + local targets = {} + + for i = 1, 10 do + local target = world:entity() + targets[i] = target + world:add(e1, pair(ROOT, target)) + end + + local i = 0 + for target in world:targets(e1, ROOT) do + i += 1 + CHECK(targets[i] == target) + end + + CHECK(i == 10) + end + do CASE "multiple iterations" + local world = jecs.world() + + local ROOT = world:entity() + local OTHER = world:entity() + + local e = world:entity() + + local root_targets = {} + local other_targets = {} + + for i = 1, 5 do + local t = world:entity() + root_targets[i] = t + world:add(e, pair(ROOT, t)) + end + + for i = 1, 3 do + local t = world:entity() + other_targets[i] = t + world:add(e, pair(OTHER, t)) + end + + local i = 0 + for target in world:targets(e, ROOT) do + i += 1 + CHECK(root_targets[i] == target) + end + CHECK(i == 5) + + local j = 0 + for target in world:targets(e, OTHER) do + j += 1 + CHECK(other_targets[j] == target) + end + CHECK(j == 3) + end + do CASE "empty iterator" + local world = jecs.world() + + local ROOT = world:entity() + local OTHER = world:entity() + + local e = world:entity() + + world:add(e, pair(ROOT, world:entity())) + + local count = 0 + for _ in world:targets(e, OTHER) do + count += 1 + end + + CHECK(count == 0) + end + do CASE "ignore deleted targets" + local world = jecs.world() + + local ROOT = world:entity() + local e = world:entity() + + local alive = {} + local dead = {} + + for i = 1, 3 do + local t = world:entity() + alive[#alive + 1] = t + world:add(e, pair(ROOT, t)) + end + + for i = 1, 2 do + local t = world:entity() + dead[#dead + 1] = t + world:add(e, pair(ROOT, t)) + world:delete(t) + end + + local count = 0 + for t in world:targets(e, ROOT) do + count += 1 + CHECK(t ~= nil) + end + + CHECK(count == #alive) + end +end) + TEST("world:target", function() do CASE "nth index" local world = jecs.world() From 8885a4d6140ee6b00633ac418a78f139fea41aaa Mon Sep 17 00:00:00 2001 From: kurokuukyo Date: Mon, 20 Apr 2026 17:58:03 -0400 Subject: [PATCH 04/17] pull idr.records index out of iterator --- src/jecs.luau | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/jecs.luau b/src/jecs.luau index 08f8dca..d16ee4e 100755 --- a/src/jecs.luau +++ b/src/jecs.luau @@ -3334,6 +3334,7 @@ local function world_new(DEBUG: boolean?) return NOOP :: () -> i53 end + local idr_record = idr.records[archetype_id] local nth = 0 return function(): i53? @@ -3341,7 +3342,7 @@ local function world_new(DEBUG: boolean?) return nil end local target = entity_index_get_alive(world.entity_index, - ECS_PAIR_SECOND(archetype.types[nth + idr.records[archetype_id]])) + ECS_PAIR_SECOND(archetype.types[nth + idr_record])) nth += 1 return target end From abaacd9ffdca2edc20a21cbb64f93b565291a9a5 Mon Sep 17 00:00:00 2001 From: kurokuukyo Date: Mon, 20 Apr 2026 17:58:43 -0400 Subject: [PATCH 05/17] move tests below world:target --- test/tests.luau | 202 ++++++++++++++++++++++++------------------------ 1 file changed, 101 insertions(+), 101 deletions(-) diff --git a/test/tests.luau b/test/tests.luau index 760267d..07b063c 100755 --- a/test/tests.luau +++ b/test/tests.luau @@ -3077,6 +3077,107 @@ TEST("world:set()", function() end end) +TEST("world:target", function() + do CASE "nth index" + local world = jecs.world() + local A = world:component() + world:set(A, jecs.Name, "A") + local B = world:component() + world:set(B, jecs.Name, "B") + local C = world:component() + world:set(C, jecs.Name, "C") + local D = world:component() + world:set(D, jecs.Name, "D") + local E = world:component() + world:set(E, jecs.Name, "E") + local e = world:entity() + + world:add(e, pair(A, B)) + world:add(e, pair(A, C)) + world:add(e, pair(A, D)) + world:add(e, pair(A, E)) + world:add(e, pair(B, C)) + world:add(e, pair(B, D)) + world:add(e, pair(C, D)) + + CHECK((pair(A, B) :: any) < (pair(A, C) :: any)) + CHECK((pair(A, C) :: any) < (pair(A, D) :: any)) + CHECK((pair(C, A) :: any) < (pair(C, D) :: any)) + + CHECK(jecs.pair_first(world, pair(B, C)) == B) + local r = (jecs.entity_index_try_get(world.entity_index :: any, e :: any) :: any) :: jecs.Record + local archetype = r.archetype + local function cdr(id) + return assert(jecs.component_record(world, id)) + end + local idr_b_c = cdr(pair(B, C)) + local idr_a_wc = cdr(pair(A, __)) + local idr_a_e = cdr(pair(A, E)) + + CHECK(idr_a_wc.counts[archetype.id] == 4) + CHECK(idr_b_c.records[archetype.id] > idr_a_e.records[archetype.id]) + CHECK(world:target(e, A, 0) == B) + CHECK(world:target(e, A, 1) == C) + CHECK(world:target(e, A, 2) == D) + CHECK(world:target(e, A, 3) == E) + CHECK(world:target(e, B, 0) == C) + CHECK(world:target(e, B, 1) == D) + CHECK(world:target(e, C, 0) == D) + CHECK(world:target(e, C, 1) == nil) + + CHECK(cdr(pair(A, B)).records[archetype.id] == 1) + CHECK(cdr(pair(A, C)).records[archetype.id] == 2) + CHECK(cdr(pair(A, D)).records[archetype.id] == 3) + CHECK(cdr(pair(A, E)).records[archetype.id] == 4) + + CHECK(world:target(e, C, 0) == D) + CHECK(world:target(e, C, 1) == nil) + end + + do CASE "infer index when unspecified" + local world = jecs.world() + local A = world:component() + local B = world:component() + local C = world:component() + local D = world:component() + local e = world:entity() + + world:add(e, pair(A, B)) + world:add(e, pair(A, C)) + world:add(e, pair(B, C)) + world:add(e, pair(B, D)) + world:add(e, pair(C, D)) + + CHECK(world:target(e, A) == world:target(e, A, 0)) + CHECK(world:target(e, B) == world:target(e, B, 0)) + CHECK(world:target(e, C) == world:target(e, C, 0)) + end + + do CASE "loop until no target" + local world = jecs.world() + + local ROOT = world:entity() + local e1 = world:entity() + local targets = {} + + for i = 1, 10 do + local target = world:entity() + targets[i] = target + world:add(e1, pair(ROOT, target)) + end + + local i = 0 + local target = world:target(e1, ROOT, 0) + while target do + i += 1 + CHECK(targets[i] == target) + target = world:target(e1, ROOT, i) + end + + CHECK(i == 10) + end +end) + TEST("world:targets", function() do CASE "can find single relation" local world = jecs.world() @@ -3201,107 +3302,6 @@ TEST("world:targets", function() end end) -TEST("world:target", function() - do CASE "nth index" - local world = jecs.world() - local A = world:component() - world:set(A, jecs.Name, "A") - local B = world:component() - world:set(B, jecs.Name, "B") - local C = world:component() - world:set(C, jecs.Name, "C") - local D = world:component() - world:set(D, jecs.Name, "D") - local E = world:component() - world:set(E, jecs.Name, "E") - local e = world:entity() - - world:add(e, pair(A, B)) - world:add(e, pair(A, C)) - world:add(e, pair(A, D)) - world:add(e, pair(A, E)) - world:add(e, pair(B, C)) - world:add(e, pair(B, D)) - world:add(e, pair(C, D)) - - CHECK((pair(A, B) :: any) < (pair(A, C) :: any)) - CHECK((pair(A, C) :: any) < (pair(A, D) :: any)) - CHECK((pair(C, A) :: any) < (pair(C, D) :: any)) - - CHECK(jecs.pair_first(world, pair(B, C)) == B) - local r = (jecs.entity_index_try_get(world.entity_index :: any, e :: any) :: any) :: jecs.Record - local archetype = r.archetype - local function cdr(id) - return assert(jecs.component_record(world, id)) - end - local idr_b_c = cdr(pair(B, C)) - local idr_a_wc = cdr(pair(A, __)) - local idr_a_e = cdr(pair(A, E)) - - CHECK(idr_a_wc.counts[archetype.id] == 4) - CHECK(idr_b_c.records[archetype.id] > idr_a_e.records[archetype.id]) - CHECK(world:target(e, A, 0) == B) - CHECK(world:target(e, A, 1) == C) - CHECK(world:target(e, A, 2) == D) - CHECK(world:target(e, A, 3) == E) - CHECK(world:target(e, B, 0) == C) - CHECK(world:target(e, B, 1) == D) - CHECK(world:target(e, C, 0) == D) - CHECK(world:target(e, C, 1) == nil) - - CHECK(cdr(pair(A, B)).records[archetype.id] == 1) - CHECK(cdr(pair(A, C)).records[archetype.id] == 2) - CHECK(cdr(pair(A, D)).records[archetype.id] == 3) - CHECK(cdr(pair(A, E)).records[archetype.id] == 4) - - CHECK(world:target(e, C, 0) == D) - CHECK(world:target(e, C, 1) == nil) - end - - do CASE "infer index when unspecified" - local world = jecs.world() - local A = world:component() - local B = world:component() - local C = world:component() - local D = world:component() - local e = world:entity() - - world:add(e, pair(A, B)) - world:add(e, pair(A, C)) - world:add(e, pair(B, C)) - world:add(e, pair(B, D)) - world:add(e, pair(C, D)) - - CHECK(world:target(e, A) == world:target(e, A, 0)) - CHECK(world:target(e, B) == world:target(e, B, 0)) - CHECK(world:target(e, C) == world:target(e, C, 0)) - end - - do CASE "loop until no target" - local world = jecs.world() - - local ROOT = world:entity() - local e1 = world:entity() - local targets = {} - - for i = 1, 10 do - local target = world:entity() - targets[i] = target - world:add(e1, pair(ROOT, target)) - end - - local i = 0 - local target = world:target(e1, ROOT, 0) - while target do - i += 1 - CHECK(targets[i] == target) - target = world:target(e1, ROOT, i) - end - - CHECK(i == 10) - end -end) - TEST("#adding a recycled target", function() local world = jecs.world() local R = world:component() From ce47155ebea660e9c678c522d05ef68b50e8c969 Mon Sep 17 00:00:00 2001 From: kurokuukyo Date: Mon, 20 Apr 2026 18:00:24 -0400 Subject: [PATCH 06/17] style --- test/tests.luau | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/tests.luau b/test/tests.luau index 07b063c..d89418a 100755 --- a/test/tests.luau +++ b/test/tests.luau @@ -3077,7 +3077,7 @@ TEST("world:set()", function() end end) -TEST("world:target", function() +TEST("world:target()", function() do CASE "nth index" local world = jecs.world() local A = world:component() @@ -3178,7 +3178,7 @@ TEST("world:target", function() end end) -TEST("world:targets", function() +TEST("world:targets()", function() do CASE "can find single relation" local world = jecs.world() From f79b894f86ecdbcee4c13ced1de06e433813522b Mon Sep 17 00:00:00 2001 From: kurokuukyo Date: Mon, 20 Apr 2026 18:01:12 -0400 Subject: [PATCH 07/17] better test names --- test/tests.luau | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/tests.luau b/test/tests.luau index d89418a..74f2081 100755 --- a/test/tests.luau +++ b/test/tests.luau @@ -3179,7 +3179,7 @@ TEST("world:target()", function() end) TEST("world:targets()", function() - do CASE "can find single relation" + do CASE "should find single relation" local world = jecs.world() local Alice = world:entity() @@ -3270,7 +3270,7 @@ TEST("world:targets()", function() CHECK(count == 0) end - do CASE "ignore deleted targets" + do CASE "should ignore deleted targets" local world = jecs.world() local ROOT = world:entity() From 5a198495c89524fee152e8533f29e72c79c9c095 Mon Sep 17 00:00:00 2001 From: kurokuukyo Date: Mon, 20 Apr 2026 20:08:14 -0400 Subject: [PATCH 08/17] change nth to use idr.records and pull out variables from iter --- src/jecs.luau | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/jecs.luau b/src/jecs.luau index d16ee4e..a602052 100755 --- a/src/jecs.luau +++ b/src/jecs.luau @@ -3334,15 +3334,18 @@ local function world_new(DEBUG: boolean?) return NOOP :: () -> i53 end - local idr_record = idr.records[archetype_id] - local nth = 0 + local nth = idr.records[archetype_id] + local end_count = nth + count + + local entity_idx = world.entity_index + local archetype_types = archetype.types return function(): i53? - if nth == count then + if nth == end_count then return nil end - local target = entity_index_get_alive(world.entity_index, - ECS_PAIR_SECOND(archetype.types[nth + idr_record])) + local target = entity_index_get_alive(entity_idx, + ECS_PAIR_SECOND(archetype_types[nth])) nth += 1 return target end From b954588b0e67ba04d767ba8aae30aae20a734616 Mon Sep 17 00:00:00 2001 From: kurokuukyo Date: Mon, 20 Apr 2026 20:11:08 -0400 Subject: [PATCH 09/17] local NOOP fn --- src/jecs.luau | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/jecs.luau b/src/jecs.luau index a602052..6fbfec5 100755 --- a/src/jecs.luau +++ b/src/jecs.luau @@ -3311,27 +3311,29 @@ local function world_new(DEBUG: boolean?) end local function world_targets(world: world, entity: i53, relation: i53): () -> i53? + local NOOP = NOOP :: () -> i53 + local record = entity_index_try_get_unsafe(entity) if not record then - return NOOP :: () -> i53 + return NOOP end local archetype = record.archetype if not archetype then - return NOOP :: () -> i53 + return NOOP end local r = ECS_PAIR(relation, EcsWildcard) local idr = world.component_index[r] if not idr then - return NOOP :: () -> i53 + return NOOP end local archetype_id = archetype.id local count = idr.counts[archetype_id] if not count then - return NOOP :: () -> i53 + return NOOP end local nth = idr.records[archetype_id] From 681065e3e7e35912ab2ea396a70af400c30f27c5 Mon Sep 17 00:00:00 2001 From: kurokuukyo Date: Mon, 20 Apr 2026 20:13:39 -0400 Subject: [PATCH 10/17] pull NOOP out of fn --- src/jecs.luau | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jecs.luau b/src/jecs.luau index 6fbfec5..26dca6f 100755 --- a/src/jecs.luau +++ b/src/jecs.luau @@ -3310,9 +3310,9 @@ local function world_new(DEBUG: boolean?) ECS_PAIR_SECOND(nth)) end + local NOOP = NOOP :: () -> i53 + local function world_targets(world: world, entity: i53, relation: i53): () -> i53? - local NOOP = NOOP :: () -> i53 - local record = entity_index_try_get_unsafe(entity) if not record then return NOOP From 3150a8bd7a01a28879876bc7980fdb4984bb4723 Mon Sep 17 00:00:00 2001 From: kurokuukyo Date: Mon, 20 Apr 2026 20:17:00 -0400 Subject: [PATCH 11/17] redeclare component_index as ct_idx --- src/jecs.luau | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/jecs.luau b/src/jecs.luau index 26dca6f..d23d0c0 100755 --- a/src/jecs.luau +++ b/src/jecs.luau @@ -3311,7 +3311,7 @@ local function world_new(DEBUG: boolean?) end local NOOP = NOOP :: () -> i53 - + local function world_targets(world: world, entity: i53, relation: i53): () -> i53? local record = entity_index_try_get_unsafe(entity) if not record then @@ -3324,7 +3324,8 @@ local function world_new(DEBUG: boolean?) end local r = ECS_PAIR(relation, EcsWildcard) - local idr = world.component_index[r] + local ct_idx = world.component_index + local idr = ct_idx[r] if not idr then return NOOP From a062172a9b9f8429152f62e407feb83448604afe Mon Sep 17 00:00:00 2001 From: kurokuukyo Date: Mon, 20 Apr 2026 20:38:17 -0400 Subject: [PATCH 12/17] black magic (inlined most of the function calls in iterator - yes it still passes tests) --- src/jecs.luau | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/jecs.luau b/src/jecs.luau index d23d0c0..0aa36a5 100755 --- a/src/jecs.luau +++ b/src/jecs.luau @@ -3340,15 +3340,15 @@ local function world_new(DEBUG: boolean?) local nth = idr.records[archetype_id] local end_count = nth + count - local entity_idx = world.entity_index local archetype_types = archetype.types + local sparse_array = entity_index.sparse_array + local dense_array = entity_index.dense_array return function(): i53? if nth == end_count then return nil end - local target = entity_index_get_alive(entity_idx, - ECS_PAIR_SECOND(archetype_types[nth])) + local target = dense_array[sparse_array[ECS_ID(ECS_PAIR_SECOND(archetype_types[nth]))].dense] nth += 1 return target end From eeb194cff9e89bf1c0740868441b916262eefd8c Mon Sep 17 00:00:00 2001 From: kurokuukyo Date: Mon, 20 Apr 2026 21:01:14 -0400 Subject: [PATCH 13/17] remove redundant fn call (shoutout to nnullcolumn for spotting this) --- src/jecs.luau | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jecs.luau b/src/jecs.luau index 0aa36a5..453bc88 100755 --- a/src/jecs.luau +++ b/src/jecs.luau @@ -3348,7 +3348,7 @@ local function world_new(DEBUG: boolean?) if nth == end_count then return nil end - local target = dense_array[sparse_array[ECS_ID(ECS_PAIR_SECOND(archetype_types[nth]))].dense] + local target = dense_array[sparse_array[ECS_PAIR_SECOND(archetype_types[nth])].dense] nth += 1 return target end From 7dfe511e1a99bd2491b46a1e4c08f500d64965af Mon Sep 17 00:00:00 2001 From: kurokuukyo Date: Tue, 21 Apr 2026 06:55:58 -0400 Subject: [PATCH 14/17] add test for rapid add/remove calls --- test/tests.luau | 75 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/test/tests.luau b/test/tests.luau index 74f2081..b0cee91 100755 --- a/test/tests.luau +++ b/test/tests.luau @@ -3300,6 +3300,81 @@ TEST("world:targets()", function() CHECK(count == #alive) end + do CASE "should properly handle rapid add/remove calls" + local world = jecs.world() + + local ROOT = world:entity() + local e = world:entity() + + local alive = {} + local all_targets = {} :: {Entity} + + for i = 1, 100 do + local t = world:entity() + all_targets[#all_targets + 1] = t + + world:add(e, jecs.pair(ROOT, t)) + alive[t] = true + end + + for i = 1, 500 do + local t: Entity + while t == nil do + t = all_targets[math.random(#all_targets)] + end + + if math.random() < 0.5 then + world:remove(e, jecs.pair(ROOT, t)) + alive[t] = nil + else + world:add(e, jecs.pair(ROOT, t)) + alive[t] = true + end + end + + local entity_index = world.entity_index + local function entity_index_check_alive(entity) + local r = entity_index.sparse_array[ECS_ID(entity)] + + if not r or r.dense == 0 then + return false + end + + local dense = r.dense + if dense > entity_index.alive_count then + return false + end + return true + + end + + for i=1, 10 do + local seen = {} + for t in world:targets(e, ROOT) do + CHECK(entity_index_check_alive(t)) + CHECK(entity_index_check_alive(jecs.pair(ROOT, t))) + + CHECK(alive[t] == true) + + CHECK(seen[t] == nil) + seen[t] = true + end + + for t, _ in pairs(alive) do + CHECK(seen[t] == true) + end + + for t in world:targets(e, ROOT) do + if math.random() < 0.5 then + world:remove(e, jecs.pair(ROOT, t)) + alive[t] = nil + else + world:add(e, jecs.pair(ROOT, t)) + alive[t] = true + end + end + end + end end) TEST("#adding a recycled target", function() From 65b6629c8f9b037b788f3d88809ff6149d6f560c Mon Sep 17 00:00:00 2001 From: kurokuukyo Date: Tue, 21 Apr 2026 07:01:06 -0400 Subject: [PATCH 15/17] run secondary mixing step for all entities and not just the alive ones --- test/tests.luau | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/tests.luau b/test/tests.luau index b0cee91..f9e82e8 100755 --- a/test/tests.luau +++ b/test/tests.luau @@ -3364,7 +3364,12 @@ TEST("world:targets()", function() CHECK(seen[t] == true) end - for t in world:targets(e, ROOT) do + for j = 1, #all_targets do + local t: Entity + while t == nil do + t = all_targets[j] + end + if math.random() < 0.5 then world:remove(e, jecs.pair(ROOT, t)) alive[t] = nil From 3077e6baa4fcb760d7e0d1dbbb6f5f9ed88c3e9f Mon Sep 17 00:00:00 2001 From: kurokuukyo Date: Tue, 21 Apr 2026 07:01:58 -0400 Subject: [PATCH 16/17] redundant check --- test/tests.luau | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/tests.luau b/test/tests.luau index f9e82e8..87bfb2c 100755 --- a/test/tests.luau +++ b/test/tests.luau @@ -3365,10 +3365,7 @@ TEST("world:targets()", function() end for j = 1, #all_targets do - local t: Entity - while t == nil do - t = all_targets[j] - end + local t = all_targets[j] if math.random() < 0.5 then world:remove(e, jecs.pair(ROOT, t)) From 3dfde5feb4b4ad6d99841a13a2a2677adfea4beb Mon Sep 17 00:00:00 2001 From: kurokuukyo Date: Tue, 21 Apr 2026 07:04:55 -0400 Subject: [PATCH 17/17] be a bit more explicit for checking if an entity is alive --- test/tests.luau | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tests.luau b/test/tests.luau index 87bfb2c..7aeab8e 100755 --- a/test/tests.luau +++ b/test/tests.luau @@ -3344,7 +3344,7 @@ TEST("world:targets()", function() if dense > entity_index.alive_count then return false end - return true + return entity_index.dense_array[dense] ~= nil end