From 27eed30b13827daeb3d9ef006f08ea349262b7c1 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sat, 18 Jan 2025 03:09:01 +0700 Subject: [PATCH 01/14] batch-multi-api declarations (not impl) --- README.md | 5 +++ evolved.lua | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3317dca..473c0ec 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,11 @@ batch_remove :: query, fragment... -> integer, boolean batch_clear :: query -> integer, boolean batch_destroy :: query -> integer, boolean +batch_multi_set :: query, fragment[], component[]? -> integer, boolean +batch_multi_assign :: query, fragment[], component[]? -> integer, boolean +batch_multi_insert :: query, fragment[], component[]? -> integer, boolean +batch_multi_remove :: query, fragment[] -> integer, boolean + chunk :: fragment... -> chunk?, entity[]? select :: chunk, fragment... -> component[]... diff --git a/evolved.lua b/evolved.lua index 5d8f840..836a4eb 100644 --- a/evolved.lua +++ b/evolved.lua @@ -1573,8 +1573,13 @@ local __defer_op = { batch_clear = 15, batch_destroy = 16, - spawn_entity_at = 17, - spawn_entity_with = 18, + batch_multi_set = 17, + batch_multi_assign = 18, + batch_multi_insert = 19, + batch_multi_remove = 20, + + spawn_entity_at = 21, + spawn_entity_with = 22, } ---@type table @@ -1687,6 +1692,18 @@ local __defer_ops = { evolved.batch_destroy(query) return 1 end, + [__defer_op.batch_multi_set] = function(bytes, index) + error('not implemented yet', 2) + end, + [__defer_op.batch_multi_assign] = function(bytes, index) + error('not implemented yet', 2) + end, + [__defer_op.batch_multi_insert] = function(bytes, index) + error('not implemented yet', 2) + end, + [__defer_op.batch_multi_remove] = function(bytes, index) + error('not implemented yet', 2) + end, [__defer_op.spawn_entity_at] = function(bytes, index) local entity = bytes[index + 0] local chunk = bytes[index + 1] @@ -2043,6 +2060,33 @@ local function __defer_batch_destroy(query) __defer_length = length + 2 end +---@param query evolved.query +---@param fragments evolved.fragment[] +---@param components evolved.component[] +local function __defer_batch_multi_set(query, fragments, components) + error('not implemented yet', 2) +end + +---@param query evolved.query +---@param fragments evolved.fragment[] +---@param components evolved.component[] +local function __defer_batch_multi_assign(query, fragments, components) + error('not implemented yet', 2) +end + +---@param query evolved.query +---@param fragments evolved.fragment[] +---@param components evolved.component[] +local function __defer_batch_multi_insert(query, fragments, components) + error('not implemented yet', 2) +end + +---@param query evolved.query +---@param fragments evolved.fragment[] +local function __defer_batch_multi_remove(query, fragments) + error('not implemented yet', 2) +end + ---@param entity evolved.entity ---@param chunk evolved.chunk ---@param fragments evolved.fragment[] @@ -3480,6 +3524,73 @@ function evolved.batch_destroy(query) return destroyed_count, false end +---@param query evolved.query +---@param fragments evolved.fragment[] +---@param components? evolved.component[] +---@return integer set_count +---@return boolean is_deferred +function evolved.batch_multi_set(query, fragments, components) + if not components then + components = __EMPTY_COMPONENT_LIST + end + + if __defer_depth > 0 then + __defer_batch_multi_set(query, fragments, components) + return 0, true + end + + error('not implemented yet', 2) +end + +---@param query evolved.query +---@param fragments evolved.fragment[] +---@param components? evolved.component[] +---@return integer assigned_count +---@return boolean is_deferred +function evolved.batch_multi_assign(query, fragments, components) + if not components then + components = __EMPTY_COMPONENT_LIST + end + + if __defer_depth > 0 then + __defer_batch_multi_assign(query, fragments, components) + return 0, true + end + + error('not implemented yet', 2) +end + +---@param query evolved.query +---@param fragments evolved.fragment[] +---@param components? evolved.component[] +---@return integer inserted_count +---@return boolean is_deferred +function evolved.batch_multi_insert(query, fragments, components) + if not components then + components = __EMPTY_COMPONENT_LIST + end + + if __defer_depth > 0 then + __defer_batch_multi_insert(query, fragments, components) + return 0, true + end + + error('not implemented yet', 2) +end + +---@param query evolved.query +---@param fragments evolved.fragment[] +---@return integer removed_count +---@return boolean is_deferred +function evolved.batch_multi_remove(query, fragments) + if __defer_depth > 0 then + __defer_batch_multi_remove(query, fragments) + return 0, true + end + + error('not implemented yet', 2) +end + --- --- --- From 9cbb808a1012e2d8d089ad152f64de8e02c6cf71 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sat, 18 Jan 2025 04:07:02 +0700 Subject: [PATCH 02/14] batch-multi-api public api boilerplate --- evolved.lua | 208 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 196 insertions(+), 12 deletions(-) diff --git a/evolved.lua b/evolved.lua index 836a4eb..703e641 100644 --- a/evolved.lua +++ b/evolved.lua @@ -1336,6 +1336,37 @@ local function __chunk_destroy(chunk) return chunk_size end +---@param chunk evolved.chunk +---@param fragments evolved.fragment[] +---@param components evolved.component[] +---@return integer set_count +local function __chunk_multi_set(chunk, fragments, components) + error('not implemented yet', 2) +end + +---@param chunk evolved.chunk +---@param fragments evolved.fragment[] +---@param components evolved.component[] +---@return integer assigned_count +local function __chunk_multi_assign(chunk, fragments, components) + error('not implemented yet', 2) +end + +---@param chunk evolved.chunk +---@param fragments evolved.fragment[] +---@param components evolved.component[] +---@return integer inserted_count +local function __chunk_multi_insert(chunk, fragments, components) + error('not implemented yet', 2) +end + +---@param chunk evolved.chunk +---@param fragments evolved.fragment[] +---@return integer removed_count +local function __chunk_multi_remove(chunk, fragments) + error('not implemented yet', 2) +end + --- --- --- @@ -1693,16 +1724,38 @@ local __defer_ops = { return 1 end, [__defer_op.batch_multi_set] = function(bytes, index) - error('not implemented yet', 2) + local query = bytes[index + 0] + local fragments = bytes[index + 1] + local components = bytes[index + 2] + evolved.batch_multi_set(query, fragments, components) + __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) + __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, components) + return 3 end, [__defer_op.batch_multi_assign] = function(bytes, index) - error('not implemented yet', 2) + local query = bytes[index + 0] + local fragments = bytes[index + 1] + local components = bytes[index + 2] + evolved.batch_multi_assign(query, fragments, components) + __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) + __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, components) + return 3 end, [__defer_op.batch_multi_insert] = function(bytes, index) - error('not implemented yet', 2) + local query = bytes[index + 0] + local fragments = bytes[index + 1] + local components = bytes[index + 2] + evolved.batch_multi_insert(query, fragments, components) + __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) + __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, components) + return 3 end, [__defer_op.batch_multi_remove] = function(bytes, index) - error('not implemented yet', 2) + local query = bytes[index + 0] + local fragments = bytes[index + 1] + evolved.batch_multi_remove(query, fragments) + __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) + return 2 end, [__defer_op.spawn_entity_at] = function(bytes, index) local entity = bytes[index + 0] @@ -2064,27 +2117,86 @@ end ---@param fragments evolved.fragment[] ---@param components evolved.component[] local function __defer_batch_multi_set(query, fragments, components) - error('not implemented yet', 2) + local fragment_count = #fragments + local fragment_list = __acquire_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragment_count, 0) + __table_move(fragments, 1, fragment_count, 1, fragment_list) + + local component_count = #components + local component_list = __acquire_table(__TABLE_POOL_TAG__COMPONENT_LIST, component_count, 0) + __table_move(components, 1, component_count, 1, component_list) + + local length = __defer_length + local bytecode = __defer_bytecode + + bytecode[length + 1] = __defer_op.batch_multi_set + bytecode[length + 2] = query + bytecode[length + 3] = fragment_list + bytecode[length + 4] = component_list + + __defer_length = length + 4 end ---@param query evolved.query ---@param fragments evolved.fragment[] ---@param components evolved.component[] local function __defer_batch_multi_assign(query, fragments, components) - error('not implemented yet', 2) + local fragment_count = #fragments + local fragment_list = __acquire_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragment_count, 0) + __table_move(fragments, 1, fragment_count, 1, fragment_list) + + local component_count = #components + local component_list = __acquire_table(__TABLE_POOL_TAG__COMPONENT_LIST, component_count, 0) + __table_move(components, 1, component_count, 1, component_list) + + local length = __defer_length + local bytecode = __defer_bytecode + + bytecode[length + 1] = __defer_op.batch_multi_assign + bytecode[length + 2] = query + bytecode[length + 3] = fragment_list + bytecode[length + 4] = component_list + + __defer_length = length + 4 end ---@param query evolved.query ---@param fragments evolved.fragment[] ---@param components evolved.component[] local function __defer_batch_multi_insert(query, fragments, components) - error('not implemented yet', 2) + local fragment_count = #fragments + local fragment_list = __acquire_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragment_count, 0) + __table_move(fragments, 1, fragment_count, 1, fragment_list) + + local component_count = #components + local component_list = __acquire_table(__TABLE_POOL_TAG__COMPONENT_LIST, component_count, 0) + __table_move(components, 1, component_count, 1, component_list) + + local length = __defer_length + local bytecode = __defer_bytecode + + bytecode[length + 1] = __defer_op.batch_multi_insert + bytecode[length + 2] = query + bytecode[length + 3] = fragment_list + bytecode[length + 4] = component_list + + __defer_length = length + 4 end ---@param query evolved.query ---@param fragments evolved.fragment[] local function __defer_batch_multi_remove(query, fragments) - error('not implemented yet', 2) + local fragment_count = #fragments + local fragment_list = __acquire_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragment_count, 0) + __table_move(fragments, 1, fragment_count, 1, fragment_list) + + local length = __defer_length + local bytecode = __defer_bytecode + + bytecode[length + 1] = __defer_op.batch_multi_remove + bytecode[length + 2] = query + bytecode[length + 3] = fragment_list + + __defer_length = length + 3 end ---@param entity evolved.entity @@ -3539,7 +3651,25 @@ function evolved.batch_multi_set(query, fragments, components) return 0, true end - error('not implemented yet', 2) + ---@type evolved.chunk[] + local chunk_list = __acquire_table(__TABLE_POOL_TAG__CHUNK_LIST, 16, 0) + + for chunk in evolved.execute(query) do + chunk_list[#chunk_list + 1] = chunk + end + + local set_count = 0 + + __defer() + do + for _, chunk in ipairs(chunk_list) do + set_count = set_count + __chunk_multi_set(chunk, fragments, components) + end + end + __defer_commit() + + __release_table(__TABLE_POOL_TAG__CHUNK_LIST, chunk_list) + return set_count, false end ---@param query evolved.query @@ -3557,7 +3687,25 @@ function evolved.batch_multi_assign(query, fragments, components) return 0, true end - error('not implemented yet', 2) + ---@type evolved.chunk[] + local chunk_list = __acquire_table(__TABLE_POOL_TAG__CHUNK_LIST, 16, 0) + + for chunk in evolved.execute(query) do + chunk_list[#chunk_list + 1] = chunk + end + + local assigned_count = 0 + + __defer() + do + for _, chunk in ipairs(chunk_list) do + assigned_count = assigned_count + __chunk_multi_assign(chunk, fragments, components) + end + end + __defer_commit() + + __release_table(__TABLE_POOL_TAG__CHUNK_LIST, chunk_list) + return assigned_count, false end ---@param query evolved.query @@ -3575,7 +3723,25 @@ function evolved.batch_multi_insert(query, fragments, components) return 0, true end - error('not implemented yet', 2) + ---@type evolved.chunk[] + local chunk_list = __acquire_table(__TABLE_POOL_TAG__CHUNK_LIST, 16, 0) + + for chunk in evolved.execute(query) do + chunk_list[#chunk_list + 1] = chunk + end + + local inserted_count = 0 + + __defer() + do + for _, chunk in ipairs(chunk_list) do + inserted_count = inserted_count + __chunk_multi_insert(chunk, fragments, components) + end + end + __defer_commit() + + __release_table(__TABLE_POOL_TAG__CHUNK_LIST, chunk_list) + return inserted_count, false end ---@param query evolved.query @@ -3588,7 +3754,25 @@ function evolved.batch_multi_remove(query, fragments) return 0, true end - error('not implemented yet', 2) + ---@type evolved.chunk[] + local chunk_list = __acquire_table(__TABLE_POOL_TAG__CHUNK_LIST, 16, 0) + + for chunk in evolved.execute(query) do + chunk_list[#chunk_list + 1] = chunk + end + + local removed_count = 0 + + __defer() + do + for _, chunk in ipairs(chunk_list) do + removed_count = removed_count + __chunk_multi_remove(chunk, fragments) + end + end + __defer_commit() + + __release_table(__TABLE_POOL_TAG__CHUNK_LIST, chunk_list) + return removed_count, false end --- From 738a4fd7b13a3cdf23d8fa3f833e2172102ce912 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sat, 18 Jan 2025 05:15:32 +0700 Subject: [PATCH 03/14] fix: remove dup fragments events --- develop/untests.lua | 2 +- evolved.lua | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/develop/untests.lua b/develop/untests.lua index aa8cb88..b890130 100644 --- a/develop/untests.lua +++ b/develop/untests.lua @@ -1249,7 +1249,7 @@ do local q = evo.id() evo.insert(q, evo.INCLUDES, f1, f2) - assert(evo.batch_remove(q, f2, f3) == 3) + assert(evo.batch_remove(q, f2, f3, f3) == 3) assert(entity_sum == e2 + e3 * 2 + e4 * 2) assert(component_sum == 43 + 45 + 46 + 48 + 49) diff --git a/evolved.lua b/evolved.lua index 703e641..cb18ca7 100644 --- a/evolved.lua +++ b/evolved.lua @@ -1143,6 +1143,12 @@ local function __chunk_remove(chunk, ...) error('batched chunk operations should be deferred', 2) end + local fragment_count = select('#', ...) + + if fragment_count == 0 then + return 0 + end + local old_chunk = chunk local new_chunk = __chunk_without_fragments(chunk, ...) @@ -1153,17 +1159,24 @@ local function __chunk_remove(chunk, ...) local old_entities = old_chunk.__entities local old_size = #old_entities + local old_fragment_set = old_chunk.__fragment_set local old_component_indices = chunk.__component_indices local old_component_storages = chunk.__component_storages if old_chunk.__has_remove_hooks then - local old_fragment_set = old_chunk.__fragment_set - for i = 1, select('#', ...) do + local removed_set = __acquire_table(__TABLE_POOL_TAG__FRAGMENT_SET, 0, fragment_count) + + for i = 1, fragment_count do local fragment = select(i, ...) - if old_fragment_set[fragment] and __fragment_has_remove_hook(fragment) then + + if not removed_set[fragment] and old_fragment_set[fragment] and __fragment_has_remove_hook(fragment) then + removed_set[fragment] = true + local old_component_index = old_component_indices[fragment] + if old_component_index then local old_component_storage = old_component_storages[old_component_index] + for old_place = 1, old_size do local entity = old_entities[old_place] local old_component = old_component_storage[old_place] From d2bdaf03c61a9294e571691a5dd4df7ff881b425 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sat, 18 Jan 2025 05:16:43 +0700 Subject: [PATCH 04/14] batch-multi-api: batch_multi_remove impl --- develop/untests.lua | 149 ++++++++++++++++++++++++++++++++++++++++++++ evolved.lua | 100 ++++++++++++++++++++++++++++- 2 files changed, 248 insertions(+), 1 deletion(-) diff --git a/develop/untests.lua b/develop/untests.lua index b890130..7388572 100644 --- a/develop/untests.lua +++ b/develop/untests.lua @@ -3710,3 +3710,152 @@ do assert(evo.has(e2, f4) and evo.get(e2, f4) == nil) end end + +do + local f1, f2, f3, f4, f5 = evo.id(5) + + local e1 = evo.entity():set(f1, 11):build() + local e2 = evo.entity():set(f1, 21):set(f2, 22):build() + local e3 = evo.entity():set(f1, 31):set(f2, 32):set(f3, 33):build() + local e4 = evo.entity():set(f1, 41):set(f2, 42):set(f3, 43):set(f4, 44):build() + + do + local q = evo.query():include(f1):build() + assert(evo.batch_multi_remove(q, {}) == 0) + assert(evo.batch_multi_remove(q, { f5 }) == 0) + end + + do + local q = evo.query():include(f3):build() + + assert(evo.batch_multi_remove(q, { f4 }) == 1) + assert(evo.has_all(e4, f1, f2, f3) and not evo.has(e4, f4)) + assert(evo.get(e4, f1) == 41) + assert(evo.get(e4, f2) == 42) + assert(evo.get(e4, f3) == 43) + assert(evo.get(e4, f4) == nil) + + for chunk in evo.execute(q) do + assert(next(evo.select(chunk, f4)) == nil) + end + + do + local chunk, entities = evo.chunk(f1, f2, f3) + assert(chunk and entities) + assert(#entities == 2) + assert(entities[1] == e3, entities[2] == e4) + assert(evo.select(chunk, f3)[1] == 33) + assert(evo.select(chunk, f3)[2] == 43) + end + + do + local chunk, entities = evo.chunk(f1, f2, f3, f4) + assert(chunk) + assert(next(evo.select(chunk, f4)) == nil) + assert(#entities == 0) + end + end + + do + local q = evo.query():include(f2):build() + + assert(evo.batch_multi_remove(q, { f1 }) == 3) + assert(evo.has_all(e1, f1) and not evo.has_any(e1, f2, f3, f4)) + assert(evo.has_all(e2, f2) and not evo.has_any(e2, f1, f3, f4)) + assert(evo.has_all(e3, f2, f3) and not evo.has_any(e3, f1, f4)) + assert(evo.has_all(e4, f2, f3) and not evo.has_any(e4, f1, f4)) + + for chunk in evo.execute(q) do + assert(next(evo.select(chunk, f1)) == nil) + end + + assert(evo.batch_multi_remove(q, { f2, f3 }) == 3) + assert(evo.has_all(e1, f1) and not evo.has_any(e1, f2, f3, f4)) + assert(not evo.has_any(e2, f1, f2, f3, f4)) + assert(not evo.has_any(e3, f1, f2, f3, f4)) + assert(not evo.has_any(e4, f1, f2, f3, f4)) + + for chunk in evo.execute(q) do + assert(next(evo.select(chunk, f2)) == nil) + assert(next(evo.select(chunk, f3)) == nil) + end + + do + local chunk, entities = evo.chunk(f1, f2) + assert(chunk) + assert(next(evo.select(chunk, f1)) == nil) + assert(next(evo.select(chunk, f2)) == nil) + assert(#entities == 0) + end + + do + local chunk, entities = evo.chunk(f1, f2, f3) + assert(chunk) + assert(next(evo.select(chunk, f1)) == nil) + assert(next(evo.select(chunk, f2)) == nil) + assert(next(evo.select(chunk, f3)) == nil) + assert(#entities == 0) + end + end + + do + local q = evo.query():include(f1):build() + + assert(evo.defer()) + assert(evo.batch_multi_remove(q, { f1 }) == 0) + assert(evo.has(e1, f1)) + assert(evo.commit()) + assert(not evo.has(e1, f1)) + end +end + +do + local f1, f2 = evo.id(2) + + evo.set(f2, evo.TAG) + + local last_remove_entity = 0 + local last_remove_component = 0 + local sum_removed_components = 0 + + evo.set(f1, evo.ON_REMOVE, function(e, f, c) + assert(f == f1) + last_remove_entity = e + last_remove_component = c + sum_removed_components = sum_removed_components + c + end) + + evo.set(f2, evo.ON_REMOVE, function(e, f, c) + assert(f == f2) + last_remove_entity = e + last_remove_component = c + end) + + local _ = evo.spawn_with({ f1 }, { 11 }) + local e2 = evo.spawn_with({ f1, f2 }, { 21, 22 }) + assert(last_remove_entity == 0 and last_remove_component == 0) + + do + last_remove_entity = 0 + last_remove_component = 0 + sum_removed_components = 0 + + local q = evo.query():include(f1):build() + + assert(evo.batch_multi_remove(q, { f1, f1 }) == 2) + assert(last_remove_entity == e2 and last_remove_component == 21) + assert(sum_removed_components == 11 + 21) + end + + do + last_remove_entity = 0 + last_remove_component = 0 + sum_removed_components = 0 + + local q = evo.query():include(f2):build() + + assert(evo.batch_multi_remove(q, { f2 }) == 1) + assert(last_remove_entity == e2 and last_remove_component == nil) + assert(sum_removed_components == 0) + end +end diff --git a/evolved.lua b/evolved.lua index cb18ca7..4b57c0b 100644 --- a/evolved.lua +++ b/evolved.lua @@ -1377,7 +1377,105 @@ end ---@param fragments evolved.fragment[] ---@return integer removed_count local function __chunk_multi_remove(chunk, fragments) - error('not implemented yet', 2) + if __defer_depth <= 0 then + error('batched chunk operations should be deferred', 2) + end + + local fragment_count = #fragments + + if fragment_count == 0 then + return 0 + end + + local old_chunk = chunk + local new_chunk = __chunk_without_fragment_list(chunk, fragments) + + if old_chunk == new_chunk then + return 0 + end + + local old_entities = old_chunk.__entities + local old_size = #old_entities + + local old_fragment_set = old_chunk.__fragment_set + local old_component_indices = old_chunk.__component_indices + local old_component_storages = old_chunk.__component_storages + + if old_chunk.__has_remove_hooks then + local removed_set = __acquire_table(__TABLE_POOL_TAG__FRAGMENT_SET, 0, fragment_count) + + for i = 1, fragment_count do + local fragment = fragments[i] + + if not removed_set[fragment] and old_fragment_set[fragment] and __fragment_has_remove_hook(fragment) then + removed_set[fragment] = true + + local old_component_index = old_component_indices[fragment] + + if old_component_index then + local old_component_storage = old_component_storages[old_component_index] + + for old_place = 1, old_size do + local entity = old_entities[old_place] + local old_component = old_component_storage[old_place] + __fragment_call_remove_hook(entity, fragment, old_component) + end + else + for place = 1, old_size do + local entity = old_entities[place] + __fragment_call_remove_hook(entity, fragment) + end + end + end + end + end + + if new_chunk then + local new_entities = new_chunk.__entities + local new_size = #new_entities + + local new_component_storages = new_chunk.__component_storages + local new_component_fragments = new_chunk.__component_fragments + + __table_move( + old_entities, 1, old_size, + new_size + 1, new_entities) + + for i = 1, #new_component_fragments do + local new_f = new_component_fragments[i] + local new_cs = new_component_storages[i] + local old_ci = old_component_indices[new_f] + if old_ci then + local old_cs = old_component_storages[old_ci] + __table_move(old_cs, 1, old_size, new_size + 1, new_cs) + end + end + + for new_place = new_size + 1, new_size + old_size do + local entity = new_entities[new_place] + local entity_index = entity % 0x100000 + __entity_chunks[entity_index] = new_chunk + __entity_places[entity_index] = new_place + end + else + for old_place = 1, old_size do + local entity = old_entities[old_place] + local entity_index = entity % 0x100000 + __entity_chunks[entity_index] = nil + __entity_places[entity_index] = nil + end + end + + do + old_chunk.__entities = {} + + for i = 1, #old_component_storages do + old_component_storages[i] = {} + end + end + + __structural_changes = __structural_changes + old_size + return old_size end --- From b4a8c1ea27bc05804cf3fbd956f8de72132a2b1c Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sat, 18 Jan 2025 05:26:05 +0700 Subject: [PATCH 05/14] fix: return removed_sets to table-pool --- evolved.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/evolved.lua b/evolved.lua index 4b57c0b..80a2311 100644 --- a/evolved.lua +++ b/evolved.lua @@ -1190,6 +1190,8 @@ local function __chunk_remove(chunk, ...) end end end + + __release_table(__TABLE_POOL_TAG__FRAGMENT_SET, removed_set) end if new_chunk then @@ -1428,6 +1430,8 @@ local function __chunk_multi_remove(chunk, fragments) end end end + + __release_table(__TABLE_POOL_TAG__FRAGMENT_SET, removed_set) end if new_chunk then From 8be08c9b5aa64e91aec0f9a9cb1c531bcb30a956 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 20 Jan 2025 22:24:13 +0700 Subject: [PATCH 06/14] batch-multi-api: batch_multi_assign impl --- develop/untests.lua | 209 ++++++++++++++++++++++++++++++++++++++++++++ evolved.lua | 103 +++++++++++++++++++++- 2 files changed, 311 insertions(+), 1 deletion(-) diff --git a/develop/untests.lua b/develop/untests.lua index 7388572..7af682b 100644 --- a/develop/untests.lua +++ b/develop/untests.lua @@ -3859,3 +3859,212 @@ do assert(sum_removed_components == 0) end end + +do + local f1, f2, f3, f4, f5 = evo.id(5) + + local e1 = evo.entity():set(f1, 11):build() + local e2 = evo.entity():set(f1, 21):set(f2, 22):build() + local e3 = evo.entity():set(f1, 31):set(f2, 32):set(f3, 33):build() + local e4 = evo.entity():set(f1, 41):set(f2, 42):set(f3, 43):set(f4, 44):build() + + assert(evo.get(e1, f1) == 11 and evo.get(e1, f2) == nil and evo.get(e1, f3) == nil) + assert(evo.get(e2, f1) == 21 and evo.get(e2, f2) == 22 and evo.get(e2, f3) == nil) + assert(evo.get(e3, f1) == 31 and evo.get(e3, f2) == 32 and evo.get(e3, f3) == 33) + assert(evo.get(e4, f1) == 41 and evo.get(e4, f2) == 42 and evo.get(e4, f3) == 43 and evo.get(e4, f4) == 44) + + do + local q = evo.query():include(f1):build() + assert(evo.batch_multi_assign(q, {}) == 0) + assert(evo.batch_multi_assign(q, { f5 }) == 0) + end + + do + local q = evo.query():include(f3):build() + + assert(evo.batch_multi_assign(q, { f4 }, { 54 }) == 1) + assert(evo.get(e3, f3) == 33 and evo.get(e3, f4) == nil) + assert(evo.get(e4, f3) == 43 and evo.get(e4, f4) == 54) + end + + do + local q = evo.query():include(f2):build() + + assert(evo.batch_multi_assign(q, { f1 }, { 51, 52 }) == 3) + assert(evo.get(e1, f1) == 11 and evo.get(e1, f2) == nil and evo.get(e1, f3) == nil) + assert(evo.get(e2, f1) == 51 and evo.get(e2, f2) == 22 and evo.get(e2, f3) == nil) + assert(evo.get(e3, f1) == 51 and evo.get(e3, f2) == 32 and evo.get(e3, f3) == 33) + assert(evo.get(e4, f1) == 51 and evo.get(e4, f2) == 42 and evo.get(e4, f3) == 43 and evo.get(e4, f4) == 54) + + assert(evo.batch_multi_assign(q, { f2, f3 }, { 52, 53 }) == 3) + assert(evo.get(e1, f1) == 11 and evo.get(e1, f2) == nil and evo.get(e1, f3) == nil) + assert(evo.get(e2, f1) == 51 and evo.get(e2, f2) == 52 and evo.get(e2, f3) == nil) + assert(evo.get(e3, f1) == 51 and evo.get(e3, f2) == 52 and evo.get(e3, f3) == 53) + assert(evo.get(e4, f1) == 51 and evo.get(e4, f2) == 52 and evo.get(e4, f3) == 53 and evo.get(e4, f4) == 54) + end +end + +do + local f1, f2, f3, f4, f5 = evo.id(4) + + evo.set(f2, evo.DEFAULT, 41) + evo.set(f3, evo.TAG) + + local e1 = evo.entity():set(f1, 11):build() + local e2 = evo.entity():set(f1, 21):set(f2, 22):build() + local e3 = evo.entity():set(f1, 31):set(f2, 32):set(f3, 33):build() + local e4 = evo.entity():set(f1, 41):set(f2, 42):set(f3, 43):set(f4, 44):build() + + assert(evo.get(e1, f1) == 11 and evo.get(e1, f2) == nil and evo.get(e1, f3) == nil) + assert(evo.get(e2, f1) == 21 and evo.get(e2, f2) == 22 and evo.get(e2, f3) == nil) + assert(evo.get(e3, f1) == 31 and evo.get(e3, f2) == 32 and evo.get(e3, f3) == nil) + assert(evo.get(e4, f1) == 41 and evo.get(e4, f2) == 42 and evo.get(e4, f3) == nil and evo.get(e4, f4) == 44) + + do + local q = evo.query():include(f1):build() + assert(evo.batch_multi_assign(q, {}) == 0) + assert(evo.batch_multi_assign(q, { f5 }) == 0) + end + + do + local q = evo.query():include(f3):build() + + assert(evo.batch_multi_assign(q, { f4 }, { 54 }) == 1) + assert(evo.get(e1, f1) == 11 and evo.get(e1, f2) == nil and evo.get(e1, f3) == nil) + assert(evo.get(e2, f1) == 21 and evo.get(e2, f2) == 22 and evo.get(e2, f3) == nil) + assert(evo.get(e3, f1) == 31 and evo.get(e3, f2) == 32 and evo.get(e3, f3) == nil) + assert(evo.get(e4, f1) == 41 and evo.get(e4, f2) == 42 and evo.get(e4, f3) == nil and evo.get(e4, f4) == 54) + end + + do + local q = evo.query():include(f2):build() + + assert(evo.batch_multi_assign(q, { f1 }, { 51, 52 }) == 3) + assert(evo.get(e1, f1) == 11 and evo.get(e1, f2) == nil and evo.get(e1, f3) == nil) + assert(evo.get(e2, f1) == 51 and evo.get(e2, f2) == 22 and evo.get(e2, f3) == nil) + assert(evo.get(e3, f1) == 51 and evo.get(e3, f2) == 32 and evo.get(e3, f3) == nil) + assert(evo.get(e4, f1) == 51 and evo.get(e4, f2) == 42 and evo.get(e4, f3) == nil and evo.get(e4, f4) == 54) + + assert(evo.batch_multi_assign(q, { f2, f3 }, { 52, 53 }) == 3) + assert(evo.get(e1, f1) == 11 and evo.get(e1, f2) == nil and evo.get(e1, f3) == nil) + assert(evo.get(e2, f1) == 51 and evo.get(e2, f2) == 52 and evo.get(e2, f3) == nil) + assert(evo.get(e3, f1) == 51 and evo.get(e3, f2) == 52 and evo.get(e3, f3) == nil) + assert(evo.get(e4, f1) == 51 and evo.get(e4, f2) == 52 and evo.get(e4, f3) == nil and evo.get(e4, f4) == 54) + end + + do + local q = evo.query():include(f1):build() + + assert(evo.batch_multi_assign(q, { f1 }) == 4) + assert(evo.get(e1, f1) == true and evo.get(e1, f2) == nil and evo.get(e1, f3) == nil) + assert(evo.get(e2, f1) == true and evo.get(e2, f2) == 52 and evo.get(e2, f3) == nil) + assert(evo.get(e3, f1) == true and evo.get(e3, f2) == 52 and evo.get(e3, f3) == nil) + assert(evo.get(e4, f1) == true and evo.get(e4, f2) == 52 and evo.get(e4, f3) == nil and evo.get(e4, f4) == 54) + + assert(evo.batch_multi_assign(q, { f2 }) == 3) + assert(evo.get(e1, f1) == true and evo.get(e1, f2) == nil and evo.get(e1, f3) == nil) + assert(evo.get(e2, f1) == true and evo.get(e2, f2) == 41 and evo.get(e2, f3) == nil) + assert(evo.get(e3, f1) == true and evo.get(e3, f2) == 41 and evo.get(e3, f3) == nil) + assert(evo.get(e4, f1) == true and evo.get(e4, f2) == 41 and evo.get(e4, f3) == nil and evo.get(e4, f4) == 54) + end +end + +do + local f1, f2, f3 = evo.id(3) + + evo.set(f2, evo.DEFAULT, 42) + evo.set(f3, evo.TAG) + + local sum_entity = 0 + local last_assign_entity = 0 + local last_assign_new_component = 0 + local last_assign_old_component = 0 + + evo.set(f1, evo.ON_ASSIGN, function(e, f, nc, oc) + assert(f == f1) + sum_entity = sum_entity + e + last_assign_entity = e + last_assign_new_component = nc + last_assign_old_component = oc + end) + + evo.set(f2, evo.ON_ASSIGN, function(e, f, nc, oc) + assert(f == f2) + sum_entity = sum_entity + e + last_assign_entity = e + last_assign_new_component = nc + last_assign_old_component = oc + end) + + evo.set(f3, evo.ON_ASSIGN, function(e, f, nc, oc) + assert(f == f3) + sum_entity = sum_entity + e + last_assign_entity = e + last_assign_new_component = nc + last_assign_old_component = oc + end) + + local e1 = evo.entity():set(f1, 11):build() + local e2 = evo.entity():set(f1, 21):set(f2, 22):build() + local e3 = evo.entity():set(f1, 31):set(f2, 32):set(f3, 33):build() + + do + local q = evo.query():include(f3):build() + + sum_entity = 0 + last_assign_entity = 0 + last_assign_new_component = 0 + last_assign_old_component = 0 + + assert(evo.batch_multi_assign(q, { f2 }, {}) == 1) + assert(sum_entity == e3) + assert(last_assign_entity == e3) + assert(last_assign_new_component == 42) + assert(last_assign_old_component == 32) + end + + do + local q = evo.query():include(f1):build() + + sum_entity = 0 + last_assign_entity = 0 + last_assign_new_component = 0 + last_assign_old_component = 0 + + assert(evo.batch_multi_assign(q, { f1 }, { 51 }) == 3) + assert(sum_entity == e1 + e2 + e3) + assert(last_assign_entity == e3) + assert(last_assign_new_component == 51) + assert(last_assign_old_component == 31) + end + + do + local q = evo.query():include(f1):build() + + sum_entity = 0 + last_assign_entity = 0 + last_assign_new_component = 0 + last_assign_old_component = 0 + + assert(evo.batch_multi_assign(q, { f1, f1 }, { 61, 61 }) == 3) + assert(sum_entity == e1 + e2 + e3 + e1 + e2 + e3) + assert(last_assign_entity == e3) + assert(last_assign_new_component == 61) + assert(last_assign_old_component == 61) + end + + do + local q = evo.query():include(f1):build() + + sum_entity = 0 + last_assign_entity = 0 + last_assign_new_component = 0 + last_assign_old_component = 0 + + assert(evo.batch_multi_assign(q, { f3 }, { 63 }) == 1) + assert(sum_entity == e3) + assert(last_assign_entity == e3) + assert(last_assign_new_component == nil) + assert(last_assign_old_component == nil) + end +end diff --git a/evolved.lua b/evolved.lua index 80a2311..bb48af6 100644 --- a/evolved.lua +++ b/evolved.lua @@ -1364,7 +1364,108 @@ end ---@param components evolved.component[] ---@return integer assigned_count local function __chunk_multi_assign(chunk, fragments, components) - error('not implemented yet', 2) + if __defer_depth <= 0 then + error('batched chunk operations should be deferred', 2) + end + + local fragment_count = #fragments + + if fragment_count == 0 then + return 0 + end + + if not __chunk_has_any_fragment_list(chunk, fragments) then + return 0 + end + + local chunk_entities = chunk.__entities + local chunk_size = #chunk_entities + + local chunk_fragment_set = chunk.__fragment_set + local chunk_component_indices = chunk.__component_indices + local chunk_component_storages = chunk.__component_storages + + for i = 1, fragment_count do + local fragment = fragments[i] + if chunk_fragment_set[fragment] then + if chunk.__has_set_or_assign_hooks and __fragment_has_set_or_assign_hooks(fragment) then + local component_index = chunk_component_indices[fragment] + if component_index then + local component_storage = chunk_component_storages[component_index] + if chunk.__has_defaults_or_constructs and __fragment_has_default_or_construct(fragment) then + local new_component = components[i] + + if new_component == nil then + new_component = evolved.get(fragment, evolved.DEFAULT) + end + + if new_component == nil then + new_component = true + end + + for place = 1, chunk_size do + local entity = chunk_entities[place] + local old_component = component_storage[place] + component_storage[place] = new_component + __fragment_call_set_and_assign_hooks(entity, fragment, new_component, old_component) + end + else + local new_component = components[i] + + if new_component == nil then + new_component = true + end + + for place = 1, chunk_size do + local entity = chunk_entities[place] + local old_component = component_storage[place] + component_storage[place] = new_component + __fragment_call_set_and_assign_hooks(entity, fragment, new_component, old_component) + end + end + else + for place = 1, chunk_size do + local entity = chunk_entities[place] + __fragment_call_set_and_assign_hooks(entity, fragment) + end + end + else + local component_index = chunk_component_indices[fragment] + if component_index then + local component_storage = chunk_component_storages[component_index] + if chunk.__has_defaults_or_constructs and __fragment_has_default_or_construct(fragment) then + local new_component = components[i] + + if new_component == nil then + new_component = evolved.get(fragment, evolved.DEFAULT) + end + + if new_component == nil then + new_component = true + end + + for place = 1, chunk_size do + component_storage[place] = new_component + end + else + local new_component = components[i] + + if new_component == nil then + new_component = true + end + + for place = 1, chunk_size do + component_storage[place] = new_component + end + end + else + -- nothing + end + end + end + end + + return chunk_size end ---@param chunk evolved.chunk From 5a715f13cca04cf0804943a48f85e091ba240612 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 21 Jan 2025 01:19:16 +0700 Subject: [PATCH 07/14] fix: batch_insert with defaults --- develop/untests.lua | 16 ++++++++++++++++ evolved.lua | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/develop/untests.lua b/develop/untests.lua index 7af682b..04f3a36 100644 --- a/develop/untests.lua +++ b/develop/untests.lua @@ -1262,6 +1262,22 @@ do end do + do + local f1, f2 = evo.id(2) + evo.set(f2, evo.DEFAULT, 42) + + local e1 = evo.entity():set(f1, 11):build() + local e2 = evo.entity():set(f1, 21):set(f2, 22):build() + + assert(evo.get(e1, f1) == 11 and evo.get(e1, f2) == nil) + assert(evo.get(e2, f1) == 21 and evo.get(e2, f2) == 22) + + local q = evo.query():include(f1):build() + assert(evo.batch_insert(q, f2) == 1) + + assert(evo.get(e1, f1) == 11 and evo.get(e1, f2) == 42) + assert(evo.get(e2, f1) == 21 and evo.get(e2, f2) == 22) + end do local f1, f2, f3, f4 = evo.id(4) diff --git a/evolved.lua b/evolved.lua index bb48af6..0068b6e 100644 --- a/evolved.lua +++ b/evolved.lua @@ -1064,7 +1064,7 @@ local function __chunk_insert(chunk, fragment, ...) local new_component_index = new_component_indices[fragment] if new_component_index then local new_component_storage = new_component_storages[new_component_index] - if chunk.__has_defaults_or_constructs and __fragment_has_default_or_construct(fragment) then + if new_chunk.__has_defaults_or_constructs and __fragment_has_default_or_construct(fragment) then for new_place = new_size + 1, new_size + old_size do local entity = new_entities[new_place] local new_component = __component_construct(fragment, ...) @@ -1094,7 +1094,7 @@ local function __chunk_insert(chunk, fragment, ...) local new_component_index = new_component_indices[fragment] if new_component_index then local new_component_storage = new_component_storages[new_component_index] - if chunk.__has_defaults_or_constructs and __fragment_has_default_or_construct(fragment) then + if new_chunk.__has_defaults_or_constructs and __fragment_has_default_or_construct(fragment) then for new_place = new_size + 1, new_size + old_size do local new_component = __component_construct(fragment, ...) new_component_storage[new_place] = new_component From 8eb3ea479be2528b3271a312e6f2cecb926bdcbb Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 21 Jan 2025 03:49:35 +0700 Subject: [PATCH 08/14] fix: raw spawn entity ops should be deferred --- evolved.lua | 365 +++++++++++++++++++++++++++++----------------------- 1 file changed, 201 insertions(+), 164 deletions(-) diff --git a/evolved.lua b/evolved.lua index 0068b6e..dac0d27 100644 --- a/evolved.lua +++ b/evolved.lua @@ -1639,6 +1639,10 @@ end ---@param fragments evolved.fragment[] ---@param components evolved.component[] local function __spawn_entity_at(entity, chunk, fragments, components) + if __defer_depth <= 0 then + error('spawn entity operations should be deferred', 2) + end + local chunk_entities = chunk.__entities local chunk_fragment_list = chunk.__fragment_list local chunk_component_indices = chunk.__component_indices @@ -1730,6 +1734,10 @@ end ---@param fragments evolved.fragment[] ---@param components evolved.component[] local function __spawn_entity_with(entity, chunk, fragments, components) + if __defer_depth <= 0 then + error('spawn entity operations should be deferred', 2) + end + local chunk_entities = chunk.__entities local chunk_fragment_list = chunk.__fragment_list local chunk_component_indices = chunk.__component_indices @@ -1830,170 +1838,7 @@ local __defer_op = { } ---@type table -local __defer_ops = { - [__defer_op.set] = function(bytes, index) - local entity = bytes[index + 0] - local fragment = bytes[index + 1] - local argument_count = bytes[index + 2] - evolved.set(entity, fragment, __table_unpack(bytes, index + 3, index + 2 + argument_count)) - return 3 + argument_count - end, - [__defer_op.assign] = function(bytes, index) - local entity = bytes[index + 0] - local fragment = bytes[index + 1] - local argument_count = bytes[index + 2] - evolved.assign(entity, fragment, __table_unpack(bytes, index + 3, index + 2 + argument_count)) - return 3 + argument_count - end, - [__defer_op.insert] = function(bytes, index) - local entity = bytes[index + 0] - local fragment = bytes[index + 1] - local argument_count = bytes[index + 2] - evolved.insert(entity, fragment, __table_unpack(bytes, index + 3, index + 2 + argument_count)) - return 3 + argument_count - end, - [__defer_op.remove] = function(bytes, index) - local entity = bytes[index + 0] - local fragment_count = bytes[index + 1] - evolved.remove(entity, __table_unpack(bytes, index + 2, index + 1 + fragment_count)) - return 2 + fragment_count - end, - [__defer_op.clear] = function(bytes, index) - local entity = bytes[index + 0] - evolved.clear(entity) - return 1 - end, - [__defer_op.destroy] = function(bytes, index) - local entity = bytes[index + 0] - evolved.destroy(entity) - return 1 - end, - [__defer_op.multi_set] = function(bytes, index) - local entity = bytes[index + 0] - local fragments = bytes[index + 1] - local components = bytes[index + 2] - evolved.multi_set(entity, fragments, components) - __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) - __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, components) - return 3 - end, - [__defer_op.multi_assign] = function(bytes, index) - local entity = bytes[index + 0] - local fragments = bytes[index + 1] - local components = bytes[index + 2] - evolved.multi_assign(entity, fragments, components) - __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) - __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, components) - return 3 - end, - [__defer_op.multi_insert] = function(bytes, index) - local entity = bytes[index + 0] - local fragments = bytes[index + 1] - local components = bytes[index + 2] - evolved.multi_insert(entity, fragments, components) - __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) - __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, components) - return 3 - end, - [__defer_op.multi_remove] = function(bytes, index) - local entity = bytes[index + 0] - local fragments = bytes[index + 1] - evolved.multi_remove(entity, fragments) - __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) - return 2 - end, - [__defer_op.batch_set] = function(bytes, index) - local query = bytes[index + 0] - local fragment = bytes[index + 1] - local argument_count = bytes[index + 2] - evolved.batch_set(query, fragment, __table_unpack(bytes, index + 3, index + 2 + argument_count)) - return 3 + argument_count - end, - [__defer_op.batch_assign] = function(bytes, index) - local query = bytes[index + 0] - local fragment = bytes[index + 1] - local argument_count = bytes[index + 2] - evolved.batch_assign(query, fragment, __table_unpack(bytes, index + 3, index + 2 + argument_count)) - return 3 + argument_count - end, - [__defer_op.batch_insert] = function(bytes, index) - local query = bytes[index + 0] - local fragment = bytes[index + 1] - local argument_count = bytes[index + 2] - evolved.batch_insert(query, fragment, __table_unpack(bytes, index + 3, index + 2 + argument_count)) - return 3 + argument_count - end, - [__defer_op.batch_remove] = function(bytes, index) - local query = bytes[index + 0] - local fragment_count = bytes[index + 1] - evolved.batch_remove(query, __table_unpack(bytes, index + 2, index + 1 + fragment_count)) - return 2 + fragment_count - end, - [__defer_op.batch_clear] = function(bytes, index) - local query = bytes[index + 0] - evolved.batch_clear(query) - return 1 - end, - [__defer_op.batch_destroy] = function(bytes, index) - local query = bytes[index + 0] - evolved.batch_destroy(query) - return 1 - end, - [__defer_op.batch_multi_set] = function(bytes, index) - local query = bytes[index + 0] - local fragments = bytes[index + 1] - local components = bytes[index + 2] - evolved.batch_multi_set(query, fragments, components) - __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) - __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, components) - return 3 - end, - [__defer_op.batch_multi_assign] = function(bytes, index) - local query = bytes[index + 0] - local fragments = bytes[index + 1] - local components = bytes[index + 2] - evolved.batch_multi_assign(query, fragments, components) - __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) - __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, components) - return 3 - end, - [__defer_op.batch_multi_insert] = function(bytes, index) - local query = bytes[index + 0] - local fragments = bytes[index + 1] - local components = bytes[index + 2] - evolved.batch_multi_insert(query, fragments, components) - __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) - __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, components) - return 3 - end, - [__defer_op.batch_multi_remove] = function(bytes, index) - local query = bytes[index + 0] - local fragments = bytes[index + 1] - evolved.batch_multi_remove(query, fragments) - __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) - return 2 - end, - [__defer_op.spawn_entity_at] = function(bytes, index) - local entity = bytes[index + 0] - local chunk = bytes[index + 1] - local fragments = bytes[index + 2] - local components = bytes[index + 3] - __spawn_entity_at(entity, chunk, fragments, components) - __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) - __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, components) - return 4 - end, - [__defer_op.spawn_entity_with] = function(bytes, index) - local entity = bytes[index + 0] - local chunk = bytes[index + 1] - local fragments = bytes[index + 2] - local components = bytes[index + 3] - __spawn_entity_with(entity, chunk, fragments, components) - __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) - __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, components) - return 4 - end, -} +local __defer_ops = {} ---@return boolean started local function __defer() @@ -2054,6 +1899,14 @@ local function __defer_set(entity, fragment, ...) __defer_length = length + 4 + argument_count end +__defer_ops[__defer_op.set] = function(bytes, index) + local entity = bytes[index + 0] + local fragment = bytes[index + 1] + local argument_count = bytes[index + 2] + evolved.set(entity, fragment, __table_unpack(bytes, index + 3, index + 2 + argument_count)) + return 3 + argument_count +end + ---@param entity evolved.entity ---@param fragment evolved.fragment ---@param ... any component arguments @@ -2075,6 +1928,14 @@ local function __defer_assign(entity, fragment, ...) __defer_length = length + 4 + argument_count end +__defer_ops[__defer_op.assign] = function(bytes, index) + local entity = bytes[index + 0] + local fragment = bytes[index + 1] + local argument_count = bytes[index + 2] + evolved.assign(entity, fragment, __table_unpack(bytes, index + 3, index + 2 + argument_count)) + return 3 + argument_count +end + ---@param entity evolved.entity ---@param fragment evolved.fragment ---@param ... any component arguments @@ -2096,6 +1957,14 @@ local function __defer_insert(entity, fragment, ...) __defer_length = length + 4 + argument_count end +__defer_ops[__defer_op.insert] = function(bytes, index) + local entity = bytes[index + 0] + local fragment = bytes[index + 1] + local argument_count = bytes[index + 2] + evolved.insert(entity, fragment, __table_unpack(bytes, index + 3, index + 2 + argument_count)) + return 3 + argument_count +end + ---@param entity evolved.entity ---@param ... evolved.fragment fragments local function __defer_remove(entity, ...) @@ -2116,6 +1985,13 @@ local function __defer_remove(entity, ...) __defer_length = length + 3 + fragment_count end +__defer_ops[__defer_op.remove] = function(bytes, index) + local entity = bytes[index + 0] + local fragment_count = bytes[index + 1] + evolved.remove(entity, __table_unpack(bytes, index + 2, index + 1 + fragment_count)) + return 2 + fragment_count +end + ---@param entity evolved.entity local function __defer_clear(entity) local length = __defer_length @@ -2127,6 +2003,12 @@ local function __defer_clear(entity) __defer_length = length + 2 end +__defer_ops[__defer_op.clear] = function(bytes, index) + local entity = bytes[index + 0] + evolved.clear(entity) + return 1 +end + ---@param entity evolved.entity local function __defer_destroy(entity) local length = __defer_length @@ -2138,6 +2020,12 @@ local function __defer_destroy(entity) __defer_length = length + 2 end +__defer_ops[__defer_op.destroy] = function(bytes, index) + local entity = bytes[index + 0] + evolved.destroy(entity) + return 1 +end + ---@param entity evolved.entity ---@param fragments evolved.fragment[] ---@param components evolved.component[] @@ -2161,6 +2049,16 @@ local function __defer_multi_set(entity, fragments, components) __defer_length = length + 4 end +__defer_ops[__defer_op.multi_set] = function(bytes, index) + local entity = bytes[index + 0] + local fragments = bytes[index + 1] + local components = bytes[index + 2] + evolved.multi_set(entity, fragments, components) + __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) + __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, components) + return 3 +end + ---@param entity evolved.entity ---@param fragments evolved.fragment[] ---@param components evolved.component[] @@ -2184,6 +2082,16 @@ local function __defer_multi_assign(entity, fragments, components) __defer_length = length + 4 end +__defer_ops[__defer_op.multi_assign] = function(bytes, index) + local entity = bytes[index + 0] + local fragments = bytes[index + 1] + local components = bytes[index + 2] + evolved.multi_assign(entity, fragments, components) + __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) + __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, components) + return 3 +end + ---@param entity evolved.entity ---@param fragments evolved.fragment[] ---@param components evolved.component[] @@ -2207,6 +2115,16 @@ local function __defer_multi_insert(entity, fragments, components) __defer_length = length + 4 end +__defer_ops[__defer_op.multi_insert] = function(bytes, index) + local entity = bytes[index + 0] + local fragments = bytes[index + 1] + local components = bytes[index + 2] + evolved.multi_insert(entity, fragments, components) + __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) + __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, components) + return 3 +end + ---@param entity evolved.entity ---@param fragments evolved.fragment[] local function __defer_multi_remove(entity, fragments) @@ -2224,6 +2142,14 @@ local function __defer_multi_remove(entity, fragments) __defer_length = length + 3 end +__defer_ops[__defer_op.multi_remove] = function(bytes, index) + local entity = bytes[index + 0] + local fragments = bytes[index + 1] + evolved.multi_remove(entity, fragments) + __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) + return 2 +end + ---@param query evolved.query ---@param fragment evolved.fragment ---@param ... any component arguments @@ -2245,6 +2171,14 @@ local function __defer_batch_set(query, fragment, ...) __defer_length = length + 4 + argument_count end +__defer_ops[__defer_op.batch_set] = function(bytes, index) + local query = bytes[index + 0] + local fragment = bytes[index + 1] + local argument_count = bytes[index + 2] + evolved.batch_set(query, fragment, __table_unpack(bytes, index + 3, index + 2 + argument_count)) + return 3 + argument_count +end + ---@param query evolved.query ---@param fragment evolved.fragment ---@param ... any component arguments @@ -2266,6 +2200,14 @@ local function __defer_batch_assign(query, fragment, ...) __defer_length = length + 4 + argument_count end +__defer_ops[__defer_op.batch_assign] = function(bytes, index) + local query = bytes[index + 0] + local fragment = bytes[index + 1] + local argument_count = bytes[index + 2] + evolved.batch_assign(query, fragment, __table_unpack(bytes, index + 3, index + 2 + argument_count)) + return 3 + argument_count +end + ---@param query evolved.query ---@param fragment evolved.fragment ---@param ... any component arguments @@ -2287,6 +2229,14 @@ local function __defer_batch_insert(query, fragment, ...) __defer_length = length + 4 + argument_count end +__defer_ops[__defer_op.batch_insert] = function(bytes, index) + local query = bytes[index + 0] + local fragment = bytes[index + 1] + local argument_count = bytes[index + 2] + evolved.batch_insert(query, fragment, __table_unpack(bytes, index + 3, index + 2 + argument_count)) + return 3 + argument_count +end + ---@param query evolved.query ---@param ... evolved.fragment fragments local function __defer_batch_remove(query, ...) @@ -2307,6 +2257,13 @@ local function __defer_batch_remove(query, ...) __defer_length = length + 3 + fragment_count end +__defer_ops[__defer_op.batch_remove] = function(bytes, index) + local query = bytes[index + 0] + local fragment_count = bytes[index + 1] + evolved.batch_remove(query, __table_unpack(bytes, index + 2, index + 1 + fragment_count)) + return 2 + fragment_count +end + ---@param query evolved.query local function __defer_batch_clear(query) local length = __defer_length @@ -2318,6 +2275,12 @@ local function __defer_batch_clear(query) __defer_length = length + 2 end +__defer_ops[__defer_op.batch_clear] = function(bytes, index) + local query = bytes[index + 0] + evolved.batch_clear(query) + return 1 +end + ---@param query evolved.query local function __defer_batch_destroy(query) local length = __defer_length @@ -2329,6 +2292,12 @@ local function __defer_batch_destroy(query) __defer_length = length + 2 end +__defer_ops[__defer_op.batch_destroy] = function(bytes, index) + local query = bytes[index + 0] + evolved.batch_destroy(query) + return 1 +end + ---@param query evolved.query ---@param fragments evolved.fragment[] ---@param components evolved.component[] @@ -2352,6 +2321,16 @@ local function __defer_batch_multi_set(query, fragments, components) __defer_length = length + 4 end +__defer_ops[__defer_op.batch_multi_set] = function(bytes, index) + local query = bytes[index + 0] + local fragments = bytes[index + 1] + local components = bytes[index + 2] + evolved.batch_multi_set(query, fragments, components) + __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) + __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, components) + return 3 +end + ---@param query evolved.query ---@param fragments evolved.fragment[] ---@param components evolved.component[] @@ -2375,6 +2354,16 @@ local function __defer_batch_multi_assign(query, fragments, components) __defer_length = length + 4 end +__defer_ops[__defer_op.batch_multi_assign] = function(bytes, index) + local query = bytes[index + 0] + local fragments = bytes[index + 1] + local components = bytes[index + 2] + evolved.batch_multi_assign(query, fragments, components) + __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) + __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, components) + return 3 +end + ---@param query evolved.query ---@param fragments evolved.fragment[] ---@param components evolved.component[] @@ -2398,6 +2387,16 @@ local function __defer_batch_multi_insert(query, fragments, components) __defer_length = length + 4 end +__defer_ops[__defer_op.batch_multi_insert] = function(bytes, index) + local query = bytes[index + 0] + local fragments = bytes[index + 1] + local components = bytes[index + 2] + evolved.batch_multi_insert(query, fragments, components) + __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) + __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, components) + return 3 +end + ---@param query evolved.query ---@param fragments evolved.fragment[] local function __defer_batch_multi_remove(query, fragments) @@ -2415,6 +2414,14 @@ local function __defer_batch_multi_remove(query, fragments) __defer_length = length + 3 end +__defer_ops[__defer_op.batch_multi_remove] = function(bytes, index) + local query = bytes[index + 0] + local fragments = bytes[index + 1] + evolved.batch_multi_remove(query, fragments) + __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) + return 2 +end + ---@param entity evolved.entity ---@param chunk evolved.chunk ---@param fragments evolved.fragment[] @@ -2440,6 +2447,21 @@ local function __defer_spawn_entity_at(entity, chunk, fragments, components) __defer_length = length + 5 end +__defer_ops[__defer_op.spawn_entity_at] = function(bytes, index) + local entity = bytes[index + 0] + local chunk = bytes[index + 1] + local fragments = bytes[index + 2] + local components = bytes[index + 3] + __defer() + do + __spawn_entity_at(entity, chunk, fragments, components) + __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) + __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, components) + end + __defer_commit() + return 4 +end + ---@param entity evolved.entity ---@param chunk evolved.chunk ---@param fragments evolved.fragment[] @@ -2465,6 +2487,21 @@ local function __defer_spawn_entity_with(entity, chunk, fragments, components) __defer_length = length + 5 end +__defer_ops[__defer_op.spawn_entity_with] = function(bytes, index) + local entity = bytes[index + 0] + local chunk = bytes[index + 1] + local fragments = bytes[index + 2] + local components = bytes[index + 3] + __defer() + do + __spawn_entity_with(entity, chunk, fragments, components) + __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) + __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, components) + end + __defer_commit() + return 4 +end + --- --- --- From 1197b55cfb167a752fe986e89b1113fe889b3b76 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 21 Jan 2025 03:51:12 +0700 Subject: [PATCH 09/14] batch-multi-api: batch_multi_insert impl --- develop/untests.lua | 255 ++++++++++++++++++++++++++++++++++++++++++++ evolved.lua | 147 ++++++++++++++++++++++++- 2 files changed, 401 insertions(+), 1 deletion(-) diff --git a/develop/untests.lua b/develop/untests.lua index 04f3a36..b557f5d 100644 --- a/develop/untests.lua +++ b/develop/untests.lua @@ -4084,3 +4084,258 @@ do assert(last_assign_old_component == nil) end end + +do + local f1, f2, f3, f4 = evo.id(4) + + local e1 = evo.entity():set(f1, 11):build() + local e2 = evo.entity():set(f1, 21):set(f2, 22):build() + local e3 = evo.entity():set(f1, 31):set(f2, 32):set(f3, 33):build() + local e4 = evo.entity():set(f1, 41):set(f2, 42):set(f3, 43):set(f4, 44):build() + + do + local q = evo.query():include(f1):build() + + assert(evo.batch_multi_insert(q, {}) == 0) + assert(evo.batch_multi_insert(q, { f1 }) == 0) + end + + do + local q = evo.query():include(f3):build() + + assert(evo.batch_multi_insert(q, { f4 }) == 1) + assert(evo.get(e3, f1) == 31 and evo.get(e3, f2) == 32 and evo.get(e3, f3) == 33 and evo.get(e3, f4) == true) + assert(evo.get(e4, f1) == 41 and evo.get(e4, f2) == 42 and evo.get(e4, f3) == 43 and evo.get(e4, f4) == 44) + + do + local c123, c123_es = evo.chunk(f1, f2, f3) + assert(c123 and #c123_es == 0) + assert(#evo.select(c123, f1) == 0) + assert(#evo.select(c123, f2) == 0) + assert(#evo.select(c123, f3) == 0) + + local c1234, c1234_es = evo.chunk(f1, f2, f3, f4) + assert(c1234 and #c1234_es == 2) + assert(#evo.select(c1234, f1) == 2) + assert(#evo.select(c1234, f2) == 2) + assert(#evo.select(c1234, f3) == 2) + assert(#evo.select(c1234, f4) == 2) + end + end + + do + local q = evo.query():include(f1):build() + + assert(evo.batch_multi_insert(q, { f3, f4 }, { 53, 54 }) == 2) + assert(evo.get(e1, f1) == 11 and evo.get(e1, f2) == nil and evo.get(e1, f3) == 53 and evo.get(e1, f4) == 54) + assert(evo.get(e2, f1) == 21 and evo.get(e2, f2) == 22 and evo.get(e2, f3) == 53 and evo.get(e2, f4) == 54) + assert(evo.get(e3, f1) == 31 and evo.get(e3, f2) == 32 and evo.get(e3, f3) == 33 and evo.get(e3, f4) == true) + assert(evo.get(e4, f1) == 41 and evo.get(e4, f2) == 42 and evo.get(e4, f3) == 43 and evo.get(e4, f4) == 44) + + do + local c1, c1_es = evo.chunk(f1) + assert(c1 and #c1_es == 0) + assert(#evo.select(c1, f1) == 0) + end + + do + local c12, c12_es = evo.chunk(f1, f2) + assert(c12 and #c12_es == 0) + assert(#evo.select(c12, f1) == 0) + assert(#evo.select(c12, f2) == 0) + end + + do + local c134, c134_es = evo.chunk(f1, f3, f4) + assert(c134 and #c134_es == 1) + assert(#evo.select(c134, f1) == 1) + assert(#evo.select(c134, f3) == 1) + assert(#evo.select(c134, f4) == 1) + end + + do + local c1234, c1234_es = evo.chunk(f1, f2, f3, f4) + assert(c1234 and #c1234_es == 3) + assert(#evo.select(c1234, f1) == 3) + assert(#evo.select(c1234, f2) == 3) + assert(#evo.select(c1234, f3) == 3) + assert(#evo.select(c1234, f4) == 3) + end + end +end + +do + local f1, f2, f3, f4 = evo.id(4) + + evo.set(f2, evo.DEFAULT, 41) + evo.set(f3, evo.TAG) + + local e1 = evo.entity():set(f1, 11):build() + local e2 = evo.entity():set(f1, 21):set(f2, 22):build() + local e3 = evo.entity():set(f1, 31):set(f2, 32):set(f3, 33):build() + local e4 = evo.entity():set(f1, 41):set(f2, 42):set(f3, 43):set(f4, 44):build() + + assert(evo.get(e1, f1) == 11 and evo.get(e1, f2) == nil and evo.get(e1, f3) == nil) + assert(evo.get(e2, f1) == 21 and evo.get(e2, f2) == 22 and evo.get(e2, f3) == nil) + assert(evo.get(e3, f1) == 31 and evo.get(e3, f2) == 32 and evo.get(e3, f3) == nil) + assert(evo.get(e4, f1) == 41 and evo.get(e4, f2) == 42 and evo.get(e4, f3) == nil and evo.get(e4, f4) == 44) + + do + local q = evo.query():include(f1):build() + assert(evo.batch_multi_insert(q, {}) == 0) + end + + do + local q = evo.query():include(f1):build() + assert(evo.batch_multi_insert(q, { f2 }) == 1) + + assert(evo.get(e1, f1) == 11 and evo.get(e1, f2) == 41 and evo.get(e1, f3) == nil) + assert(evo.get(e2, f1) == 21 and evo.get(e2, f2) == 22 and evo.get(e2, f3) == nil) + assert(evo.get(e3, f1) == 31 and evo.get(e3, f2) == 32 and evo.get(e3, f3) == nil) + assert(evo.get(e4, f1) == 41 and evo.get(e4, f2) == 42 and evo.get(e4, f3) == nil and evo.get(e4, f4) == 44) + end +end + +do + local f1, f2, f3, f4, f5 = evo.id(5) + + evo.set(f2, evo.DEFAULT, 42) + evo.set(f3, evo.TAG) + + local sum_entity = 0 + local last_insert_entity = 0 + local last_insert_component = 0 + + evo.set(f1, evo.ON_INSERT, function(e, f, c) + assert(f == f1) + sum_entity = sum_entity + e + last_insert_entity = e + last_insert_component = c + end) + + evo.set(f2, evo.ON_INSERT, function(e, f, c) + assert(f == f2) + sum_entity = sum_entity + e + last_insert_entity = e + last_insert_component = c + end) + + evo.set(f3, evo.ON_INSERT, function(e, f, c) + assert(f == f3) + sum_entity = sum_entity + e + last_insert_entity = e + last_insert_component = c + end) + + evo.set(f4, evo.ON_INSERT, function(e, f, c) + assert(f == f4) + sum_entity = sum_entity + e + last_insert_entity = e + last_insert_component = c + end) + + evo.set(f5, evo.ON_INSERT, function(e, f, c) + assert(f == f5) + sum_entity = sum_entity + e + last_insert_entity = e + last_insert_component = c + end) + + local e1 = evo.entity():set(f1, 11):build() + local e2 = evo.entity():set(f1, 21):set(f2, 22):build() + local e3 = evo.entity():set(f1, 31):set(f2, 32):set(f3, 33):build() + + do + local q = evo.query():include(f1):build() + + sum_entity = 0 + last_insert_entity = 0 + last_insert_component = 0 + + assert(evo.batch_multi_insert(q, { f2 }) == 1) + assert(sum_entity == e1) + assert(last_insert_entity == e1) + assert(last_insert_component == 42) + assert(evo.has(e1, f1) and evo.has(e1, f2) and not evo.has(e1, f3)) + assert(evo.has(e2, f1) and evo.has(e2, f2) and not evo.has(e2, f3)) + assert(evo.has(e3, f1) and evo.has(e3, f2) and evo.has(e3, f3)) + assert(evo.get(e1, f1) == 11 and evo.get(e1, f2) == 42 and evo.get(e1, f3) == nil) + assert(evo.get(e2, f1) == 21 and evo.get(e2, f2) == 22 and evo.get(e2, f3) == nil) + assert(evo.get(e3, f1) == 31 and evo.get(e3, f2) == 32 and evo.get(e3, f3) == nil) + end + + do + local q = evo.query():include(f2):build() + + sum_entity = 0 + last_insert_entity = 0 + last_insert_component = 0 + + assert(evo.batch_multi_insert(q, { f3 }) == 2) + assert(sum_entity == e1 + e2) + assert(last_insert_entity == e1) + assert(last_insert_component == nil) + assert(evo.has(e1, f1) and evo.has(e1, f2) and evo.has(e1, f3)) + assert(evo.has(e2, f1) and evo.has(e2, f2) and evo.has(e2, f3)) + assert(evo.has(e3, f1) and evo.has(e3, f2) and evo.has(e3, f3)) + assert(evo.get(e1, f1) == 11 and evo.get(e1, f2) == 42 and evo.get(e1, f3) == nil) + assert(evo.get(e2, f1) == 21 and evo.get(e2, f2) == 22 and evo.get(e2, f3) == nil) + assert(evo.get(e3, f1) == 31 and evo.get(e3, f2) == 32 and evo.get(e3, f3) == nil) + end + + do + local q = evo.query():include(f1, f2, f3):build() + + sum_entity = 0 + last_insert_entity = 0 + last_insert_component = 0 + + assert(evo.batch_multi_insert(q, { f3, f4, f5, f5 }, { 53, 54, 55, 65 }) == 3) + assert(sum_entity == e1 + e2 + e3 + e1 + e2 + e3) + assert(last_insert_entity == e1) + assert(last_insert_component == 55) + assert(evo.has_all(e1, f1, f2, f3, f4, f5)) + assert(evo.has_all(e2, f1, f2, f3, f4, f5)) + assert(evo.has_all(e3, f1, f2, f3, f4, f5)) + assert(evo.get(e1, f1) == 11 and evo.get(e1, f2) == 42 and evo.get(e1, f3) == nil) + assert(evo.get(e2, f1) == 21 and evo.get(e2, f2) == 22 and evo.get(e2, f3) == nil) + assert(evo.get(e3, f1) == 31 and evo.get(e3, f2) == 32 and evo.get(e3, f3) == nil) + assert(evo.get(e1, f4) == 54 and evo.get(e1, f5) == 55) + assert(evo.get(e2, f4) == 54 and evo.get(e2, f5) == 55) + assert(evo.get(e3, f4) == 54 and evo.get(e3, f5) == 55) + end +end + +do + local f1, f2, f3 = evo.id(3) + + local e1 = evo.entity():set(f1, 11):build() + local e2 = evo.entity():set(f1, 21):set(f2, 22):build() + local e3 = evo.entity():set(f1, 31):set(f2, 32):set(f3, 33):build() + + assert(evo.defer()) + do + local q = evo.query():include(f1):build() + do + local n, d = evo.batch_multi_insert(q, { f2 }, { 42 }) + assert(n == 0 and d == true) + end + do + local n, d = evo.batch_multi_assign(q, { f3 }, { 43 }) + assert(n == 0 and d == true) + end + do + local n, d = evo.batch_multi_remove(q, { f1 }) + assert(n == 0 and d == true) + end + assert(evo.get(e1, f1) == 11 and evo.get(e1, f2) == nil and evo.get(e1, f3) == nil) + assert(evo.get(e2, f1) == 21 and evo.get(e2, f2) == 22 and evo.get(e2, f3) == nil) + assert(evo.get(e3, f1) == 31 and evo.get(e3, f2) == 32 and evo.get(e3, f3) == 33) + end + assert(evo.commit()) + do + assert(evo.get(e1, f1) == nil and evo.get(e1, f2) == 42 and evo.get(e1, f3) == nil) + assert(evo.get(e2, f1) == nil and evo.get(e2, f2) == 22 and evo.get(e2, f3) == nil) + assert(evo.get(e3, f1) == nil and evo.get(e3, f2) == 32 and evo.get(e3, f3) == 43) + end +end diff --git a/evolved.lua b/evolved.lua index dac0d27..6106a5e 100644 --- a/evolved.lua +++ b/evolved.lua @@ -1473,7 +1473,152 @@ end ---@param components evolved.component[] ---@return integer inserted_count local function __chunk_multi_insert(chunk, fragments, components) - error('not implemented yet', 2) + if __defer_depth <= 0 then + error('batched chunk operations should be deferred', 2) + end + + local fragment_count = #fragments + + if fragment_count == 0 then + return 0 + end + + local old_chunk = chunk + local new_chunk = __chunk_with_fragment_list(chunk, fragments) + + if not new_chunk or old_chunk == new_chunk then + return 0 + end + + local old_entities = old_chunk.__entities + local old_size = #old_entities + + local old_fragment_set = old_chunk.__fragment_set + local old_component_storages = old_chunk.__component_storages + local old_component_fragments = old_chunk.__component_fragments + + local new_entities = new_chunk.__entities + local new_size = #new_entities + + local new_component_indices = new_chunk.__component_indices + local new_component_storages = new_chunk.__component_storages + + do + __table_move( + old_entities, 1, old_size, + new_size + 1, new_entities) + + for i = 1, #old_component_fragments do + local old_f = old_component_fragments[i] + local old_cs = old_component_storages[i] + local new_ci = new_component_indices[old_f] + if new_ci then + local new_cs = new_component_storages[new_ci] + __table_move(old_cs, 1, old_size, new_size + 1, new_cs) + end + end + end + + local inserted_set = __acquire_table(__TABLE_POOL_TAG__FRAGMENT_SET, 0, fragment_count) + + for i = 1, fragment_count do + local fragment = fragments[i] + if not inserted_set[fragment] and not old_fragment_set[fragment] then + inserted_set[fragment] = true + if new_chunk.__has_set_or_insert_hooks and __fragment_has_set_or_insert_hooks(fragment) then + local new_component_index = new_component_indices[fragment] + if new_component_index then + local new_component_storage = new_component_storages[new_component_index] + if new_chunk.__has_defaults_or_constructs and __fragment_has_default_or_construct(fragment) then + local new_component = components[i] + + if new_component == nil then + new_component = evolved.get(fragment, evolved.DEFAULT) + end + + if new_component == nil then + new_component = true + end + + for new_place = new_size + 1, new_size + old_size do + local entity = new_entities[new_place] + new_component_storage[new_place] = new_component + __fragment_call_set_and_insert_hooks(entity, fragment, new_component) + end + else + local new_component = components[i] + + if new_component == nil then + new_component = true + end + + for new_place = new_size + 1, new_size + old_size do + local entity = new_entities[new_place] + new_component_storage[new_place] = new_component + __fragment_call_set_and_insert_hooks(entity, fragment, new_component) + end + end + else + for new_place = new_size + 1, new_size + old_size do + local entity = new_entities[new_place] + __fragment_call_set_and_insert_hooks(entity, fragment) + end + end + else + local new_component_index = new_component_indices[fragment] + if new_component_index then + local new_component_storage = new_component_storages[new_component_index] + if new_chunk.__has_defaults_or_constructs and __fragment_has_default_or_construct(fragment) then + local new_component = components[i] + + if new_component == nil then + new_component = evolved.get(fragment, evolved.DEFAULT) + end + + if new_component == nil then + new_component = true + end + + for new_place = new_size + 1, new_size + old_size do + new_component_storage[new_place] = new_component + end + else + local new_component = components[i] + + if new_component == nil then + new_component = true + end + + for new_place = new_size + 1, new_size + old_size do + new_component_storage[new_place] = new_component + end + end + else + -- nothing + end + end + end + end + + __release_table(__TABLE_POOL_TAG__FRAGMENT_SET, inserted_set) + + for new_place = new_size + 1, new_size + old_size do + local entity = new_entities[new_place] + local entity_index = entity % 0x100000 + __entity_chunks[entity_index] = new_chunk + __entity_places[entity_index] = new_place + end + + do + old_chunk.__entities = {} + + for i = 1, #old_component_storages do + old_component_storages[i] = {} + end + end + + __structural_changes = __structural_changes + old_size + return old_size end ---@param chunk evolved.chunk From 9e532cce95e4f02cf7e6cfc1fc1f005e54df0ae4 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 21 Jan 2025 08:50:24 +0700 Subject: [PATCH 10/14] batch-multi-api: batch_multi_set impl --- develop/untests.lua | 377 +++++++++++++++++++++++++++++++++++++++++++- evolved.lua | 304 ++++++++++++++++++++++++++++++++++- 2 files changed, 679 insertions(+), 2 deletions(-) diff --git a/develop/untests.lua b/develop/untests.lua index b557f5d..4de4ea9 100644 --- a/develop/untests.lua +++ b/develop/untests.lua @@ -4307,7 +4307,368 @@ do end do - local f1, f2, f3 = evo.id(3) + local f1, f2, f3, f4 = evo.id(4) + + evo.set(f2, evo.DEFAULT, 52) + evo.set(f4, evo.TAG) + + local e1a = evo.entity():set(f1, 11):build() + local e1b = evo.entity():set(f1, 11):build() + + local e2a = evo.entity():set(f1, 21):set(f2, 22):build() + local e2b = evo.entity():set(f1, 21):set(f2, 22):build() + + local e3a = evo.entity():set(f1, 31):set(f2, 32):set(f3, 33):build() + local e3b = evo.entity():set(f1, 31):set(f2, 32):set(f3, 33):build() + + local e4a = evo.entity():set(f1, 41):set(f2, 42):set(f3, 43):set(f4, 44):build() + local e4b = evo.entity():set(f1, 41):set(f2, 42):set(f3, 43):set(f4, 44):build() + + do + local q = evo.query():include(f1):build() + assert(evo.batch_multi_set(q, {}) == 0) + end + + do + local q = evo.query():include(f3):exclude(f4):build() + assert(evo.batch_multi_set(q, { f3 }) == 2) + assert(evo.get(e1a, f1) == 11 and evo.get(e1a, f2) == nil and evo.get(e1a, f3) == nil) + assert(evo.get(e1b, f1) == 11 and evo.get(e1b, f2) == nil and evo.get(e1b, f3) == nil) + assert(evo.get(e2a, f1) == 21 and evo.get(e2a, f2) == 22 and evo.get(e2a, f3) == nil) + assert(evo.get(e2b, f1) == 21 and evo.get(e2b, f2) == 22 and evo.get(e2b, f3) == nil) + assert(evo.get(e3a, f1) == 31 and evo.get(e3a, f2) == 32 and evo.get(e3a, f3) == true) + assert(evo.get(e3b, f1) == 31 and evo.get(e3b, f2) == 32 and evo.get(e3b, f3) == true) + assert(evo.get(e4a, f1) == 41 and evo.get(e4a, f2) == 42 and evo.get(e4a, f3) == 43 and evo.get(e4a, f4) == nil) + assert(evo.get(e4b, f1) == 41 and evo.get(e4b, f2) == 42 and evo.get(e4b, f3) == 43 and evo.get(e4b, f4) == nil) + end + + do + local q = evo.query():include(f3):exclude(f4):build() + assert(evo.batch_multi_set(q, { f3 }, { 43, 44 }) == 2) + assert(evo.get(e1a, f1) == 11 and evo.get(e1a, f2) == nil and evo.get(e1a, f3) == nil) + assert(evo.get(e1b, f1) == 11 and evo.get(e1b, f2) == nil and evo.get(e1b, f3) == nil) + assert(evo.get(e2a, f1) == 21 and evo.get(e2a, f2) == 22 and evo.get(e2a, f3) == nil) + assert(evo.get(e2b, f1) == 21 and evo.get(e2b, f2) == 22 and evo.get(e2b, f3) == nil) + assert(evo.get(e3a, f1) == 31 and evo.get(e3a, f2) == 32 and evo.get(e3a, f3) == 43) + assert(evo.get(e3b, f1) == 31 and evo.get(e3b, f2) == 32 and evo.get(e3b, f3) == 43) + assert(evo.get(e4a, f1) == 41 and evo.get(e4a, f2) == 42 and evo.get(e4a, f3) == 43 and evo.get(e4a, f4) == nil) + assert(evo.get(e4b, f1) == 41 and evo.get(e4b, f2) == 42 and evo.get(e4b, f3) == 43 and evo.get(e4b, f4) == nil) + end + + do + local q = evo.query():include(f2):exclude(f3, f4):build() + assert(evo.batch_multi_set(q, { f2 }, {}) == 2) + assert(evo.get(e1a, f1) == 11 and evo.get(e1a, f2) == nil and evo.get(e1a, f3) == nil) + assert(evo.get(e1b, f1) == 11 and evo.get(e1b, f2) == nil and evo.get(e1b, f3) == nil) + assert(evo.get(e2a, f1) == 21 and evo.get(e2a, f2) == 52 and evo.get(e2a, f3) == nil) + assert(evo.get(e2b, f1) == 21 and evo.get(e2b, f2) == 52 and evo.get(e2b, f3) == nil) + assert(evo.get(e3a, f1) == 31 and evo.get(e3a, f2) == 32 and evo.get(e3a, f3) == 43) + assert(evo.get(e3b, f1) == 31 and evo.get(e3b, f2) == 32 and evo.get(e3b, f3) == 43) + assert(evo.get(e4a, f1) == 41 and evo.get(e4a, f2) == 42 and evo.get(e4a, f3) == 43 and evo.get(e4a, f4) == nil) + assert(evo.get(e4b, f1) == 41 and evo.get(e4b, f2) == 42 and evo.get(e4b, f3) == 43 and evo.get(e4b, f4) == nil) + end + + do + local q = evo.query():include(f2):exclude(f3, f4):build() + assert(evo.batch_multi_set(q, { f2 }, { 62, 63 }) == 2) + assert(evo.get(e1a, f1) == 11 and evo.get(e1a, f2) == nil and evo.get(e1a, f3) == nil) + assert(evo.get(e1b, f1) == 11 and evo.get(e1b, f2) == nil and evo.get(e1b, f3) == nil) + assert(evo.get(e2a, f1) == 21 and evo.get(e2a, f2) == 62 and evo.get(e2a, f3) == nil) + assert(evo.get(e2b, f1) == 21 and evo.get(e2b, f2) == 62 and evo.get(e2b, f3) == nil) + assert(evo.get(e3a, f1) == 31 and evo.get(e3a, f2) == 32 and evo.get(e3a, f3) == 43) + assert(evo.get(e3b, f1) == 31 and evo.get(e3b, f2) == 32 and evo.get(e3b, f3) == 43) + assert(evo.get(e4a, f1) == 41 and evo.get(e4a, f2) == 42 and evo.get(e4a, f3) == 43 and evo.get(e4a, f4) == nil) + assert(evo.get(e4b, f1) == 41 and evo.get(e4b, f2) == 42 and evo.get(e4b, f3) == 43 and evo.get(e4b, f4) == nil) + end +end + +do + local fc = evo.id() + evo.set(fc, evo.TAG) + + local f1, f2, f3, f4 = evo.id(4) + + evo.set(f2, evo.DEFAULT, 52) + evo.set(f4, evo.TAG) + + evo.set(f1, fc) + evo.set(f2, fc) + evo.set(f3, fc) + evo.set(f4, fc) + + local sum_entity = 0 + local last_assign_entity = 0 + local last_assign_component = 0 + + do + local q = evo.query():include(fc):build() + evo.batch_set(q, evo.ON_ASSIGN, function(e, f, c) + assert(f == f1 or f == f2 or f == f3 or f == f4) + sum_entity = sum_entity + e + last_assign_entity = e + last_assign_component = c + end) + end + + local e2a = evo.entity():set(f1, 21):set(f2, 22):build() + local e2b = evo.entity():set(f1, 21):set(f2, 22):build() + + local e3a = evo.entity():set(f1, 31):set(f2, 32):set(f3, 33):build() + local e3b = evo.entity():set(f1, 31):set(f2, 32):set(f3, 33):build() + + local e4a = evo.entity():set(f1, 41):set(f2, 42):set(f3, 43):set(f4, 44):build() + local e4b = evo.entity():set(f1, 41):set(f2, 42):set(f3, 43):set(f4, 44):build() + + do + local q = evo.query():include(f1):build() + assert(evo.batch_multi_set(q, {}) == 0) + end + + do + local q = evo.query():include(f2):exclude(f3, f4):build() + + sum_entity = 0 + last_assign_entity = 0 + last_assign_component = 0 + + assert(evo.batch_multi_set(q, { f2 }) == 2) + assert(sum_entity == e2a + e2b) + assert(last_assign_entity == e2b) + assert(last_assign_component == 52) + assert(evo.get(e2a, f2) == 52 and evo.get(e2b, f2) == 52) + + sum_entity = 0 + last_assign_entity = 0 + last_assign_component = 0 + + assert(evo.batch_multi_set(q, { f2, f2 }) == 2) + assert(sum_entity == e2a + e2b + e2a + e2b) + assert(last_assign_entity == e2b) + assert(last_assign_component == 52) + assert(evo.get(e2a, f2) == 52 and evo.get(e2b, f2) == 52) + end + + do + local q = evo.query():include(f2):exclude(f3, f4):build() + + sum_entity = 0 + last_assign_entity = 0 + last_assign_component = 0 + + assert(evo.batch_multi_set(q, { f2 }, { 62, 63 }) == 2) + assert(sum_entity == e2a + e2b) + assert(last_assign_entity == e2b) + assert(last_assign_component == 62) + assert(evo.get(e2a, f2) == 62 and evo.get(e2b, f2) == 62) + + sum_entity = 0 + last_assign_entity = 0 + last_assign_component = 0 + + assert(evo.batch_multi_set(q, { f2, f2 }, { 62, 63 }) == 2) + assert(sum_entity == e2a + e2b + e2a + e2b) + assert(last_assign_entity == e2b) + assert(last_assign_component == 63) + assert(evo.get(e2a, f2) == 63 and evo.get(e2b, f2) == 63) + end + + do + local q = evo.query():include(f3):exclude(f4):build() + + sum_entity = 0 + last_assign_entity = 0 + last_assign_component = 0 + + assert(evo.batch_multi_set(q, { f3 }) == 2) + assert(sum_entity == e3a + e3b) + assert(last_assign_entity == e3b) + assert(last_assign_component == true) + assert(evo.get(e3a, f3) == true and evo.get(e3b, f3) == true) + end + + do + local q = evo.query():include(f4):build() + + sum_entity = 0 + last_assign_entity = 0 + last_assign_component = 0 + + assert(evo.batch_multi_set(q, { f4 }, { 62, 63 }) == 2) + assert(sum_entity == e4a + e4b) + assert(last_assign_entity == e4b) + assert(last_assign_component == nil) + assert(evo.has(e4a, f4) and evo.has(e4b, f4)) + assert(evo.get(e4a, f4) == nil and evo.get(e4b, f4) == nil) + + sum_entity = 0 + last_assign_entity = 0 + last_assign_component = 0 + + assert(evo.batch_multi_set(q, { f4, f4 }, { 62, 63 }) == 2) + assert(sum_entity == e4a + e4b + e4a + e4b) + assert(last_assign_entity == e4b) + assert(last_assign_component == nil) + assert(evo.get(e2a, f4) == nil and evo.get(e2b, f4) == nil) + end +end + +do + local f1, f2, f3, f4 = evo.id(4) + + evo.set(f2, evo.DEFAULT, 52) + evo.set(f4, evo.TAG) + + local e1a = evo.entity():set(f1, 11):build() + local e1b = evo.entity():set(f1, 11):build() + + local e2a = evo.entity():set(f1, 21):set(f2, 22):build() + local e2b = evo.entity():set(f1, 21):set(f2, 22):build() + + local e3a = evo.entity():set(f1, 31):set(f2, 32):set(f3, 33):build() + local e3b = evo.entity():set(f1, 31):set(f2, 32):set(f3, 33):build() + + do + local q = evo.query():include(f2):exclude(f3, f4):build() + assert(evo.batch_multi_set(q, { f3 }) == 2) + assert(evo.get(e2a, f1) == 21 and evo.get(e2a, f2) == 22 and evo.get(e2a, f3) == true) + assert(evo.get(e2b, f1) == 21 and evo.get(e2b, f2) == 22 and evo.get(e2b, f3) == true) + assert(evo.get(e3a, f1) == 31 and evo.get(e3a, f2) == 32 and evo.get(e3a, f3) == 33) + assert(evo.get(e3b, f1) == 31 and evo.get(e3b, f2) == 32 and evo.get(e3b, f3) == 33) + do + local c12, c12_es = evo.chunk(f1, f2) + assert(c12 and #c12_es == 0) + assert(#evo.select(c12, f1) == 0) + assert(#evo.select(c12, f2) == 0) + + local c123, c123_es = evo.chunk(f1, f2, f3) + assert(c123 and #c123_es == 4) + assert(#evo.select(c123, f1) == 4) + assert(#evo.select(c123, f2) == 4) + assert(#evo.select(c123, f3) == 4) + end + end + + do + local q = evo.query():include(f2, f3):exclude(f4):build() + assert(evo.batch_multi_set(q, { f2, f3, f4, f4 }, { 62, 63, 64, 65 }) == 4) + assert(evo.has_all(e2a, f2, f3, f4) and evo.has_all(e2b, f2, f3, f4)) + assert(evo.get(e2a, f1) == 21 and evo.get(e2a, f2) == 62 and evo.get(e2a, f3) == 63 and evo.get(e2a, f4) == nil) + assert(evo.get(e2b, f1) == 21 and evo.get(e2b, f2) == 62 and evo.get(e2b, f3) == 63 and evo.get(e2b, f4) == nil) + assert(evo.get(e3a, f1) == 31 and evo.get(e3a, f2) == 62 and evo.get(e3a, f3) == 63 and evo.get(e3a, f4) == nil) + assert(evo.get(e3b, f1) == 31 and evo.get(e3b, f2) == 62 and evo.get(e3b, f3) == 63 and evo.get(e3b, f4) == nil) + end + + do + local q = evo.query():include(f1):exclude(f2, f3, f4):build() + assert(evo.batch_multi_set(q, { f2, f1 }, { nil, 71 }) == 2) + assert(evo.get(e1a, f1) == 71 and evo.get(e1a, f2) == 52) + assert(evo.get(e1b, f1) == 71 and evo.get(e1b, f2) == 52) + do + local c1, c1_es = evo.chunk(f1) + assert(c1 and #c1_es == 0) + assert(#evo.select(c1, f1) == 0) + + local c12, c12_es = evo.chunk(f1, f2) + assert(c12 and #c12_es == 2) + assert(#evo.select(c12, f1) == 2) + assert(#evo.select(c12, f2) == 2) + end + end +end + +do + local fc = evo.id() + evo.set(fc, evo.TAG) + + local f0, f1, f2, f3, f4 = evo.id(5) + + evo.set(f2, evo.DEFAULT, 52) + evo.set(f1, evo.TAG) + + evo.set(f0, fc) + evo.set(f1, fc) + evo.set(f2, fc) + evo.set(f3, fc) + evo.set(f4, fc) + + local sum_entity = 0 + local last_assign_entity = 0 + local last_assign_component = 0 + local last_insert_entity = 0 + local last_insert_component = 0 + + do + local q = evo.query():include(fc):build() + evo.batch_set(q, evo.ON_ASSIGN, function(e, f, c) + assert(f == f0 or f == f1 or f == f2 or f == f3 or f == f4) + sum_entity = sum_entity + e + last_assign_entity = e + last_assign_component = c + end) + evo.batch_set(q, evo.ON_INSERT, function(e, f, c) + assert(f == f0 or f == f1 or f == f2 or f == f3 or f == f4) + sum_entity = sum_entity + e + last_insert_entity = e + last_insert_component = c + end) + end + + local e0a = evo.entity():set(f0, 0):build() + local e0b = evo.entity():set(f0, 0):build() + + local e3a = evo.entity():set(f1, 31):set(f2, 32):set(f3, 33):build() + local e3b = evo.entity():set(f1, 31):set(f2, 32):set(f3, 33):build() + + do + local q = evo.query():include(f0):build() + + sum_entity = 0 + last_assign_entity, last_assign_component = 0, 0 + last_insert_entity, last_insert_component = 0, 0 + + assert(evo.batch_multi_set(q, { f1, f2 }, { 51 }) == 2) + assert(sum_entity == e0a + e0b + e0a + e0b) + assert(last_assign_entity == 0) + assert(last_assign_component == 0) + assert(last_insert_entity == e0b) + assert(last_insert_component == 52) + assert(evo.get(e0a, f0) == 0 and evo.get(e0a, f1) == nil and evo.get(e0a, f2) == 52 and evo.get(e0a, f3) == nil) + assert(evo.get(e0b, f0) == 0 and evo.get(e0b, f1) == nil and evo.get(e0b, f2) == 52 and evo.get(e0b, f3) == nil) + + sum_entity = 0 + last_assign_entity, last_assign_component = 0, 0 + last_insert_entity, last_insert_component = 0, 0 + + assert(evo.batch_multi_set(q, { f1, f3, f2 }, { 61 }) == 2) + assert(sum_entity == e0a + e0b + e0a + e0b + e0a + e0b) + assert(last_assign_entity == e0b) + assert(last_assign_component == 52) + assert(last_insert_entity == e0b) + assert(last_insert_component == true) + assert(evo.get(e0a, f0) == 0 and evo.get(e0a, f1) == nil and evo.get(e0a, f2) == 52 and evo.get(e0a, f3) == true) + assert(evo.get(e0b, f0) == 0 and evo.get(e0b, f1) == nil and evo.get(e0b, f2) == 52 and evo.get(e0b, f3) == true) + end + + do + local q = evo.query():include(f3):exclude(f0, f4):build() + + sum_entity = 0 + last_assign_entity, last_assign_component = 0, 0 + last_insert_entity, last_insert_component = 0, 0 + + assert(evo.batch_multi_set(q, { f3, f4 }, { 53, 54 }) == 2) + assert(sum_entity == e3a + e3b + e3a + e3b) + assert(last_assign_entity == e3b) + assert(last_assign_component == 53) + assert(last_insert_entity == e3b) + assert(last_insert_component == 54) + assert(evo.get(e3a, f1) == nil and evo.get(e3a, f2) == 32 and evo.get(e3a, f3) == 53 and evo.get(e3a, f4) == 54) + assert(evo.get(e3b, f1) == nil and evo.get(e3b, f2) == 32 and evo.get(e3b, f3) == 53 and evo.get(e3b, f4) == 54) + end +end + +do + local f1, f2, f3, f4 = evo.id(4) local e1 = evo.entity():set(f1, 11):build() local e2 = evo.entity():set(f1, 21):set(f2, 22):build() @@ -4338,4 +4699,18 @@ do assert(evo.get(e2, f1) == nil and evo.get(e2, f2) == 22 and evo.get(e2, f3) == nil) assert(evo.get(e3, f1) == nil and evo.get(e3, f2) == 32 and evo.get(e3, f3) == 43) end + assert(evo.defer()) + do + local q = evo.query():include(f2):build() + do + local n, d = evo.batch_multi_set(q, { f3, f4 }, { 53, 54 }) + assert(n == 0 and d == true) + end + end + assert(evo.commit()) + do + assert(evo.get(e1, f1) == nil and evo.get(e1, f2) == 42 and evo.get(e1, f3) == 53 and evo.get(e1, f4) == 54) + assert(evo.get(e2, f1) == nil and evo.get(e2, f2) == 22 and evo.get(e2, f3) == 53 and evo.get(e2, f4) == 54) + assert(evo.get(e3, f1) == nil and evo.get(e3, f2) == 32 and evo.get(e3, f3) == 53 and evo.get(e3, f4) == 54) + end end diff --git a/evolved.lua b/evolved.lua index 6106a5e..6eb6a2a 100644 --- a/evolved.lua +++ b/evolved.lua @@ -1356,7 +1356,309 @@ end ---@param components evolved.component[] ---@return integer set_count local function __chunk_multi_set(chunk, fragments, components) - error('not implemented yet', 2) + if __defer_depth <= 0 then + error('batched chunk operations should be deferred', 2) + end + + local fragment_count = #fragments + + if fragment_count == 0 then + return 0 + end + + local old_chunk = chunk + local new_chunk = __chunk_with_fragment_list(chunk, fragments) + + if not new_chunk then + return 0 + end + + local old_entities = old_chunk.__entities + local old_size = #old_entities + + local old_fragment_set = old_chunk.__fragment_set + local old_component_indices = old_chunk.__component_indices + local old_component_storages = old_chunk.__component_storages + local old_component_fragments = old_chunk.__component_fragments + + if old_chunk == new_chunk then + for i = 1, fragment_count do + local fragment = fragments[i] + if chunk.__has_set_or_assign_hooks and __fragment_has_set_or_assign_hooks(fragment) then + local old_component_index = old_component_indices[fragment] + if old_component_index then + local old_component_storage = old_component_storages[old_component_index] + if chunk.__has_defaults_or_constructs and __fragment_has_default_or_construct(fragment) then + local new_component = components[i] + + if new_component == nil then + new_component = evolved.get(fragment, evolved.DEFAULT) + end + + if new_component == nil then + new_component = true + end + + for place = 1, old_size do + local entity = old_entities[place] + local old_component = old_component_storage[place] + old_component_storage[place] = new_component + __fragment_call_set_and_assign_hooks(entity, fragment, new_component, old_component) + end + else + local new_component = components[i] + + if new_component == nil then + new_component = true + end + + for place = 1, old_size do + local entity = old_entities[place] + local old_component = old_component_storage[place] + old_component_storage[place] = new_component + __fragment_call_set_and_assign_hooks(entity, fragment, new_component, old_component) + end + end + else + for place = 1, old_size do + local entity = old_entities[place] + __fragment_call_set_and_assign_hooks(entity, fragment) + end + end + else + local old_component_index = old_component_indices[fragment] + if old_component_index then + local old_component_storage = old_component_storages[old_component_index] + if chunk.__has_defaults_or_constructs and __fragment_has_default_or_construct(fragment) then + local new_component = components[i] + + if new_component == nil then + new_component = evolved.get(fragment, evolved.DEFAULT) + end + + if new_component == nil then + new_component = true + end + + for place = 1, old_size do + old_component_storage[place] = new_component + end + else + local new_component = components[i] + + if new_component == nil then + new_component = true + end + + for place = 1, old_size do + old_component_storage[place] = new_component + end + end + else + -- nothing + end + end + end + else + local new_entities = new_chunk.__entities + local new_size = #new_entities + + local new_component_indices = new_chunk.__component_indices + local new_component_storages = new_chunk.__component_storages + + do + __table_move( + old_entities, 1, old_size, + new_size + 1, new_entities) + + for i = 1, #old_component_fragments do + local old_f = old_component_fragments[i] + local old_cs = old_component_storages[i] + local new_ci = new_component_indices[old_f] + if new_ci then + local new_cs = new_component_storages[new_ci] + __table_move(old_cs, 1, old_size, new_size + 1, new_cs) + end + end + end + + local inserted_set = __acquire_table(__TABLE_POOL_TAG__FRAGMENT_SET, 0, fragment_count) + + for i = 1, fragment_count do + local fragment = fragments[i] + if inserted_set[fragment] or old_fragment_set[fragment] then + if new_chunk.__has_set_or_assign_hooks and __fragment_has_set_or_assign_hooks(fragment) then + local new_component_index = new_component_indices[fragment] + if new_component_index then + local new_component_storage = new_component_storages[new_component_index] + if new_chunk.__has_defaults_or_constructs and __fragment_has_default_or_construct(fragment) then + local new_component = components[i] + + if new_component == nil then + new_component = evolved.get(fragment, evolved.DEFAULT) + end + + if new_component == nil then + new_component = true + end + + for new_place = new_size + 1, new_size + old_size do + local entity = new_entities[new_place] + local old_component = new_component_storage[new_place] + new_component_storage[new_place] = new_component + __fragment_call_set_and_assign_hooks(entity, fragment, new_component, old_component) + end + else + local new_component = components[i] + + if new_component == nil then + new_component = true + end + + for new_place = new_size + 1, new_size + old_size do + local entity = new_entities[new_place] + local old_component = new_component_storage[new_place] + new_component_storage[new_place] = new_component + __fragment_call_set_and_assign_hooks(entity, fragment, new_component, old_component) + end + end + else + for new_place = new_size + 1, new_size + old_size do + local entity = new_entities[new_place] + __fragment_call_set_and_assign_hooks(entity, fragment) + end + end + else + local new_component_index = new_component_indices[fragment] + if new_component_index then + local new_component_storage = new_component_storages[new_component_index] + if new_chunk.__has_defaults_or_constructs and __fragment_has_default_or_construct(fragment) then + local new_component = components[i] + + if new_component == nil then + new_component = evolved.get(fragment, evolved.DEFAULT) + end + + if new_component == nil then + new_component = true + end + + for new_place = new_size + 1, new_size + old_size do + new_component_storage[new_place] = new_component + end + else + local new_component = components[i] + + if new_component == nil then + new_component = true + end + + for new_place = new_size + 1, new_size + old_size do + new_component_storage[new_place] = new_component + end + end + else + -- nothing + end + end + else + inserted_set[fragment] = true + if new_chunk.__has_set_or_insert_hooks and __fragment_has_set_or_insert_hooks(fragment) then + local new_component_index = new_component_indices[fragment] + if new_component_index then + local new_component_storage = new_component_storages[new_component_index] + if new_chunk.__has_defaults_or_constructs and __fragment_has_default_or_construct(fragment) then + local new_component = components[i] + + if new_component == nil then + new_component = evolved.get(fragment, evolved.DEFAULT) + end + + if new_component == nil then + new_component = true + end + + for new_place = new_size + 1, new_size + old_size do + local entity = new_entities[new_place] + new_component_storage[new_place] = new_component + __fragment_call_set_and_insert_hooks(entity, fragment, new_component) + end + else + local new_component = components[i] + + if new_component == nil then + new_component = true + end + + for new_place = new_size + 1, new_size + old_size do + local entity = new_entities[new_place] + new_component_storage[new_place] = new_component + __fragment_call_set_and_insert_hooks(entity, fragment, new_component) + end + end + else + for new_place = new_size + 1, new_size + old_size do + local entity = new_entities[new_place] + __fragment_call_set_and_insert_hooks(entity, fragment) + end + end + else + local new_component_index = new_component_indices[fragment] + if new_component_index then + local new_component_storage = new_component_storages[new_component_index] + if new_chunk.__has_defaults_or_constructs and __fragment_has_default_or_construct(fragment) then + local new_component = components[i] + + if new_component == nil then + new_component = evolved.get(fragment, evolved.DEFAULT) + end + + if new_component == nil then + new_component = true + end + + for new_place = new_size + 1, new_size + old_size do + new_component_storage[new_place] = new_component + end + else + local new_component = components[i] + + if new_component == nil then + new_component = true + end + + for new_place = new_size + 1, new_size + old_size do + new_component_storage[new_place] = new_component + end + end + else + -- nothing + end + end + end + end + + __release_table(__TABLE_POOL_TAG__FRAGMENT_SET, inserted_set) + + for new_place = new_size + 1, new_size + old_size do + local entity = new_entities[new_place] + local entity_index = entity % 0x100000 + __entity_chunks[entity_index] = new_chunk + __entity_places[entity_index] = new_place + end + + do + old_chunk.__entities = {} + + for i = 1, #old_component_storages do + old_component_storages[i] = {} + end + end + + __structural_changes = __structural_changes + old_size + end + + return old_size end ---@param chunk evolved.chunk From 1abe97a253e98c1c80bc05e8da3a9134707c8a85 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 21 Jan 2025 09:17:39 +0700 Subject: [PATCH 11/14] increase coverage --- develop/untests.lua | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/develop/untests.lua b/develop/untests.lua index 4de4ea9..ff3e1f1 100644 --- a/develop/untests.lua +++ b/develop/untests.lua @@ -4714,3 +4714,36 @@ do assert(evo.get(e3, f1) == nil and evo.get(e3, f2) == 32 and evo.get(e3, f3) == 53 and evo.get(e3, f4) == 54) end end + +do + local f1, f2, f3, f4 = evo.id(4) + local e = evo.entity():set(f1, 11):set(f2, 22):set(f3, 33):set(f4, 44):build() + do + local c1 = evo.get(e, f1) + assert(c1 == 11) + end + do + local c1, c2 = evo.get(e, f1, f2) + assert(c1 == 11 and c2 == 22) + end + do + local c2, c1 = evo.get(e, f2, f1) + assert(c1 == 11 and c2 == 22) + end + do + local c1, c2, c3 = evo.get(e, f1, f2, f3) + assert(c1 == 11 and c2 == 22 and c3 == 33) + end + do + local c3, c2, c1 = evo.get(e, f3, f2, f1) + assert(c1 == 11 and c2 == 22 and c3 == 33) + end + do + local c1, c2, c3, c4 = evo.get(e, f1, f2, f3, f4) + assert(c1 == 11 and c2 == 22 and c3 == 33 and c4 == 44) + end + do + local c4, c3, c2, c1 = evo.get(e, f4, f3, f2, f1) + assert(c1 == 11 and c2 == 22 and c3 == 33 and c4 == 44) + end +end From 46f86daef85e67e47e63e0ccb5f612d462383f9e Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 21 Jan 2025 09:42:03 +0700 Subject: [PATCH 12/14] increase coverage --- develop/untests.lua | 142 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/develop/untests.lua b/develop/untests.lua index ff3e1f1..a777ccc 100644 --- a/develop/untests.lua +++ b/develop/untests.lua @@ -4747,3 +4747,145 @@ do assert(c1 == 11 and c2 == 22 and c3 == 33 and c4 == 44) end end + +do + local f1, f2, f3 = evo.id(3) + + evo.set(f2, evo.DEFAULT, 42) + + local e1a = evo.entity():set(f1, 11):build() + local e1b = evo.entity():set(f1, 11):build() + + local e2a = evo.entity():set(f1, 11):set(f2, 22):build() + local e2b = evo.entity():set(f1, 11):set(f2, 22):build() + + local q = evo.query():include(f1):build() + + assert(evo.batch_insert(q, f2) == 2) + assert(evo.get(e1a, f1) == 11 and evo.get(e1a, f2) == 42) + assert(evo.get(e1b, f1) == 11 and evo.get(e1b, f2) == 42) + assert(evo.get(e2a, f1) == 11 and evo.get(e2a, f2) == 22) + assert(evo.get(e2b, f1) == 11 and evo.get(e2b, f2) == 22) + + assert(evo.batch_assign(q, f2) == 4) + assert(evo.get(e1a, f1) == 11 and evo.get(e1a, f2) == 42) + assert(evo.get(e1b, f1) == 11 and evo.get(e1b, f2) == 42) + assert(evo.get(e2a, f1) == 11 and evo.get(e2a, f2) == 42) + assert(evo.get(e2b, f1) == 11 and evo.get(e2b, f2) == 42) + + assert(evo.batch_assign(q, f1) == 4) + assert(evo.get(e1a, f1) == true and evo.get(e1a, f2) == 42) + assert(evo.get(e1b, f1) == true and evo.get(e1b, f2) == 42) + assert(evo.get(e2a, f1) == true and evo.get(e2a, f2) == 42) + assert(evo.get(e2b, f1) == true and evo.get(e2b, f2) == 42) + + assert(evo.batch_insert(q, f3) == 4) + assert(evo.get(e1a, f1) == true and evo.get(e1a, f2) == 42 and evo.get(e1a, f3) == true) + assert(evo.get(e1b, f1) == true and evo.get(e1b, f2) == 42 and evo.get(e1b, f3) == true) + assert(evo.get(e2a, f1) == true and evo.get(e2a, f2) == 42 and evo.get(e2a, f3) == true) + assert(evo.get(e2b, f1) == true and evo.get(e2b, f2) == 42 and evo.get(e2b, f3) == true) +end + +do + local fc = evo.id() + local f1, f2, f3, f4 = evo.id(4) + + evo.set(f2, evo.DEFAULT, 42) + evo.set(f3, evo.TAG) + + evo.set(f1, fc) + evo.set(f2, fc) + evo.set(f3, fc) + evo.set(f4, fc) + + local sum_entity = 0 + local last_assign_entity = 0 + local last_assign_component = 0 + local last_insert_entity = 0 + local last_insert_component = 0 + + do + local q = evo.query():include(fc):build() + evo.batch_insert(q, evo.ON_ASSIGN, function(e, f, c) + assert(f == f1 or f == f2 or f == f3 or f == f4) + sum_entity = sum_entity + e + last_assign_entity = e + last_assign_component = c + end) + evo.batch_insert(q, evo.ON_INSERT, function(e, f, c) + assert(f == f1 or f == f2 or f == f3 or f == f4) + sum_entity = sum_entity + e + last_insert_entity = e + last_insert_component = c + end) + end + + local e1a = evo.entity():set(f1, 11):build() + local e1b = evo.entity():set(f1, 11):build() + + local e2a = evo.entity():set(f1, 11):set(f2, 22):build() + local e2b = evo.entity():set(f1, 11):set(f2, 22):build() + + do + local q = evo.query():include(f1):build() + + sum_entity = 0 + last_insert_entity = 0 + last_insert_component = 0 + + assert(evo.batch_insert(q, f2) == 2) + assert(evo.get(e1a, f1) == 11 and evo.get(e1a, f2) == 42) + assert(evo.get(e1b, f1) == 11 and evo.get(e1b, f2) == 42) + assert(evo.get(e2a, f1) == 11 and evo.get(e2a, f2) == 22) + assert(evo.get(e2b, f1) == 11 and evo.get(e2b, f2) == 22) + + assert(sum_entity == e1a + e1b) + assert(last_insert_entity == e1b) + assert(last_insert_component == 42) + end + + do + local q = evo.query():include(f2):build() + + sum_entity = 0 + last_insert_entity = 0 + last_insert_component = 0 + + assert(evo.batch_insert(q, f3) == 4) + assert(evo.has_all(e1a, f1, f2, f3) and evo.has_all(e1b, f1, f2, f3)) + assert(evo.has_all(e2a, f1, f2, f3) and evo.has_all(e2b, f1, f2, f3)) + assert(evo.get(e1a, f1) == 11 and evo.get(e1a, f2) == 42 and evo.get(e1a, f3) == nil) + assert(evo.get(e1b, f1) == 11 and evo.get(e1b, f2) == 42 and evo.get(e1b, f3) == nil) + assert(evo.get(e2a, f1) == 11 and evo.get(e2a, f2) == 22 and evo.get(e2a, f3) == nil) + assert(evo.get(e2b, f1) == 11 and evo.get(e2b, f2) == 22 and evo.get(e2b, f3) == nil) + end + + do + local q = evo.query():include(f2):build() + + sum_entity = 0 + last_insert_entity = 0 + last_insert_component = 0 + + assert(evo.batch_insert(q, f4) == 4) + assert(evo.has_all(e1a, f1, f2, f3, f4) and evo.has_all(e1b, f1, f2, f3, f4)) + assert(evo.has_all(e2a, f1, f2, f3, f4) and evo.has_all(e2b, f1, f2, f3, f4)) + assert(evo.get(e1a, f1) == 11 and evo.get(e1a, f2) == 42 and evo.get(e1a, f3) == nil and evo.get(e1a, f4) == true) + assert(evo.get(e1b, f1) == 11 and evo.get(e1b, f2) == 42 and evo.get(e1b, f3) == nil and evo.get(e1b, f4) == true) + assert(evo.get(e2a, f1) == 11 and evo.get(e2a, f2) == 22 and evo.get(e2a, f3) == nil and evo.get(e2a, f4) == true) + assert(evo.get(e2b, f1) == 11 and evo.get(e2b, f2) == 22 and evo.get(e2b, f3) == nil and evo.get(e2b, f4) == true) + end + + do + local q = evo.query():include(f3):build() + + sum_entity = 0 + last_assign_entity = 0 + last_assign_component = 0 + + assert(evo.batch_assign(q, f2) == 4) + assert(sum_entity == e1a + e1b + e2a + e2b) + assert(last_assign_entity == e1b) + assert(last_assign_component == 42) + end +end From 4036c273505fedc996cc549b59f4c7223063a72f Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 21 Jan 2025 10:18:16 +0700 Subject: [PATCH 13/14] increase coverage --- develop/untests.lua | 145 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/develop/untests.lua b/develop/untests.lua index a777ccc..f24595d 100644 --- a/develop/untests.lua +++ b/develop/untests.lua @@ -4889,3 +4889,148 @@ do assert(last_assign_component == 42) end end + +do + local f1, f2 = evo.id(2) + + do + local e = evo.id() + assert(evo.defer()) + do + local s, d = evo.multi_set(e, { f1 }, { 11 }) + assert(not s and d == true) + assert(not evo.has_any(e, f1)) + end + assert(evo.commit()) + do + assert(evo.has_all(e, f1)) + assert(evo.get(e, f1) == 11) + end + assert(evo.defer()) + do + local s, d = evo.multi_set(e, { f1, f2 }, { 21, 22 }) + assert(not s and d == true) + assert(not evo.has_any(e, f2)) + end + assert(evo.commit()) + do + assert(evo.has_all(e, f1, f2)) + assert(evo.get(e, f1) == 21 and evo.get(e, f2) == 22) + end + end +end + +do + local f1, f2 = evo.id(2) + + assert(evo.defer()) + local c2, c12 = evo.chunk(f2), evo.chunk(f2, f1) + local e2 = evo.spawn_at(c2, { f2 }, { 22 }) + local e12 = evo.spawn_at(c12, { f1, f2 }, { 11, 12 }) + assert(evo.is_alive(e2) and evo.is_empty(e2)) + assert(evo.is_alive(e12) and evo.is_empty(e12)) + assert(evo.commit()) + assert(evo.is_alive(e2) and not evo.is_empty(e2)) + assert(evo.is_alive(e12) and not evo.is_empty(e12)) + assert(evo.has(e2, f2) and evo.get(e2, f2) == 22) + assert(evo.has(e12, f1) and evo.get(e12, f1) == 11) + assert(evo.has(e12, f2) and evo.get(e12, f2) == 12) +end + +do + local id = evo.pack(7, 3) + assert(id == 0x300007) + local index, version = evo.unpack(0x500004) + assert(index == 4 and version == 5) +end + +do + local f1, f2 = evo.id(2) + + local e = evo.id() + + assert(evo.set(e, f1, 11)) + assert(evo.set(e, f1)) + + assert(evo.set(e, f2, 22)) + assert(evo.assign(e, f2)) + + assert(evo.get(e, f1) == true and evo.get(e, f2) == true) + + assert(evo.destroy(e)) + assert(not evo.has(e, f1) and not evo.has(e, f2)) + assert(not evo.has_all(e, f1, f2) and not evo.has_any(e, f1, f2)) + + assert(not evo.set(e, f1, 11)) + assert(not evo.assign(e, f1, 11)) + assert(not evo.insert(e, f1, 11)) + + assert(not evo.multi_set(e, { f1 }, { 11 })) + assert(not evo.multi_assign(e, { f1 }, { 11 })) + assert(not evo.multi_insert(e, { f1 }, { 11 })) +end + +do + local f1 = evo.id(2) + + local e = evo.id() + assert(evo.clear(e) and evo.clear(e)) + assert(evo.set(e, f1, 11)) + assert(evo.clear(e) and evo.clear(e)) + assert(evo.destroy(e) and evo.destroy(e)) + assert(not evo.clear(e)) +end + +do + local f1, f2, f3 = evo.id(3) + + evo.set(f2, evo.DEFAULT, 42) + evo.set(f3, evo.TAG) + + local last_assign_f2_new_component = 0 + local last_assign_f2_old_component = 0 + + local last_insert_f3_new_component = 0 + + evo.set(f2, evo.ON_ASSIGN, function(_, f, nc, oc) + assert(f == f2) + last_assign_f2_new_component = nc + last_assign_f2_old_component = oc + end) + + evo.set(f3, evo.ON_INSERT, function(_, f, nc) + assert(f == f3) + last_insert_f3_new_component = nc + end) + + do + local e = evo.id() + + assert(evo.multi_set(e, { f1, f2, f3 }, { 11, 22 })) + assert(evo.has_all(e, f1, f2, f3)) + assert(evo.get(e, f1) == 11 and evo.get(e, f2) == 22 and evo.get(e, f3) == nil) + assert(last_assign_f2_new_component == 0 and last_assign_f2_old_component == 0) + + assert(evo.multi_set(e, { f1, f2, f3, f3 }, {})) + assert(evo.has_all(e, f1, f2, f3)) + assert(evo.get(e, f1) == true and evo.get(e, f2) == 42 and evo.get(e, f3) == nil) + assert(last_assign_f2_new_component == 42 and last_assign_f2_old_component == 22) + assert(last_insert_f3_new_component == nil) + end + + do + local e = evo.id() + + assert(evo.multi_set(e, { f1, f1, f3, f3 }, {})) + assert(evo.has_all(e, f1, f3)) + assert(evo.get(e, f1) == true and evo.get(e, f3) == nil) + end + + do + local e = evo.id() + + assert(evo.multi_set(e, { f1, f1, f2, f2, f3 }, {})) + assert(evo.has_all(e, f1, f2, f3)) + assert(evo.get(e, f1) == true and evo.get(e, f2) == 42 and evo.get(e, f3) == nil) + end +end From 0e76708754607fe02455b364b0f525adcac1d661 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 21 Jan 2025 10:49:00 +0700 Subject: [PATCH 14/14] increase coverage --- develop/untests.lua | 86 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/develop/untests.lua b/develop/untests.lua index f24595d..81c1e4e 100644 --- a/develop/untests.lua +++ b/develop/untests.lua @@ -3503,6 +3503,15 @@ do assert(evo.has(e4, f3) and evo.get(e4, f3) == 43) assert(evo.has(e4, f4) and evo.get(e4, f4) == nil) end + + do + local c = evo.chunk(f1, f2, f3) + + local e1 = evo.spawn_at(c, { f1, f2, f3 }) + assert(evo.has(e1, f1) and evo.get(e1, f1) == true) + assert(evo.has(e1, f2) and evo.get(e1, f2) == true) + assert(evo.has(e1, f3) and evo.get(e1, f3) == 33) + end end do @@ -4887,6 +4896,15 @@ do assert(sum_entity == e1a + e1b + e2a + e2b) assert(last_assign_entity == e1b) assert(last_assign_component == 42) + + sum_entity = 0 + last_assign_entity = 0 + last_assign_component = 0 + + assert(evo.batch_assign(q, f1) == 4) + assert(sum_entity == e1a + e1b + e2a + e2b) + assert(last_assign_entity == e1b) + assert(last_assign_component == true) end end @@ -4968,6 +4986,7 @@ do assert(not evo.multi_set(e, { f1 }, { 11 })) assert(not evo.multi_assign(e, { f1 }, { 11 })) assert(not evo.multi_insert(e, { f1 }, { 11 })) + assert(not evo.multi_remove(e, { f1 })) end do @@ -4990,6 +5009,7 @@ do local last_assign_f2_new_component = 0 local last_assign_f2_old_component = 0 + local last_insert_f2_new_component = 0 local last_insert_f3_new_component = 0 evo.set(f2, evo.ON_ASSIGN, function(_, f, nc, oc) @@ -4998,6 +5018,11 @@ do last_assign_f2_old_component = oc end) + evo.set(f2, evo.ON_INSERT, function(_, f, nc) + assert(f == f2) + last_insert_f2_new_component = nc + end) + evo.set(f3, evo.ON_INSERT, function(_, f, nc) assert(f == f3) last_insert_f3_new_component = nc @@ -5016,6 +5041,11 @@ do assert(evo.get(e, f1) == true and evo.get(e, f2) == 42 and evo.get(e, f3) == nil) assert(last_assign_f2_new_component == 42 and last_assign_f2_old_component == 22) assert(last_insert_f3_new_component == nil) + + assert(evo.multi_assign(e, { f1, f2, f3 }, { 11, 22, 33 })) + assert(evo.get(e, f1) == 11 and evo.get(e, f2) == 22 and evo.get(e, f3) == nil) + assert(evo.multi_assign(e, { f1, f2, f3 }, {})) + assert(evo.get(e, f1) == true and evo.get(e, f2) == 42 and evo.get(e, f3) == nil) end do @@ -5033,4 +5063,60 @@ do assert(evo.has_all(e, f1, f2, f3)) assert(evo.get(e, f1) == true and evo.get(e, f2) == 42 and evo.get(e, f3) == nil) end + + do + local e = evo.id() + + last_insert_f2_new_component = 0 + + assert(evo.multi_insert(e, { f2, f2 }, { nil, 22 })) + assert(evo.get(e, f2) == 42) + assert(last_insert_f2_new_component == 42) + end +end + +do + local f1, f2 = evo.id(2) + + evo.set(f1, evo.DEFAULT, 41) + + do + local e = evo.id() + + assert(evo.multi_insert(e, { f1, f2 })) + assert(evo.get(e, f1) == 41 and evo.get(e, f2) == true) + assert(evo.multi_assign(e, { f1, f2 }, { 11, 22 })) + assert(evo.get(e, f1) == 11 and evo.get(e, f2) == 22) + assert(evo.multi_assign(e, { f1, f2 })) + assert(evo.get(e, f1) == 41 and evo.get(e, f2) == true) + end +end + +do + local f1, f2, f3, f4 = evo.id(4) + local e = evo.spawn_with({ f1, f2, f3, f4 }, { 1, 2, 3, 4 }) + + local c, es = evo.chunk(f1, f2, f3, f4) + assert(c and es and #es == 1 and es[1] == e) + + do + local c1, c2 = evo.select(c, f1, f2) + assert(c1 and #c1 == 1 and c1[1] == 1) + assert(c2 and #c2 == 1 and c2[1] == 2) + end + + do + local c1, c2, c3 = evo.select(c, f1, f2, f3) + assert(c1 and #c1 == 1 and c1[1] == 1) + assert(c2 and #c2 == 1 and c2[1] == 2) + assert(c3 and #c3 == 1 and c3[1] == 3) + end + + do + local c1, c2, c3, c4 = evo.select(c, f1, f2, f3, f4) + assert(c1 and #c1 == 1 and c1[1] == 1) + assert(c2 and #c2 == 1 and c2[1] == 2) + assert(c3 and #c3 == 1 and c3[1] == 3) + assert(c4 and #c4 == 1 and c4[1] == 4) + end end