From a1440f26df5c52d15fb1de37eb15978588824c7d Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 2 Feb 2026 17:32:07 +0700 Subject: [PATCH 01/18] size hint for some internal list functions --- evolved.lua | 78 +++++++++++++++++++++++------------------------------ 1 file changed, 34 insertions(+), 44 deletions(-) diff --git a/evolved.lua b/evolved.lua index 36efd2d..90f346b 100644 --- a/evolved.lua +++ b/evolved.lua @@ -798,10 +798,11 @@ end ---@generic V ---@param list V[] +---@param size? integer ---@return V[] ---@nodiscard -function __list_dup(list) - local list_size = #list +function __list_dup(list, size) + local list_size = size or #list if list_size == 0 then return {} @@ -820,10 +821,11 @@ end ---@param list V[] ---@param item V ---@param comp? fun(a: V, b: V): boolean +---@param size? integer ---@return integer lower_bound_index ---@nodiscard -function __list_lwr(list, item, comp) - local lower, upper = 1, #list +function __list_lwr(list, item, comp, size) + local lower, upper = 1, size or #list if comp then while lower <= upper do @@ -858,6 +860,28 @@ end --- --- +local __table_dup + +---@generic K, V +---@param table table +---@return table +---@nodiscard +function __table_dup(table) + local dup_table = {} + + for k, v in __lua_next, table do + dup_table[k] = v + end + + return dup_table +end + +--- +--- +--- +--- +--- + ---@class (exact) evolved.assoc_list: { --- __item_set: { [K]: integer }, --- __item_list: K[], @@ -1407,7 +1431,7 @@ end function __add_root_chunk(root) local root_index = __list_lwr(__root_list, root, function(a, b) return a.__fragment < b.__fragment - end) + end, __root_count) for sib_root_index = __root_count, root_index, -1 do local sib_root = __root_list[sib_root_index] @@ -1452,7 +1476,7 @@ end function __add_child_chunk(child, parent) local child_index = __list_lwr(parent.__child_list, child, function(a, b) return a.__fragment < b.__fragment - end) + end, parent.__child_count) for sib_child_index = parent.__child_count, child_index, -1 do local sib_child = parent.__child_list[sib_child_index] @@ -6226,47 +6250,13 @@ function __evolved_collect_garbage() end do - ---@type table - local new_entity_chunks = {} - - for entity_primary, entity_chunk in __lua_next, __entity_chunks do - new_entity_chunks[entity_primary] = entity_chunk - end - - __entity_chunks = new_entity_chunks + __entity_chunks = __table_dup(__entity_chunks) + __entity_places = __table_dup(__entity_places) end do - ---@type table - local new_entity_places = {} - - for entity_primary, entity_place in __lua_next, __entity_places do - new_entity_places[entity_primary] = entity_place - end - - __entity_places = new_entity_places - end - - do - ---@type integer[] - local new_defer_points = __lua_table_new(__defer_depth) - - __lua_table_move( - __defer_points, 1, __defer_depth, - 1, new_defer_points) - - __defer_points = new_defer_points - end - - do - ---@type any[] - local new_defer_bytecode = __lua_table_new(__defer_length) - - __lua_table_move( - __defer_bytecode, 1, __defer_length, - 1, new_defer_bytecode) - - __defer_bytecode = new_defer_bytecode + __defer_points = __list_dup(__defer_points, __defer_depth) + __defer_bytecode = __list_dup(__defer_bytecode, __defer_length) end __evolved_commit() From d1105c15ad7fca12cc5a6eec108426541278a562 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 2 Feb 2026 20:46:50 +0700 Subject: [PATCH 02/18] non-shrinking version of garbage collection --- README.md | 24 +++++++++++-- develop/ROADMAP.md | 1 - develop/fuzzing/batch_destroy_fuzz.lua | 4 +-- develop/fuzzing/destroy_fuzz.lua | 4 +-- develop/fuzzing/execute_fuzz.lua | 4 +-- develop/fuzzing/explicit_fuzz.lua | 4 +-- develop/fuzzing/requires_fuzz.lua | 6 ++-- develop/fuzzing/unique_fuzz.lua | 4 +-- evolved.d.tl | 2 +- evolved.lua | 47 ++++++++++++++------------ 10 files changed, 61 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index b8da5c4..0ccb7b0 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ - [Fragment Requirements](#fragment-requirements) - [Destruction Policies](#destruction-policies) - [Custom Component Storages](#custom-component-storages) + - [Garbage Collection](#garbage-collection) - [Cheat Sheet](#cheat-sheet) - [Aliases](#aliases) - [Predefs](#predefs) @@ -1346,6 +1347,24 @@ evolved.builder() evolved.process_with(MOVEMENT_SYSTEM, 0.016) ``` +### Garbage Collection + +While using the library, some internal data structures can become obsolete and should be cleaned up to free memory. For example, empty chunks that no longer contain entities can be removed. Component storages can also have unused capacity that can be shrunk to save memory. The library provides a function to control this garbage collection process. + +```lua +---@param no_shrink? boolean +function evolved.collect_garbage(no_shrink) end +``` + +By default, [`evolved.collect_garbage`](#evolvedcollect_garbage) cleans up obsolete data structures and shrinks component storages to fit their current size. If you pass `true`, it only cleans up obsolete data structures and skips shrinking. This avoids the overhead of resizing storages, which can be expensive. + +Call this function periodically to keep memory usage under control. It is best to call it between levels or during loading screens when performance is not critical. Also, call Lua's built-in garbage collector afterward to ensure all unused memory is freed. + +```lua +evolved.collect_garbage() +collectgarbage('collect') +``` + ## Cheat Sheet ### Aliases @@ -1481,7 +1500,7 @@ process :: system... -> () process_with :: system, ... -> () debug_mode :: boolean -> () -collect_garbage :: () +collect_garbage :: boolean? -> () ``` ### Classes @@ -2007,7 +2026,8 @@ function evolved.debug_mode(yesno) end ### `evolved.collect_garbage` ```lua -function evolved.collect_garbage() end +---@param no_shrink? boolean +function evolved.collect_garbage(no_shrink) end ``` ## Classes diff --git a/develop/ROADMAP.md b/develop/ROADMAP.md index 863bc8d..85b9616 100644 --- a/develop/ROADMAP.md +++ b/develop/ROADMAP.md @@ -9,7 +9,6 @@ ## Thoughts - We should have a way to not copy components on deferred spawn/clone -- Having a light version of the gargabe collector can be useful for some use-cases - Basic default component value as true looks awful, should we use something else? ## Known Issues diff --git a/develop/fuzzing/batch_destroy_fuzz.lua b/develop/fuzzing/batch_destroy_fuzz.lua index 858270b..c1eba25 100644 --- a/develop/fuzzing/batch_destroy_fuzz.lua +++ b/develop/fuzzing/batch_destroy_fuzz.lua @@ -118,11 +118,11 @@ end --- if math.random(1, 2) == 1 then - evo.collect_garbage() + evo.collect_garbage(math.random(1, 2) == 1) end evo.destroy(__table_unpack(all_entity_list)) if math.random(1, 2) == 1 then - evo.collect_garbage() + evo.collect_garbage(math.random(1, 2) == 1) end diff --git a/develop/fuzzing/destroy_fuzz.lua b/develop/fuzzing/destroy_fuzz.lua index 0414da1..a274c10 100644 --- a/develop/fuzzing/destroy_fuzz.lua +++ b/develop/fuzzing/destroy_fuzz.lua @@ -124,11 +124,11 @@ end --- if math.random(1, 2) == 1 then - evo.collect_garbage() + evo.collect_garbage(math.random(1, 2) == 1) end evo.destroy(__table_unpack(all_entity_list)) if math.random(1, 2) == 1 then - evo.collect_garbage() + evo.collect_garbage(math.random(1, 2) == 1) end diff --git a/develop/fuzzing/execute_fuzz.lua b/develop/fuzzing/execute_fuzz.lua index 044c007..25fa377 100644 --- a/develop/fuzzing/execute_fuzz.lua +++ b/develop/fuzzing/execute_fuzz.lua @@ -296,7 +296,7 @@ end --- if math.random(1, 2) == 1 then - evo.collect_garbage() + evo.collect_garbage(math.random(1, 2) == 1) end evo.destroy(__table_unpack(all_query_list)) @@ -304,5 +304,5 @@ evo.destroy(__table_unpack(all_entity_list)) evo.destroy(__table_unpack(all_fragment_list)) if math.random(1, 2) == 1 then - evo.collect_garbage() + evo.collect_garbage(math.random(1, 2) == 1) end diff --git a/develop/fuzzing/explicit_fuzz.lua b/develop/fuzzing/explicit_fuzz.lua index 2744268..adc17e1 100644 --- a/develop/fuzzing/explicit_fuzz.lua +++ b/develop/fuzzing/explicit_fuzz.lua @@ -78,11 +78,11 @@ end --- if math.random(1, 2) == 1 then - evo.collect_garbage() + evo.collect_garbage(math.random(1, 2) == 1) end evo.destroy(__table_unpack(all_entity_list)) if math.random(1, 2) == 1 then - evo.collect_garbage() + evo.collect_garbage(math.random(1, 2) == 1) end diff --git a/develop/fuzzing/requires_fuzz.lua b/develop/fuzzing/requires_fuzz.lua index 073f2f4..b6822bf 100644 --- a/develop/fuzzing/requires_fuzz.lua +++ b/develop/fuzzing/requires_fuzz.lua @@ -115,17 +115,17 @@ end if math.random(1, 2) == 1 then evo.destroy(__table_unpack(all_entity_list)) if math.random(1, 2) == 1 then - evo.collect_garbage() + evo.collect_garbage(math.random(1, 2) == 1) end evo.destroy(__table_unpack(all_fragment_list)) else evo.destroy(__table_unpack(all_fragment_list)) if math.random(1, 2) == 1 then - evo.collect_garbage() + evo.collect_garbage(math.random(1, 2) == 1) end evo.destroy(__table_unpack(all_entity_list)) end if math.random(1, 2) == 1 then - evo.collect_garbage() + evo.collect_garbage(math.random(1, 2) == 1) end diff --git a/develop/fuzzing/unique_fuzz.lua b/develop/fuzzing/unique_fuzz.lua index a572bd6..e24a4f8 100644 --- a/develop/fuzzing/unique_fuzz.lua +++ b/develop/fuzzing/unique_fuzz.lua @@ -64,11 +64,11 @@ end --- if math.random(1, 2) == 1 then - evo.collect_garbage() + evo.collect_garbage(math.random(1, 2) == 1) end evo.destroy(__table_unpack(all_entity_list)) if math.random(1, 2) == 1 then - evo.collect_garbage() + evo.collect_garbage(math.random(1, 2) == 1) end diff --git a/evolved.d.tl b/evolved.d.tl index 54d3ac4..76bcdcc 100644 --- a/evolved.d.tl +++ b/evolved.d.tl @@ -212,7 +212,7 @@ process_with: function(system: System, ...: any) debug_mode: function(yesno: boolean) - collect_garbage: function() + collect_garbage: function(no_shrink?: boolean) chunk: function(fragment: Fragment, ...: Fragment): Chunk, { Entity }, integer builder: function(): Builder diff --git a/evolved.lua b/evolved.lua index 90f346b..e40f5d1 100644 --- a/evolved.lua +++ b/evolved.lua @@ -6145,7 +6145,8 @@ function __evolved_debug_mode(yesno) __debug_mode = yesno end -function __evolved_collect_garbage() +---@param no_shrink boolean? +function __evolved_collect_garbage(no_shrink) if __defer_depth > 0 then __defer_call_hook(__evolved_collect_garbage) return @@ -6209,16 +6210,16 @@ function __evolved_collect_garbage() local postorder_chunk_entity_count = postorder_chunk.__entity_count local postorder_chunk_entity_capacity = postorder_chunk.__entity_capacity - local should_be_purged = + local can_be_purged = postorder_chunk_child_count == 0 and postorder_chunk_entity_count == 0 - local should_be_shrunk = + local can_be_shrunk = postorder_chunk_entity_count < postorder_chunk_entity_capacity - if should_be_purged then + if can_be_purged then __purge_chunk(postorder_chunk) - elseif should_be_shrunk then + elseif can_be_shrunk and not no_shrink then __shrink_chunk(postorder_chunk, 0) end end @@ -6234,29 +6235,31 @@ function __evolved_collect_garbage() end end - for table_pool_tag = 1, __table_pool_tag.__count do - local table_pool_reserve = __table_pool_reserve[table_pool_tag] + if not no_shrink then + for table_pool_tag = 1, __table_pool_tag.__count do + local table_pool_reserve = __table_pool_reserve[table_pool_tag] - ---@type evolved.table_pool - local new_table_pool = __lua_table_new(table_pool_reserve) + ---@type evolved.table_pool + local new_table_pool = __lua_table_new(table_pool_reserve) - for table_pool_index = 1, table_pool_reserve do - new_table_pool[table_pool_index] = {} - end + for table_pool_index = 1, table_pool_reserve do + new_table_pool[table_pool_index] = {} + end - new_table_pool.__size = table_pool_reserve + new_table_pool.__size = table_pool_reserve - __tagged_table_pools[table_pool_tag] = new_table_pool - end + __tagged_table_pools[table_pool_tag] = new_table_pool + end - do - __entity_chunks = __table_dup(__entity_chunks) - __entity_places = __table_dup(__entity_places) - end + do + __entity_chunks = __table_dup(__entity_chunks) + __entity_places = __table_dup(__entity_places) + end - do - __defer_points = __list_dup(__defer_points, __defer_depth) - __defer_bytecode = __list_dup(__defer_bytecode, __defer_length) + do + __defer_points = __list_dup(__defer_points, __defer_depth) + __defer_bytecode = __list_dup(__defer_bytecode, __defer_length) + end end __evolved_commit() From 260f841a3b1fbf3ffee8915311de72754a6fcda1 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 3 Feb 2026 06:11:58 +0700 Subject: [PATCH 03/18] fix some package usage --- develop/testing/realloc_tests.lua | 12 +++++++++--- evolved.lua | 27 +++++++++++++++++++++------ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/develop/testing/realloc_tests.lua b/develop/testing/realloc_tests.lua index 2d81905..7af3a90 100644 --- a/develop/testing/realloc_tests.lua +++ b/develop/testing/realloc_tests.lua @@ -2,9 +2,15 @@ local evo = require 'evolved' ---@type ffilib? local ffi = (function() - local ffi_loader = package and package.preload and package.preload['ffi'] - local ffi = ffi_loader and ffi_loader() - return ffi + if package and package.loaded then + local loaded_ffi = package.loaded.ffi + if loaded_ffi then return loaded_ffi end + end + + if package and package.preload then + local ffi_loader = package.preload.ffi + if ffi_loader then return ffi_loader() end + end end)() if not ffi then diff --git a/evolved.lua b/evolved.lua index e40f5d1..fc80580 100644 --- a/evolved.lua +++ b/evolved.lua @@ -243,8 +243,13 @@ local __lua_table_new = (function() if table_create then return function(nseq) return table_create(nseq or 0) end end end - do - local table_new_loader = package and package.preload and package.preload['table.new'] + if package and package.loaded then + local loaded_table_create = package.loaded.table and package.loaded.table.create + if loaded_table_create then return function(nseq) return loaded_table_create(nseq or 0) end end + end + + if package and package.preload then + local table_new_loader = package.preload['table.new'] local table_new = table_new_loader and table_new_loader() if table_new then return function(nseq) return table_new(nseq or 0, 0) end end end @@ -265,8 +270,13 @@ local __lua_table_clear = (function() if table_clear then return table_clear end end - do - local table_clear_loader = package and package.preload and package.preload['table.clear'] + if package and package.loaded then + local loaded_table_clear = package.loaded.table and package.loaded.table.clear + if loaded_table_clear then return loaded_table_clear end + end + + if package and package.preload then + local table_clear_loader = package.preload['table.clear'] local table_clear = table_clear_loader and table_clear_loader() if table_clear then return table_clear end end @@ -299,8 +309,13 @@ local __lua_table_move = (function() if table_move then return table_move end end - do - local table_move_loader = package and package.preload and package.preload['table.move'] + if package and package.loaded then + local loaded_table_move = package.loaded.table and package.loaded.table.move + if loaded_table_move then return loaded_table_move end + end + + if package and package.preload then + local table_move_loader = package.preload['table.move'] local table_move = table_move_loader and table_move_loader() if table_move then return table_move end end From 287f03ef8766079a3ee2f55ad347136049137c08 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 3 Feb 2026 06:12:29 +0700 Subject: [PATCH 04/18] enable the debug mode for all tests --- develop/testing/build_tests.lua | 2 ++ develop/testing/cancel_tests.lua | 2 ++ develop/testing/clone_tests.lua | 2 ++ develop/testing/depth_tests.lua | 2 ++ develop/testing/destroy_tests.lua | 2 ++ develop/testing/locate_tests.lua | 2 ++ develop/testing/mappers_tests.lua | 2 ++ develop/testing/multi_spawn_tests.lua | 2 ++ develop/testing/name_tests.lua | 2 ++ develop/testing/process_with_tests.lua | 2 ++ develop/testing/realloc_tests.lua | 2 ++ develop/testing/requires_fragment_tests.lua | 2 ++ develop/testing/spawn_tests.lua | 2 ++ develop/testing/system_as_query_tests.lua | 2 ++ 14 files changed, 28 insertions(+) diff --git a/develop/testing/build_tests.lua b/develop/testing/build_tests.lua index 626b88b..5d74faa 100644 --- a/develop/testing/build_tests.lua +++ b/develop/testing/build_tests.lua @@ -1,5 +1,7 @@ local evo = require 'evolved' +evo.debug_mode(true) + do local f1, f2 = evo.id(2) diff --git a/develop/testing/cancel_tests.lua b/develop/testing/cancel_tests.lua index 43d8b11..6829172 100644 --- a/develop/testing/cancel_tests.lua +++ b/develop/testing/cancel_tests.lua @@ -1,5 +1,7 @@ local evo = require 'evolved' +evo.debug_mode(true) + do assert(evo.defer()) assert(evo.cancel()) diff --git a/develop/testing/clone_tests.lua b/develop/testing/clone_tests.lua index 7b9edce..26b2d6e 100644 --- a/develop/testing/clone_tests.lua +++ b/develop/testing/clone_tests.lua @@ -1,5 +1,7 @@ local evo = require 'evolved' +evo.debug_mode(true) + do do local p = evo.spawn() diff --git a/develop/testing/depth_tests.lua b/develop/testing/depth_tests.lua index 15a0e6f..afc08d9 100644 --- a/develop/testing/depth_tests.lua +++ b/develop/testing/depth_tests.lua @@ -1,5 +1,7 @@ local evo = require 'evolved' +evo.debug_mode(true) + do assert(evo.depth() == 0) diff --git a/develop/testing/destroy_tests.lua b/develop/testing/destroy_tests.lua index 5325533..6b4c72a 100644 --- a/develop/testing/destroy_tests.lua +++ b/develop/testing/destroy_tests.lua @@ -1,5 +1,7 @@ local evo = require 'evolved' +evo.debug_mode(true) + do local e = evo.id() assert(evo.alive(e)) diff --git a/develop/testing/locate_tests.lua b/develop/testing/locate_tests.lua index 2c0723c..52a4b1a 100644 --- a/develop/testing/locate_tests.lua +++ b/develop/testing/locate_tests.lua @@ -1,5 +1,7 @@ local evo = require 'evolved' +evo.debug_mode(true) + do local e1, e2, f1, f2 = evo.id(4) diff --git a/develop/testing/mappers_tests.lua b/develop/testing/mappers_tests.lua index 1e84950..890e245 100644 --- a/develop/testing/mappers_tests.lua +++ b/develop/testing/mappers_tests.lua @@ -1,5 +1,7 @@ local evo = require 'evolved' +evo.debug_mode(true) + do local f1, f2 = evo.id(2) diff --git a/develop/testing/multi_spawn_tests.lua b/develop/testing/multi_spawn_tests.lua index 14dbd65..8908e17 100644 --- a/develop/testing/multi_spawn_tests.lua +++ b/develop/testing/multi_spawn_tests.lua @@ -1,5 +1,7 @@ local evo = require 'evolved' +evo.debug_mode(true) + do local entity_list diff --git a/develop/testing/name_tests.lua b/develop/testing/name_tests.lua index 21b2e08..5ba6177 100644 --- a/develop/testing/name_tests.lua +++ b/develop/testing/name_tests.lua @@ -1,5 +1,7 @@ local evo = require 'evolved' +evo.debug_mode(true) + do local id = evo.id() diff --git a/develop/testing/process_with_tests.lua b/develop/testing/process_with_tests.lua index 669eb75..b0fa703 100644 --- a/develop/testing/process_with_tests.lua +++ b/develop/testing/process_with_tests.lua @@ -1,5 +1,7 @@ local evo = require 'evolved' +evo.debug_mode(true) + do local f = evo.id() local e = evo.builder():set(f, 42):spawn() diff --git a/develop/testing/realloc_tests.lua b/develop/testing/realloc_tests.lua index 7af3a90..a54c6aa 100644 --- a/develop/testing/realloc_tests.lua +++ b/develop/testing/realloc_tests.lua @@ -1,5 +1,7 @@ local evo = require 'evolved' +evo.debug_mode(true) + ---@type ffilib? local ffi = (function() if package and package.loaded then diff --git a/develop/testing/requires_fragment_tests.lua b/develop/testing/requires_fragment_tests.lua index 89312f5..b4f8569 100644 --- a/develop/testing/requires_fragment_tests.lua +++ b/develop/testing/requires_fragment_tests.lua @@ -1,5 +1,7 @@ local evo = require 'evolved' +evo.debug_mode(true) + do local f1, f2 = evo.id(2) evo.set(f1, evo.REQUIRES) diff --git a/develop/testing/spawn_tests.lua b/develop/testing/spawn_tests.lua index 1927817..4f8272a 100644 --- a/develop/testing/spawn_tests.lua +++ b/develop/testing/spawn_tests.lua @@ -1,5 +1,7 @@ local evo = require 'evolved' +evo.debug_mode(true) + do do local e = evo.spawn() diff --git a/develop/testing/system_as_query_tests.lua b/develop/testing/system_as_query_tests.lua index 42effca..63d11f2 100644 --- a/develop/testing/system_as_query_tests.lua +++ b/develop/testing/system_as_query_tests.lua @@ -1,5 +1,7 @@ local evo = require 'evolved' +evo.debug_mode(true) + do local f1, f2, f3 = evo.id(3) From b7419eec5d53c3431dfa36f6c4b778215ae66558 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Thu, 5 Feb 2026 15:30:50 +0700 Subject: [PATCH 05/18] do not clear array part of builder component table --- evolved.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evolved.lua b/evolved.lua index fc80580..710b72a 100644 --- a/evolved.lua +++ b/evolved.lua @@ -6780,7 +6780,7 @@ end ---@return evolved.builder builder function __builder_mt:clear() self.__chunk = nil - __lua_table_clear(self.__component_table) + __lua_table_clear(self.__component_table, true, false) return self end From 5efd9ab2ae0c24c031d07e8485e604a1c14610ea Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 6 Feb 2026 07:57:26 +0700 Subject: [PATCH 06/18] ensure that entity_chunks and entity_places have only array-parts --- evolved.lua | 66 +++++++++++++++++------------------------------------ 1 file changed, 21 insertions(+), 45 deletions(-) diff --git a/evolved.lua b/evolved.lua index 710b72a..fe4622b 100644 --- a/evolved.lua +++ b/evolved.lua @@ -141,8 +141,8 @@ local __minor_chunks = {} ---@type table> local __major_queries = {} ---@type table> -local __entity_chunks = {} ---@type table -local __entity_places = {} ---@type table +local __entity_chunks = {} ---@type (evolved.chunk|false)[] +local __entity_places = {} ---@type integer[] local __sorted_includes = {} ---@type table> local __sorted_excludes = {} ---@type table> @@ -674,6 +674,9 @@ local function __acquire_id() local acquired_id = acquired_primary + shifted_secondary freelist_ids[acquired_primary] = acquired_id + __entity_chunks[acquired_primary] = false + __entity_places[acquired_primary] = 0 + return acquired_id --[[@as evolved.id]] end end @@ -875,28 +878,6 @@ end --- --- -local __table_dup - ----@generic K, V ----@param table table ----@return table ----@nodiscard -function __table_dup(table) - local dup_table = {} - - for k, v in __lua_next, table do - dup_table[k] = v - end - - return dup_table -end - ---- ---- ---- ---- ---- - ---@class (exact) evolved.assoc_list: { --- __item_set: { [K]: integer }, --- __item_list: K[], @@ -3364,8 +3345,8 @@ function __clear_entity_one(entity) if chunk then __detach_entity(chunk, place) - entity_chunks[entity_primary] = nil - entity_places[entity_primary] = nil + entity_chunks[entity_primary] = false + entity_places[entity_primary] = 0 __structural_changes = __structural_changes + 1 end @@ -3431,8 +3412,8 @@ function __destroy_entity_one(entity) if chunk then __detach_entity(chunk, place) - entity_chunks[entity_primary] = nil - entity_places[entity_primary] = nil + entity_chunks[entity_primary] = false + entity_places[entity_primary] = 0 __structural_changes = __structural_changes + 1 end @@ -4144,8 +4125,8 @@ function __chunk_remove(old_chunk, ...) for old_place = 1, old_entity_count do local entity = old_entity_list[old_place] local entity_primary = entity % 2 ^ 20 - entity_chunks[entity_primary] = nil - entity_places[entity_primary] = nil + entity_chunks[entity_primary] = false + entity_places[entity_primary] = 0 end __detach_all_entities(old_chunk) @@ -4207,8 +4188,8 @@ function __chunk_clear(chunk) for place = 1, chunk_entity_count do local entity = chunk_entity_list[place] local entity_primary = entity % 2 ^ 20 - entity_chunks[entity_primary] = nil - entity_places[entity_primary] = nil + entity_chunks[entity_primary] = false + entity_places[entity_primary] = 0 end __detach_all_entities(chunk) @@ -5255,11 +5236,11 @@ function __evolved_set(entity, fragment, component) local old_chunk = entity_chunks[entity_primary] local old_place = entity_places[entity_primary] - local new_chunk = __chunk_with_fragment(old_chunk, fragment) + local new_chunk = __chunk_with_fragment(old_chunk or nil, fragment) __evolved_defer() - if old_chunk == new_chunk then + if old_chunk and old_chunk == new_chunk then local old_component_indices = old_chunk.__component_indices local old_component_storages = old_chunk.__component_storages @@ -5479,16 +5460,11 @@ function __evolved_remove(entity, ...) local old_chunk = entity_chunks[entity_primary] local old_place = entity_places[entity_primary] - local new_chunk = __chunk_without_fragments(old_chunk, ...) - - if old_chunk == new_chunk then - -- nothing to remove - return - end + local new_chunk = __chunk_without_fragments(old_chunk or nil, ...) __evolved_defer() - do + if old_chunk and old_chunk ~= new_chunk then local old_fragment_list = old_chunk.__fragment_list local old_fragment_count = old_chunk.__fragment_count local old_component_indices = old_chunk.__component_indices @@ -5550,8 +5526,8 @@ function __evolved_remove(entity, ...) do __detach_entity(old_chunk, old_place) - entity_chunks[entity_primary] = new_chunk - entity_places[entity_primary] = new_chunk and new_chunk.__entity_count + entity_chunks[entity_primary] = new_chunk or false + entity_places[entity_primary] = new_chunk and new_chunk.__entity_count or 0 __structural_changes = __structural_changes + 1 end @@ -6267,8 +6243,8 @@ function __evolved_collect_garbage(no_shrink) end do - __entity_chunks = __table_dup(__entity_chunks) - __entity_places = __table_dup(__entity_places) + __entity_chunks = __list_dup(__entity_chunks, __acquired_count) + __entity_places = __list_dup(__entity_places, __acquired_count) end do From cd1a58368203f3abc7e7d63bb74a8a13fc34ab36 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sat, 7 Feb 2026 08:08:43 +0700 Subject: [PATCH 07/18] lookup api and basic tests --- README.md | 22 +++++++ develop/all.lua | 1 + develop/testing/lookup_tests.lua | 110 +++++++++++++++++++++++++++++++ evolved.d.tl | 3 + 4 files changed, 136 insertions(+) create mode 100644 develop/testing/lookup_tests.lua diff --git a/README.md b/README.md index 0ccb7b0..071c792 100644 --- a/README.md +++ b/README.md @@ -1496,6 +1496,9 @@ execute :: query -> {execute_state? -> chunk?, entity[]?, integer?}, execute_sta locate :: entity -> chunk?, integer +lookup :: string -> entity? +multi_lookup :: string -> entity[], integer + process :: system... -> () process_with :: system, ... -> () @@ -2001,6 +2004,25 @@ function evolved.execute(query) end function evolved.locate(entity) end ``` +### `evolved.lookup` + +```lua +---@param name string +---@return evolved.entity? entity +---@nodiscard +function evolved.lookup(name) end +``` + +### `evolved.multi_lookup` + +```lua +---@param name string +---@return evolved.entity[] entity_list +---@return integer entity_count +---@nodiscard +function evolved.multi_lookup(name) end +``` + ### `evolved.process` ```lua diff --git a/develop/all.lua b/develop/all.lua index a4ff731..f6c928b 100644 --- a/develop/all.lua +++ b/develop/all.lua @@ -4,6 +4,7 @@ require 'develop.testing.clone_tests' require 'develop.testing.depth_tests' require 'develop.testing.destroy_tests' require 'develop.testing.locate_tests' +require 'develop.testing.lookup_tests' require 'develop.testing.main_tests' require 'develop.testing.mappers_tests' require 'develop.testing.multi_spawn_tests' diff --git a/develop/testing/lookup_tests.lua b/develop/testing/lookup_tests.lua new file mode 100644 index 0000000..a6c0441 --- /dev/null +++ b/develop/testing/lookup_tests.lua @@ -0,0 +1,110 @@ +local evo = require 'evolved' + +evo.debug_mode(true) + +do + local e1, e2, e3 = evo.id(3) + + do + assert(evo.lookup('hello') == nil) + assert(evo.lookup('world') == nil) + + do + local entity_list, entity_count = evo.multi_lookup('hello') + assert(entity_list and #entity_list == 0 and entity_count == 0) + end + + do + local entity_list, entity_count = evo.multi_lookup('world') + assert(entity_list and #entity_list == 0 and entity_count == 0) + end + end + + evo.set(e1, evo.NAME, 'hello') + + do + assert(evo.lookup('hello') == e1) + assert(evo.lookup('world') == nil) + + do + local entity_list, entity_count = evo.multi_lookup('hello') + assert(entity_list and #entity_list == 1 and entity_count == 1) + assert(entity_list[1] == e1) + end + + do + local entity_list, entity_count = evo.multi_lookup('world') + assert(entity_list and #entity_list == 0 and entity_count == 0) + end + end + + evo.set(e2, evo.NAME, 'hello') + evo.set(e3, evo.NAME, 'hello') + + do + assert(evo.lookup('hello') == e3) + assert(evo.lookup('world') == nil) + + do + local entity_list, entity_count = evo.multi_lookup('hello') + assert(entity_list and #entity_list == 3 and entity_count == 3) + assert(entity_list[1] == e1 and entity_list[2] == e2 and entity_list[3] == e3) + end + end + + evo.set(e2, evo.NAME, 'world') + + do + assert(evo.lookup('hello') == e3) + assert(evo.lookup('world') == e2) + + do + local entity_list, entity_count = evo.multi_lookup('hello') + assert(entity_list and #entity_list == 2 and entity_count == 2) + assert(entity_list[1] == e1 and entity_list[2] == e3) + end + + do + local entity_list, entity_count = evo.multi_lookup('world') + assert(entity_list and #entity_list == 1 and entity_count == 1) + assert(entity_list[1] == e2) + end + end + + evo.set(e3, evo.NAME, 'world') + + do + assert(evo.lookup('hello') == e1) + assert(evo.lookup('world') == e3) + + do + local entity_list, entity_count = evo.multi_lookup('hello') + assert(entity_list and #entity_list == 1 and entity_count == 1) + assert(entity_list[1] == e1) + end + + do + local entity_list, entity_count = evo.multi_lookup('world') + assert(entity_list and #entity_list == 2 and entity_count == 2) + assert(entity_list[1] == e2 or entity_list[1] == e3) + end + end + + evo.remove(e1, evo.NAME) + + do + assert(evo.lookup('hello') == nil) + assert(evo.lookup('world') == e3) + + do + local entity_list, entity_count = evo.multi_lookup('hello') + assert(entity_list and #entity_list == 0 and entity_count == 0) + end + + do + local entity_list, entity_count = evo.multi_lookup('world') + assert(entity_list and #entity_list == 2 and entity_count == 2) + assert(entity_list[1] == e2 or entity_list[1] == e3) + end + end +end diff --git a/evolved.d.tl b/evolved.d.tl index 76bcdcc..c31c92f 100644 --- a/evolved.d.tl +++ b/evolved.d.tl @@ -208,6 +208,9 @@ locate: function(entity: Entity): Chunk | nil, integer + lookup: function(name: string): Entity | nil + multi_lookup: function(name: string): { Entity }, integer + process: function(...: System) process_with: function(system: System, ...: any) From caa8cc56254db36c66adb5249940e572a2b4acb1 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sun, 8 Feb 2026 06:48:17 +0700 Subject: [PATCH 08/18] move container functions to separated local tables --- evolved.lua | 152 ++++++++++++++++++++++++---------------------------- 1 file changed, 71 insertions(+), 81 deletions(-) diff --git a/evolved.lua b/evolved.lua index fe4622b..5086951 100644 --- a/evolved.lua +++ b/evolved.lua @@ -803,14 +803,12 @@ end --- --- -local __list_new -local __list_dup -local __list_lwr +local __list_fns = {} ---@param reserve? integer ---@return any[] ---@nodiscard -function __list_new(reserve) +function __list_fns.new(reserve) return __lua_table_new(reserve) end @@ -819,14 +817,14 @@ end ---@param size? integer ---@return V[] ---@nodiscard -function __list_dup(list, size) +function __list_fns.dup(list, size) local list_size = size or #list if list_size == 0 then return {} end - local dup_list = __list_new(list_size) + local dup_list = __list_fns.new(list_size) __lua_table_move( list, 1, list_size, @@ -842,7 +840,7 @@ end ---@param size? integer ---@return integer lower_bound_index ---@nodiscard -function __list_lwr(list, item, comp, size) +function __list_fns.lwr(list, item, comp, size) local lower, upper = 1, size or #list if comp then @@ -884,20 +882,12 @@ end --- __item_count: integer, --- } -local __assoc_list_new -local __assoc_list_move -local __assoc_list_move_ex -local __assoc_list_sort -local __assoc_list_sort_ex -local __assoc_list_insert -local __assoc_list_insert_ex -local __assoc_list_remove -local __assoc_list_remove_ex +local __assoc_list_fns = {} ---@param reserve? integer ---@return evolved.assoc_list ---@nodiscard -function __assoc_list_new(reserve) +function __assoc_list_fns.new(reserve) ---@type evolved.assoc_list return { __item_set = __lua_table_new(), @@ -912,8 +902,8 @@ end ---@param src_item_last integer ---@param dst_al evolved.assoc_list ---@return integer new_dst_item_count -function __assoc_list_move(src_item_list, src_item_first, src_item_last, dst_al) - local new_dst_item_count = __assoc_list_move_ex( +function __assoc_list_fns.move(src_item_list, src_item_first, src_item_last, dst_al) + local new_dst_item_count = __assoc_list_fns.move_ex( src_item_list, src_item_first, src_item_last, dst_al.__item_set, dst_al.__item_list, dst_al.__item_count) @@ -930,8 +920,8 @@ end ---@param dst_item_count integer ---@return integer new_dst_item_count ---@nodiscard -function __assoc_list_move_ex(src_item_list, src_item_first, src_item_last, - dst_item_set, dst_item_list, dst_item_count) +function __assoc_list_fns.move_ex(src_item_list, src_item_first, src_item_last, + dst_item_set, dst_item_list, dst_item_count) if src_item_last < src_item_first then return dst_item_count end @@ -951,8 +941,8 @@ end ---@generic K ---@param al evolved.assoc_list ---@param comp? fun(a: K, b: K): boolean -function __assoc_list_sort(al, comp) - __assoc_list_sort_ex( +function __assoc_list_fns.sort(al, comp) + __assoc_list_fns.sort_ex( al.__item_set, al.__item_list, al.__item_count, comp) end @@ -962,7 +952,7 @@ end ---@param al_item_list K[] ---@param al_item_count integer ---@param comp? fun(a: K, b: K): boolean -function __assoc_list_sort_ex(al_item_set, al_item_list, al_item_count, comp) +function __assoc_list_fns.sort_ex(al_item_set, al_item_list, al_item_count, comp) if al_item_count < 2 then return end @@ -979,8 +969,8 @@ end ---@param al evolved.assoc_list ---@param item K ---@return integer new_al_count -function __assoc_list_insert(al, item) - local new_al_count = __assoc_list_insert_ex( +function __assoc_list_fns.insert(al, item) + local new_al_count = __assoc_list_fns.insert_ex( al.__item_set, al.__item_list, al.__item_count, item) @@ -995,7 +985,7 @@ end ---@param item K ---@return integer new_al_count ---@nodiscard -function __assoc_list_insert_ex(al_item_set, al_item_list, al_item_count, item) +function __assoc_list_fns.insert_ex(al_item_set, al_item_list, al_item_count, item) local item_index = al_item_set[item] if item_index then @@ -1013,8 +1003,8 @@ end ---@param al evolved.assoc_list ---@param item K ---@return integer new_al_count -function __assoc_list_remove(al, item) - local new_al_count = __assoc_list_remove_ex( +function __assoc_list_fns.remove(al, item) + local new_al_count = __assoc_list_fns.remove_ex( al.__item_set, al.__item_list, al.__item_count, item) @@ -1029,7 +1019,7 @@ end ---@param item K ---@return integer new_al_count ---@nodiscard -function __assoc_list_remove_ex(al_item_set, al_item_list, al_item_count, item) +function __assoc_list_fns.remove_ex(al_item_set, al_item_list, al_item_count, item) local item_index = al_item_set[item] if not item_index then @@ -1300,7 +1290,7 @@ function __new_chunk(chunk_parent, chunk_fragment) local chunk_parent_fragment_list = chunk_parent.__fragment_list local chunk_parent_fragment_count = chunk_parent.__fragment_count - chunk_fragment_count = __assoc_list_move_ex( + chunk_fragment_count = __assoc_list_fns.move_ex( chunk_parent_fragment_list, 1, chunk_parent_fragment_count, chunk_fragment_set, chunk_fragment_list, chunk_fragment_count) end @@ -1365,11 +1355,11 @@ function __new_chunk(chunk_parent, chunk_fragment) if not major_chunks then ---@type evolved.assoc_list - major_chunks = __assoc_list_new(4) + major_chunks = __assoc_list_fns.new(4) __major_chunks[major] = major_chunks end - __assoc_list_insert(major_chunks, chunk) + __assoc_list_fns.insert(major_chunks, chunk) end for chunk_fragment_index = 1, chunk_fragment_count do @@ -1378,11 +1368,11 @@ function __new_chunk(chunk_parent, chunk_fragment) if not minor_chunks then ---@type evolved.assoc_list - minor_chunks = __assoc_list_new(4) + minor_chunks = __assoc_list_fns.new(4) __minor_chunks[minor] = minor_chunks end - __assoc_list_insert(minor_chunks, chunk) + __assoc_list_fns.insert(minor_chunks, chunk) end __update_chunk_caches(chunk) @@ -1425,7 +1415,7 @@ end ---@param root evolved.chunk function __add_root_chunk(root) - local root_index = __list_lwr(__root_list, root, function(a, b) + local root_index = __list_fns.lwr(__root_list, root, function(a, b) return a.__fragment < b.__fragment end, __root_count) @@ -1470,7 +1460,7 @@ end ---@param child evolved.chunk ---@param parent evolved.chunk function __add_child_chunk(child, parent) - local child_index = __list_lwr(parent.__child_list, child, function(a, b) + local child_index = __list_fns.lwr(parent.__child_list, child, function(a, b) return a.__fragment < b.__fragment end, parent.__child_count) @@ -1624,9 +1614,9 @@ function __update_chunk_queries(chunk) if major_query_chunks then if __query_major_matches(chunk, major_query) then - __assoc_list_insert(major_query_chunks, chunk) + __assoc_list_fns.insert(major_query_chunks, chunk) else - __assoc_list_remove(major_query_chunks, chunk) + __assoc_list_fns.remove(major_query_chunks, chunk) end end end @@ -1901,7 +1891,7 @@ function __cache_query_chunks(query) local query_variant_count = query_variants and query_variants.__item_count or 0 ---@type evolved.assoc_list - local query_chunks = __assoc_list_new(4) + local query_chunks = __assoc_list_fns.new(4) __query_chunks[query] = query_chunks if query_include_count > 0 then @@ -1915,7 +1905,7 @@ function __cache_query_chunks(query) local major_chunk = major_chunk_list[major_chunk_index] if __query_major_matches(major_chunk, query) then - __assoc_list_insert(query_chunks, major_chunk) + __assoc_list_fns.insert(query_chunks, major_chunk) end end end @@ -1932,7 +1922,7 @@ function __cache_query_chunks(query) local major_chunk = major_chunk_list[major_chunk_index] if __query_major_matches(major_chunk, query) then - __assoc_list_insert(query_chunks, major_chunk) + __assoc_list_fns.insert(query_chunks, major_chunk) end end end @@ -3118,7 +3108,7 @@ function __purge_chunk(chunk) local major = chunk.__fragment local major_chunks = __major_chunks[major] - if major_chunks and __assoc_list_remove(major_chunks, chunk) == 0 then + if major_chunks and __assoc_list_fns.remove(major_chunks, chunk) == 0 then __major_chunks[major] = nil end end @@ -3127,7 +3117,7 @@ function __purge_chunk(chunk) local minor = chunk.__fragment_list[chunk_fragment_index] local minor_chunks = __minor_chunks[minor] - if minor_chunks and __assoc_list_remove(minor_chunks, chunk) == 0 then + if minor_chunks and __assoc_list_fns.remove(minor_chunks, chunk) == 0 then __minor_chunks[minor] = nil end end @@ -6243,13 +6233,13 @@ function __evolved_collect_garbage(no_shrink) end do - __entity_chunks = __list_dup(__entity_chunks, __acquired_count) - __entity_places = __list_dup(__entity_places, __acquired_count) + __entity_chunks = __list_fns.dup(__entity_chunks, __acquired_count) + __entity_places = __list_fns.dup(__entity_places, __acquired_count) end do - __defer_points = __list_dup(__defer_points, __defer_depth) - __defer_bytecode = __list_dup(__defer_bytecode, __defer_length) + __defer_points = __list_fns.dup(__defer_points, __defer_depth) + __defer_bytecode = __list_fns.dup(__defer_bytecode, __defer_length) end end @@ -6833,7 +6823,7 @@ function __builder_mt:include(...) local include_count = include_list and #include_list or 0 if include_count == 0 then - include_list = __list_new(argument_count) + include_list = __list_fns.new(argument_count) end for argument_index = 1, argument_count do @@ -6858,7 +6848,7 @@ function __builder_mt:exclude(...) local exclude_count = exclude_list and #exclude_list or 0 if exclude_count == 0 then - exclude_list = __list_new(argument_count) + exclude_list = __list_fns.new(argument_count) end for argument_index = 1, argument_count do @@ -6883,7 +6873,7 @@ function __builder_mt:variant(...) local variant_count = variant_list and #variant_list or 0 if variant_count == 0 then - variant_list = __list_new(argument_count) + variant_list = __list_fns.new(argument_count) end for argument_index = 1, argument_count do @@ -6908,7 +6898,7 @@ function __builder_mt:require(...) local require_count = require_list and #require_list or 0 if require_count == 0 then - require_list = __list_new(argument_count) + require_list = __list_fns.new(argument_count) end for argument_index = 1, argument_count do @@ -7140,17 +7130,17 @@ __evolved_set(__DISABLED, __TAG) __evolved_set(__DISABLED, __UNIQUE) __evolved_set(__DISABLED, __EXPLICIT) -__evolved_set(__INCLUDES, __DEFAULT, __list_new()) -__evolved_set(__INCLUDES, __DUPLICATE, __list_dup) +__evolved_set(__INCLUDES, __DEFAULT, __list_fns.new()) +__evolved_set(__INCLUDES, __DUPLICATE, __list_fns.dup) -__evolved_set(__EXCLUDES, __DEFAULT, __list_new()) -__evolved_set(__EXCLUDES, __DUPLICATE, __list_dup) +__evolved_set(__EXCLUDES, __DEFAULT, __list_fns.new()) +__evolved_set(__EXCLUDES, __DUPLICATE, __list_fns.dup) -__evolved_set(__VARIANTS, __DEFAULT, __list_new()) -__evolved_set(__VARIANTS, __DUPLICATE, __list_dup) +__evolved_set(__VARIANTS, __DEFAULT, __list_fns.new()) +__evolved_set(__VARIANTS, __DUPLICATE, __list_fns.dup) -__evolved_set(__REQUIRES, __DEFAULT, __list_new()) -__evolved_set(__REQUIRES, __DUPLICATE, __list_dup) +__evolved_set(__REQUIRES, __DEFAULT, __list_fns.new()) +__evolved_set(__REQUIRES, __DUPLICATE, __list_fns.dup) __evolved_set(__ON_SET, __UNIQUE) __evolved_set(__ON_ASSIGN, __UNIQUE) @@ -7179,11 +7169,11 @@ local function __insert_query(query) if not major_queries then ---@type evolved.assoc_list - major_queries = __assoc_list_new(4) + major_queries = __assoc_list_fns.new(4) __major_queries[query_major] = major_queries end - __assoc_list_insert(major_queries, query) + __assoc_list_fns.insert(major_queries, query) end for query_variant_index = 1, query_variant_count do @@ -7194,11 +7184,11 @@ local function __insert_query(query) if not major_queries then ---@type evolved.assoc_list - major_queries = __assoc_list_new(4) + major_queries = __assoc_list_fns.new(4) __major_queries[query_variant] = major_queries end - __assoc_list_insert(major_queries, query) + __assoc_list_fns.insert(major_queries, query) end end end @@ -7217,7 +7207,7 @@ local function __remove_query(query) local query_major = query_include_list[query_include_count] local major_queries = __major_queries[query_major] - if major_queries and __assoc_list_remove(major_queries, query) == 0 then + if major_queries and __assoc_list_fns.remove(major_queries, query) == 0 then __major_queries[query_major] = nil end end @@ -7228,7 +7218,7 @@ local function __remove_query(query) if query_include_count == 0 or query_variant > query_include_list[query_include_count] then local major_queries = __major_queries[query_variant] - if major_queries and __assoc_list_remove(major_queries, query) == 0 then + if major_queries and __assoc_list_fns.remove(major_queries, query) == 0 then __major_queries[query_variant] = nil end end @@ -7252,10 +7242,10 @@ __evolved_set(__INCLUDES, __ON_SET, function(query, _, include_list) if include_count > 0 then ---@type evolved.assoc_list - local sorted_includes = __assoc_list_new(include_count) + local sorted_includes = __assoc_list_fns.new(include_count) - __assoc_list_move(include_list, 1, include_count, sorted_includes) - __assoc_list_sort(sorted_includes) + __assoc_list_fns.move(include_list, 1, include_count, sorted_includes) + __assoc_list_fns.sort(sorted_includes) __sorted_includes[query] = sorted_includes else @@ -7290,10 +7280,10 @@ __evolved_set(__EXCLUDES, __ON_SET, function(query, _, exclude_list) if exclude_count > 0 then ---@type evolved.assoc_list - local sorted_excludes = __assoc_list_new(exclude_count) + local sorted_excludes = __assoc_list_fns.new(exclude_count) - __assoc_list_move(exclude_list, 1, exclude_count, sorted_excludes) - __assoc_list_sort(sorted_excludes) + __assoc_list_fns.move(exclude_list, 1, exclude_count, sorted_excludes) + __assoc_list_fns.sort(sorted_excludes) __sorted_excludes[query] = sorted_excludes else @@ -7328,10 +7318,10 @@ __evolved_set(__VARIANTS, __ON_SET, function(query, _, variant_list) if variant_count > 0 then ---@type evolved.assoc_list - local sorted_variants = __assoc_list_new(variant_count) + local sorted_variants = __assoc_list_fns.new(variant_count) - __assoc_list_move(variant_list, 1, variant_count, sorted_variants) - __assoc_list_sort(sorted_variants) + __assoc_list_fns.move(variant_list, 1, variant_count, sorted_variants) + __assoc_list_fns.sort(sorted_variants) __sorted_variants[query] = sorted_variants else @@ -7364,10 +7354,10 @@ __evolved_set(__REQUIRES, __ON_SET, function(fragment, _, require_list) if require_count > 0 then ---@type evolved.assoc_list - local sorted_requires = __assoc_list_new(require_count) + local sorted_requires = __assoc_list_fns.new(require_count) - __assoc_list_move(require_list, 1, require_count, sorted_requires) - __assoc_list_sort(sorted_requires) + __assoc_list_fns.move(require_list, 1, require_count, sorted_requires) + __assoc_list_fns.sort(sorted_requires) __sorted_requires[fragment] = sorted_requires else @@ -7397,11 +7387,11 @@ local function __add_subsystem(subsystem) if not group_subsystems then ---@type evolved.assoc_list - group_subsystems = __assoc_list_new(4) + group_subsystems = __assoc_list_fns.new(4) __group_subsystems[subsystem_group] = group_subsystems end - __assoc_list_insert(group_subsystems, subsystem) + __assoc_list_fns.insert(group_subsystems, subsystem) end end @@ -7412,7 +7402,7 @@ local function __remove_subsystem(subsystem) if subsystem_group then local group_subsystems = __group_subsystems[subsystem_group] - if group_subsystems and __assoc_list_remove(group_subsystems, subsystem) == 0 then + if group_subsystems and __assoc_list_fns.remove(group_subsystems, subsystem) == 0 then __group_subsystems[subsystem_group] = nil end end From e49a339f5ed169f73029316e082b49531ff16a4a Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sun, 8 Feb 2026 06:56:20 +0700 Subject: [PATCH 09/18] dummy lookup functions and name hooks --- evolved.lua | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/evolved.lua b/evolved.lua index 5086951..a94d03f 100644 --- a/evolved.lua +++ b/evolved.lua @@ -1155,6 +1155,9 @@ local __evolved_execute local __evolved_locate +local __evolved_lookup +local __evolved_multi_lookup + local __evolved_process local __evolved_process_with @@ -6086,6 +6089,19 @@ function __evolved_locate(entity) return entity_chunk, __entity_places[entity_primary] end +---@param name string +---@return evolved.entity? entity +---@nodiscard +function __evolved_lookup(name) +end + +---@param name string +---@return evolved.entity[] entity_list +---@return integer entity_count +---@nodiscard +function __evolved_multi_lookup(name) +end + ---@param ... evolved.system systems function __evolved_process(...) local argument_count = __lua_select('#', ...) @@ -7147,6 +7163,24 @@ __evolved_set(__ON_ASSIGN, __UNIQUE) __evolved_set(__ON_INSERT, __UNIQUE) __evolved_set(__ON_REMOVE, __UNIQUE) +--- +--- +--- +--- +--- + +---@param entity evolved.entity +---@param new_name string +---@param old_name string +__evolved_set(__NAME, __ON_SET, function(entity, _, new_name, old_name) +end) + +---@param entity evolved.entity +---@param old_name string +__evolved_set(__NAME, __ON_REMOVE, function(entity, _, old_name) +end) + + --- --- --- @@ -7524,6 +7558,9 @@ evolved.execute = __evolved_execute evolved.locate = __evolved_locate +evolved.lookup = __evolved_lookup +evolved.multi_lookup = __evolved_multi_lookup + evolved.process = __evolved_process evolved.process_with = __evolved_process_with From 2c4cb179bcf7851471862c83e8519327439b9cb2 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 9 Feb 2026 09:27:00 +0700 Subject: [PATCH 10/18] (multi_)lookup first impl --- develop/testing/lookup_tests.lua | 122 +++++++++++++++++++------ evolved.lua | 151 +++++++++++++++++++++++++++++-- 2 files changed, 237 insertions(+), 36 deletions(-) diff --git a/develop/testing/lookup_tests.lua b/develop/testing/lookup_tests.lua index a6c0441..bc1374d 100644 --- a/develop/testing/lookup_tests.lua +++ b/develop/testing/lookup_tests.lua @@ -6,85 +6,85 @@ do local e1, e2, e3 = evo.id(3) do - assert(evo.lookup('hello') == nil) - assert(evo.lookup('world') == nil) + assert(evo.lookup('lookup_hello') == nil) + assert(evo.lookup('lookup_world') == nil) do - local entity_list, entity_count = evo.multi_lookup('hello') + local entity_list, entity_count = evo.multi_lookup('lookup_hello') assert(entity_list and #entity_list == 0 and entity_count == 0) end do - local entity_list, entity_count = evo.multi_lookup('world') + local entity_list, entity_count = evo.multi_lookup('lookup_world') assert(entity_list and #entity_list == 0 and entity_count == 0) end end - evo.set(e1, evo.NAME, 'hello') + evo.set(e1, evo.NAME, 'lookup_hello') do - assert(evo.lookup('hello') == e1) - assert(evo.lookup('world') == nil) + assert(evo.lookup('lookup_hello') == e1) + assert(evo.lookup('lookup_world') == nil) do - local entity_list, entity_count = evo.multi_lookup('hello') + local entity_list, entity_count = evo.multi_lookup('lookup_hello') assert(entity_list and #entity_list == 1 and entity_count == 1) assert(entity_list[1] == e1) end do - local entity_list, entity_count = evo.multi_lookup('world') + local entity_list, entity_count = evo.multi_lookup('lookup_world') assert(entity_list and #entity_list == 0 and entity_count == 0) end end - evo.set(e2, evo.NAME, 'hello') - evo.set(e3, evo.NAME, 'hello') + evo.set(e2, evo.NAME, 'lookup_hello') + evo.set(e3, evo.NAME, 'lookup_hello') do - assert(evo.lookup('hello') == e3) - assert(evo.lookup('world') == nil) + assert(evo.lookup('lookup_hello') == e1) + assert(evo.lookup('lookup_world') == nil) do - local entity_list, entity_count = evo.multi_lookup('hello') + local entity_list, entity_count = evo.multi_lookup('lookup_hello') assert(entity_list and #entity_list == 3 and entity_count == 3) assert(entity_list[1] == e1 and entity_list[2] == e2 and entity_list[3] == e3) end end - evo.set(e2, evo.NAME, 'world') + evo.set(e2, evo.NAME, 'lookup_world') do - assert(evo.lookup('hello') == e3) - assert(evo.lookup('world') == e2) + assert(evo.lookup('lookup_hello') == e1) + assert(evo.lookup('lookup_world') == e2) do - local entity_list, entity_count = evo.multi_lookup('hello') + local entity_list, entity_count = evo.multi_lookup('lookup_hello') assert(entity_list and #entity_list == 2 and entity_count == 2) assert(entity_list[1] == e1 and entity_list[2] == e3) end do - local entity_list, entity_count = evo.multi_lookup('world') + local entity_list, entity_count = evo.multi_lookup('lookup_world') assert(entity_list and #entity_list == 1 and entity_count == 1) assert(entity_list[1] == e2) end end - evo.set(e3, evo.NAME, 'world') + evo.set(e3, evo.NAME, 'lookup_world') do - assert(evo.lookup('hello') == e1) - assert(evo.lookup('world') == e3) + assert(evo.lookup('lookup_hello') == e1) + assert(evo.lookup('lookup_world') == e2) do - local entity_list, entity_count = evo.multi_lookup('hello') + local entity_list, entity_count = evo.multi_lookup('lookup_hello') assert(entity_list and #entity_list == 1 and entity_count == 1) assert(entity_list[1] == e1) end do - local entity_list, entity_count = evo.multi_lookup('world') + local entity_list, entity_count = evo.multi_lookup('lookup_world') assert(entity_list and #entity_list == 2 and entity_count == 2) assert(entity_list[1] == e2 or entity_list[1] == e3) end @@ -93,18 +93,84 @@ do evo.remove(e1, evo.NAME) do - assert(evo.lookup('hello') == nil) - assert(evo.lookup('world') == e3) + assert(evo.lookup('lookup_hello') == nil) + assert(evo.lookup('lookup_world') == e2) do - local entity_list, entity_count = evo.multi_lookup('hello') + local entity_list, entity_count = evo.multi_lookup('lookup_hello') assert(entity_list and #entity_list == 0 and entity_count == 0) end do - local entity_list, entity_count = evo.multi_lookup('world') + local entity_list, entity_count = evo.multi_lookup('lookup_world') assert(entity_list and #entity_list == 2 and entity_count == 2) assert(entity_list[1] == e2 or entity_list[1] == e3) end end end + +do + local e1, e2, e3 = evo.id(3) + + evo.set(e1, evo.NAME, 'lookup_e') + + do + local entity_list, entity_count = evo.multi_lookup('lookup_e') + assert(entity_list and #entity_list == 1 and entity_count == 1) + assert(entity_list[1] == e1) + end + + evo.set(e2, evo.NAME, 'lookup_e') + + do + local entity_list, entity_count = evo.multi_lookup('lookup_e') + assert(entity_list and #entity_list == 2 and entity_count == 2) + assert(entity_list[1] == e1 and entity_list[2] == e2) + end + + evo.set(e3, evo.NAME, 'lookup_e') + + do + local entity_list, entity_count = evo.multi_lookup('lookup_e') + assert(entity_list and #entity_list == 3 and entity_count == 3) + assert(entity_list[1] == e1 and entity_list[2] == e2 and entity_list[3] == e3) + end + + evo.clear(e1, e2, e3) + + do + local entity_list, entity_count = evo.multi_lookup('lookup_e') + assert(entity_list and #entity_list == 0 and entity_count == 0) + end + + evo.set(e3, evo.NAME, 'lookup_e') + + do + local entity_list, entity_count = evo.multi_lookup('lookup_e') + assert(entity_list and #entity_list == 1 and entity_count == 1) + assert(entity_list[1] == e3) + end + + evo.set(e2, evo.NAME, 'lookup_e') + + do + local entity_list, entity_count = evo.multi_lookup('lookup_e') + assert(entity_list and #entity_list == 2 and entity_count == 2) + assert(entity_list[1] == e3 and entity_list[2] == e2) + end + + evo.set(e1, evo.NAME, 'lookup_e') + + do + local entity_list, entity_count = evo.multi_lookup('lookup_e') + assert(entity_list and #entity_list == 3 and entity_count == 3) + assert(entity_list[1] == e3 and entity_list[2] == e2 and entity_list[3] == e1) + end + + evo.destroy(e3, e2, e1) + + do + local entity_list, entity_count = evo.multi_lookup('lookup_e') + assert(entity_list and #entity_list == 0 and entity_count == 0) + end +end diff --git a/evolved.lua b/evolved.lua index a94d03f..451369b 100644 --- a/evolved.lua +++ b/evolved.lua @@ -144,6 +144,9 @@ local __major_queries = {} ---@type table +local __named_entities = {} ---@type table> + local __sorted_includes = {} ---@type table> local __sorted_excludes = {} ---@type table> local __sorted_variants = {} ---@type table> @@ -896,6 +899,22 @@ function __assoc_list_fns.new(reserve) } end +---@generic K +---@param ... K +---@return evolved.assoc_list +---@nodiscard +function __assoc_list_fns.from(...) + local item_count = __lua_select('#', ...) + + local al = __assoc_list_fns.new(item_count) + + for item_index = 1, item_count do + __assoc_list_fns.insert(al, __lua_select(item_index, ...)) + end + + return al +end + ---@generic K ---@param src_item_list K[] ---@param src_item_first integer @@ -1039,6 +1058,46 @@ function __assoc_list_fns.remove_ex(al_item_set, al_item_list, al_item_count, it return al_item_count end +---@generic K +---@param al evolved.assoc_list +---@param item K +---@return integer new_al_count +function __assoc_list_fns.unordered_remove(al, item) + local new_al_count = __assoc_list_fns.unordered_remove_ex( + al.__item_set, al.__item_list, al.__item_count, + item) + + al.__item_count = new_al_count + return new_al_count +end + +---@generic K +---@param al_item_set table +---@param al_item_list K[] +---@param al_item_count integer +---@param item K +---@return integer new_al_count +---@nodiscard +function __assoc_list_fns.unordered_remove_ex(al_item_set, al_item_list, al_item_count, item) + local item_index = al_item_set[item] + + if not item_index then + return al_item_count + end + + if item_index ~= al_item_count then + local al_last_item = al_item_list[al_item_count] + al_item_set[al_last_item] = item_index + al_item_list[item_index] = al_last_item + end + + al_item_set[item] = nil + al_item_list[al_item_count] = nil + al_item_count = al_item_count - 1 + + return al_item_count +end + --- --- --- @@ -1090,6 +1149,11 @@ local __DESTRUCTION_POLICY_REMOVE_FRAGMENT = __acquire_id() --- local __safe_tbls = { + __EMPTY_ENTITY_LIST = __lua_setmetatable({}, { + __tostring = function() return 'empty entity list' end, + __newindex = function() __error_fmt 'attempt to modify empty entity list' end + }) --[=[@as evolved.id[]]=], + __EMPTY_FRAGMENT_SET = __lua_setmetatable({}, { __tostring = function() return 'empty fragment set' end, __newindex = function() __error_fmt 'attempt to modify empty fragment set' end @@ -4860,7 +4924,7 @@ end ---@return integer entity_count function __evolved_multi_spawn(entity_count, component_table, component_mapper) if entity_count <= 0 then - return {}, 0 + return __safe_tbls.__EMPTY_ENTITY_LIST, 0 end if __debug_mode then @@ -4941,7 +5005,7 @@ end ---@return integer entity_count function __evolved_multi_clone(entity_count, prefab, component_table, component_mapper) if entity_count <= 0 then - return {}, 0 + return __safe_tbls.__EMPTY_ENTITY_LIST, 0 end if __debug_mode then @@ -6093,6 +6157,7 @@ end ---@return evolved.entity? entity ---@nodiscard function __evolved_lookup(name) + return __named_entity[name] end ---@param name string @@ -6100,6 +6165,25 @@ end ---@return integer entity_count ---@nodiscard function __evolved_multi_lookup(name) + do + local named_entities = __named_entities[name] + local named_entity_list = named_entities and named_entities.__item_list + local named_entity_count = named_entities and named_entities.__item_count or 0 + + if named_entity_count > 0 then + return __list_fns.dup(named_entity_list, named_entity_count), named_entity_count + end + end + + do + local named_entity = __named_entity[name] + + if named_entity then + return { named_entity }, 1 + end + end + + return __safe_tbls.__EMPTY_ENTITY_LIST, 0 end ---@param ... evolved.system systems @@ -6497,7 +6581,7 @@ end ---@return integer entity_count function __builder_mt:multi_spawn(entity_count, component_mapper) if entity_count <= 0 then - return {}, 0 + return __safe_tbls.__EMPTY_ENTITY_LIST, 0 end local chunk = self.__chunk @@ -6581,7 +6665,7 @@ end ---@return integer entity_count function __builder_mt:multi_clone(entity_count, prefab, component_mapper) if entity_count <= 0 then - return {}, 0 + return __safe_tbls.__EMPTY_ENTITY_LIST, 0 end local component_table = self.__component_table @@ -7169,18 +7253,69 @@ __evolved_set(__ON_REMOVE, __UNIQUE) --- --- +---@param name string +---@param entity evolved.entity +local function __insert_named_entity(name, entity) + ---@type evolved.entity? + local named_entity = __named_entity[name] + + if not named_entity then + __named_entity[name] = entity + return + end + + ---@type evolved.assoc_list? + local named_entities = __named_entities[name] + + if not named_entities then + __named_entities[name] = __assoc_list_fns.from(named_entity, entity) + return + end + + __assoc_list_fns.insert(named_entities, entity) +end + +---@param name string +---@param entity evolved.entity +local function __remove_named_entity(name, entity) + ---@type evolved.assoc_list? + local named_entities = __named_entities[name] + + if named_entities then + if __assoc_list_fns.remove(named_entities, entity) == 0 then + __named_entities[name], named_entities = nil, nil + end + end + + ---@type evolved.entity? + local named_entity = __named_entity[name] + + if named_entity == entity then + __named_entity[name] = named_entities and named_entities.__item_list[1] or nil + end +end + ---@param entity evolved.entity ----@param new_name string ----@param old_name string +---@param new_name? string +---@param old_name? string __evolved_set(__NAME, __ON_SET, function(entity, _, new_name, old_name) + if old_name then + __remove_named_entity(old_name, entity) + end + + if new_name then + __insert_named_entity(new_name, entity) + end end) ---@param entity evolved.entity ----@param old_name string +---@param old_name? string __evolved_set(__NAME, __ON_REMOVE, function(entity, _, old_name) + if old_name then + __remove_named_entity(old_name, entity) + end end) - --- --- --- From 655c0aef07c2d01c1b751eccc58a00719a7c6798 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 9 Feb 2026 09:47:18 +0700 Subject: [PATCH 11/18] update README --- README.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/README.md b/README.md index 071c792..dac0735 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ - [Internal Fragments](#internal-fragments) - [Shared Components](#shared-components) - [Fragment Requirements](#fragment-requirements) + - [Id Names](#id-names) - [Destruction Policies](#destruction-policies) - [Custom Component Storages](#custom-component-storages) - [Garbage Collection](#garbage-collection) @@ -64,6 +65,7 @@ - [Chunk](#chunk) - [Builder](#builder) - [Changelog](#changelog) + - [vX.Y.Z](#vxyz) - [v1.9.0](#v190) - [v1.8.0](#v180) - [v1.7.0](#v170) @@ -1161,6 +1163,41 @@ local enemy = evolved.builder() assert(evolved.has_all(enemy, position, velocity)) ``` +#### Id Names + +The library provides a way to assign names to any id using the [`evolved.NAME`](#evolvedname) fragment. This is useful for debugging and development purposes, as it allows you to identify entities or fragments by their names instead of their identifiers. The name of an entity can be retrieved using the [`evolved.name`](#evolvedname-1) function. + +```lua +local evolved = require 'evolved' + +local player = evolved.builder() + :name('Player') + :build() + +assert(evolved.name(player) == 'Player') +``` + +Names are not unique, so multiple entities can have the same name. Also, the name of an entity can be changed at any time by setting a new name using the [`evolved.NAME`](#evolvedname) fragment as a usual component. + +You can find entities by their names using the [`evolved.lookup`](#evolvedlookup) and [`evolved.multi_lookup`](#evolvedmulti_lookup) functions. The [`evolved.lookup`](#evolvedlookup) function returns the first entity with the specified name, while the [`evolved.multi_lookup`](#evolvedmulti_lookup) function returns a list of all entities with the specified name. + +```lua +local evolved = require 'evolved' + +local player1 = evolved.builder() + :name('Player') + :build() + +local player2 = evolved.builder() + :name('Player') + :build() + +assert(evolved.lookup('Player') == player1) + +local player_list, player_count = evolved.multi_lookup('Player') +assert(player_count == 2 and player_list[1] == player1 and player_list[2] == player2) +``` + #### Destruction Policies Typically, fragments remain alive for the entire lifetime of the program. However, in some cases, you might want to destroy fragments when they are no longer needed. For example, you can use some runtime entities as fragments for other entities. In this case, you might want to destroy such fragments even while they are still attached to other entities. Since entities cannot have destroyed fragments, a destruction policy must be applied to resolve this. By default, the library will remove the destroyed fragment from all entities that have it. @@ -1588,6 +1625,10 @@ builder_mt:destruction_policy :: id -> builder ## Changelog +### vX.Y.Z + +- Added the new [`evolved.lookup`](#evolvedlookup) and [`evolved.multi_lookup`](#evolvedmulti_lookup) functions that allow finding ids by their names + ### v1.9.0 - Performance improvements of the [`evolved.destroy`](#evolveddestroy) and [`evolved.batch_destroy`](#evolvedbatch_destroy) functions From eb853c8392654fbb69ca48f435821243877e1c50 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 9 Feb 2026 09:52:42 +0700 Subject: [PATCH 12/18] update README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dac0735..e1d814e 100644 --- a/README.md +++ b/README.md @@ -1628,6 +1628,7 @@ builder_mt:destruction_policy :: id -> builder ### vX.Y.Z - Added the new [`evolved.lookup`](#evolvedlookup) and [`evolved.multi_lookup`](#evolvedmulti_lookup) functions that allow finding ids by their names +- Added a non-shrinking version of the [`evolved.collect_garbage`](#evolvedcollect_garbage) function that only collects garbage without shrinking storages ### v1.9.0 From f9943c9fca11983128eed7bacdcba1788b9435cd Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 13 Feb 2026 07:35:35 +0700 Subject: [PATCH 13/18] nr/to multi_spawn/lookup versions --- README.md | 131 ++++++++++++++++++++ evolved.lua | 349 +++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 405 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index e1d814e..1ae24f2 100644 --- a/README.md +++ b/README.md @@ -1500,9 +1500,13 @@ cancel :: boolean spawn :: component_table?, component_mapper? -> entity multi_spawn :: integer, component_table?, component_mapper? -> entity[], integer +multi_spawn_nr :: integer, component_table?, component_mapper? -> () +multi_spawn_to :: entity[], integer, integer, component_table?, component_mapper? -> () clone :: entity, component_table?, component_mapper? -> entity multi_clone :: integer, entity, component_table?, component_mapper? -> entity[], integer +multi_clone_nr :: integer, entity, component_table?, component_mapper? -> () +multi_clone_to :: entity[], integer, integer, entity, component_table?, component_mapper? -> () alive :: entity -> boolean alive_all :: entity... -> boolean @@ -1535,6 +1539,7 @@ locate :: entity -> chunk?, integer lookup :: string -> entity? multi_lookup :: string -> entity[], integer +multi_lookup_to :: entity[], integer, string -> integer process :: system... -> () process_with :: system, ... -> () @@ -1569,12 +1574,18 @@ builder :: builder builder_mt:build :: entity?, component_mapper? -> entity builder_mt:multi_build :: integer, entity?, component_mapper? -> entity[], integer +builder_mt:multi_build_nr :: integer, entity?, component_mapper? -> () +builder_mt:multi_build_to :: entity[], integer, integer, entity?, component_mapper? -> () builder_mt:spawn :: component_mapper? -> entity builder_mt:multi_spawn :: integer, component_mapper? -> entity[], integer +builder_mt:multi_spawn_nr :: integer, component_mapper? -> () +builder_mt:multi_spawn_to :: entity[], integer, integer, component_mapper? -> () builder_mt:clone :: entity, component_mapper? -> entity builder_mt:multi_clone :: integer, entity, component_mapper? -> entity[], integer +builder_mt:multi_clone_nr :: integer, entity, component_mapper? -> () +builder_mt:multi_clone_to :: entity[], integer, integer, entity, component_mapper? -> () builder_mt:has :: fragment -> boolean builder_mt:has_all :: fragment... -> boolean @@ -1835,9 +1846,31 @@ function evolved.spawn(component_table, component_mapper) end ---@param component_mapper? evolved.component_mapper ---@return evolved.entity[] entity_list ---@return integer entity_count +---@nodiscard function evolved.multi_spawn(entity_count, component_table, component_mapper) end ``` +### `evolved.multi_spawn_nr` + +```lua +---@param entity_count integer +---@param component_table? evolved.component_table +---@param component_mapper? evolved.component_mapper +function evolved.multi_spawn_nr(entity_count, component_table, component_mapper) end +``` + +### `evolved.multi_spawn_to` + +```lua +---@param out_entity_list evolved.entity[] +---@param out_entity_first integer +---@param entity_count integer +---@param component_table? evolved.component_table +---@param component_mapper? evolved.component_mapper +function evolved.multi_spawn_to(out_entity_list, out_entity_first, + entity_count, component_table, component_mapper) end +``` + ### `evolved.clone` ```lua @@ -1857,9 +1890,33 @@ function evolved.clone(prefab, component_table, component_mapper) end ---@param component_mapper? evolved.component_mapper ---@return evolved.entity[] entity_list ---@return integer entity_count +---@nodiscard function evolved.multi_clone(entity_count, prefab, component_table, component_mapper) end ``` +### `evolved.multi_clone_nr` + +```lua +---@param entity_count integer +---@param prefab evolved.entity +---@param component_table? evolved.component_table +---@param component_mapper? evolved.component_mapper +function evolved.multi_clone_nr(entity_count, prefab, component_table, component_mapper) end +``` + +### `evolved.multi_clone_to` + +```lua +---@param out_entity_list evolved.entity[] +---@param out_entity_first integer +---@param entity_count integer +---@param prefab evolved.entity +---@param component_table? evolved.component_table +---@param component_mapper? evolved.component_mapper +function evolved.multi_clone_to(out_entity_list, out_entity_first, + entity_count, prefab, component_table, component_mapper) end +``` + ### `evolved.alive` ```lua @@ -2065,6 +2122,16 @@ function evolved.lookup(name) end function evolved.multi_lookup(name) end ``` +### `evolved.multi_lookup_to` + +```lua +---@param out_entity_list evolved.entity[] +---@param out_entity_first integer +---@param name string +---@return integer entity_count +function evolved.multi_lookup_to(out_entity_list, out_entity_first, name) end +``` + ### `evolved.process` ```lua @@ -2207,9 +2274,31 @@ function evolved.builder_mt:build(prefab, component_mapper) end ---@param component_mapper? evolved.component_mapper ---@return evolved.entity[] entity_list ---@return integer entity_count +---@nodiscard function evolved.builder_mt:multi_build(entity_count, prefab, component_mapper) end ``` +### `evolved.builder_mt:multi_build_nr` + +```lua +---@param entity_count integer +---@param prefab? evolved.entity +---@param component_mapper? evolved.component_mapper +function evolved.builder_mt:multi_build_nr(entity_count, prefab, component_mapper) end +``` + +### `evolved.builder_mt:multi_build_to` + +```lua +---@param out_entity_list evolved.entity[] +---@param out_entity_first integer +---@param entity_count integer +---@param prefab? evolved.entity +---@param component_mapper? evolved.component_mapper +function evolved.builder_mt:multi_build_to(out_entity_list, out_entity_first, + entity_count, prefab, component_mapper) end +``` + #### `evolved.builder_mt:spawn` ```lua @@ -2225,9 +2314,29 @@ function evolved.builder_mt:spawn(component_mapper) end ---@param component_mapper? evolved.component_mapper ---@return evolved.entity[] entity_list ---@return integer entity_count +---@nodiscard function evolved.builder_mt:multi_spawn(entity_count, component_mapper) end ``` +#### `evolved.builder_mt:multi_spawn_nr` + +```lua +---@param entity_count integer +---@param component_mapper? evolved.component_mapper +function evolved.builder_mt:multi_spawn_nr(entity_count, component_mapper) end +``` + +#### `evolved.builder_mt:multi_spawn_to` + +```lua +---@param out_entity_list evolved.entity[] +---@param out_entity_first integer +---@param entity_count integer +---@param component_mapper? evolved.component_mapper +function evolved.builder_mt:multi_spawn_to(out_entity_list, out_entity_first, + entity_count, component_mapper) end +``` + #### `evolved.builder_mt:clone` ```lua @@ -2245,9 +2354,31 @@ function evolved.builder_mt:clone(prefab, component_mapper) end ---@param component_mapper? evolved.component_mapper ---@return evolved.entity[] entity_list ---@return integer entity_count +---@nodiscard function evolved.builder_mt:multi_clone(entity_count, prefab, component_mapper) end ``` +#### `evolved.builder_mt:multi_clone_nr` + +```lua +---@param entity_count integer +---@param prefab evolved.entity +---@param component_mapper? evolved.component_mapper +function evolved.builder_mt:multi_clone_nr(entity_count, prefab, component_mapper) end +``` + +#### `evolved.builder_mt:multi_clone_to` + +```lua +---@param out_entity_list evolved.entity[] +---@param out_entity_first integer +---@param entity_count integer +---@param prefab evolved.entity +---@param component_mapper? evolved.component_mapper +function evolved.builder_mt:multi_clone_to(out_entity_list, out_entity_first, + entity_count, prefab, component_mapper) end +``` + #### `evolved.builder_mt:has` ```lua diff --git a/evolved.lua b/evolved.lua index 451369b..9311f98 100644 --- a/evolved.lua +++ b/evolved.lua @@ -1149,11 +1149,6 @@ local __DESTRUCTION_POLICY_REMOVE_FRAGMENT = __acquire_id() --- local __safe_tbls = { - __EMPTY_ENTITY_LIST = __lua_setmetatable({}, { - __tostring = function() return 'empty entity list' end, - __newindex = function() __error_fmt 'attempt to modify empty entity list' end - }) --[=[@as evolved.id[]]=], - __EMPTY_FRAGMENT_SET = __lua_setmetatable({}, { __tostring = function() return 'empty fragment set' end, __newindex = function() __error_fmt 'attempt to modify empty fragment set' end @@ -1184,9 +1179,13 @@ local __evolved_cancel local __evolved_spawn local __evolved_multi_spawn +local __evolved_multi_spawn_nr +local __evolved_multi_spawn_to local __evolved_clone local __evolved_multi_clone +local __evolved_multi_clone_nr +local __evolved_multi_clone_to local __evolved_alive local __evolved_alive_all @@ -1221,6 +1220,7 @@ local __evolved_locate local __evolved_lookup local __evolved_multi_lookup +local __evolved_multi_lookup_to local __evolved_process local __evolved_process_with @@ -2681,10 +2681,11 @@ end ---@param chunk? evolved.chunk ---@param entity_list evolved.entity[] +---@param entity_first integer ---@param entity_count integer ---@param component_table? evolved.component_table ---@param component_mapper? evolved.component_mapper -function __multi_spawn_entity(chunk, entity_list, entity_count, component_table, component_mapper) +function __multi_spawn_entity(chunk, entity_list, entity_first, entity_count, component_table, component_mapper) if __defer_depth <= 0 then __error_fmt('spawn entity operations should be deferred') end @@ -2732,7 +2733,7 @@ function __multi_spawn_entity(chunk, entity_list, entity_count, component_table, local entity_places = __entity_places for place = b_place, e_place do - local entity = entity_list[place - b_place + 1] + local entity = entity_list[place - b_place + entity_first] chunk_entity_list[place] = entity local entity_primary = entity % 2 ^ 20 @@ -2980,10 +2981,11 @@ end ---@param prefab evolved.entity ---@param entity_list evolved.entity[] +---@param entity_first integer ---@param entity_count integer ---@param component_table? evolved.component_table ---@param component_mapper? evolved.component_mapper -function __multi_clone_entity(prefab, entity_list, entity_count, component_table, component_mapper) +function __multi_clone_entity(prefab, entity_list, entity_first, entity_count, component_table, component_mapper) if __defer_depth <= 0 then __error_fmt('clone entity operations should be deferred') end @@ -2999,7 +3001,9 @@ function __multi_clone_entity(prefab, entity_list, entity_count, component_table end if not prefab_chunk or not prefab_chunk.__without_unique_fragments then - return __multi_spawn_entity(nil, entity_list, entity_count, component_table, component_mapper) + return __multi_spawn_entity(nil, + entity_list, entity_first, entity_count, + component_table, component_mapper) end local chunk = component_table @@ -3048,7 +3052,7 @@ function __multi_clone_entity(prefab, entity_list, entity_count, component_table local entity_places = __entity_places for place = b_place, e_place do - local entity = entity_list[place - b_place + 1] + local entity = entity_list[place - b_place + entity_first] chunk_entity_list[place] = entity local entity_primary = entity % 2 ^ 20 @@ -4379,10 +4383,10 @@ function __defer_spawn_entity(chunk, entity, component_table, component_mapper) end __defer_ops[__defer_op.spawn_entity] = function(bytes, index) - local chunk = bytes[index + 0] - local entity = bytes[index + 1] - local component_table2 = bytes[index + 2] - local component_mapper = bytes[index + 3] + local chunk = bytes[index + 0] ---@type evolved.chunk + local entity = bytes[index + 1] ---@type evolved.entity + local component_table2 = bytes[index + 2] ---@type evolved.component_table? + local component_mapper = bytes[index + 3] ---@type evolved.component_mapper? __evolved_defer() do @@ -4399,15 +4403,16 @@ end ---@param chunk? evolved.chunk ---@param entity_list evolved.entity[] +---@param entity_first integer ---@param entity_count integer ---@param component_table? evolved.component_table ---@param component_mapper? evolved.component_mapper -function __defer_multi_spawn_entity(chunk, entity_list, entity_count, component_table, component_mapper) +function __defer_multi_spawn_entity(chunk, entity_list, entity_first, entity_count, component_table, component_mapper) ---@type evolved.entity[] local entity_list2 = __acquire_table(__table_pool_tag.entity_list) __lua_table_move( - entity_list, 1, entity_count, + entity_list, entity_first, entity_first + entity_count - 1, 1, entity_list2) ---@type evolved.component_table? @@ -4435,15 +4440,17 @@ function __defer_multi_spawn_entity(chunk, entity_list, entity_count, component_ end __defer_ops[__defer_op.multi_spawn_entity] = function(bytes, index) - local chunk = bytes[index + 0] - local entity_count = bytes[index + 1] - local entity_list2 = bytes[index + 2] - local component_table2 = bytes[index + 3] - local component_mapper = bytes[index + 4] + local chunk = bytes[index + 0] ---@type evolved.chunk + local entity_count = bytes[index + 1] ---@type integer + local entity_list2 = bytes[index + 2] ---@type evolved.entity[] + local component_table2 = bytes[index + 3] ---@type evolved.component_table? + local component_mapper = bytes[index + 4] ---@type evolved.component_mapper? __evolved_defer() do - __multi_spawn_entity(chunk, entity_list2, entity_count, component_table2, component_mapper) + __multi_spawn_entity(chunk, + entity_list2, 1, entity_count, + component_table2, component_mapper) if entity_list2 then __release_table(__table_pool_tag.entity_list, entity_list2, false, true) @@ -4487,10 +4494,10 @@ function __defer_clone_entity(prefab, entity, component_table, component_mapper) end __defer_ops[__defer_op.clone_entity] = function(bytes, index) - local prefab = bytes[index + 0] - local entity = bytes[index + 1] - local component_table2 = bytes[index + 2] - local component_mapper = bytes[index + 3] + local prefab = bytes[index + 0] ---@type evolved.entity + local entity = bytes[index + 1] ---@type evolved.entity + local component_table2 = bytes[index + 2] ---@type evolved.component_table? + local component_mapper = bytes[index + 3] ---@type evolved.component_mapper? __evolved_defer() do @@ -4507,15 +4514,16 @@ end ---@param prefab evolved.entity ---@param entity_list evolved.entity[] +---@param entity_first integer ---@param entity_count integer ---@param component_table? evolved.component_table ---@param component_mapper? evolved.component_mapper -function __defer_multi_clone_entity(prefab, entity_list, entity_count, component_table, component_mapper) +function __defer_multi_clone_entity(prefab, entity_list, entity_first, entity_count, component_table, component_mapper) ---@type evolved.entity[] local entity_list2 = __acquire_table(__table_pool_tag.entity_list) __lua_table_move( - entity_list, 1, entity_count, + entity_list, entity_first, entity_first + entity_count - 1, 1, entity_list2) ---@type evolved.component_table? @@ -4543,15 +4551,17 @@ function __defer_multi_clone_entity(prefab, entity_list, entity_count, component end __defer_ops[__defer_op.multi_clone_entity] = function(bytes, index) - local prefab = bytes[index + 0] - local entity_count = bytes[index + 1] - local entity_list2 = bytes[index + 2] - local component_table2 = bytes[index + 3] - local component_mapper = bytes[index + 4] + local prefab = bytes[index + 0] ---@type evolved.entity + local entity_count = bytes[index + 1] ---@type integer + local entity_list2 = bytes[index + 2] ---@type evolved.entity[] + local component_table2 = bytes[index + 3] ---@type evolved.component_table? + local component_mapper = bytes[index + 4] ---@type evolved.component_mapper? __evolved_defer() do - __multi_clone_entity(prefab, entity_list2, entity_count, component_table2, component_mapper) + __multi_clone_entity(prefab, + entity_list2, 1, entity_count, + component_table2, component_mapper) if entity_list2 then __release_table(__table_pool_tag.entity_list, entity_list2, false, true) @@ -4922,9 +4932,47 @@ end ---@param component_mapper? evolved.component_mapper ---@return evolved.entity[] entity_list ---@return integer entity_count +---@nodiscard function __evolved_multi_spawn(entity_count, component_table, component_mapper) if entity_count <= 0 then - return __safe_tbls.__EMPTY_ENTITY_LIST, 0 + return {}, 0 + end + + local entity_list = __lua_table_new(entity_count) + + __evolved_multi_spawn_to( + entity_list, 1, entity_count, + component_table, component_mapper) + + return entity_list, entity_count +end + +---@param entity_count integer +---@param component_table? evolved.component_table +---@param component_mapper? evolved.component_mapper +function __evolved_multi_spawn_nr(entity_count, component_table, component_mapper) + if entity_count <= 0 then + return + end + + local entity_list = __acquire_table(__table_pool_tag.entity_list) + + __evolved_multi_spawn_to( + entity_list, 1, entity_count, + component_table, component_mapper) + + __release_table(__table_pool_tag.entity_list, entity_list, false, true) +end + +---@param out_entity_list evolved.entity[] +---@param out_entity_first integer +---@param entity_count integer +---@param component_table? evolved.component_table +---@param component_mapper? evolved.component_mapper +function __evolved_multi_spawn_to(out_entity_list, out_entity_first, + entity_count, component_table, component_mapper) + if entity_count <= 0 then + return end if __debug_mode then @@ -4938,27 +4986,27 @@ function __evolved_multi_spawn(entity_count, component_table, component_mapper) end end - local entity_list = __lua_table_new(entity_count) - - for entity_index = 1, entity_count do - entity_list[entity_index] = __acquire_id() + for entity_index = out_entity_first, out_entity_first + entity_count - 1 do + out_entity_list[entity_index] = __acquire_id() end if not component_table or not __lua_next(component_table) then - return entity_list, entity_count + return end if __defer_depth > 0 then - __defer_multi_spawn_entity(nil, entity_list, entity_count, component_table, component_mapper) + __defer_multi_spawn_entity(nil, + out_entity_list, out_entity_first, entity_count, + component_table, component_mapper) else __evolved_defer() do - __multi_spawn_entity(nil, entity_list, entity_count, component_table, component_mapper) + __multi_spawn_entity(nil, + out_entity_list, out_entity_first, entity_count, + component_table, component_mapper) end __evolved_commit() end - - return entity_list, entity_count end ---@param prefab evolved.entity @@ -5003,9 +5051,49 @@ end ---@param component_mapper? evolved.component_mapper ---@return evolved.entity[] entity_list ---@return integer entity_count +---@nodiscard function __evolved_multi_clone(entity_count, prefab, component_table, component_mapper) if entity_count <= 0 then - return __safe_tbls.__EMPTY_ENTITY_LIST, 0 + return {}, 0 + end + + local entity_list = __lua_table_new(entity_count) + + __evolved_multi_clone_to( + entity_list, 1, entity_count, + prefab, component_table, component_mapper) + + return entity_list, entity_count +end + +---@param entity_count integer +---@param prefab evolved.entity +---@param component_table? evolved.component_table +---@param component_mapper? evolved.component_mapper +function __evolved_multi_clone_nr(entity_count, prefab, component_table, component_mapper) + if entity_count <= 0 then + return + end + + local entity_list = __acquire_table(__table_pool_tag.entity_list) + + __evolved_multi_clone_to( + entity_list, 1, entity_count, + prefab, component_table, component_mapper) + + __release_table(__table_pool_tag.entity_list, entity_list, false, true) +end + +---@param out_entity_list evolved.entity[] +---@param out_entity_first integer +---@param entity_count integer +---@param prefab evolved.entity +---@param component_table? evolved.component_table +---@param component_mapper? evolved.component_mapper +function __evolved_multi_clone_to(out_entity_list, out_entity_first, + entity_count, prefab, component_table, component_mapper) + if entity_count <= 0 then + return end if __debug_mode then @@ -5024,23 +5112,23 @@ function __evolved_multi_clone(entity_count, prefab, component_table, component_ end end - local entity_list = __lua_table_new(entity_count) - - for entity_index = 1, entity_count do - entity_list[entity_index] = __acquire_id() + for entity_index = out_entity_first, out_entity_first + entity_count - 1 do + out_entity_list[entity_index] = __acquire_id() end if __defer_depth > 0 then - __defer_multi_clone_entity(prefab, entity_list, entity_count, component_table, component_mapper) + __defer_multi_clone_entity(prefab, + out_entity_list, out_entity_first, entity_count, + component_table, component_mapper) else __evolved_defer() do - __multi_clone_entity(prefab, entity_list, entity_count, component_table, component_mapper) + __multi_clone_entity(prefab, + out_entity_list, out_entity_first, entity_count, + component_table, component_mapper) end __evolved_commit() end - - return entity_list, entity_count end ---@param entity evolved.entity @@ -6165,13 +6253,26 @@ end ---@return integer entity_count ---@nodiscard function __evolved_multi_lookup(name) + local entity_list = {} + local entity_count = __evolved_multi_lookup_to(entity_list, 1, name) + return entity_list, entity_count +end + +---@param out_entity_list evolved.entity[] +---@param out_entity_first integer +---@param name string +---@return integer entity_count +function __evolved_multi_lookup_to(out_entity_list, out_entity_first, name) do local named_entities = __named_entities[name] local named_entity_list = named_entities and named_entities.__item_list local named_entity_count = named_entities and named_entities.__item_count or 0 if named_entity_count > 0 then - return __list_fns.dup(named_entity_list, named_entity_count), named_entity_count + __lua_table_move( + named_entity_list, 1, named_entity_count, + out_entity_first, out_entity_list) + return named_entity_count end end @@ -6179,11 +6280,12 @@ function __evolved_multi_lookup(name) local named_entity = __named_entity[name] if named_entity then - return { named_entity }, 1 + out_entity_list[out_entity_first] = named_entity + return 1 end end - return __safe_tbls.__EMPTY_ENTITY_LIST, 0 + return 0 end ---@param ... evolved.system systems @@ -6531,6 +6633,7 @@ end ---@param component_mapper? evolved.component_mapper ---@return evolved.entity[] entity_list ---@return integer entity_count +---@nodiscard function __builder_mt:multi_build(entity_count, prefab, component_mapper) if prefab then return self:multi_clone(entity_count, prefab, component_mapper) @@ -6539,6 +6642,31 @@ function __builder_mt:multi_build(entity_count, prefab, component_mapper) end end +---@param entity_count integer +---@param prefab? evolved.entity +---@param component_mapper? evolved.component_mapper +function __builder_mt:multi_build_nr(entity_count, prefab, component_mapper) + if prefab then + self:multi_clone_nr(entity_count, prefab, component_mapper) + else + self:multi_spawn_nr(entity_count, component_mapper) + end +end + +---@param out_entity_list evolved.entity[] +---@param out_entity_first integer +---@param entity_count integer +---@param prefab? evolved.entity +---@param component_mapper? evolved.component_mapper +function __builder_mt:multi_build_to(out_entity_list, out_entity_first, + entity_count, prefab, component_mapper) + if prefab then + self:multi_clone_to(out_entity_list, out_entity_first, entity_count, prefab, component_mapper) + else + self:multi_spawn_to(out_entity_list, out_entity_first, entity_count, component_mapper) + end +end + ---@param component_mapper? evolved.component_mapper ---@return evolved.entity entity function __builder_mt:spawn(component_mapper) @@ -6579,9 +6707,41 @@ end ---@param component_mapper? evolved.component_mapper ---@return evolved.entity[] entity_list ---@return integer entity_count +---@nodiscard function __builder_mt:multi_spawn(entity_count, component_mapper) if entity_count <= 0 then - return __safe_tbls.__EMPTY_ENTITY_LIST, 0 + return {}, 0 + end + + local entity_list = __lua_table_new(entity_count) + + self:multi_spawn_to(entity_list, 1, entity_count, component_mapper) + + return entity_list, entity_count +end + +---@param entity_count integer +---@param component_mapper? evolved.component_mapper +function __builder_mt:multi_spawn_nr(entity_count, component_mapper) + if entity_count <= 0 then + return + end + + local entity_list = __acquire_table(__table_pool_tag.entity_list) + + self:multi_spawn_to(entity_list, 1, entity_count, component_mapper) + + __release_table(__table_pool_tag.entity_list, entity_list, false, true) +end + +---@param out_entity_list evolved.entity[] +---@param out_entity_first integer +---@param entity_count integer +---@param component_mapper? evolved.component_mapper +function __builder_mt:multi_spawn_to(out_entity_list, out_entity_first, + entity_count, component_mapper) + if entity_count <= 0 then + return end local chunk = self.__chunk @@ -6598,27 +6758,27 @@ function __builder_mt:multi_spawn(entity_count, component_mapper) end end - local entity_list = __lua_table_new(entity_count) - - for entity_index = 1, entity_count do - entity_list[entity_index] = __acquire_id() + for entity_index = out_entity_first, out_entity_first + entity_count - 1 do + out_entity_list[entity_index] = __acquire_id() end if not component_table or not __lua_next(component_table) then - return entity_list, entity_count + return end if __defer_depth > 0 then - __defer_multi_spawn_entity(chunk, entity_list, entity_count, component_table, component_mapper) + __defer_multi_spawn_entity(chunk, + out_entity_list, out_entity_first, entity_count, + component_table, component_mapper) else __evolved_defer() do - __multi_spawn_entity(chunk, entity_list, entity_count, component_table, component_mapper) + __multi_spawn_entity(chunk, + out_entity_list, out_entity_first, entity_count, + component_table, component_mapper) end __evolved_commit() end - - return entity_list, entity_count end ---@param prefab evolved.entity @@ -6663,9 +6823,43 @@ end ---@param component_mapper? evolved.component_mapper ---@return evolved.entity[] entity_list ---@return integer entity_count +---@nodiscard function __builder_mt:multi_clone(entity_count, prefab, component_mapper) if entity_count <= 0 then - return __safe_tbls.__EMPTY_ENTITY_LIST, 0 + return {}, 0 + end + + local entity_list = __lua_table_new(entity_count) + + self:multi_clone_to(entity_list, 1, entity_count, prefab, component_mapper) + + return entity_list, entity_count +end + +---@param entity_count integer +---@param prefab evolved.entity +---@param component_mapper? evolved.component_mapper +function __builder_mt:multi_clone_nr(entity_count, prefab, component_mapper) + if entity_count <= 0 then + return + end + + local entity_list = __acquire_table(__table_pool_tag.entity_list) + + self:multi_clone_to(entity_list, 1, entity_count, prefab, component_mapper) + + __release_table(__table_pool_tag.entity_list, entity_list, false, true) +end + +---@param out_entity_list evolved.entity[] +---@param out_entity_first integer +---@param entity_count integer +---@param prefab evolved.entity +---@param component_mapper? evolved.component_mapper +function __builder_mt:multi_clone_to(out_entity_list, out_entity_first, + entity_count, prefab, component_mapper) + if entity_count <= 0 then + return end local component_table = self.__component_table @@ -6686,23 +6880,23 @@ function __builder_mt:multi_clone(entity_count, prefab, component_mapper) end end - local entity_list = __lua_table_new(entity_count) - - for entity_index = 1, entity_count do - entity_list[entity_index] = __acquire_id() + for entity_index = out_entity_first, out_entity_first + entity_count - 1 do + out_entity_list[entity_index] = __acquire_id() end if __defer_depth > 0 then - __defer_multi_clone_entity(prefab, entity_list, entity_count, component_table, component_mapper) + __defer_multi_clone_entity(prefab, + out_entity_list, out_entity_first, entity_count, + component_table, component_mapper) else __evolved_defer() do - __multi_clone_entity(prefab, entity_list, entity_count, component_table, component_mapper) + __multi_clone_entity(prefab, + out_entity_list, out_entity_first, entity_count, + component_table, component_mapper) end __evolved_commit() end - - return entity_list, entity_count end ---@param fragment evolved.fragment @@ -7660,9 +7854,13 @@ evolved.cancel = __evolved_cancel evolved.spawn = __evolved_spawn evolved.multi_spawn = __evolved_multi_spawn +evolved.multi_spawn_nr = __evolved_multi_spawn_nr +evolved.multi_spawn_to = __evolved_multi_spawn_to evolved.clone = __evolved_clone evolved.multi_clone = __evolved_multi_clone +evolved.multi_clone_nr = __evolved_multi_clone_nr +evolved.multi_clone_to = __evolved_multi_clone_to evolved.alive = __evolved_alive evolved.alive_all = __evolved_alive_all @@ -7695,6 +7893,7 @@ evolved.locate = __evolved_locate evolved.lookup = __evolved_lookup evolved.multi_lookup = __evolved_multi_lookup +evolved.multi_lookup_to = __evolved_multi_lookup_to evolved.process = __evolved_process evolved.process_with = __evolved_process_with From 8b77b45421ddd89cd2ae09f6ec08ef1c61dac62d Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 13 Feb 2026 07:36:52 +0700 Subject: [PATCH 14/18] fix new discards in tests --- develop/benchmarks/clone_bmarks.lua | 72 +++++++++++++-------------- develop/benchmarks/common_bmarks.lua | 14 +++--- develop/benchmarks/process_bmarks.lua | 4 +- develop/benchmarks/spawn_bmarks.lua | 72 +++++++++++++-------------- example/main.lua | 2 +- 5 files changed, 82 insertions(+), 82 deletions(-) diff --git a/develop/benchmarks/clone_bmarks.lua b/develop/benchmarks/clone_bmarks.lua index 863f244..e2d297c 100644 --- a/develop/benchmarks/clone_bmarks.lua +++ b/develop/benchmarks/clone_bmarks.lua @@ -307,11 +307,11 @@ print '----------------------------------------' basics.describe_bench( string.format('Clone Benchmarks: Multi Clone | %d entities with 1 component', N), function() - local multi_clone = evo.multi_clone + local multi_clone_nr = evo.multi_clone_nr local prefab = evo.spawn { [F1] = true } - multi_clone(N, prefab) + multi_clone_nr(N, prefab) evo.batch_destroy(QF1) end) @@ -319,12 +319,12 @@ basics.describe_bench( basics.describe_bench( string.format('Clone Benchmarks: Multi Defer Clone | %d entities with 1 component', N), function() - local multi_clone = evo.multi_clone + local multi_clone_nr = evo.multi_clone_nr local prefab = evo.spawn { [F1] = true } evo.defer() - multi_clone(N, prefab) + multi_clone_nr(N, prefab) evo.commit() evo.batch_destroy(QF1) @@ -333,11 +333,11 @@ basics.describe_bench( basics.describe_bench( string.format('Clone Benchmarks: Multi Clone With Defaults | %d entities with 1 component', N), function() - local multi_clone = evo.multi_clone + local multi_clone_nr = evo.multi_clone_nr local prefab = evo.spawn { [D1] = true } - multi_clone(N, prefab) + multi_clone_nr(N, prefab) evo.batch_destroy(QD1) end) @@ -345,11 +345,11 @@ basics.describe_bench( basics.describe_bench( string.format('Clone Benchmarks: Multi Clone | %d entities with 3 components', N), function() - local multi_clone = evo.multi_clone + local multi_clone_nr = evo.multi_clone_nr local prefab = evo.spawn { [F1] = true, [F2] = true, [F3] = true } - multi_clone(N, prefab) + multi_clone_nr(N, prefab) evo.batch_destroy(QF1) end) @@ -357,12 +357,12 @@ basics.describe_bench( basics.describe_bench( string.format('Clone Benchmarks: Multi Defer Clone | %d entities with 3 components', N), function() - local multi_clone = evo.multi_clone + local multi_clone_nr = evo.multi_clone_nr local prefab = evo.spawn { [F1] = true, [F2] = true, [F3] = true } evo.defer() - multi_clone(N, prefab) + multi_clone_nr(N, prefab) evo.commit() evo.batch_destroy(QF1) @@ -371,11 +371,11 @@ basics.describe_bench( basics.describe_bench( string.format('Clone Benchmarks: Multi Clone With Defaults | %d entities with 3 components', N), function() - local multi_clone = evo.multi_clone + local multi_clone_nr = evo.multi_clone_nr local prefab = evo.spawn { [D1] = true, [D2] = true, [D3] = true } - multi_clone(N, prefab) + multi_clone_nr(N, prefab) evo.batch_destroy(QD1) end) @@ -383,11 +383,11 @@ basics.describe_bench( basics.describe_bench( string.format('Clone Benchmarks: Multi Clone | %d entities with 5 components', N), function() - local multi_clone = evo.multi_clone + local multi_clone_nr = evo.multi_clone_nr local prefab = evo.spawn { [F1] = true, [F2] = true, [F3] = true, [F4] = true, [F5] = true } - multi_clone(N, prefab) + multi_clone_nr(N, prefab) evo.batch_destroy(QF1) end) @@ -395,12 +395,12 @@ basics.describe_bench( basics.describe_bench( string.format('Clone Benchmarks: Multi Defer Clone | %d entities with 5 components', N), function() - local multi_clone = evo.multi_clone + local multi_clone_nr = evo.multi_clone_nr local prefab = evo.spawn { [F1] = true, [F2] = true, [F3] = true, [F4] = true, [F5] = true } evo.defer() - multi_clone(N, prefab) + multi_clone_nr(N, prefab) evo.commit() evo.batch_destroy(QF1) @@ -409,11 +409,11 @@ basics.describe_bench( basics.describe_bench( string.format('Clone Benchmarks: Multi Clone With Defaults | %d entities with 5 components', N), function() - local multi_clone = evo.multi_clone + local multi_clone_nr = evo.multi_clone_nr local prefab = evo.spawn { [D1] = true, [D2] = true, [D3] = true, [D4] = true, [D5] = true } - multi_clone(N, prefab) + multi_clone_nr(N, prefab) evo.batch_destroy(QD1) end) @@ -423,11 +423,11 @@ print '----------------------------------------' basics.describe_bench( string.format('Clone Benchmarks: Multi Clone | %d entities with 1 required component', N), function() - local multi_clone = evo.multi_clone + local multi_clone_nr = evo.multi_clone_nr local prefab = evo.spawn { [RF1] = true } - multi_clone(N, prefab) + multi_clone_nr(N, prefab) evo.batch_destroy(QF1) end) @@ -435,12 +435,12 @@ basics.describe_bench( basics.describe_bench( string.format('Clone Benchmarks: Multi Defer Clone | %d entities with 1 required component', N), function() - local multi_clone = evo.multi_clone + local multi_clone_nr = evo.multi_clone_nr local prefab = evo.spawn { [RF1] = true } evo.defer() - multi_clone(N, prefab) + multi_clone_nr(N, prefab) evo.commit() evo.batch_destroy(QF1) @@ -449,11 +449,11 @@ basics.describe_bench( basics.describe_bench( string.format('Clone Benchmarks: Multi Clone With Defaults | %d entities with 1 required component', N), function() - local multi_clone = evo.multi_clone + local multi_clone_nr = evo.multi_clone_nr local prefab = evo.spawn { [RD1] = true } - multi_clone(N, prefab) + multi_clone_nr(N, prefab) evo.batch_destroy(QD1) end) @@ -461,11 +461,11 @@ basics.describe_bench( basics.describe_bench( string.format('Clone Benchmarks: Multi Clone | %d entities with 3 required components', N), function() - local multi_clone = evo.multi_clone + local multi_clone_nr = evo.multi_clone_nr local prefab = evo.spawn { [RF123] = true } - multi_clone(N, prefab) + multi_clone_nr(N, prefab) evo.batch_destroy(QF1) end) @@ -473,12 +473,12 @@ basics.describe_bench( basics.describe_bench( string.format('Clone Benchmarks: Multi Defer Clone | %d entities with 3 required components', N), function() - local multi_clone = evo.multi_clone + local multi_clone_nr = evo.multi_clone_nr local prefab = evo.spawn { [RF123] = true } evo.defer() - multi_clone(N, prefab) + multi_clone_nr(N, prefab) evo.commit() evo.batch_destroy(QF1) @@ -487,11 +487,11 @@ basics.describe_bench( basics.describe_bench( string.format('Clone Benchmarks: Multi Clone With Defaults | %d entities with 3 required components', N), function() - local multi_clone = evo.multi_clone + local multi_clone_nr = evo.multi_clone_nr local prefab = evo.spawn { [RD123] = true } - multi_clone(N, prefab) + multi_clone_nr(N, prefab) evo.batch_destroy(QD1) end) @@ -499,11 +499,11 @@ basics.describe_bench( basics.describe_bench( string.format('Clone Benchmarks: Multi Clone | %d entities with 5 required components', N), function() - local multi_clone = evo.multi_clone + local multi_clone_nr = evo.multi_clone_nr local prefab = evo.spawn { [RF12345] = true } - multi_clone(N, prefab) + multi_clone_nr(N, prefab) evo.batch_destroy(QF1) end) @@ -511,12 +511,12 @@ basics.describe_bench( basics.describe_bench( string.format('Clone Benchmarks: Multi Defer Clone | %d entities with 5 required components', N), function() - local multi_clone = evo.multi_clone + local multi_clone_nr = evo.multi_clone_nr local prefab = evo.spawn { [RF12345] = true } evo.defer() - multi_clone(N, prefab) + multi_clone_nr(N, prefab) evo.commit() evo.batch_destroy(QF1) @@ -525,11 +525,11 @@ basics.describe_bench( basics.describe_bench( string.format('Clone Benchmarks: Multi Clone With Defaults | %d entities with 5 required components', N), function() - local multi_clone = evo.multi_clone + local multi_clone_nr = evo.multi_clone_nr local prefab = evo.spawn { [RD12345] = true } - multi_clone(N, prefab) + multi_clone_nr(N, prefab) evo.batch_destroy(QD1) end) diff --git a/develop/benchmarks/common_bmarks.lua b/develop/benchmarks/common_bmarks.lua index 4e5964f..c63f232 100644 --- a/develop/benchmarks/common_bmarks.lua +++ b/develop/benchmarks/common_bmarks.lua @@ -54,7 +54,7 @@ basics.describe_bench(string.format('Common Benchmarks: Evolved Entity Cycle | % local prefab_a = evo.builder():prefab():set(world):set(a, 0):spawn() local prefab_b = evo.builder():prefab():set(world):set(b, 0):spawn() - evo.multi_clone(N, prefab_a) + evo.multi_clone_nr(N, prefab_a) evo.builder() :set(world):group(world):query(query_a) @@ -129,10 +129,10 @@ basics.describe_bench(string.format('Common Benchmarks: Evolved Simple Iteration local query_cd = evo.builder():set(world):include(c, d):spawn() local query_ce = evo.builder():set(world):include(c, e):spawn() - evo.multi_spawn(N, { [world] = true, [a] = 0, [b] = 0 }) - evo.multi_spawn(N, { [world] = true, [a] = 0, [b] = 0, [c] = 0 }) - evo.multi_spawn(N, { [world] = true, [a] = 0, [b] = 0, [c] = 0, [d] = 0 }) - evo.multi_spawn(N, { [world] = true, [a] = 0, [b] = 0, [c] = 0, [e] = 0 }) + evo.multi_spawn_nr(N, { [world] = true, [a] = 0, [b] = 0 }) + evo.multi_spawn_nr(N, { [world] = true, [a] = 0, [b] = 0, [c] = 0 }) + evo.multi_spawn_nr(N, { [world] = true, [a] = 0, [b] = 0, [c] = 0, [d] = 0 }) + evo.multi_spawn_nr(N, { [world] = true, [a] = 0, [b] = 0, [c] = 0, [e] = 0 }) evo.builder() :set(world):group(world):query(query_ab) @@ -223,7 +223,7 @@ basics.describe_bench(string.format('Common Benchmarks: Evolved Packed Iteration local query_d = evo.builder():set(world):include(d):spawn() local query_e = evo.builder():set(world):include(e):spawn() - evo.multi_spawn(N, { [world] = true, [a] = 0, [b] = 0, [c] = 0, [d] = 0, [e] = 0 }) + evo.multi_spawn_nr(N, { [world] = true, [a] = 0, [b] = 0, [c] = 0, [d] = 0, [e] = 0 }) evo.builder() :set(world):group(world):query(query_a) @@ -317,7 +317,7 @@ basics.describe_bench(string.format('Common Benchmarks: Evolved Fragmented Itera local query_z = evo.builder():set(world):include(chars[#chars]):spawn() for i = 1, #chars do - evo.multi_spawn(N, { [world] = true, [chars[i]] = i, [data] = i }) + evo.multi_spawn_nr(N, { [world] = true, [chars[i]] = i, [data] = i }) end evo.builder() diff --git a/develop/benchmarks/process_bmarks.lua b/develop/benchmarks/process_bmarks.lua index ce1e780..287a8dd 100644 --- a/develop/benchmarks/process_bmarks.lua +++ b/develop/benchmarks/process_bmarks.lua @@ -20,7 +20,7 @@ basics.describe_bench(string.format('Process Benchmarks: Evolved AoS Processing local pf = evo.builder():set(wf):spawn() local vf = evo.builder():set(wf):spawn() - evo.multi_spawn(N, { + evo.multi_spawn_nr(N, { [wf] = true, [pf] = { x = 0, y = 0, z = 0, w = 0 }, [vf] = { x = 0, y = 0, z = 0, w = 0 }, @@ -67,7 +67,7 @@ basics.describe_bench(string.format('Process Benchmarks: Evolved SoA Processing local vzf = evo.builder():set(wf):spawn() local vwf = evo.builder():set(wf):spawn() - evo.multi_spawn(N, { + evo.multi_spawn_nr(N, { [wf] = true, [pxf] = 0, [pyf] = 0, diff --git a/develop/benchmarks/spawn_bmarks.lua b/develop/benchmarks/spawn_bmarks.lua index 5725425..a6ee5dc 100644 --- a/develop/benchmarks/spawn_bmarks.lua +++ b/develop/benchmarks/spawn_bmarks.lua @@ -536,11 +536,11 @@ print '----------------------------------------' basics.describe_bench( string.format('Spawn Benchmarks: Multi Spawn | %d entities with 1 component', N), function() - local multi_spawn = evo.multi_spawn + local multi_spawn_nr = evo.multi_spawn_nr local components = { [F1] = true } - multi_spawn(N, components) + multi_spawn_nr(N, components) evo.batch_destroy(QF1) end) @@ -548,12 +548,12 @@ basics.describe_bench( basics.describe_bench( string.format('Spawn Benchmarks: Multi Defer Spawn | %d entities with 1 component', N), function() - local multi_spawn = evo.multi_spawn + local multi_spawn_nr = evo.multi_spawn_nr local components = { [F1] = true } evo.defer() - multi_spawn(N, components) + multi_spawn_nr(N, components) evo.commit() evo.batch_destroy(QF1) @@ -562,11 +562,11 @@ basics.describe_bench( basics.describe_bench( string.format('Spawn Benchmarks: Multi Spawn With Defaults | %d entities with 1 component', N), function() - local multi_spawn = evo.multi_spawn + local multi_spawn_nr = evo.multi_spawn_nr local components = { [D1] = true } - multi_spawn(N, components) + multi_spawn_nr(N, components) evo.batch_destroy(QD1) end) @@ -574,11 +574,11 @@ basics.describe_bench( basics.describe_bench( string.format('Spawn Benchmarks: Multi Spawn | %d entities with 3 components', N), function() - local multi_spawn = evo.multi_spawn + local multi_spawn_nr = evo.multi_spawn_nr local components = { [F1] = true, [F2] = true, [F3] = true } - multi_spawn(N, components) + multi_spawn_nr(N, components) evo.batch_destroy(QF1) end) @@ -586,12 +586,12 @@ basics.describe_bench( basics.describe_bench( string.format('Spawn Benchmarks: Multi Defer Spawn | %d entities with 3 components', N), function() - local multi_spawn = evo.multi_spawn + local multi_spawn_nr = evo.multi_spawn_nr local components = { [F1] = true, [F2] = true, [F3] = true } evo.defer() - multi_spawn(N, components) + multi_spawn_nr(N, components) evo.commit() evo.batch_destroy(QF1) @@ -600,11 +600,11 @@ basics.describe_bench( basics.describe_bench( string.format('Spawn Benchmarks: Multi Spawn With Defaults | %d entities with 3 components', N), function() - local multi_spawn = evo.multi_spawn + local multi_spawn_nr = evo.multi_spawn_nr local components = { [D1] = true, [D2] = true, [D3] = true } - multi_spawn(N, components) + multi_spawn_nr(N, components) evo.batch_destroy(QD1) end) @@ -612,11 +612,11 @@ basics.describe_bench( basics.describe_bench( string.format('Spawn Benchmarks: Multi Spawn | %d entities with 5 components', N), function() - local multi_spawn = evo.multi_spawn + local multi_spawn_nr = evo.multi_spawn_nr local components = { [F1] = true, [F2] = true, [F3] = true, [F4] = true, [F5] = true } - multi_spawn(N, components) + multi_spawn_nr(N, components) evo.batch_destroy(QF1) end) @@ -624,12 +624,12 @@ basics.describe_bench( basics.describe_bench( string.format('Spawn Benchmarks: Multi Defer Spawn | %d entities with 5 components', N), function() - local multi_spawn = evo.multi_spawn + local multi_spawn_nr = evo.multi_spawn_nr local components = { [F1] = true, [F2] = true, [F3] = true, [F4] = true, [F5] = true } evo.defer() - multi_spawn(N, components) + multi_spawn_nr(N, components) evo.commit() evo.batch_destroy(QF1) @@ -638,11 +638,11 @@ basics.describe_bench( basics.describe_bench( string.format('Spawn Benchmarks: Multi Spawn With Defaults | %d entities with 5 components', N), function() - local multi_spawn = evo.multi_spawn + local multi_spawn_nr = evo.multi_spawn_nr local components = { [D1] = true, [D2] = true, [D3] = true, [D4] = true, [D5] = true } - multi_spawn(N, components) + multi_spawn_nr(N, components) evo.batch_destroy(QD1) end) @@ -652,11 +652,11 @@ print '----------------------------------------' basics.describe_bench( string.format('Spawn Benchmarks: Multi Spawn | %d entities with 1 required component', N), function() - local multi_spawn = evo.multi_spawn + local multi_spawn_nr = evo.multi_spawn_nr local components = { [F1] = true } - multi_spawn(N, components) + multi_spawn_nr(N, components) evo.batch_destroy(QF1) end) @@ -664,12 +664,12 @@ basics.describe_bench( basics.describe_bench( string.format('Spawn Benchmarks: Multi Defer Spawn | %d entities with 1 required component', N), function() - local multi_spawn = evo.multi_spawn + local multi_spawn_nr = evo.multi_spawn_nr local components = { [F1] = true } evo.defer() - multi_spawn(N, components) + multi_spawn_nr(N, components) evo.commit() evo.batch_destroy(QF1) @@ -678,11 +678,11 @@ basics.describe_bench( basics.describe_bench( string.format('Spawn Benchmarks: Multi Spawn With Defaults | %d entities with 1 required component', N), function() - local multi_spawn = evo.multi_spawn + local multi_spawn_nr = evo.multi_spawn_nr local components = { [D1] = true } - multi_spawn(N, components) + multi_spawn_nr(N, components) evo.batch_destroy(QD1) end) @@ -690,11 +690,11 @@ basics.describe_bench( basics.describe_bench( string.format('Spawn Benchmarks: Multi Spawn | %d entities with 3 required components', N), function() - local multi_spawn = evo.multi_spawn + local multi_spawn_nr = evo.multi_spawn_nr local components = { [RF123] = true } - multi_spawn(N, components) + multi_spawn_nr(N, components) evo.batch_destroy(QF1) end) @@ -702,12 +702,12 @@ basics.describe_bench( basics.describe_bench( string.format('Spawn Benchmarks: Multi Defer Spawn | %d entities with 3 required components', N), function() - local multi_spawn = evo.multi_spawn + local multi_spawn_nr = evo.multi_spawn_nr local components = { [RF123] = true } evo.defer() - multi_spawn(N, components) + multi_spawn_nr(N, components) evo.commit() evo.batch_destroy(QF1) @@ -716,11 +716,11 @@ basics.describe_bench( basics.describe_bench( string.format('Spawn Benchmarks: Multi Spawn With Defaults | %d entities with 3 required components', N), function() - local multi_spawn = evo.multi_spawn + local multi_spawn_nr = evo.multi_spawn_nr local components = { [RD123] = true } - multi_spawn(N, components) + multi_spawn_nr(N, components) evo.batch_destroy(QD1) end) @@ -728,11 +728,11 @@ basics.describe_bench( basics.describe_bench( string.format('Spawn Benchmarks: Multi Spawn | %d entities with 5 required components', N), function() - local multi_spawn = evo.multi_spawn + local multi_spawn_nr = evo.multi_spawn_nr local components = { [RF12345] = true } - multi_spawn(N, components) + multi_spawn_nr(N, components) evo.batch_destroy(QF1) end) @@ -740,12 +740,12 @@ basics.describe_bench( basics.describe_bench( string.format('Spawn Benchmarks: Multi Defer Spawn | %d entities with 5 required components', N), function() - local multi_spawn = evo.multi_spawn + local multi_spawn_nr = evo.multi_spawn_nr local components = { [RF12345] = true } evo.defer() - multi_spawn(N, components) + multi_spawn_nr(N, components) evo.commit() evo.batch_destroy(QF1) @@ -754,11 +754,11 @@ basics.describe_bench( basics.describe_bench( string.format('Spawn Benchmarks: Multi Spawn With Defaults | %d entities with 5 required components', N), function() - local multi_spawn = evo.multi_spawn + local multi_spawn_nr = evo.multi_spawn_nr local components = { [RD12345] = true } - multi_spawn(N, components) + multi_spawn_nr(N, components) evo.batch_destroy(QD1) end) diff --git a/example/main.lua b/example/main.lua index 84abfec..634084b 100644 --- a/example/main.lua +++ b/example/main.lua @@ -52,7 +52,7 @@ evolved.builder() :name('SYSTEMS.STARTUP') :group(STAGES.ON_SETUP) :prologue(function() - evolved.multi_clone(500, PREFABS.CIRCLE, nil, function(chunk, b_place, e_place) + evolved.multi_clone_nr(500, PREFABS.CIRCLE, nil, function(chunk, b_place, e_place) local screen_width, screen_height = love.graphics.getDimensions() ---@type number[], number[] From b941baf6bbb96a3f68a8a0047597c11756a242fc Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 13 Feb 2026 07:52:38 +0700 Subject: [PATCH 15/18] additional nr/to spawn/lookup tests --- develop/testing/clone_tests.lua | 162 ++++++++++++++++++++++++++ develop/testing/lookup_tests.lua | 19 +++ develop/testing/multi_spawn_tests.lua | 161 +++++++++++++++++++++++++ 3 files changed, 342 insertions(+) diff --git a/develop/testing/clone_tests.lua b/develop/testing/clone_tests.lua index 26b2d6e..29b3c32 100644 --- a/develop/testing/clone_tests.lua +++ b/develop/testing/clone_tests.lua @@ -397,3 +397,165 @@ do end end end + +do + local f1, f2 = evo.id(2) + local p = evo.spawn { [f1] = 42, [f2] = 'hello' } + + do + local entity_list, entity_count = {}, 2 + evo.multi_clone_to(entity_list, 1, entity_count, p) + assert(evo.has_all(entity_list[1], f1, f2)) + assert(evo.has_all(entity_list[2], f1, f2)) + assert(evo.get(entity_list[1], f1) == 42 and evo.get(entity_list[1], f2) == 'hello') + assert(evo.get(entity_list[2], f1) == 42 and evo.get(entity_list[2], f2) == 'hello') + end + + do + local entity_list, entity_count = {}, 2 + evo.multi_clone_to(entity_list, 2, entity_count, p) + assert(evo.has_all(entity_list[2], f1, f2)) + assert(evo.has_all(entity_list[3], f1, f2)) + assert(evo.get(entity_list[2], f1) == 42 and evo.get(entity_list[2], f2) == 'hello') + assert(evo.get(entity_list[3], f1) == 42 and evo.get(entity_list[3], f2) == 'hello') + end + + do + local entity_list, entity_count = {}, 2 + evo.defer() + evo.multi_clone_to(entity_list, 1, entity_count, p) + assert(entity_list[1] and entity_list[2]) + assert(evo.empty_all(entity_list[1], entity_list[2])) + evo.commit() + assert(evo.has_all(entity_list[1], f1, f2)) + assert(evo.has_all(entity_list[2], f1, f2)) + assert(evo.get(entity_list[1], f1) == 42 and evo.get(entity_list[1], f2) == 'hello') + assert(evo.get(entity_list[2], f1) == 42 and evo.get(entity_list[2], f2) == 'hello') + end + + do + local entity_list, entity_count = {}, 2 + evo.defer() + evo.multi_clone_to(entity_list, 2, entity_count, p) + assert(entity_list[2] and entity_list[3]) + assert(evo.empty_all(entity_list[2], entity_list[3])) + evo.commit() + assert(evo.has_all(entity_list[2], f1, f2)) + assert(evo.has_all(entity_list[3], f1, f2)) + assert(evo.get(entity_list[2], f1) == 42 and evo.get(entity_list[2], f2) == 'hello') + assert(evo.get(entity_list[3], f1) == 42 and evo.get(entity_list[3], f2) == 'hello') + end +end + +do + local f1, f2 = evo.id(2) + local q12 = evo.builder():include(f1, f2):build() + local p = evo.spawn { [f1] = 42, [f2] = 'hello' } + + do + assert(select('#', evo.multi_clone_nr(2, p)) == 0) + + do + local entity_count = 0 + + for chunk in evo.execute(q12) do + local _, chunk_entity_count = chunk:entities() + entity_count = entity_count + chunk_entity_count + end + + assert(entity_count == 3) + end + end + + do + local b = evo.builder():set(f1, 42):set(f2, 'hello') + + assert(select('#', b:multi_clone_nr(2, p)) == 0) + + do + local entity_count = 0 + + for chunk in evo.execute(q12) do + local _, chunk_entity_count = chunk:entities() + entity_count = entity_count + chunk_entity_count + end + + assert(entity_count == 5) + end + end + + do + local b = evo.builder():set(f1, 42):set(f2, 'hello') + + assert(select('#', b:multi_build_nr(2, p)) == 0) + + do + local entity_count = 0 + + for chunk in evo.execute(q12) do + local _, chunk_entity_count = chunk:entities() + entity_count = entity_count + chunk_entity_count + end + + assert(entity_count == 7) + end + end +end + +do + local f1, f2 = evo.id(2) + local q12 = evo.builder():include(f1, f2):build() + local p = evo.spawn { [f1] = 42, [f2] = 'hello' } + + do + local entity_list = {} + assert(select('#', evo.multi_clone_to(entity_list, 1, 2, p)) == 0) + + do + local entity_count = 0 + + for chunk in evo.execute(q12) do + local _, chunk_entity_count = chunk:entities() + entity_count = entity_count + chunk_entity_count + end + + assert(entity_count == 3) + end + end + + do + local b = evo.builder():set(f1, 42):set(f2, 'hello') + + local entity_list = {} + assert(select('#', b:multi_clone_to(entity_list, 1, 2, p)) == 0) + + do + local entity_count = 0 + + for chunk in evo.execute(q12) do + local _, chunk_entity_count = chunk:entities() + entity_count = entity_count + chunk_entity_count + end + + assert(entity_count == 5) + end + end + + do + local b = evo.builder():set(f1, 42):set(f2, 'hello') + + local entity_list = {} + assert(select('#', b:multi_build_to(entity_list, 1, 2, p)) == 0) + + do + local entity_count = 0 + + for chunk in evo.execute(q12) do + local _, chunk_entity_count = chunk:entities() + entity_count = entity_count + chunk_entity_count + end + + assert(entity_count == 7) + end + end +end diff --git a/develop/testing/lookup_tests.lua b/develop/testing/lookup_tests.lua index bc1374d..1120662 100644 --- a/develop/testing/lookup_tests.lua +++ b/develop/testing/lookup_tests.lua @@ -174,3 +174,22 @@ do assert(entity_list and #entity_list == 0 and entity_count == 0) end end + +do + local e1, e2 = evo.id(2) + + evo.set(e1, evo.NAME, 'lookup_e') + evo.set(e2, evo.NAME, 'lookup_e') + + do + local entity_list = {} + local entity_count = evo.multi_lookup_to(entity_list, 1, 'lookup_e') + assert(entity_count == 2 and entity_list[1] == e1 and entity_list[2] == e2) + end + + do + local entity_list = {} + local entity_count = evo.multi_lookup_to(entity_list, 2, 'lookup_e') + assert(entity_count == 2 and entity_list[2] == e1 and entity_list[3] == e2) + end +end diff --git a/develop/testing/multi_spawn_tests.lua b/develop/testing/multi_spawn_tests.lua index 8908e17..e0d0e7b 100644 --- a/develop/testing/multi_spawn_tests.lua +++ b/develop/testing/multi_spawn_tests.lua @@ -607,3 +607,164 @@ do end end end + +do + local f1, f2 = evo.id(2) + + do + local entity_list, entity_count = {}, 2 + evo.multi_spawn_to(entity_list, 1, entity_count, { [f1] = 42, [f2] = "hello" }) + assert(evo.has_all(entity_list[1], f1, f2)) + assert(evo.has_all(entity_list[2], f1, f2)) + assert(evo.get(entity_list[1], f1) == 42 and evo.get(entity_list[1], f2) == "hello") + assert(evo.get(entity_list[2], f1) == 42 and evo.get(entity_list[2], f2) == "hello") + end + + do + local entity_list, entity_count = {}, 2 + evo.multi_spawn_to(entity_list, 2, entity_count, { [f1] = 42, [f2] = "hello" }) + assert(evo.has_all(entity_list[2], f1, f2)) + assert(evo.has_all(entity_list[3], f1, f2)) + assert(evo.get(entity_list[2], f1) == 42 and evo.get(entity_list[2], f2) == "hello") + assert(evo.get(entity_list[3], f1) == 42 and evo.get(entity_list[3], f2) == "hello") + end + + do + local entity_list, entity_count = {}, 2 + evo.defer() + evo.multi_spawn_to(entity_list, 1, entity_count, { [f1] = 42, [f2] = "hello" }) + assert(entity_list[1] and entity_list[2]) + assert(evo.empty_all(entity_list[1], entity_list[2])) + evo.commit() + assert(evo.has_all(entity_list[1], f1, f2)) + assert(evo.has_all(entity_list[2], f1, f2)) + assert(evo.get(entity_list[1], f1) == 42 and evo.get(entity_list[1], f2) == "hello") + assert(evo.get(entity_list[2], f1) == 42 and evo.get(entity_list[2], f2) == "hello") + end + + do + local entity_list, entity_count = {}, 2 + evo.defer() + evo.multi_spawn_to(entity_list, 2, entity_count, { [f1] = 42, [f2] = "hello" }) + assert(entity_list[2] and entity_list[3]) + assert(evo.empty_all(entity_list[2], entity_list[3])) + evo.commit() + assert(evo.has_all(entity_list[2], f1, f2)) + assert(evo.has_all(entity_list[3], f1, f2)) + assert(evo.get(entity_list[2], f1) == 42 and evo.get(entity_list[2], f2) == "hello") + assert(evo.get(entity_list[3], f1) == 42 and evo.get(entity_list[3], f2) == "hello") + end +end + +do + local f1, f2 = evo.id(2) + local q12 = evo.builder():include(f1, f2):spawn() + + do + assert(select('#', evo.multi_spawn_nr(2, { [f1] = 42, [f2] = "hello" })) == 0) + + do + local entity_count = 0 + + for chunk in evo.execute(q12) do + local _, chunk_entity_count = chunk:entities() + entity_count = entity_count + chunk_entity_count + end + + assert(entity_count == 2) + end + end + + do + local b = evo.builder():set(f1, 42):set(f2, "hello") + + assert(select('#', b:multi_spawn_nr(2)) == 0) + + do + local entity_count = 0 + + for chunk in evo.execute(q12) do + local _, chunk_entity_count = chunk:entities() + entity_count = entity_count + chunk_entity_count + end + + assert(entity_count == 4) + end + end + + do + local b = evo.builder():set(f1, 42):set(f2, "hello") + + assert(select('#', b:multi_build_nr(2)) == 0) + + do + local entity_count = 0 + + for chunk in evo.execute(q12) do + local _, chunk_entity_count = chunk:entities() + entity_count = entity_count + chunk_entity_count + end + + assert(entity_count == 6) + end + end +end + +do + local f1, f2 = evo.id(2) + local q12 = evo.builder():include(f1, f2):spawn() + + do + local entity_list = {} + assert(select('#', evo.multi_spawn_to(entity_list, 1, 2, { [f1] = 42, [f2] = "hello" })) == 0) + + do + local entity_count = 0 + + for chunk in evo.execute(q12) do + local _, chunk_entity_count = chunk:entities() + entity_count = entity_count + chunk_entity_count + end + + assert(entity_count == 2) + end + end + + do + local b = evo.builder():set(f1, 42):set(f2, "hello") + + + local entity_list = {} + assert(select('#', b:multi_spawn_to(entity_list, 1, 2)) == 0) + + do + local entity_count = 0 + + for chunk in evo.execute(q12) do + local _, chunk_entity_count = chunk:entities() + entity_count = entity_count + chunk_entity_count + end + + assert(entity_count == 4) + end + end + + do + local b = evo.builder():set(f1, 42):set(f2, "hello") + + + local entity_list = {} + assert(select('#', b:multi_build_to(entity_list, 1, 2)) == 0) + + do + local entity_count = 0 + + for chunk in evo.execute(q12) do + local _, chunk_entity_count = chunk:entities() + entity_count = entity_count + chunk_entity_count + end + + assert(entity_count == 6) + end + end +end From e364aaab37c95d56427b0d67d23617e82f24260f Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 13 Feb 2026 08:07:14 +0700 Subject: [PATCH 16/18] update teal definitions --- evolved.d.tl | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/evolved.d.tl b/evolved.d.tl index c31c92f..5f7795f 100644 --- a/evolved.d.tl +++ b/evolved.d.tl @@ -40,6 +40,18 @@ prefab?: Entity, component_mapper?: function(Chunk, integer, integer)): { Entity }, integer + multi_build_nr: function(self: Builder, + entity_count: integer, + prefab?: Entity, + component_mapper?: function(Chunk, integer, integer)) + + multi_build_to: function(self: Builder, + out_entity_list: { Entity }, + out_entity_first: integer, + entity_count: integer, + prefab?: Entity, + component_mapper?: function(Chunk, integer, integer)) + spawn: function(self: Builder, component_mapper?: function(Chunk, integer)): Entity @@ -47,6 +59,16 @@ entity_count: integer, component_mapper?: function(Chunk, integer, integer)): { Entity }, integer + multi_spawn_nr: function(self: Builder, + entity_count: integer, + component_mapper?: function(Chunk, integer, integer)) + + multi_spawn_to: function(self: Builder, + out_entity_list: { Entity }, + out_entity_first: integer, + entity_count: integer, + component_mapper?: function(Chunk, integer, integer)) + clone: function(self: Builder, prefab: Entity, component_mapper?: function(Chunk, integer)): Entity @@ -56,6 +78,18 @@ prefab: Entity, component_mapper?: function(Chunk, integer, integer)): { Entity }, integer + multi_clone_nr: function(self: Builder, + entity_count: integer, + prefab: Entity, + component_mapper?: function(Chunk, integer, integer)) + + multi_clone_to: function(self: Builder, + out_entity_list: { Entity }, + out_entity_first: integer, + entity_count: integer, + prefab: Entity, + component_mapper?: function(Chunk, integer, integer)) + has: function(self: Builder, fragment: Fragment): boolean has_all: function(self: Builder, ...: Fragment): boolean has_any: function(self: Builder, ...: Fragment): boolean @@ -164,6 +198,18 @@ component_table?: { Fragment: any }, component_mapper?: function(Chunk, integer, integer)): { Entity }, integer + multi_spawn_nr: function( + entity_count: integer, + component_table?: { Fragment: any }, + component_mapper?: function(Chunk, integer, integer)) + + multi_spawn_to: function( + out_entity_list: { Entity }, + out_entity_first: integer, + entity_count: integer, + component_table?: { Fragment: any }, + component_mapper?: function(Chunk, integer, integer)) + clone: function( prefab: Entity, component_table?: { Fragment: any }, @@ -175,6 +221,20 @@ component_table?: { Fragment: any }, component_mapper?: function(Chunk, integer, integer)): { Entity }, integer + multi_clone_nr: function( + entity_count: integer, + prefab: Entity, + component_table?: { Fragment: any }, + component_mapper?: function(Chunk, integer, integer)) + + multi_clone_to: function( + out_entity_list: { Entity }, + out_entity_first: integer, + entity_count: integer, + prefab: Entity, + component_table?: { Fragment: any }, + component_mapper?: function(Chunk, integer, integer)) + alive: function(entity: Entity): boolean alive_all: function(...: Entity): boolean alive_any: function(...: Entity): boolean @@ -210,6 +270,7 @@ lookup: function(name: string): Entity | nil multi_lookup: function(name: string): { Entity }, integer + multi_lookup_to: function(out_entity_list: { Entity }, out_entity_first: integer, name: string): integer process: function(...: System) process_with: function(system: System, ...: any) From 766f7b92be81533df326c2cc9942636541cf8049 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 13 Feb 2026 21:13:51 +0700 Subject: [PATCH 17/18] update README --- README.md | 76 +++++++++++++++++++++++++++---------------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 1ae24f2..9d49700 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,8 @@ - [Batch Operations](#batch-operations) - [Systems](#systems) - [Processing Payloads](#processing-payloads) - - [Predefined Traits](#predefined-traits) + - [Predefined Fragments](#predefined-fragments) + - [Entity Names](#entity-names) - [Fragment Tags](#fragment-tags) - [Fragment Hooks](#fragment-hooks) - [Unique Fragments](#unique-fragments) @@ -53,7 +54,6 @@ - [Internal Fragments](#internal-fragments) - [Shared Components](#shared-components) - [Fragment Requirements](#fragment-requirements) - - [Id Names](#id-names) - [Destruction Policies](#destruction-policies) - [Custom Component Storages](#custom-component-storages) - [Garbage Collection](#garbage-collection) @@ -983,7 +983,42 @@ evolved.process_with(physics_system, delta_time) `delta_time` in this example is passed as a processing payload to the system's execution callback. Payloads can be of any type and can be multiple values. Also, payloads are passed to prologue and epilogue callbacks if they are defined. Every subsystem in a group will receive the same payload when the group is processed with [`evolved.process_with`](#evolvedprocess_with). -### Predefined Traits +### Predefined Fragments + +#### Entity Names + +The library provides a way to assign names to any id using the [`evolved.NAME`](#evolvedname) fragment. This is useful for debugging and development purposes, as it allows you to identify entities or fragments by their names instead of their identifiers. The name of an entity can be retrieved using the [`evolved.name`](#evolvedname-1) function. + +```lua +local evolved = require 'evolved' + +local player = evolved.builder() + :name('Player') + :build() + +assert(evolved.name(player) == 'Player') +``` + +Names are not unique, so multiple entities can have the same name. Also, the name of an entity can be changed at any time by setting a new name using the [`evolved.NAME`](#evolvedname) fragment as a usual component. + +You can find entities by their names using the [`evolved.lookup`](#evolvedlookup) and [`evolved.multi_lookup`](#evolvedmulti_lookup) functions. The [`evolved.lookup`](#evolvedlookup) function returns the first entity with the specified name, while the [`evolved.multi_lookup`](#evolvedmulti_lookup) function returns a list of all entities with the specified name. + +```lua +local evolved = require 'evolved' + +local player1 = evolved.builder() + :name('Player') + :build() + +local player2 = evolved.builder() + :name('Player') + :build() + +assert(evolved.lookup('Player') == player1) + +local player_list, player_count = evolved.multi_lookup('Player') +assert(player_count == 2 and player_list[1] == player1 and player_list[2] == player2) +``` #### Fragment Tags @@ -1163,41 +1198,6 @@ local enemy = evolved.builder() assert(evolved.has_all(enemy, position, velocity)) ``` -#### Id Names - -The library provides a way to assign names to any id using the [`evolved.NAME`](#evolvedname) fragment. This is useful for debugging and development purposes, as it allows you to identify entities or fragments by their names instead of their identifiers. The name of an entity can be retrieved using the [`evolved.name`](#evolvedname-1) function. - -```lua -local evolved = require 'evolved' - -local player = evolved.builder() - :name('Player') - :build() - -assert(evolved.name(player) == 'Player') -``` - -Names are not unique, so multiple entities can have the same name. Also, the name of an entity can be changed at any time by setting a new name using the [`evolved.NAME`](#evolvedname) fragment as a usual component. - -You can find entities by their names using the [`evolved.lookup`](#evolvedlookup) and [`evolved.multi_lookup`](#evolvedmulti_lookup) functions. The [`evolved.lookup`](#evolvedlookup) function returns the first entity with the specified name, while the [`evolved.multi_lookup`](#evolvedmulti_lookup) function returns a list of all entities with the specified name. - -```lua -local evolved = require 'evolved' - -local player1 = evolved.builder() - :name('Player') - :build() - -local player2 = evolved.builder() - :name('Player') - :build() - -assert(evolved.lookup('Player') == player1) - -local player_list, player_count = evolved.multi_lookup('Player') -assert(player_count == 2 and player_list[1] == player1 and player_list[2] == player2) -``` - #### Destruction Policies Typically, fragments remain alive for the entire lifetime of the program. However, in some cases, you might want to destroy fragments when they are no longer needed. For example, you can use some runtime entities as fragments for other entities. In this case, you might want to destroy such fragments even while they are still attached to other entities. Since entities cannot have destroyed fragments, a destruction policy must be applied to resolve this. By default, the library will remove the destroyed fragment from all entities that have it. From 39c0b988b578b42bea13d8e4e3400c4cde98c753 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 13 Feb 2026 21:20:08 +0700 Subject: [PATCH 18/18] v1.10.0 --- README.md | 37 +++++++++++++++++++++++-- evolved.lua | 2 +- rockspecs/evolved.lua-1.10.0-0.rockspec | 34 +++++++++++++++++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 rockspecs/evolved.lua-1.10.0-0.rockspec diff --git a/README.md b/README.md index 9d49700..c32fb20 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ - [Chunk](#chunk) - [Builder](#builder) - [Changelog](#changelog) - - [vX.Y.Z](#vxyz) + - [v1.10.0](#v1100) - [v1.9.0](#v190) - [v1.8.0](#v180) - [v1.7.0](#v170) @@ -491,6 +491,7 @@ When you need to spawn multiple entities with identical fragments, use `multi_sp ---@param component_mapper? evolved.component_mapper ---@return evolved.entity[] entity_list ---@return integer entity_count +---@nodiscard function evolved.multi_spawn(entity_count, component_table, component_mapper) end ---@param entity_count integer @@ -499,6 +500,7 @@ function evolved.multi_spawn(entity_count, component_table, component_mapper) en ---@param component_mapper? evolved.component_mapper ---@return evolved.entity[] entity_list ---@return integer entity_count +---@nodiscard function evolved.multi_clone(entity_count, prefab, component_table, component_mapper) end ``` @@ -529,6 +531,36 @@ end) Of course, you can use `evolved.multi_clone` in the same way. Builders can also be used for multi-entity spawning and cloning by calling the corresponding methods on the builder object. +Also, for all `multi_` functions, the library provides [`_nr`](#evolvedmulti_spawn_nr) and [`_to`](#evolvedmulti_spawn_to) suffix variants that allow you to perform these operations without returning the list of spawned entities or by copying the spawned entities to your own table, which can be more efficient because it avoids the overhead of allocating and returning a new table. + +```lua +local evolved = require 'evolved' + +local position_x, position_y = evolved.id(2) + +do + -- we don't interest in the list of spawned entities, + -- so we use the _nr variant of the multi_spawn function + + evolved.multi_spawn_nr(100, { + [position_x] = 0, + [position_y] = 0, + }) +end + +do + -- store spawned entities in our own table starting at index 1, + -- so we use the _to variant of the multi_spawn function + + local entity_list = {} + + evolved.multi_spawn_to(entity_list, 1, 100, { + [position_x] = 0, + [position_y] = 0, + }) +end +``` + ### Access Operations The library provides all the necessary functions to access entities and their components. I'm not going to cover all the accessor functions here, because they are pretty straightforward and self-explanatory. You can check the [API Reference](#api-reference) for all of them. Here are some of the most important ones: @@ -1636,10 +1668,11 @@ builder_mt:destruction_policy :: id -> builder ## Changelog -### vX.Y.Z +### v1.10.0 - Added the new [`evolved.lookup`](#evolvedlookup) and [`evolved.multi_lookup`](#evolvedmulti_lookup) functions that allow finding ids by their names - Added a non-shrinking version of the [`evolved.collect_garbage`](#evolvedcollect_garbage) function that only collects garbage without shrinking storages +- Added [`_nr`](#evolvedmulti_spawn_nr) and [`_to`](#evolvedmulti_spawn_to) variants of the [`evolved.multi_spawn`](#evolvedmulti_spawn) and [`evolved.multi_clone`](#evolvedmulti_clone) functions that provide more efficient ways to spawn or clone entities in some cases ### v1.9.0 diff --git a/evolved.lua b/evolved.lua index 9311f98..fc87cf1 100644 --- a/evolved.lua +++ b/evolved.lua @@ -1,7 +1,7 @@ local evolved = { __HOMEPAGE = 'https://github.com/BlackMATov/evolved.lua', __DESCRIPTION = 'Evolved ECS (Entity-Component-System) for Lua', - __VERSION = '1.9.0', + __VERSION = '1.10.0', __LICENSE = [[ MIT License diff --git a/rockspecs/evolved.lua-1.10.0-0.rockspec b/rockspecs/evolved.lua-1.10.0-0.rockspec new file mode 100644 index 0000000..f3c7b02 --- /dev/null +++ b/rockspecs/evolved.lua-1.10.0-0.rockspec @@ -0,0 +1,34 @@ +rockspec_format = "3.0" +package = "evolved.lua" +version = "1.10.0-0" +source = { + url = "git://github.com/BlackMATov/evolved.lua", + tag = "v1.10.0", +} +description = { + homepage = "https://github.com/BlackMATov/evolved.lua", + summary = "Evolved ECS (Entity-Component-System) for Lua", + detailed = [[ + `evolved.lua` is a fast and flexible ECS (Entity-Component-System) library for Lua. + It is designed to be simple and easy to use, while providing all the features needed to create complex systems with blazing performance. + ]], + license = "MIT", + labels = { + "ecs", + "entity", + "entities", + "component", + "components", + "entity-component", + "entity-component-system", + }, +} +dependencies = { + "lua >= 5.1", +} +build = { + type = "builtin", + modules = { + evolved = "evolved.lua", + } +}