From 11f9615495bb6ac40b93318e12d5e7172772af47 Mon Sep 17 00:00:00 2001 From: Magic <64741104+Mark-Marks@users.noreply.github.com> Date: Sat, 12 Oct 2024 17:04:49 +0200 Subject: [PATCH 1/2] fix: `query:archetypes()` didnt take self in type (#143) --- src/init.luau | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.luau b/src/init.luau index 503e32c..55faacd 100644 --- a/src/init.luau +++ b/src/init.luau @@ -1681,7 +1681,7 @@ type Query = typeof(setmetatable({}, { with: (self: Query, ...i53) -> Query, without: (self: Query, ...i53) -> Query, replace: (self: Query, (T...) -> U...) -> (), - archetypes: () -> { Archetype }, + archetypes: (self: Query) -> { Archetype }, } export type World = { From c5e20aaf508af902cdbf3511a27534c745e2177c Mon Sep 17 00:00:00 2001 From: Marcus Date: Sat, 12 Oct 2024 21:55:24 +0200 Subject: [PATCH 2/2] Reuse archetype_delete on world:clear (#141) * Reuse archetype_delete on world:clear * Add tests --- src/init.luau | 105 +++++++++++++++++++++++++----------------------- test/tests.luau | 42 +++++++++++++++++++ 2 files changed, 97 insertions(+), 50 deletions(-) diff --git a/src/init.luau b/src/init.luau index 55faacd..6cbbcf3 100644 --- a/src/init.luau +++ b/src/init.luau @@ -808,23 +808,6 @@ local function world_remove(world: World, entity: i53, id: i53) end end -local function world_clear(world: World, entity: i53) - --TODO: use sparse_get (stashed) - local record = world.entityIndex.sparse[entity] - if not record then - return - end - - local ROOT_ARCHETYPE = world.ROOT_ARCHETYPE - local archetype = record.archetype - - if archetype == nil or archetype == ROOT_ARCHETYPE then - return - end - - entity_move(world.entityIndex, entity, record, ROOT_ARCHETYPE) -end - local function archetype_fast_delete_last(columns: { Column }, column_count: number, types: { i53 }, entity: i53) for i, column in columns do if column ~= NULL_ARRAY then @@ -842,6 +825,59 @@ local function archetype_fast_delete(columns: { Column }, column_count: number, end end +local function archetype_delete(world: World, archetype: Archetype, row: number, destruct: boolean?) + local entityIndex = world.entityIndex + local columns = archetype.columns + local types = archetype.types + local entities = archetype.entities + local column_count = #entities + local last = #entities + local move = entities[last] + local delete = entities[row] + entities[row] = move + entities[last] = nil + + if row ~= last then + -- TODO: should be "entity_index_sparse_get(entityIndex, move)" + local record_to_move = entityIndex.sparse[move] + if record_to_move then + record_to_move.row = row + end + end + + -- TODO: if last == 0 then deactivate table + + for _, id in types do + invoke_hook(world, EcsOnRemove, id, delete) + end + + if row == last then + archetype_fast_delete_last(columns, column_count, types, delete) + else + archetype_fast_delete(columns, column_count, row, types, delete) + end +end + +local function world_clear(world: World, entity: i53) + --TODO: use sparse_get (stashed) + local record = world.entityIndex.sparse[entity] + if not record then + return + end + + local archetype = record.archetype + local row = record.row + + if archetype then + -- In the future should have a destruct mode for + -- deleting archetypes themselves. Maybe requires recycling + archetype_delete(world, archetype, row) + end + + record.archetype = nil + record.row = nil +end + local function archetype_disconnect_edge(edge: GraphEdge) local edge_next = edge.next local edge_prev = edge.prev @@ -936,41 +972,10 @@ local function world_cleanup(world) world.archetypeIndex = new_archetype_map end + + local world_delete: (world: World, entity: i53, destruct: boolean?) -> () do - local function archetype_delete(world: World, archetype: Archetype, row: number, destruct: boolean?) - local entityIndex = world.entityIndex - local columns = archetype.columns - local types = archetype.types - local entities = archetype.entities - local column_count = #entities - local last = #entities - local move = entities[last] - local delete = entities[row] - entities[row] = move - entities[last] = nil - - if row ~= last then - -- TODO: should be "entity_index_sparse_get(entityIndex, move)" - local record_to_move = entityIndex.sparse[move] - if record_to_move then - record_to_move.row = row - end - end - - -- TODO: if last == 0 then deactivate table - - for _, id in types do - invoke_hook(world, EcsOnRemove, id, delete) - end - - if row == last then - archetype_fast_delete_last(columns, column_count, types, delete) - else - archetype_fast_delete(columns, column_count, row, types, delete) - end - end - function world_delete(world: World, entity: i53, destruct: boolean?) local entityIndex = world.entityIndex local sparse_array = entityIndex.sparse diff --git a/test/tests.luau b/test/tests.luau index af5b446..4a4e4f3 100644 --- a/test/tests.luau +++ b/test/tests.luau @@ -827,6 +827,48 @@ TEST("world:clear()", function() CHECK(world:get(e, A) == nil) CHECK(world:get(e, B) == nil) end + + do CASE "should move last record" + local world = world_new() + local A = world:component() + + local e = world:entity() + local e1 = world:entity() + + world:add(e, A) + world:add(e1, A) + + local archetype = world.archetypeIndex["1"] + local archetype_entities = archetype.entities + + local _e = e :: number + local _e1 = e1 :: number + + CHECK(archetype_entities[1] == _e) + CHECK(archetype_entities[2] == _e1) + + local sparse_array = world.entityIndex.sparse + local e_record = sparse_array[e] + local e1_record = sparse_array[e1] + CHECK(e_record.archetype == archetype) + CHECK(e1_record.archetype == archetype) + CHECK(e1_record.row == 2) + + world:clear(e) + + CHECK(e_record.archetype == nil) + CHECK(e_record.row == nil) + CHECK(e1_record.archetype == archetype) + CHECK(e1_record.row == 1) + + CHECK(archetype_entities[1] == _e1) + CHECK(archetype_entities[2] == nil) + + CHECK(world:contains(e) == true) + CHECK(world:has(e, A) == false) + CHECK(world:contains(e1) == true) + CHECK(world:has(e1, A) == true) + end end) TEST("world:has()", function()