From 50afb722d182503e8b5da4c20241258102aa6ad5 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Thu, 29 May 2025 07:19:50 +0700 Subject: [PATCH] proof of concept REQUIRES fragment impl --- develop/testing/requires_fragment_tests.lua | 173 ++++++++ evolved.lua | 423 ++++++++++++++++++++ 2 files changed, 596 insertions(+) diff --git a/develop/testing/requires_fragment_tests.lua b/develop/testing/requires_fragment_tests.lua index b341122..529817a 100644 --- a/develop/testing/requires_fragment_tests.lua +++ b/develop/testing/requires_fragment_tests.lua @@ -22,3 +22,176 @@ do local f3_rs = evo.get(f3, evo.REQUIRES) assert(f3_rs and #f3_rs == 2 and f3_rs[1] == f1 and f3_rs[2] == f2) end + +do + local f1, f2 = evo.id(2) + evo.set(f1, evo.REQUIRES, { f2 }) + + do + local e = evo.id() + evo.set(e, f1) + assert(evo.has(e, f2)) + assert(evo.get(e, f2) == true) + end + + do + local e = evo.builder():set(f1):spawn() + assert(evo.has(e, f2)) + assert(evo.get(e, f2) == true) + end + + do + local e = evo.spawn { [f1] = true } + assert(evo.has(e, f2)) + assert(evo.get(e, f2) == true) + + evo.remove(e, f2) + assert(not evo.has(e, f2)) + + local e2 = evo.clone(e) + assert(evo.has(e2, f2)) + assert(evo.get(e2, f2) == true) + end + + do + local f0 = evo.id() + local q0 = evo.builder():include(f0):spawn() + + local e1 = evo.builder():set(f0):spawn() + local e2 = evo.builder():set(f0):spawn() + local e3 = evo.builder():set(f0):set(f2, false):spawn() + + evo.batch_set(q0, f1) + + assert(evo.has(e1, f2) and evo.get(e1, f2) == true) + assert(evo.has(e2, f2) and evo.get(e2, f2) == true) + assert(evo.has(e3, f2) and evo.get(e3, f2) == false) + end +end + +do + local f1, f2 = evo.id(2) + evo.set(f1, evo.REQUIRES, { f2 }) + evo.set(f2, evo.DEFAULT, 42) + + do + local e = evo.id() + evo.set(e, f1) + assert(evo.has(e, f2)) + assert(evo.get(e, f2) == 42) + end + + do + local e = evo.builder():set(f1):spawn() + assert(evo.has(e, f2)) + assert(evo.get(e, f2) == 42) + end + + do + local e = evo.spawn { [f1] = true, } + assert(evo.has(e, f2)) + assert(evo.get(e, f2) == 42) + + evo.remove(e, f2) + assert(not evo.has(e, f2)) + + local e2 = evo.clone(e) + assert(evo.has(e2, f2)) + assert(evo.get(e2, f2) == 42) + end + + do + local f0 = evo.id() + local q0 = evo.builder():include(f0):spawn() + + local e1 = evo.builder():set(f0):spawn() + local e2 = evo.builder():set(f0):spawn() + local e3 = evo.builder():set(f0):set(f2, 21):spawn() + + evo.batch_set(q0, f1) + + assert(evo.has(e1, f2) and evo.get(e1, f2) == 42) + assert(evo.has(e2, f2) and evo.get(e2, f2) == 42) + assert(evo.has(e3, f2) and evo.get(e3, f2) == 21) + end +end + +do + local f1, f2, f3 = evo.id(3) + evo.set(f1, evo.REQUIRES, { f2 }) + evo.set(f2, evo.REQUIRES, { f3 }) + evo.set(f3, evo.DEFAULT, 42) + + do + local e = evo.id() + evo.set(e, f1) + assert(evo.has(e, f2)) + assert(evo.get(e, f2) == true) + assert(evo.has(e, f3)) + assert(evo.get(e, f3) == 42) + end + + do + local e = evo.builder():set(f1):spawn() + assert(evo.has(e, f2)) + assert(evo.get(e, f2) == true) + assert(evo.has(e, f3)) + assert(evo.get(e, f3) == 42) + end + + do + local e = evo.spawn { [f1] = true } + assert(evo.has(e, f2)) + assert(evo.get(e, f2) == true) + assert(evo.has(e, f3)) + assert(evo.get(e, f3) == 42) + + evo.remove(e, f2, f3) + assert(not evo.has(e, f2)) + assert(not evo.has(e, f3)) + + local e2 = evo.clone(e) + assert(evo.has(e2, f2)) + assert(evo.get(e2, f2) == true) + assert(evo.has(e2, f3)) + assert(evo.get(e2, f3) == 42) + end +end + +do + local f1, f2, f3 = evo.id(3) + evo.set(f1, evo.REQUIRES, { f2 }) + evo.set(f2, evo.REQUIRES, { f3 }) + evo.set(f3, evo.REQUIRES, { f1, f2, f3 }) + + do + local e = evo.id() + evo.set(e, f1, 42) + assert(evo.has(e, f1) and evo.get(e, f1) == 42) + assert(evo.has(e, f2) and evo.get(e, f2) == true) + assert(evo.has(e, f3) and evo.get(e, f3) == true) + end + + do + local e = evo.builder():set(f1, 42):spawn() + assert(evo.has(e, f1) and evo.get(e, f1) == 42) + assert(evo.has(e, f2) and evo.get(e, f2) == true) + assert(evo.has(e, f3) and evo.get(e, f3) == true) + end + + do + local e = evo.spawn { [f1] = 42 } + assert(evo.has(e, f1) and evo.get(e, f1) == 42) + assert(evo.has(e, f2) and evo.get(e, f2) == true) + assert(evo.has(e, f3) and evo.get(e, f3) == true) + + evo.remove(e, f2, f3) + assert(not evo.has(e, f2)) + assert(not evo.has(e, f3)) + + local e2 = evo.clone(e) + assert(evo.has(e2, f1) and evo.get(e2, f1) == 42) + assert(evo.has(e2, f2) and evo.get(e2, f2) == true) + assert(evo.has(e2, f3) and evo.get(e2, f3) == true) + end +end diff --git a/evolved.lua b/evolved.lua index 2d1049b..a8d6b61 100644 --- a/evolved.lua +++ b/evolved.lua @@ -1376,6 +1376,63 @@ local function __chunk_without_unique_fragments(chunk) return new_chunk end +---@param chunk evolved.chunk +---@param req_fragment_set table +---@param req_fragment_list evolved.fragment[] +---@param req_fragment_count integer +---@return integer +---@nodiscard +local function __chunk_required_fragments(chunk, req_fragment_set, req_fragment_list, req_fragment_count) + if not chunk.__has_required_fragments then + return req_fragment_count + end + + ---@type evolved.fragment[] + local fragment_stack = __acquire_table(__table_pool_tag.fragment_list) + local fragment_stack_size = 0 + + do + local chunk_fragment_list = chunk.__fragment_list + local chunk_fragment_count = chunk.__fragment_count + + __lua_table_move( + chunk_fragment_list, 1, chunk_fragment_count, + 1, fragment_stack) + + fragment_stack_size = fragment_stack_size + chunk_fragment_count + end + + while fragment_stack_size > 0 do + local fragment = fragment_stack[fragment_stack_size] + + fragment_stack[fragment_stack_size] = nil + fragment_stack_size = fragment_stack_size - 1 + + local fragment_requires = __sorted_requires[fragment] + local fragment_require_list = fragment_requires and fragment_requires.__item_list + local fragment_require_count = fragment_requires and fragment_requires.__item_count or 0 + + for fragment_require_index = 1, fragment_require_count do + ---@cast fragment_require_list -? + local fragment_require = fragment_require_list[fragment_require_index] + + if req_fragment_set[fragment_require] then + -- this fragment has already been gathered + else + req_fragment_count = req_fragment_count + 1 + req_fragment_set[fragment_require] = req_fragment_count + req_fragment_list[req_fragment_count] = fragment_require + + fragment_stack_size = fragment_stack_size + 1 + fragment_stack[fragment_stack_size] = fragment_require + end + end + end + + __release_table(__table_pool_tag.fragment_list, fragment_stack, true) + return req_fragment_count +end + --- --- --- @@ -1703,6 +1760,29 @@ local function __spawn_entity(entity, components) return end + local req_fragment_set + local req_fragment_list + local req_fragment_count = 0 + + local ini_chunk = chunk + local ini_fragment_set = ini_chunk.__fragment_set + + if chunk.__has_required_fragments then + ---@type table + req_fragment_set = __acquire_table(__table_pool_tag.fragment_set) + + ---@type evolved.fragment[] + req_fragment_list = __acquire_table(__table_pool_tag.fragment_list) + + req_fragment_count = __chunk_required_fragments(ini_chunk, + req_fragment_set, req_fragment_list, req_fragment_count) + + for i = 1, req_fragment_count do + local req_fragment = req_fragment_list[i] + chunk = __chunk_with_fragment(chunk, req_fragment) + end + end + local chunk_entity_list = chunk.__entity_list local chunk_entity_count = chunk.__entity_count @@ -1747,6 +1827,36 @@ local function __spawn_entity(entity, components) component_storage[place] = new_component end end + + for i = 1, req_fragment_count do + local req_fragment = req_fragment_list[i] + + if ini_fragment_set[req_fragment] then + -- this fragment has already been initialized + else + local req_component_index = chunk_component_indices[req_fragment] + + if req_component_index then + ---@type evolved.default?, evolved.duplicate? + local req_fragment_default, req_fragment_duplicate = + __evolved_get(req_fragment, __DEFAULT, __DUPLICATE) + + local req_component = req_fragment_default + + if req_component ~= nil and req_fragment_duplicate then + req_component = req_fragment_duplicate(req_component) + end + + if req_component == nil then + req_component = true + end + + local req_component_storage = chunk_component_storages[req_component_index] + + req_component_storage[place] = req_component + end + end + end else for fragment, component in __lua_next, components do local component_index = chunk_component_indices[fragment] @@ -1759,6 +1869,24 @@ local function __spawn_entity(entity, components) component_storage[place] = new_component end end + + for i = 1, req_fragment_count do + local req_fragment = req_fragment_list[i] + + if ini_fragment_set[req_fragment] then + -- this fragment has already been initialized + else + local req_component_index = chunk_component_indices[req_fragment] + + if req_component_index then + local req_component = true + + local req_component_storage = chunk_component_storages[req_component_index] + + req_component_storage[place] = req_component + end + end + end end if chunk.__has_insert_hooks then @@ -1797,6 +1925,14 @@ local function __spawn_entity(entity, components) end end end + + if req_fragment_set then + __release_table(__table_pool_tag.fragment_set, req_fragment_set) + end + + if req_fragment_list then + __release_table(__table_pool_tag.fragment_list, req_fragment_list) + end end ---@param entity evolved.entity @@ -1819,6 +1955,29 @@ local function __clone_entity(entity, prefab, components) return end + local req_fragment_set + local req_fragment_list + local req_fragment_count = 0 + + local ini_chunk = chunk + local ini_fragment_set = ini_chunk.__fragment_set + + if chunk.__has_required_fragments then + ---@type table + req_fragment_set = __acquire_table(__table_pool_tag.fragment_set) + + ---@type evolved.fragment[] + req_fragment_list = __acquire_table(__table_pool_tag.fragment_list) + + req_fragment_count = __chunk_required_fragments(ini_chunk, + req_fragment_set, req_fragment_list, req_fragment_count) + + for i = 1, req_fragment_count do + local req_fragment = req_fragment_list[i] + chunk = __chunk_with_fragment(chunk, req_fragment) + end + end + local chunk_entity_list = chunk.__entity_list local chunk_entity_count = chunk.__entity_count @@ -1919,6 +2078,36 @@ local function __clone_entity(entity, prefab, components) component_storage[place] = new_component end end + + for i = 1, req_fragment_count do + local req_fragment = req_fragment_list[i] + + if ini_fragment_set[req_fragment] then + -- this fragment has already been initialized + else + local req_component_index = chunk_component_indices[req_fragment] + + if req_component_index then + ---@type evolved.default?, evolved.duplicate? + local req_fragment_default, req_fragment_duplicate = + __evolved_get(req_fragment, __DEFAULT, __DUPLICATE) + + local req_component = req_fragment_default + + if req_component ~= nil and req_fragment_duplicate then + req_component = req_fragment_duplicate(req_component) + end + + if req_component == nil then + req_component = true + end + + local req_component_storage = chunk_component_storages[req_component_index] + + req_component_storage[place] = req_component + end + end + end else for fragment, component in __lua_next, components do local component_index = chunk_component_indices[fragment] @@ -1931,6 +2120,24 @@ local function __clone_entity(entity, prefab, components) component_storage[place] = new_component end end + + for i = 1, req_fragment_count do + local req_fragment = req_fragment_list[i] + + if ini_fragment_set[req_fragment] then + -- this fragment has already been initialized + else + local req_component_index = chunk_component_indices[req_fragment] + + if req_component_index then + local req_component = true + + local req_component_storage = chunk_component_storages[req_component_index] + + req_component_storage[place] = req_component + end + end + end end if chunk.__has_insert_hooks then @@ -1969,6 +2176,14 @@ local function __clone_entity(entity, prefab, components) end end end + + if req_fragment_set then + __release_table(__table_pool_tag.fragment_set, req_fragment_set) + end + + if req_fragment_list then + __release_table(__table_pool_tag.fragment_list, req_fragment_list) + end end --- @@ -2381,6 +2596,29 @@ function __chunk_set(old_chunk, fragment, component) end end else + local req_fragment_set + local req_fragment_list + local req_fragment_count = 0 + + local ini_new_chunk = new_chunk + local ini_fragment_set = ini_new_chunk.__fragment_set + + if new_chunk.__has_required_fragments then + ---@type table + req_fragment_set = __acquire_table(__table_pool_tag.fragment_set) + + ---@type evolved.fragment[] + req_fragment_list = __acquire_table(__table_pool_tag.fragment_list) + + req_fragment_count = __chunk_required_fragments(ini_new_chunk, + req_fragment_set, req_fragment_list, req_fragment_count) + + for i = 1, req_fragment_count do + local req_fragment = req_fragment_list[i] + new_chunk = __chunk_with_fragment(new_chunk, req_fragment) + end + end + local new_entity_list = new_chunk.__entity_list local new_entity_count = new_chunk.__entity_count @@ -2527,6 +2765,110 @@ function __chunk_set(old_chunk, fragment, component) end end + for i = 1, req_fragment_count do + local req_fragment = req_fragment_list[i] + + if ini_fragment_set[req_fragment] then + -- this fragment has already been initialized + else + ---@type evolved.default?, evolved.duplicate?, evolved.set_hook?, evolved.insert_hook? + local req_fragment_default, req_fragment_duplicate, req_fragment_on_set, req_fragment_on_insert + + if new_chunk_has_setup_hooks or new_chunk_has_insert_hooks then + req_fragment_default, req_fragment_duplicate, req_fragment_on_set, req_fragment_on_insert = + __evolved_get(req_fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_INSERT) + end + + if req_fragment_on_set or req_fragment_on_insert then + local req_component_index = new_component_indices[req_fragment] + + if req_component_index then + local req_component_storage = new_component_storages[req_component_index] + + if req_fragment_duplicate then + for new_place = new_entity_count + 1, new_entity_count + old_entity_count do + local entity = new_entity_list[new_place] + + local req_component = req_fragment_default + if req_component ~= nil then req_component = req_fragment_duplicate(req_component) end + if req_component == nil then req_component = true end + + req_component_storage[new_place] = req_component + + if req_fragment_on_set then + __defer_call_hook(req_fragment_on_set, entity, req_fragment, req_component) + end + + if req_fragment_on_insert then + __defer_call_hook(req_fragment_on_insert, entity, req_fragment, req_component) + end + end + else + local req_component = req_fragment_default + if req_component == nil then req_component = true end + + for new_place = new_entity_count + 1, new_entity_count + old_entity_count do + local entity = new_entity_list[new_place] + + req_component_storage[new_place] = req_component + + if req_fragment_on_set then + __defer_call_hook(req_fragment_on_set, entity, req_fragment, req_component) + end + + if req_fragment_on_insert then + __defer_call_hook(req_fragment_on_insert, entity, req_fragment, req_component) + end + end + end + else + for new_place = new_entity_count + 1, new_entity_count + old_entity_count do + local entity = new_entity_list[new_place] + + if req_fragment_on_set then + __defer_call_hook(req_fragment_on_set, entity, req_fragment) + end + + if req_fragment_on_insert then + __defer_call_hook(req_fragment_on_insert, entity, req_fragment) + end + end + end + else + local req_component_index = new_component_indices[req_fragment] + + if req_component_index then + local req_component_storage = new_component_storages[req_component_index] + + if req_fragment_duplicate then + for new_place = new_entity_count + 1, new_entity_count + old_entity_count do + local req_component = req_fragment_default + if req_component ~= nil then req_component = req_fragment_duplicate(req_component) end + if req_component == nil then req_component = true end + req_component_storage[new_place] = req_component + end + else + local req_component = req_fragment_default + if req_component == nil then req_component = true end + for new_place = new_entity_count + 1, new_entity_count + old_entity_count do + req_component_storage[new_place] = req_component + end + end + else + -- nothing + end + end + end + end + + if req_fragment_set then + __release_table(__table_pool_tag.fragment_set, req_fragment_set) + end + + if req_fragment_list then + __release_table(__table_pool_tag.fragment_list, req_fragment_list) + end + __structural_changes = __structural_changes + 1 end end @@ -3883,6 +4225,29 @@ function __evolved_set(entity, fragment, component) end end else + local req_fragment_set + local req_fragment_list + local req_fragment_count = 0 + + local ini_new_chunk = new_chunk + local ini_fragment_set = ini_new_chunk.__fragment_set + + if new_chunk.__has_required_fragments then + ---@type table + req_fragment_set = __acquire_table(__table_pool_tag.fragment_set) + + ---@type evolved.fragment[] + req_fragment_list = __acquire_table(__table_pool_tag.fragment_list) + + req_fragment_count = __chunk_required_fragments(ini_new_chunk, + req_fragment_set, req_fragment_list, req_fragment_count) + + for i = 1, req_fragment_count do + local req_fragment = req_fragment_list[i] + new_chunk = __chunk_with_fragment(new_chunk, req_fragment) + end + end + local new_entity_list = new_chunk.__entity_list local new_entity_count = new_chunk.__entity_count @@ -3958,6 +4323,64 @@ function __evolved_set(entity, fragment, component) end end end + + for i = 1, req_fragment_count do + local req_fragment = req_fragment_list[i] + + if ini_fragment_set[req_fragment] then + -- this fragment has already been initialized + else + ---@type evolved.default?, evolved.duplicate?, evolved.set_hook?, evolved.insert_hook? + local req_fragment_default, req_fragment_duplicate, req_fragment_on_set, req_fragment_on_insert + + if new_chunk_has_setup_hooks or new_chunk_has_insert_hooks then + req_fragment_default, req_fragment_duplicate, req_fragment_on_set, req_fragment_on_insert = + __evolved_get(req_fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_INSERT) + end + + local req_component_index = new_component_indices[req_fragment] + + if req_component_index then + local req_component_storage = new_component_storages[req_component_index] + + local req_component = req_fragment_default + + if req_component ~= nil and req_fragment_duplicate then + req_component = req_fragment_duplicate(req_component) + end + + if req_component == nil then + req_component = true + end + + req_component_storage[new_place] = req_component + + if req_fragment_on_set then + __defer_call_hook(req_fragment_on_set, entity, req_fragment, req_component) + end + + if req_fragment_on_insert then + __defer_call_hook(req_fragment_on_insert, entity, req_fragment, req_component) + end + else + if req_fragment_on_set then + __defer_call_hook(req_fragment_on_set, entity, req_fragment) + end + + if req_fragment_on_insert then + __defer_call_hook(req_fragment_on_insert, entity, req_fragment) + end + end + end + end + + if req_fragment_set then + __release_table(__table_pool_tag.fragment_set, req_fragment_set) + end + + if req_fragment_list then + __release_table(__table_pool_tag.fragment_list, req_fragment_list) + end end __evolved_commit()