From 473881a07d7d6e63c21e5301261a0a92e6d51fdf Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 25 Apr 2025 16:47:24 +0700 Subject: [PATCH 1/8] name empty containers --- evolved.lua | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/evolved.lua b/evolved.lua index 408fdcb..123ef5d 100644 --- a/evolved.lua +++ b/evolved.lua @@ -692,27 +692,32 @@ local __DESTROY_POLICY_REMOVE_FRAGMENT = __acquire_id() local __safe_tbls = { ---@type table __EMPTY_FRAGMENT_SET = __lua_setmetatable({}, { - __newindex = function() __error_fmt('attempt to modify empty fragment set') end + __tostring = function() return 'empty fragment set' end, + __newindex = function() __error_fmt 'attempt to modify empty fragment set' end }), ---@type evolved.fragment[] __EMPTY_FRAGMENT_LIST = __lua_setmetatable({}, { - __newindex = function() __error_fmt('attempt to modify empty fragment list') end + __tostring = function() return 'empty fragment list' end, + __newindex = function() __error_fmt 'attempt to modify empty fragment list' end }), ---@type table __EMPTY_COMPONENT_MAP = __lua_setmetatable({}, { - __newindex = function() __error_fmt('attempt to modify empty component map') end + __tostring = function() return 'empty component map' end, + __newindex = function() __error_fmt 'attempt to modify empty component map' end }), ---@type evolved.component[] __EMPTY_COMPONENT_LIST = __lua_setmetatable({}, { - __newindex = function() __error_fmt('attempt to modify empty component list') end + __tostring = function() return 'empty component list' end, + __newindex = function() __error_fmt 'attempt to modify empty component list' end }), ---@type evolved.component[] __EMPTY_COMPONENT_STORAGE = __lua_setmetatable({}, { - __newindex = function() __error_fmt('attempt to modify empty component storage') end + __tostring = function() return 'empty component storage' end, + __newindex = function() __error_fmt 'attempt to modify empty component storage' end }), } From 1799f577185bceec288d4aa107050ded092232fd Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 25 Apr 2025 16:56:46 +0700 Subject: [PATCH 2/8] new builtin prefab/hidden fragments, without additional functionality yet --- README.md | 4 ++++ evolved.lua | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/README.md b/README.md index e8c2d65..68dcac1 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,9 @@ ``` TAG :: fragment NAME :: fragment +PREFAB :: fragment +HIDDEN :: fragment DEFAULT :: fragment DUPLICATE :: fragment @@ -140,7 +142,9 @@ builder:clear :: builder builder:tag :: builder builder:name :: string -> builder +builder:prefab :: builder +builder:hidden :: builder builder:default :: component -> builder builder:duplicate :: {component -> component} -> builder diff --git a/evolved.lua b/evolved.lua index 123ef5d..be6ef8a 100644 --- a/evolved.lua +++ b/evolved.lua @@ -657,7 +657,9 @@ end local __TAG = __acquire_id() local __NAME = __acquire_id() +local __PREFAB = __acquire_id() +local __HIDDEN = __acquire_id() local __DEFAULT = __acquire_id() local __DUPLICATE = __acquire_id() @@ -4861,6 +4863,16 @@ function __builder_mt:name(name) return self:set(__NAME, name) end +---@return evolved.builder builder +function __builder_mt:prefab() + return self:set(__PREFAB) +end + +---@return evolved.builder builder +function __builder_mt:hidden() + return self:set(__HIDDEN) +end + ---@param default evolved.component ---@return evolved.builder builder function __builder_mt:default(default) @@ -5133,7 +5145,9 @@ __evolved_set(__DUPLICATE, __ON_REMOVE, __update_fragment_duplicates) __evolved_set(__TAG, __NAME, 'TAG') __evolved_set(__NAME, __NAME, 'NAME') +__evolved_set(__PREFAB, __NAME, 'PREFAB') +__evolved_set(__HIDDEN, __NAME, 'HIDDEN') __evolved_set(__DEFAULT, __NAME, 'DEFAULT') __evolved_set(__DUPLICATE, __NAME, 'DUPLICATE') @@ -5167,6 +5181,12 @@ __evolved_set(__DESTROY_POLICY_REMOVE_FRAGMENT, __NAME, 'DESTROY_POLICY_REMOVE_F __evolved_set(__TAG, __TAG) +__evolved_set(__PREFAB, __TAG) +__evolved_set(__PREFAB, __HIDDEN) + +__evolved_set(__HIDDEN, __TAG) +__evolved_set(__HIDDEN, __HIDDEN) + __evolved_set(__INCLUDES, __DEFAULT, {}) __evolved_set(__INCLUDES, __DUPLICATE, __list_copy) @@ -5295,7 +5315,9 @@ end) evolved.TAG = __TAG evolved.NAME = __NAME +evolved.PREFAB = __PREFAB +evolved.HIDDEN = __HIDDEN evolved.DEFAULT = __DEFAULT evolved.DUPLICATE = __DUPLICATE From f816d4246bf4ba6204999d5f85bc8df333fec141 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 25 Apr 2025 17:07:54 +0700 Subject: [PATCH 3/8] cache has_hidden_fragments flag in chunks --- evolved.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/evolved.lua b/evolved.lua index be6ef8a..cba1d99 100644 --- a/evolved.lua +++ b/evolved.lua @@ -124,6 +124,7 @@ local __query_sorted_excludes = {} ---@type table Date: Fri, 25 Apr 2025 18:44:58 +0700 Subject: [PATCH 4/8] don't clone hidden fragments (while they are not explicit) --- develop/untests.lua | 81 ++++++++++++++++++++++++++++++++++++- evolved.lua | 98 ++++++++++++++++++++++++++++++--------------- 2 files changed, 145 insertions(+), 34 deletions(-) diff --git a/develop/untests.lua b/develop/untests.lua index 0fbfe74..596a9d3 100644 --- a/develop/untests.lua +++ b/develop/untests.lua @@ -5,7 +5,7 @@ local evo = require 'evolved' evo.debug_mode(true) -do +if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == nil then local i = evo.id() for _ = 1, 0xFFFFE do @@ -6156,3 +6156,82 @@ do assert(evo.has(e3g, f3) and evo.get(e3g, f3) == 33) end end + +do + local f1, f2, f3 = evo.id(3) + + evo.set(f2, evo.HIDDEN) + evo.set(f3, evo.HIDDEN) + + do + local p = evo.spawn { [f1] = 11, [f2] = 22 } + local e = evo.clone(p) + + assert(evo.has(p, f1) and evo.get(p, f1) == 11) + assert(evo.has(p, f2) and evo.get(p, f2) == 22) + + assert(evo.has(e, f1) and evo.get(e, f1) == 11) + assert(not evo.has(e, f2) and evo.get(e, f2) == nil) + end + + do + local p = evo.spawn { [f1] = 11, [f2] = 22, [f3] = 33 } + local e = evo.clone(p) + + assert(evo.has(p, f1) and evo.get(p, f1) == 11) + assert(evo.has(p, f2) and evo.get(p, f2) == 22) + assert(evo.has(p, f3) and evo.get(p, f3) == 33) + + assert(evo.has(e, f1) and evo.get(e, f1) == 11) + assert(not evo.has(e, f2) and evo.get(e, f2) == nil) + assert(not evo.has(e, f3) and evo.get(e, f3) == nil) + end + + do + local p = evo.spawn { [f2] = 22 } + local e = evo.clone(p) + + assert(not evo.has(p, f1) and evo.get(p, f1) == nil) + assert(evo.has(p, f2) and evo.get(p, f2) == 22) + assert(not evo.has(p, f3) and evo.get(p, f3) == nil) + + assert(not evo.has(e, f1) and evo.get(e, f1) == nil) + assert(not evo.has(e, f2) and evo.get(e, f2) == nil) + assert(not evo.has(e, f3) and evo.get(e, f3) == nil) + end + do + local p = evo.spawn { [f2] = 22, [f3] = 33 } + local e = evo.clone(p) + + assert(not evo.has(p, f1) and evo.get(p, f1) == nil) + assert(evo.has(p, f2) and evo.get(p, f2) == 22) + assert(evo.has(p, f3) and evo.get(p, f3) == 33) + + assert(not evo.has(e, f1) and evo.get(e, f1) == nil) + assert(not evo.has(e, f2) and evo.get(e, f2) == nil) + assert(not evo.has(e, f3) and evo.get(e, f3) == nil) + end + + do + local p = evo.spawn { [f1] = 11, [f2] = 22 } + local e = evo.clone(p, { [f2] = 2 }) + + assert(evo.has(p, f1) and evo.get(p, f1) == 11) + assert(evo.has(p, f2) and evo.get(p, f2) == 22) + + assert(evo.has(e, f1) and evo.get(e, f1) == 11) + assert(evo.has(e, f2) and evo.get(e, f2) == 2) + end + + do + local p = evo.spawn { [f1] = 11, [f2] = 22 } + local e = evo.clone(p, { [f2] = 2, [f3] = 3 }) + + assert(evo.has(p, f1) and evo.get(p, f1) == 11) + assert(evo.has(p, f2) and evo.get(p, f2) == 22) + + assert(evo.has(e, f1) and evo.get(e, f1) == 11) + assert(evo.has(e, f2) and evo.get(e, f2) == 2) + assert(evo.has(e, f3) and evo.get(e, f3) == 3) + end +end diff --git a/evolved.lua b/evolved.lua index cba1d99..374f2ec 100644 --- a/evolved.lua +++ b/evolved.lua @@ -1225,6 +1225,32 @@ local function __chunk_without_fragments(chunk, ...) return chunk end +---@param chunk? evolved.chunk +---@return evolved.chunk? +---@nodiscard +local function __chunk_without_hidden_fragments(chunk) + if not chunk then + return nil + end + + if not chunk.__has_hidden_fragments then + return chunk + end + + local chunk_fragment_list = chunk.__fragment_list + local chunk_fragment_count = chunk.__fragment_count + + for i = chunk_fragment_count, 1, -1 do + local fragment = chunk_fragment_list[i] + + if __evolved_has(fragment, __HIDDEN) then + chunk = __chunk_without_fragment(chunk, fragment) + end + end + + return chunk +end + --- --- --- @@ -1660,7 +1686,9 @@ local function __clone_entity(entity, prefab, components) local prefab_chunk = __entity_chunks[prefab_index] local prefab_place = __entity_places[prefab_index] - local chunk = __chunk_with_components(prefab_chunk, components) + local chunk = __chunk_with_components( + __chunk_without_hidden_fragments(prefab_chunk), + components) if not chunk then return @@ -1694,46 +1722,50 @@ local function __clone_entity(entity, prefab, components) if prefab_chunk.__has_setup_hooks then for prefab_component_index = 1, prefab_component_count do local fragment = prefab_component_fragments[prefab_component_index] - - ---@type evolved.duplicate? - local fragment_duplicate = - __evolved_get(fragment, __DUPLICATE) - - local prefab_component_storage = prefab_component_storages[prefab_component_index] - local prefab_component = prefab_component_storage[prefab_place] - - local new_component = prefab_component - - if new_component ~= nil and fragment_duplicate then - new_component = fragment_duplicate(new_component) - end - - if new_component == nil then - new_component = true - end - local component_index = chunk_component_indices[fragment] - local component_storage = chunk_component_storages[component_index] - component_storage[place] = new_component + if component_index then + ---@type evolved.duplicate? + local fragment_duplicate = + __evolved_get(fragment, __DUPLICATE) + + local prefab_component_storage = prefab_component_storages[prefab_component_index] + local prefab_component = prefab_component_storage[prefab_place] + + local new_component = prefab_component + + if new_component ~= nil and fragment_duplicate then + new_component = fragment_duplicate(new_component) + end + + if new_component == nil then + new_component = true + end + + local component_storage = chunk_component_storages[component_index] + + component_storage[place] = new_component + end end else for prefab_component_index = 1, prefab_component_count do local fragment = prefab_component_fragments[prefab_component_index] - - local prefab_component_storage = prefab_component_storages[prefab_component_index] - local prefab_component = prefab_component_storage[prefab_place] - - local new_component = prefab_component - - if new_component == nil then - new_component = true - end - local component_index = chunk_component_indices[fragment] - local component_storage = chunk_component_storages[component_index] - component_storage[place] = new_component + if component_index then + local prefab_component_storage = prefab_component_storages[prefab_component_index] + local prefab_component = prefab_component_storage[prefab_place] + + local new_component = prefab_component + + if new_component == nil then + new_component = true + end + + local component_storage = chunk_component_storages[component_index] + + component_storage[place] = new_component + end end end end From 4632a61bb5461a6dcfd83b42a2c7d12adcb00237 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 28 Apr 2025 03:12:30 +0700 Subject: [PATCH 5/8] don't match chunks with hidden fragments (while they are not explicit) --- develop/fuzzing/hidden_fuzz.lua | 81 ++++++++++++++++++++++ develop/untests.lua | 84 +++++++++++++++++++++++ evolved.lua | 115 ++++++++++++++++++++++---------- 3 files changed, 244 insertions(+), 36 deletions(-) create mode 100644 develop/fuzzing/hidden_fuzz.lua diff --git a/develop/fuzzing/hidden_fuzz.lua b/develop/fuzzing/hidden_fuzz.lua new file mode 100644 index 0000000..ed8fd35 --- /dev/null +++ b/develop/fuzzing/hidden_fuzz.lua @@ -0,0 +1,81 @@ +local evo = require 'evolved' + +evo.debug_mode(true) + +--- +--- +--- +--- +--- + +local __table_unpack = (function() + ---@diagnostic disable-next-line: deprecated + return table.unpack or unpack +end)() + +--- +--- +--- +--- +--- + +local all_entity_list = {} ---@type evolved.entity[] + +for i = 1, math.random(1, 10) do + local entity = evo.id() + all_entity_list[i] = entity +end + +for _, entity in ipairs(all_entity_list) do + for _ = 0, math.random(0, #all_entity_list) do + local fragment = all_entity_list[math.random(1, #all_entity_list)] + evo.set(entity, fragment) + end + + if math.random(1, 5) == 1 then + evo.set(entity, evo.HIDDEN) + end +end + +--- +--- +--- +--- +--- + +for _ = 1, 100 do + local include_set = {} ---@type table + local include_list = {} ---@type evolved.entity[] + local include_count = 0 + + for _ = 1, math.random(1, #all_entity_list) do + local include = all_entity_list[math.random(1, #all_entity_list)] + + if not include_set[include] then + include_count = include_count + 1 + include_set[include] = include_count + include_list[include_count] = include + end + end + + local q = evo.builder():include(__table_unpack(include_list)):spawn() + + for chunk in evo.execute(q) do + local fragment_list, fragment_count = chunk:fragments() + for i = 1, fragment_count do + local fragment = fragment_list[i] + assert(include_set[fragment] or not evo.has(fragment, evo.HIDDEN)) + end + end + + evo.destroy(q) +end + +--- +--- +--- +--- +--- + +evo.destroy(__table_unpack(all_entity_list)) +evo.collect_garbage() diff --git a/develop/untests.lua b/develop/untests.lua index 596a9d3..0709345 100644 --- a/develop/untests.lua +++ b/develop/untests.lua @@ -6235,3 +6235,87 @@ do assert(evo.has(e, f3) and evo.get(e, f3) == 3) end end + +do + local f1, f2, f3 = evo.id(3) + + evo.set(f2, evo.HIDDEN) + + do + local p = evo.spawn { [f1] = 11, [f2] = 22, [f3] = 33 } + local e = evo.clone(p) + + assert(evo.has(p, f1) and evo.get(p, f1) == 11) + assert(evo.has(p, f2) and evo.get(p, f2) == 22) + assert(evo.has(p, f3) and evo.get(p, f3) == 33) + + assert(evo.has(e, f1) and evo.get(e, f1) == 11) + assert(not evo.has(e, f2) and evo.get(e, f2) == nil) + assert(evo.has(e, f3) and evo.get(e, f3) == 33) + end +end + +do + local f1, f2 = evo.id(2) + + local p = evo.builder():prefab():set(f1, 11):set(f2, 22):spawn() + local e = evo.clone(p) + + do + local q = evo.builder():include(f1, f2):spawn() + local iter, state = evo.execute(q) + local chunk, entity_list, entity_count = iter(state) + assert(chunk and entity_list and entity_count) + assert(chunk == evo.chunk(f1, f2)) + assert(entity_count == 1 and entity_list[1] == e) + end + + do + local q = evo.builder():exclude(f1):spawn() + + for c in evo.execute(q) do + local fs, fc = c:fragments() + for i = 1, fc do assert(not evo.has(fs[i], evo.HIDDEN)) end + end + end + + do + local q = evo.builder():spawn() + + for c in evo.execute(q) do + local fs, fc = c:fragments() + for i = 1, fc do assert(not evo.has(fs[i], evo.HIDDEN)) end + end + end +end + +do + local f1, f2 = evo.id(2) + + evo.set(f2, evo.HIDDEN) + + local e1 = evo.builder():set(f1, 11):spawn() + local e2 = evo.builder():set(f1, 11):set(f2, 22):spawn() + + do + local q = evo.builder():include(f1):spawn() + local iter, state = evo.execute(q) + local chunk, entity_list, entity_count = iter(state) + assert(chunk and entity_list and entity_count) + assert(chunk == evo.chunk(f1)) + assert(entity_count == 1 and entity_list[1] == e1) + chunk, entity_list, entity_count = iter(state) + assert(not chunk and not entity_list and not entity_count) + end + + do + local q = evo.builder():include(f1, f2):spawn() + local iter, state = evo.execute(q) + local chunk, entity_list, entity_count = iter(state) + assert(chunk and entity_list and entity_count) + assert(chunk == evo.chunk(f1, f2)) + assert(entity_count == 1 and entity_list[1] == e2) + chunk, entity_list, entity_count = iter(state) + assert(not chunk and not entity_list and not entity_count) + end +end diff --git a/evolved.lua b/evolved.lua index 374f2ec..ff77472 100644 --- a/evolved.lua +++ b/evolved.lua @@ -124,6 +124,8 @@ local __query_sorted_excludes = {} ---@type table __EMPTY_FRAGMENT_SET = __lua_setmetatable({}, { __tostring = function() return 'empty fragment set' end, @@ -1002,20 +1006,21 @@ local function __new_chunk(chunk_parent, chunk_fragment) ---@type evolved.fragment[] local chunk_component_fragments = {} - local has_setup_hooks = (chunk_parent and chunk_parent.__has_setup_hooks) + local has_setup_hooks = (chunk_parent ~= nil and chunk_parent.__has_setup_hooks) or __evolved_has_any(chunk_fragment, __DEFAULT, __DUPLICATE) - local has_assign_hooks = (chunk_parent and chunk_parent.__has_assign_hooks) + local has_assign_hooks = (chunk_parent ~= nil and chunk_parent.__has_assign_hooks) or __evolved_has_any(chunk_fragment, __ON_SET, __ON_ASSIGN) - local has_insert_hooks = (chunk_parent and chunk_parent.__has_insert_hooks) + local has_insert_hooks = (chunk_parent ~= nil and chunk_parent.__has_insert_hooks) or __evolved_has_any(chunk_fragment, __ON_SET, __ON_INSERT) - local has_remove_hooks = (chunk_parent and chunk_parent.__has_remove_hooks) + local has_remove_hooks = (chunk_parent ~= nil and chunk_parent.__has_remove_hooks) or __evolved_has(chunk_fragment, __ON_REMOVE) - local has_hidden_fragments = (chunk_parent and chunk_parent.__has_hidden_fragments) - or __evolved_has(chunk_fragment, __HIDDEN) + local has_hidden_major = __evolved_has(chunk_fragment, __HIDDEN) + local has_hidden_minors = chunk_parent ~= nil and chunk_parent.__has_hidden_fragments + local has_hidden_fragments = has_hidden_major or has_hidden_minors ---@type evolved.chunk local chunk = __lua_setmetatable({ @@ -1040,6 +1045,8 @@ local function __new_chunk(chunk_parent, chunk_fragment) __has_assign_hooks = has_assign_hooks, __has_insert_hooks = has_insert_hooks, __has_remove_hooks = has_remove_hooks, + __has_hidden_major = has_hidden_major, + __has_hidden_minors = has_hidden_minors, __has_hidden_fragments = has_hidden_fragments, }, __chunk_mt) @@ -1237,18 +1244,26 @@ local function __chunk_without_hidden_fragments(chunk) return chunk end - local chunk_fragment_list = chunk.__fragment_list - local chunk_fragment_count = chunk.__fragment_count + while chunk and chunk.__has_hidden_major do + chunk = chunk.__parent + end - for i = chunk_fragment_count, 1, -1 do - local fragment = chunk_fragment_list[i] + local new_chunk = nil - if __evolved_has(fragment, __HIDDEN) then - chunk = __chunk_without_fragment(chunk, fragment) + if chunk then + local chunk_fragment_list = chunk.__fragment_list + local chunk_fragment_count = chunk.__fragment_count + + for i = 1, chunk_fragment_count do + local fragment = chunk_fragment_list[i] + + if not __evolved_has(fragment, __HIDDEN) then + new_chunk = __chunk_with_fragment(new_chunk, fragment) + end end end - return chunk + return new_chunk end --- @@ -4329,6 +4344,7 @@ function __evolved_execute(query) local chunk_stack_size = 0 local query_includes = __query_sorted_includes[query] + local query_include_set = query_includes and query_includes.__item_set --[[@as table]] local query_include_list = query_includes and query_includes.__item_list --[=[@as evolved.fragment[]]=] local query_include_count = query_includes and query_includes.__item_count or 0 --[[@as integer]] @@ -4353,6 +4369,20 @@ function __evolved_execute(query) (query_exclude_count == 0 or not __chunk_has_any_fragment_list( major_chunk, query_exclude_list, query_exclude_count)) + if is_major_chunk_matched and major_chunk.__has_hidden_minors then + local major_chunk_fragment_list = major_chunk.__fragment_list + local major_chunk_fragment_count = major_chunk.__fragment_count + + for major_chunk_fragment_index = 1, major_chunk_fragment_count - 1 do + local major_chunk_fragment = major_chunk_fragment_list[major_chunk_fragment_index] + + if not query_include_set[major_chunk_fragment] and __evolved_has(major_chunk_fragment, __HIDDEN) then + is_major_chunk_matched = false + break + end + end + end + if is_major_chunk_matched then chunk_stack_size = chunk_stack_size + 1 chunk_stack[chunk_stack_size] = major_chunk @@ -4360,15 +4390,24 @@ function __evolved_execute(query) end elseif query_exclude_count > 0 then for root_fragment, root_chunk in __lua_next, __root_chunks do - if not query_exclude_set[root_fragment] then + local is_root_chunk_matched = + not root_chunk.__has_hidden_major and + not query_exclude_set[root_fragment] + + if is_root_chunk_matched then chunk_stack_size = chunk_stack_size + 1 chunk_stack[chunk_stack_size] = root_chunk end end else for _, root_chunk in __lua_next, __root_chunks do - chunk_stack_size = chunk_stack_size + 1 - chunk_stack[chunk_stack_size] = root_chunk + local is_root_chunk_matched = + not root_chunk.__has_hidden_major + + if is_root_chunk_matched then + chunk_stack_size = chunk_stack_size + 1 + chunk_stack[chunk_stack_size] = root_chunk + end end end @@ -5048,25 +5087,29 @@ end local function __update_chunk_caches_trace(chunk) local chunk_parent, chunk_fragment = chunk.__parent, chunk.__fragment - local has_setup_hooks = (chunk_parent and chunk_parent.__has_setup_hooks) + local has_setup_hooks = (chunk_parent ~= nil and chunk_parent.__has_setup_hooks) or __evolved_has_any(chunk_fragment, __DEFAULT, __DUPLICATE) - local has_assign_hooks = (chunk_parent and chunk_parent.__has_assign_hooks) + local has_assign_hooks = (chunk_parent ~= nil and chunk_parent.__has_assign_hooks) or __evolved_has_any(chunk_fragment, __ON_SET, __ON_ASSIGN) - local has_insert_hooks = (chunk_parent and chunk_parent.__has_insert_hooks) + local has_insert_hooks = (chunk_parent ~= nil and chunk_parent.__has_insert_hooks) or __evolved_has_any(chunk_fragment, __ON_SET, __ON_INSERT) - local has_remove_hooks = (chunk_parent and chunk_parent.__has_remove_hooks) + local has_remove_hooks = (chunk_parent ~= nil and chunk_parent.__has_remove_hooks) or __evolved_has(chunk_fragment, __ON_REMOVE) - local has_hidden_fragments = (chunk_parent and chunk_parent.__has_hidden_fragments) - or __evolved_has(chunk_fragment, __HIDDEN) + local has_hidden_major = __evolved_has(chunk_fragment, __HIDDEN) + local has_hidden_minors = chunk_parent ~= nil and chunk_parent.__has_hidden_fragments + local has_hidden_fragments = has_hidden_major or has_hidden_minors chunk.__has_setup_hooks = has_setup_hooks chunk.__has_assign_hooks = has_assign_hooks chunk.__has_insert_hooks = has_insert_hooks chunk.__has_remove_hooks = has_remove_hooks + + chunk.__has_hidden_major = has_hidden_major + chunk.__has_hidden_minors = has_hidden_minors chunk.__has_hidden_fragments = has_hidden_fragments return true From 943f6ead0a7717ba4a34685ddb39baa4c4535adb Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 28 Apr 2025 04:30:32 +0700 Subject: [PATCH 6/8] separate unique/explicit traits instead hidden --- README.md | 4 +- develop/all.lua | 4 + .../{hidden_fuzz.lua => explicit_fuzz.lua} | 4 +- develop/fuzzing/unique_fuzz.lua | 67 ++++++++++ develop/untests.lua | 12 +- evolved.lua | 114 ++++++++++++------ 6 files changed, 160 insertions(+), 45 deletions(-) rename develop/fuzzing/{hidden_fuzz.lua => explicit_fuzz.lua} (96%) create mode 100644 develop/fuzzing/unique_fuzz.lua diff --git a/README.md b/README.md index 68dcac1..aa118e2 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,9 @@ TAG :: fragment NAME :: fragment PREFAB :: fragment -HIDDEN :: fragment +UNIQUE :: fragment +EXPLICIT :: fragment + DEFAULT :: fragment DUPLICATE :: fragment diff --git a/develop/all.lua b/develop/all.lua index 13d3ed0..1a518e5 100644 --- a/develop/all.lua +++ b/develop/all.lua @@ -9,3 +9,7 @@ print '----------------------------------------' basics.describe_fuzz 'develop.fuzzing.destroy_fuzz' print '----------------------------------------' basics.describe_fuzz 'develop.fuzzing.batch_destroy_fuzz' +print '----------------------------------------' +basics.describe_fuzz 'develop.fuzzing.explicit_fuzz' +print '----------------------------------------' +basics.describe_fuzz 'develop.fuzzing.unique_fuzz' diff --git a/develop/fuzzing/hidden_fuzz.lua b/develop/fuzzing/explicit_fuzz.lua similarity index 96% rename from develop/fuzzing/hidden_fuzz.lua rename to develop/fuzzing/explicit_fuzz.lua index ed8fd35..6aeb7c6 100644 --- a/develop/fuzzing/hidden_fuzz.lua +++ b/develop/fuzzing/explicit_fuzz.lua @@ -33,7 +33,7 @@ for _, entity in ipairs(all_entity_list) do end if math.random(1, 5) == 1 then - evo.set(entity, evo.HIDDEN) + evo.set(entity, evo.EXPLICIT) end end @@ -64,7 +64,7 @@ for _ = 1, 100 do local fragment_list, fragment_count = chunk:fragments() for i = 1, fragment_count do local fragment = fragment_list[i] - assert(include_set[fragment] or not evo.has(fragment, evo.HIDDEN)) + assert(include_set[fragment] or not evo.has(fragment, evo.EXPLICIT)) end end diff --git a/develop/fuzzing/unique_fuzz.lua b/develop/fuzzing/unique_fuzz.lua new file mode 100644 index 0000000..731ba5e --- /dev/null +++ b/develop/fuzzing/unique_fuzz.lua @@ -0,0 +1,67 @@ +local evo = require 'evolved' + +evo.debug_mode(true) + +--- +--- +--- +--- +--- + +local __table_unpack = (function() + ---@diagnostic disable-next-line: deprecated + return table.unpack or unpack +end)() + +--- +--- +--- +--- +--- + +local all_entity_list = {} ---@type evolved.entity[] + +for i = 1, math.random(1, 10) do + local entity = evo.id() + all_entity_list[i] = entity +end + +for _, entity in ipairs(all_entity_list) do + for _ = 0, math.random(0, #all_entity_list) do + local fragment = all_entity_list[math.random(1, #all_entity_list)] + evo.set(entity, fragment) + end + + if math.random(1, 5) == 1 then + evo.set(entity, evo.UNIQUE) + end +end + +--- +--- +--- +--- +--- + +for _, entity in ipairs(all_entity_list) do + local entity_clone = evo.clone(entity) + + for fragment in evo.each(entity_clone) do + assert(not evo.has(fragment, evo.UNIQUE)) + end + + for fragment in evo.each(entity) do + assert(evo.has(entity_clone, fragment) or evo.has(fragment, evo.UNIQUE)) + end + + evo.destroy(entity_clone) +end + +--- +--- +--- +--- +--- + +evo.destroy(__table_unpack(all_entity_list)) +evo.collect_garbage() diff --git a/develop/untests.lua b/develop/untests.lua index 0709345..06c1c1f 100644 --- a/develop/untests.lua +++ b/develop/untests.lua @@ -6160,8 +6160,8 @@ end do local f1, f2, f3 = evo.id(3) - evo.set(f2, evo.HIDDEN) - evo.set(f3, evo.HIDDEN) + evo.set(f2, evo.UNIQUE) + evo.set(f3, evo.UNIQUE) do local p = evo.spawn { [f1] = 11, [f2] = 22 } @@ -6239,7 +6239,7 @@ end do local f1, f2, f3 = evo.id(3) - evo.set(f2, evo.HIDDEN) + evo.set(f2, evo.UNIQUE) do local p = evo.spawn { [f1] = 11, [f2] = 22, [f3] = 33 } @@ -6275,7 +6275,7 @@ do for c in evo.execute(q) do local fs, fc = c:fragments() - for i = 1, fc do assert(not evo.has(fs[i], evo.HIDDEN)) end + for i = 1, fc do assert(not evo.has(fs[i], evo.EXPLICIT)) end end end @@ -6284,7 +6284,7 @@ do for c in evo.execute(q) do local fs, fc = c:fragments() - for i = 1, fc do assert(not evo.has(fs[i], evo.HIDDEN)) end + for i = 1, fc do assert(not evo.has(fs[i], evo.EXPLICIT)) end end end end @@ -6292,7 +6292,7 @@ end do local f1, f2 = evo.id(2) - evo.set(f2, evo.HIDDEN) + evo.set(f2, evo.EXPLICIT) local e1 = evo.builder():set(f1, 11):spawn() local e2 = evo.builder():set(f1, 11):set(f2, 22):spawn() diff --git a/evolved.lua b/evolved.lua index ff77472..9f68f60 100644 --- a/evolved.lua +++ b/evolved.lua @@ -124,9 +124,12 @@ local __query_sorted_excludes = {} ---@type table 0 then for root_fragment, root_chunk in __lua_next, __root_chunks do local is_root_chunk_matched = - not root_chunk.__has_hidden_major and + not root_chunk.__has_explicit_major and not query_exclude_set[root_fragment] if is_root_chunk_matched then @@ -4402,7 +4414,7 @@ function __evolved_execute(query) else for _, root_chunk in __lua_next, __root_chunks do local is_root_chunk_matched = - not root_chunk.__has_hidden_major + not root_chunk.__has_explicit_major if is_root_chunk_matched then chunk_stack_size = chunk_stack_size + 1 @@ -4945,8 +4957,13 @@ function __builder_mt:prefab() end ---@return evolved.builder builder -function __builder_mt:hidden() - return self:set(__HIDDEN) +function __builder_mt:unique() + return self:set(__UNIQUE) +end + +---@return evolved.builder builder +function __builder_mt:explicit() + return self:set(__EXPLICIT) end ---@param default evolved.component @@ -5099,18 +5116,26 @@ local function __update_chunk_caches_trace(chunk) local has_remove_hooks = (chunk_parent ~= nil and chunk_parent.__has_remove_hooks) or __evolved_has(chunk_fragment, __ON_REMOVE) - local has_hidden_major = __evolved_has(chunk_fragment, __HIDDEN) - local has_hidden_minors = chunk_parent ~= nil and chunk_parent.__has_hidden_fragments - local has_hidden_fragments = has_hidden_major or has_hidden_minors + local has_unique_major = __evolved_has(chunk_fragment, __UNIQUE) + local has_unique_minors = chunk_parent ~= nil and chunk_parent.__has_unique_fragments + local has_unique_fragments = has_unique_major or has_unique_minors + + local has_explicit_major = __evolved_has(chunk_fragment, __EXPLICIT) + local has_explicit_minors = chunk_parent ~= nil and chunk_parent.__has_explicit_fragments + local has_explicit_fragments = has_explicit_major or has_explicit_minors chunk.__has_setup_hooks = has_setup_hooks chunk.__has_assign_hooks = has_assign_hooks chunk.__has_insert_hooks = has_insert_hooks chunk.__has_remove_hooks = has_remove_hooks - chunk.__has_hidden_major = has_hidden_major - chunk.__has_hidden_minors = has_hidden_minors - chunk.__has_hidden_fragments = has_hidden_fragments + chunk.__has_unique_major = has_unique_major + chunk.__has_unique_minors = has_unique_minors + chunk.__has_unique_fragments = has_unique_fragments + + chunk.__has_explicit_major = has_explicit_major + chunk.__has_explicit_minors = has_explicit_minors + chunk.__has_explicit_fragments = has_explicit_fragments return true end @@ -5202,7 +5227,11 @@ local function __update_fragment_tags(fragment) __trace_fragment_chunks(fragment, __update_chunk_tags_trace, fragment) end -local function __update_fragment_hiddens(fragment) +local function __update_fragment_uniques(fragment) + __trace_fragment_chunks(fragment, __update_chunk_caches_trace, fragment) +end + +local function __update_fragment_explicits(fragment) __trace_fragment_chunks(fragment, __update_chunk_caches_trace, fragment) end @@ -5219,8 +5248,11 @@ end __evolved_set(__TAG, __ON_INSERT, __update_fragment_tags) __evolved_set(__TAG, __ON_REMOVE, __update_fragment_tags) -__evolved_set(__HIDDEN, __ON_INSERT, __update_fragment_hiddens) -__evolved_set(__HIDDEN, __ON_REMOVE, __update_fragment_hiddens) +__evolved_set(__UNIQUE, __ON_INSERT, __update_fragment_uniques) +__evolved_set(__UNIQUE, __ON_REMOVE, __update_fragment_uniques) + +__evolved_set(__EXPLICIT, __ON_INSERT, __update_fragment_explicits) +__evolved_set(__EXPLICIT, __ON_REMOVE, __update_fragment_explicits) __evolved_set(__DEFAULT, __ON_INSERT, __update_fragment_defaults) __evolved_set(__DEFAULT, __ON_REMOVE, __update_fragment_defaults) @@ -5238,7 +5270,9 @@ __evolved_set(__TAG, __NAME, 'TAG') __evolved_set(__NAME, __NAME, 'NAME') __evolved_set(__PREFAB, __NAME, 'PREFAB') -__evolved_set(__HIDDEN, __NAME, 'HIDDEN') +__evolved_set(__UNIQUE, __NAME, 'UNIQUE') +__evolved_set(__EXPLICIT, __NAME, 'EXPLICIT') + __evolved_set(__DEFAULT, __NAME, 'DEFAULT') __evolved_set(__DUPLICATE, __NAME, 'DUPLICATE') @@ -5273,10 +5307,16 @@ __evolved_set(__DESTROY_POLICY_REMOVE_FRAGMENT, __NAME, 'DESTROY_POLICY_REMOVE_F __evolved_set(__TAG, __TAG) __evolved_set(__PREFAB, __TAG) -__evolved_set(__PREFAB, __HIDDEN) +__evolved_set(__PREFAB, __UNIQUE) +__evolved_set(__PREFAB, __EXPLICIT) -__evolved_set(__HIDDEN, __TAG) -__evolved_set(__HIDDEN, __HIDDEN) +__evolved_set(__UNIQUE, __TAG) +__evolved_set(__UNIQUE, __UNIQUE) +__evolved_set(__UNIQUE, __EXPLICIT) + +__evolved_set(__EXPLICIT, __TAG) +__evolved_set(__EXPLICIT, __UNIQUE) +__evolved_set(__EXPLICIT, __EXPLICIT) __evolved_set(__INCLUDES, __DEFAULT, {}) __evolved_set(__INCLUDES, __DUPLICATE, __list_copy) @@ -5408,7 +5448,9 @@ evolved.TAG = __TAG evolved.NAME = __NAME evolved.PREFAB = __PREFAB -evolved.HIDDEN = __HIDDEN +evolved.UNIQUE = __UNIQUE +evolved.EXPLICIT = __EXPLICIT + evolved.DEFAULT = __DEFAULT evolved.DUPLICATE = __DUPLICATE From d5d5620fea0ce05ed1f868506a773f0406443cf7 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 28 Apr 2025 06:39:20 +0700 Subject: [PATCH 7/8] mark on_xxx fragments as unique --- README.md | 4 +++- evolved.lua | 9 +++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index aa118e2..a225bf6 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,9 @@ builder:tag :: builder builder:name :: string -> builder builder:prefab :: builder -builder:hidden :: builder +builder:unique :: builder +builder:explicit :: builder + builder:default :: component -> builder builder:duplicate :: {component -> component} -> builder diff --git a/evolved.lua b/evolved.lua index 9f68f60..3d7d023 100644 --- a/evolved.lua +++ b/evolved.lua @@ -5311,12 +5311,8 @@ __evolved_set(__PREFAB, __UNIQUE) __evolved_set(__PREFAB, __EXPLICIT) __evolved_set(__UNIQUE, __TAG) -__evolved_set(__UNIQUE, __UNIQUE) -__evolved_set(__UNIQUE, __EXPLICIT) __evolved_set(__EXPLICIT, __TAG) -__evolved_set(__EXPLICIT, __UNIQUE) -__evolved_set(__EXPLICIT, __EXPLICIT) __evolved_set(__INCLUDES, __DEFAULT, {}) __evolved_set(__INCLUDES, __DUPLICATE, __list_copy) @@ -5324,6 +5320,11 @@ __evolved_set(__INCLUDES, __DUPLICATE, __list_copy) __evolved_set(__EXCLUDES, __DEFAULT, {}) __evolved_set(__EXCLUDES, __DUPLICATE, __list_copy) +__evolved_set(__ON_SET, __UNIQUE) +__evolved_set(__ON_ASSIGN, __UNIQUE) +__evolved_set(__ON_INSERT, __UNIQUE) +__evolved_set(__ON_REMOVE, __UNIQUE) + __evolved_set(__DISABLED, __TAG) --- From b263e89be59878177bfe61308297d8d47adc54db Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 28 Apr 2025 15:47:35 +0700 Subject: [PATCH 8/8] update roadmap --- ROADMAP.md | 3 --- evolved.lua | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 69b31a3..7092a02 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -2,9 +2,6 @@ ## Backlog -- add PREFAB entity trait -- is/has_all/any for lists? - ## After first release - cached queries diff --git a/evolved.lua b/evolved.lua index 3d7d023..a547b46 100644 --- a/evolved.lua +++ b/evolved.lua @@ -5496,12 +5496,12 @@ evolved.empty = __evolved_empty evolved.empty_all = __evolved_empty_all evolved.empty_any = __evolved_empty_any -evolved.get = __evolved_get - evolved.has = __evolved_has evolved.has_all = __evolved_has_all evolved.has_any = __evolved_has_any +evolved.get = __evolved_get + evolved.set = __evolved_set evolved.remove = __evolved_remove evolved.clear = __evolved_clear