From 255e66dba69e9e310ec54faa1187b067b9e007e1 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Thu, 9 Jan 2025 16:51:46 +0700 Subject: [PATCH] first multi api impl --- develop/untests.lua | 433 ++++++++++++++++++++++++++++++++++ evolved.lua | 564 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 942 insertions(+), 55 deletions(-) diff --git a/develop/untests.lua b/develop/untests.lua index bc5257b..bb96c93 100644 --- a/develop/untests.lua +++ b/develop/untests.lua @@ -2509,3 +2509,436 @@ do assert(entities[1] == e2) end end + +do + local f1, f2, f3 = evo.id(3) + + do + local e = evo.id() + assert(not evo.multi_insert(e, {})) + assert(not evo.multi_insert(e, {}, {})) + assert(not evo.multi_insert(e, {}, { 41 })) + assert(evo.is_alive(e) and evo.is_empty(e)) + + assert(evo.multi_insert(e, { f1 })) + assert(evo.has(e, f1) and evo.get(e, f1) == true) + + assert(not evo.multi_insert(e, { f1 })) + assert(evo.has(e, f1) and evo.get(e, f1) == true) + + assert(evo.multi_insert(e, { f2 }, { 42, 43 })) + assert(evo.has(e, f1) and evo.get(e, f1) == true) + assert(evo.has(e, f2) and evo.get(e, f2) == 42) + end + + do + local e = evo.id() + assert(evo.multi_insert(e, { f1, f2 }, { 41 })) + assert(evo.has(e, f1) and evo.get(e, f1) == 41) + assert(evo.has(e, f2) and evo.get(e, f2) == true) + + assert(evo.multi_insert(e, { f1, f3 }, { 20, 43 })) + assert(evo.has(e, f1) and evo.get(e, f1) == 41) + assert(evo.has(e, f2) and evo.get(e, f2) == true) + assert(evo.has(e, f3) and evo.get(e, f3) == 43) + end +end + +do + local f1, f2, f3 = evo.id(3) + + do + local e1 = evo.id() + assert(evo.multi_insert(e1, { f1, f2 }, { 41, 42 })) + assert(evo.has(e1, f1) and evo.get(e1, f1) == 41) + assert(evo.has(e1, f2) and evo.get(e1, f2) == 42) + + local e2 = evo.id() + assert(evo.multi_insert(e2, { f1, f2 }, { 43, 44 })) + assert(evo.has(e2, f1) and evo.get(e2, f1) == 43) + assert(evo.has(e2, f2) and evo.get(e2, f2) == 44) + + assert(evo.multi_insert(e1, { f3 })) + do + local chunk, entities = evo.chunk(f1, f2) + assert(entities and #entities == 1 and entities[1] == e2) + assert(chunk and evo.select(chunk, f2)[1] == 44) + end + end + + do + local e1, e2 = evo.id(2) + evo.defer() + do + evo.multi_insert(e1, { f1, f2 }, { 41, 42 }) + evo.multi_insert(e2, { f2, f2 }, { 43, 44 }) + end + assert(evo.is_alive(e1) and evo.is_empty(e1)) + assert(evo.is_alive(e2) and evo.is_empty(e2)) + assert(evo.commit()) + assert(evo.has(e1, f1) and evo.get(e1, f1) == 41) + assert(evo.has(e1, f2) and evo.get(e1, f2) == 42) + assert(evo.has(e2, f2) and evo.get(e2, f2) == 44) + end +end + +do + local f1, f2, f3 = evo.id(3) + + evo.set(f3, evo.TAG) + + local last_set_entity = 0 + local last_set_component = 0 + + evo.set(f1, evo.ON_SET, function(e, f, c) + assert(f == f1) + last_set_entity = e + last_set_component = c + end) + + evo.set(f2, evo.ON_SET, function(e, f, c) + assert(f == f2) + last_set_entity = e + last_set_component = c + end) + + evo.set(f3, evo.ON_SET, function(e, f, c) + assert(f == f3) + last_set_entity = e + last_set_component = c + end) + + do + local e = evo.id() + assert(evo.multi_insert(e, { f1, f2 }, { 41, 42 })) + assert(last_set_entity == e and last_set_component == 42) + end + + do + local e = evo.id() + assert(evo.multi_insert(e, { f1, f2, f3 }, { 41, 42, 43 })) + assert(last_set_entity == e and last_set_component == nil) + end +end + +do + local f1, f2, f3 = evo.id(3) + + do + local e = evo.id() + assert(not evo.multi_assign(e, {})) + assert(not evo.multi_assign(e, {}, {})) + assert(not evo.multi_assign(e, {}, { 41 })) + assert(evo.is_alive(e) and evo.is_empty(e)) + + assert(evo.multi_insert(e, { f1 }, { 21 })) + assert(evo.multi_assign(e, { f1, f2 }, { 41, 42 })) + assert(not evo.multi_assign(e, { f2 }, { 42 })) + assert(evo.has(e, f1) and evo.get(e, f1) == 41) + assert(not evo.has(e, f2) and evo.get(e, f2) == nil) + + assert(not evo.multi_assign(e, { f3 }, { 43 })) + assert(evo.has(e, f1) and evo.get(e, f1) == 41) + assert(not evo.has(e, f2) and evo.get(e, f2) == nil) + assert(not evo.has(e, f3) and evo.get(e, f3) == nil) + + assert(evo.multi_insert(e, { f2 }, { 22 })) + assert(evo.multi_assign(e, { f2 })) + assert(evo.has(e, f1) and evo.get(e, f1) == 41) + assert(evo.has(e, f2) and evo.get(e, f2) == true) + assert(evo.multi_assign(e, { f2 }, { 42, 43 })) + assert(evo.has(e, f1) and evo.get(e, f1) == 41) + assert(evo.has(e, f2) and evo.get(e, f2) == 42) + end + + do + local e1, e2 = evo.id(2) + evo.defer() + do + evo.multi_insert(e1, { f1, f2 }, { 21, 22 }) + evo.multi_assign(e1, { f1, f2 }, { 41, 42 }) + + evo.multi_insert(e2, { f1, f2 }, { 31, 32 }) + evo.multi_assign(e2, { f1, f2 }, { 51, 52 }) + end + assert(evo.is_alive(e1) and evo.is_empty(e1)) + assert(evo.is_alive(e2) and evo.is_empty(e2)) + assert(evo.commit()) + assert(evo.has(e1, f1) and evo.get(e1, f1) == 41) + assert(evo.has(e1, f2) and evo.get(e1, f2) == 42) + assert(evo.has(e2, f1) and evo.get(e2, f1) == 51) + assert(evo.has(e2, f2) and evo.get(e2, f2) == 52) + end +end + +do + local f1, f2, f3 = evo.id(3) + + evo.set(f3, evo.TAG) + + local last_set_entity = 0 + local last_set_component = 0 + + evo.set(f1, evo.ON_SET, function(e, f, c) + assert(f == f1) + last_set_entity = e + last_set_component = c + end) + + evo.set(f2, evo.ON_SET, function(e, f, c) + assert(f == f2) + last_set_entity = e + last_set_component = c + end) + + evo.set(f3, evo.ON_SET, function(e, f, c) + assert(f == f3) + last_set_entity = e + last_set_component = c + end) + + do + local e = evo.id() + assert(not evo.multi_assign(e, { f1, f2 }, { 41, 42 })) + assert(last_set_entity == 0 and last_set_component == 0) + + assert(evo.multi_insert(e, { f1, f2 }, { 21, 22 })) + assert(last_set_entity == e and last_set_component == 22) + + assert(evo.multi_assign(e, { f1, f2 }, { 41, 42 })) + assert(last_set_entity == e and last_set_component == 42) + end + + do + local e = evo.id() + assert(evo.multi_insert(e, { f1, f2, f3 }, { 21, 22, 23 })) + assert(last_set_entity == e and last_set_component == nil) + + last_set_entity, last_set_component = 0, 0 + assert(evo.multi_assign(e, { f1, f2, f3 }, { 41, 42, 43 })) + assert(last_set_entity == e and last_set_component == nil) + assert(evo.has(e, f1) and evo.get(e, f1) == 41) + assert(evo.has(e, f2) and evo.get(e, f2) == 42) + assert(evo.has(e, f3) and evo.get(e, f3) == nil) + end +end + +do + local f1, f2, f3, f4 = evo.id(4) + + evo.set(f3, evo.TAG) + + do + local e = evo.id() + assert(evo.multi_insert(e, { f1, f2, f3 }, { 41, 42, 43 })) + assert(evo.has_all(e, f1, f2, f3)) + + assert(evo.multi_remove(e, {})) + assert(evo.multi_remove(e, { f4 })) + assert(evo.has_all(e, f1, f2, f3)) + + assert(evo.multi_remove(e, { f3 })) + assert(evo.has(e, f1) and evo.has(e, f2) and not evo.has(e, f3)) + assert(evo.get(e, f1) == 41 and evo.get(e, f2) == 42 and evo.get(e, f3) == nil) + + assert(evo.multi_remove(e, { f1, f2, f4 })) + assert(not evo.has_any(e, f1, f2, f3)) + assert(evo.get(e, f1) == nil and evo.get(e, f2) == nil and evo.get(e, f3) == nil) + end + + do + local e = evo.id() + assert(evo.multi_insert(e, { f1, f2, f3 }, { 41, 42, 43 })) + assert(evo.has_all(e, f1, f2, f3)) + evo.defer() + evo.multi_remove(e, { f1, f2 }) + assert(evo.has_all(e, f1, f2, f3)) + assert(evo.commit()) + assert(not evo.has(e, f1) and not evo.has(e, f2) and evo.has(e, f3)) + end +end + +do + local f1, f2, f3 = evo.id(3) + + evo.set(f3, evo.TAG) + + local last_remove_entity = 0 + local last_remove_component = 0 + + evo.set(f1, evo.ON_REMOVE, function(e, f, c) + assert(f == f1) + last_remove_entity = e + last_remove_component = c + end) + + evo.set(f2, evo.ON_REMOVE, function(e, f, c) + assert(f == f2) + last_remove_entity = e + last_remove_component = c + end) + + evo.set(f3, evo.ON_REMOVE, function(e, f, c) + assert(f == f3) + last_remove_entity = e + last_remove_component = c + end) + + do + local e = evo.id() + assert(evo.multi_remove(e, { f1, f2 })) + assert(last_remove_entity == 0 and last_remove_component == 0) + + assert(evo.multi_insert(e, { f1, f2, f3 }, { 41, 42 })) + assert(last_remove_entity == 0 and last_remove_component == 0) + assert(evo.multi_remove(e, { f1, f2 })) + assert(last_remove_entity == e and last_remove_component == 42) + assert(evo.multi_remove(e, { f3 })) + assert(last_remove_entity == e and last_remove_component == nil) + end +end + +do + local f1, f2, f3 = evo.id(3) + + do + local e1, e2 = evo.id(2) + assert(evo.multi_insert(e1, { f1, f2, f3 }, { 41, 42, 43 })) + assert(evo.multi_insert(e2, { f1, f2, f3 }, { 44, 45, 46 })) + + assert(evo.multi_remove(e1, { f1, f2 })) + + do + local chunk, entities = evo.chunk(f1, f2, f3) + assert(entities and #entities == 1 and entities[1] == e2) + assert(chunk and evo.select(chunk, f2)[1] == 45) + end + + do + local chunk, entities = evo.chunk(f3) + assert(entities and #entities == 1 and entities[1] == e1) + assert(chunk and evo.select(chunk, f3)[1] == 43) + end + end +end + +do + local f1, f2, f3, f4 = evo.id(4) + + evo.set(f3, evo.DEFAULT, 43) + evo.set(f4, evo.TAG) + + + do + local e = evo.id() + assert(not evo.multi_set(e, {})) + assert(not evo.multi_set(e, {}, {})) + assert(not evo.multi_set(e, {}, { 41 })) + assert(evo.is_alive(e) and evo.is_empty(e)) + + assert(evo.multi_set(e, { f1 })) + assert(evo.has(e, f1) and evo.get(e, f1) == true) + + assert(evo.multi_set(e, { f1 })) + assert(evo.has(e, f1) and evo.get(e, f1) == true) + + assert(evo.multi_set(e, { f1 }, { 41 })) + assert(evo.has(e, f1) and evo.get(e, f1) == 41) + + assert(evo.multi_set(e, { f2 }, { 42 })) + assert(evo.has(e, f1) and evo.get(e, f1) == 41) + assert(evo.has(e, f2) and evo.get(e, f2) == 42) + + assert(evo.multi_set(e, { f2 })) + assert(evo.has(e, f1) and evo.get(e, f1) == 41) + assert(evo.has(e, f2) and evo.get(e, f2) == true) + + assert(evo.multi_set(e, { f2, f3 }, { 42 })) + assert(evo.has(e, f1) and evo.get(e, f1) == 41) + assert(evo.has(e, f2) and evo.get(e, f2) == 42) + assert(evo.has(e, f3) and evo.get(e, f3) == 43) + + assert(evo.multi_set(e, { f3, f4 }, { 33, 44 })) + assert(evo.has(e, f1) and evo.get(e, f1) == 41) + assert(evo.has(e, f2) and evo.get(e, f2) == 42) + assert(evo.has(e, f3) and evo.get(e, f3) == 33) + assert(evo.has(e, f4) and evo.get(e, f4) == nil) + end +end + +do + local f1, f2, f3 = evo.id(3) + + evo.set(f2, evo.DEFAULT, 42) + evo.set(f3, evo.TAG) + + 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) + 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) + 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) + last_assign_entity = e + last_assign_new_component = nc + last_assign_old_component = oc + end) + + local last_insert_entity = 0 + local last_insert_component = 0 + + evo.set(f1, evo.ON_INSERT, function(e, f, nc) + assert(f == f1) + last_insert_entity = e + last_insert_component = nc + end) + + evo.set(f2, evo.ON_INSERT, function(e, f, nc) + assert(f == f2) + last_insert_entity = e + last_insert_component = nc + end) + + evo.set(f3, evo.ON_INSERT, function(e, f, nc) + assert(f == f3) + last_insert_entity = e + last_insert_component = nc + end) + + do + last_assign_entity, last_assign_old_component, last_assign_new_component = 0, 0, 0 + last_insert_entity, last_insert_component = 0, 0 + + local e = evo.id() + assert(evo.multi_set(e, { f1 })) + assert(last_assign_entity == 0 and last_assign_old_component == 0 and last_assign_new_component == 0) + assert(last_insert_entity == e and last_insert_component == true) + + last_assign_entity, last_assign_old_component, last_assign_new_component = 0, 0, 0 + last_insert_entity, last_insert_component = 0, 0 + + assert(evo.multi_set(e, { f1 }, { 41 })) + assert(last_assign_entity == e and last_assign_old_component == true and last_assign_new_component == 41) + assert(last_insert_entity == 0 and last_insert_component == 0) + + last_assign_entity, last_assign_old_component, last_assign_new_component = 0, 0, 0 + last_insert_entity, last_insert_component = 0, 0 + + assert(evo.multi_set(e, { f1, f2 }, { 11 })) + assert(last_assign_entity == e and last_assign_old_component == 41 and last_assign_new_component == 11) + assert(last_insert_entity == e and last_insert_component == 42) + end +end diff --git a/evolved.lua b/evolved.lua index 682dc09..093a732 100644 --- a/evolved.lua +++ b/evolved.lua @@ -649,6 +649,25 @@ local function __chunk_with_fragment(parent_chunk, child_fragment) return child_chunk end +---@param chunk? evolved.chunk +---@param fragment_list evolved.fragment[] +---@return evolved.chunk? +---@nodiscard +local function __chunk_with_fragment_list(chunk, fragment_list) + local fragment_count = #fragment_list + + if fragment_count == 0 then + return chunk + end + + for i = 1, fragment_count do + local fragment = fragment_list[i] + chunk = __chunk_with_fragment(chunk, fragment) + end + + return chunk +end + ---@param chunk? evolved.chunk ---@param fragment evolved.fragment ---@return evolved.chunk? @@ -704,6 +723,25 @@ local function __chunk_without_fragments(chunk, ...) return chunk end +---@param chunk? evolved.chunk +---@param fragment_list evolved.fragment[] +---@return evolved.chunk? +---@nodiscard +local function __chunk_without_fragment_list(chunk, fragment_list) + local fragment_count = #fragment_list + + if fragment_count == 0 then + return chunk + end + + for i = 1, fragment_count do + local fragment = fragment_list[i] + chunk = __chunk_without_fragment(chunk, fragment) + end + + return chunk +end + --- --- --- @@ -960,8 +998,8 @@ local function __chunk_insert(chunk, fragment, ...) local old_f = old_component_fragments[i] local old_cs = old_component_storages[i] local new_ci = new_component_indices[old_f] - local new_cs = new_component_storages[new_ci] - if new_cs then + 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 @@ -1103,8 +1141,8 @@ local function __chunk_remove(chunk, ...) local new_f = new_component_fragments[i] local new_cs = new_component_storages[i] local old_ci = old_component_indices[new_f] - local old_cs = old_component_storages[old_ci] - if old_cs then + 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 @@ -1965,6 +2003,12 @@ function evolved.set(entity, fragment, ...) local new_chunk = __chunk_with_fragment(old_chunk, fragment) + if not new_chunk then + return false, false + end + + __defer() + if old_chunk == new_chunk then local old_component_indices = old_chunk.__component_indices local old_component_storages = old_chunk.__component_storages @@ -1998,12 +2042,7 @@ function evolved.set(entity, fragment, ...) __fragment_call_set_and_assign_hooks(entity, fragment) end end - - return true, false - end - - __defer() - do + else local new_entities = new_chunk.__entities local new_component_indices = new_chunk.__component_indices local new_component_storages = new_chunk.__component_storages @@ -2011,6 +2050,23 @@ function evolved.set(entity, fragment, ...) local new_place = #new_entities + 1 new_entities[new_place] = entity + if old_chunk then + local old_component_storages = old_chunk.__component_storages + local old_component_fragments = old_chunk.__component_fragments + + 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] + new_cs[new_place] = old_cs[old_place] + end + end + + __detach_entity(entity) + end + do local new_component_index = new_component_indices[fragment] @@ -2042,28 +2098,12 @@ function evolved.set(entity, fragment, ...) end end - if old_chunk then - local old_component_storages = old_chunk.__component_storages - local old_component_fragments = old_chunk.__component_fragments - - 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] - local new_cs = new_component_storages[new_ci] - if new_cs then - new_cs[new_place] = old_cs[old_place] - end - end - - __detach_entity(entity) - end - __entity_chunks[entity_index] = new_chunk __entity_places[entity_index] = new_place __structural_changes = __structural_changes + 1 end + __defer_commit() return true, false end @@ -2092,6 +2132,8 @@ function evolved.assign(entity, fragment, ...) return false, false end + __defer() + do local component_indices = chunk.__component_indices local component_storages = chunk.__component_storages @@ -2127,6 +2169,7 @@ function evolved.assign(entity, fragment, ...) end end + __defer_commit() return true, false end @@ -2152,11 +2195,12 @@ function evolved.insert(entity, fragment, ...) local new_chunk = __chunk_with_fragment(old_chunk, fragment) - if old_chunk == new_chunk then + if not new_chunk or old_chunk == new_chunk then return false, false end __defer() + do local new_entities = new_chunk.__entities local new_component_indices = new_chunk.__component_indices @@ -2165,6 +2209,23 @@ function evolved.insert(entity, fragment, ...) local new_place = #new_entities + 1 new_entities[new_place] = entity + if old_chunk then + local old_component_storages = old_chunk.__component_storages + local old_component_fragments = old_chunk.__component_fragments + + 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] + new_cs[new_place] = old_cs[old_place] + end + end + + __detach_entity(entity) + end + do local new_component_index = new_component_indices[fragment] @@ -2196,28 +2257,12 @@ function evolved.insert(entity, fragment, ...) end end - if old_chunk then - local old_component_storages = old_chunk.__component_storages - local old_component_fragments = old_chunk.__component_fragments - - 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] - local new_cs = new_component_storages[new_ci] - if new_cs then - new_cs[new_place] = old_cs[old_place] - end - end - - __detach_entity(entity) - end - __entity_chunks[entity_index] = new_chunk __entity_places[entity_index] = new_place __structural_changes = __structural_changes + 1 end + __defer_commit() return true, false end @@ -2248,6 +2293,7 @@ function evolved.remove(entity, ...) end __defer() + do local old_fragment_set = old_chunk.__fragment_set local old_component_indices = old_chunk.__component_indices @@ -2281,8 +2327,8 @@ function evolved.remove(entity, ...) local new_f = new_component_fragments[i] local new_cs = new_component_storages[i] local old_ci = old_component_indices[new_f] - local old_cs = old_component_storages[old_ci] - if old_cs then + if old_ci then + local old_cs = old_component_storages[old_ci] new_cs[new_place] = old_cs[old_place] end end @@ -2297,6 +2343,7 @@ function evolved.remove(entity, ...) __structural_changes = __structural_changes + 1 end + __defer_commit() return true, false end @@ -2324,6 +2371,7 @@ function evolved.clear(entity) end __defer() + do local fragment_list = chunk.__fragment_list local component_indices = chunk.__component_indices @@ -2347,6 +2395,7 @@ function evolved.clear(entity) __structural_changes = __structural_changes + 1 end + __defer_commit() return true, false end @@ -2375,6 +2424,7 @@ function evolved.destroy(entity) end __defer() + do local fragment_list = chunk.__fragment_list local component_indices = chunk.__component_indices @@ -2399,6 +2449,7 @@ function evolved.destroy(entity) __structural_changes = __structural_changes + 1 end + __defer_commit() return true, false end @@ -2406,9 +2457,15 @@ end ---@param entity evolved.entity ---@param fragments evolved.fragment[] ---@param components? any[] ----@return boolean is_set +---@return boolean is_any_set ---@return boolean is_deferred function evolved.multi_set(entity, fragments, components) + local fragment_count = #fragments + + if fragment_count == 0 then + return false, false + end + if not components then components = __EMPTY_COMPONENT_LIST end @@ -2418,15 +2475,184 @@ function evolved.multi_set(entity, fragments, components) return false, true end - error('not implemented, yet', 2) + local entity_index = entity % 0x100000 + + if __freelist_ids[entity_index] ~= entity then + return false, false + end + + local old_chunk = __entity_chunks[entity_index] + local old_place = __entity_places[entity_index] + + local new_chunk = __chunk_with_fragment_list(old_chunk, fragments) + + if not new_chunk then + return false, false + end + + __defer() + + if old_chunk == new_chunk then + local old_fragment_set = old_chunk.__fragment_set + local old_component_indices = old_chunk.__component_indices + local old_component_storages = old_chunk.__component_storages + + for i = 1, fragment_count do + local fragment = fragments[i] + if old_fragment_set[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] + local old_component = old_component_storage[old_place] + + if old_chunk.__has_defaults_or_constructs 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 + + old_component_storage[old_place] = new_component + + if old_chunk.__has_set_or_assign_hooks then + __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 + + old_component_storage[old_place] = new_component + + if old_chunk.__has_set_or_assign_hooks then + __fragment_call_set_and_assign_hooks(entity, fragment, new_component, old_component) + end + end + else + if old_chunk.__has_set_or_assign_hooks then + __fragment_call_set_and_assign_hooks(entity, fragment) + end + end + end + end + else + local new_entities = new_chunk.__entities + local new_component_indices = new_chunk.__component_indices + local new_component_storages = new_chunk.__component_storages + + local old_fragment_set = old_chunk and old_chunk.__fragment_set or __EMPTY_FRAGMENT_SET + + local new_place = #new_entities + 1 + new_entities[new_place] = entity + + if old_chunk then + local old_component_storages = old_chunk.__component_storages + local old_component_fragments = old_chunk.__component_fragments + + 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] + new_cs[new_place] = old_cs[old_place] + end + end + + __detach_entity(entity) + end + + for i = 1, fragment_count do + local fragment = fragments[i] + if old_fragment_set[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] + local old_component = new_component_storage[new_place] + + if new_chunk.__has_defaults_or_constructs 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 + + new_component_storage[new_place] = new_component + + if new_chunk.__has_set_or_assign_hooks then + __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 + + new_component_storage[new_place] = new_component + + if new_chunk.__has_set_or_assign_hooks then + __fragment_call_set_and_assign_hooks(entity, fragment, new_component, old_component) + end + end + else + if new_chunk.__has_set_or_assign_hooks then + __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 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 + + new_component_storage[new_place] = new_component + + if new_chunk.__has_set_or_insert_hooks then + __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 + + new_component_storage[new_place] = new_component + + if new_chunk.__has_set_or_insert_hooks then + __fragment_call_set_and_insert_hooks(entity, fragment, new_component) + end + end + else + if new_chunk.__has_set_or_insert_hooks then + __fragment_call_set_and_insert_hooks(entity, fragment) + end + end + end + end + + __entity_chunks[entity_index] = new_chunk + __entity_places[entity_index] = new_place + + __structural_changes = __structural_changes + 1 + end + + __defer_commit() + return true, false end ---@param entity evolved.entity ---@param fragments evolved.fragment[] ---@param components? any[] ----@return boolean is_assigned +---@return boolean is_any_assigned ---@return boolean is_deferred function evolved.multi_assign(entity, fragments, components) + local fragment_count = #fragments + + if fragment_count == 0 then + return false, false + end + if not components then components = __EMPTY_COMPONENT_LIST end @@ -2436,15 +2662,81 @@ function evolved.multi_assign(entity, fragments, components) return false, true end - error('not implemented, yet', 2) + local entity_index = entity % 0x100000 + + if __freelist_ids[entity_index] ~= entity then + return false, false + end + + local chunk = __entity_chunks[entity_index] + local place = __entity_places[entity_index] + + if not chunk or not __chunk_has_any_fragment_list(chunk, fragments) then + return false, false + end + + __defer() + + do + local fragment_set = chunk.__fragment_set + local component_indices = chunk.__component_indices + local component_storages = chunk.__component_storages + + for i = 1, fragment_count do + local fragment = fragments[i] + if fragment_set[fragment] then + local component_index = component_indices[fragment] + + if component_index then + local component_storage = component_storages[component_index] + local old_component = component_storage[place] + + if chunk.__has_defaults_or_constructs 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 + + component_storage[place] = new_component + + if chunk.__has_set_or_assign_hooks then + __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 + + component_storage[place] = new_component + + if chunk.__has_set_or_assign_hooks then + __fragment_call_set_and_assign_hooks(entity, fragment, new_component, old_component) + end + end + else + if chunk.__has_set_or_assign_hooks then + __fragment_call_set_and_assign_hooks(entity, fragment) + end + end + end + end + end + + __defer_commit() + return true, false end ---@param entity evolved.entity ---@param fragments evolved.fragment[] ---@param components? any[] ----@return boolean is_inserted +---@return boolean is_any_inserted ---@return boolean is_deferred function evolved.multi_insert(entity, fragments, components) + local fragment_count = #fragments + + if fragment_count == 0 then + return false, false + end + if not components then components = __EMPTY_COMPONENT_LIST end @@ -2454,20 +2746,182 @@ function evolved.multi_insert(entity, fragments, components) return false, true end - error('not implemented, yet', 2) + local entity_index = entity % 0x100000 + + if __freelist_ids[entity_index] ~= entity then + return false, false + end + + local old_chunk = __entity_chunks[entity_index] + local old_place = __entity_places[entity_index] + + local new_chunk = __chunk_with_fragment_list(old_chunk, fragments) + + if not new_chunk or old_chunk == new_chunk then + return false, false + end + + __defer() + + do + local new_entities = new_chunk.__entities + local new_component_indices = new_chunk.__component_indices + local new_component_storages = new_chunk.__component_storages + + local old_fragment_set = old_chunk and old_chunk.__fragment_set or __EMPTY_FRAGMENT_SET + + local new_place = #new_entities + 1 + new_entities[new_place] = entity + + if old_chunk then + local old_component_storages = old_chunk.__component_storages + local old_component_fragments = old_chunk.__component_fragments + + 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] + new_cs[new_place] = old_cs[old_place] + end + end + + __detach_entity(entity) + end + + for i = 1, fragment_count do + local fragment = fragments[i] + if not old_fragment_set[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 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 + + new_component_storage[new_place] = new_component + + if new_chunk.__has_set_or_insert_hooks then + __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 + + new_component_storage[new_place] = new_component + + if new_chunk.__has_set_or_insert_hooks then + __fragment_call_set_and_insert_hooks(entity, fragment, new_component) + end + end + else + if new_chunk.__has_set_or_insert_hooks then + __fragment_call_set_and_insert_hooks(entity, fragment) + end + end + end + end + + __entity_chunks[entity_index] = new_chunk + __entity_places[entity_index] = new_place + + __structural_changes = __structural_changes + 1 + end + + __defer_commit() + return true, false end ---@param entity evolved.entity ---@param fragments evolved.fragment[] ----@return boolean is_removed +---@return boolean is_all_removed ---@return boolean is_deferred function evolved.multi_remove(entity, fragments) + local fragment_count = #fragments + + if fragment_count == 0 then + return true, false + end + if __defer_depth > 0 then __defer_multi_remove(entity, fragments) return false, true end - error('not implemented, yet', 2) + local entity_index = entity % 0x100000 + + if __freelist_ids[entity_index] ~= entity then + return false, false + end + + local old_chunk = __entity_chunks[entity_index] + local old_place = __entity_places[entity_index] + + local new_chunk = __chunk_without_fragment_list(old_chunk, fragments) + + if old_chunk == new_chunk then + return true, false + end + + __defer() + + do + 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 + for i = 1, fragment_count do + local fragment = fragments[i] + if old_fragment_set[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] + local old_component = old_component_storage[old_place] + __fragment_call_remove_hook(entity, fragment, old_component) + else + __fragment_call_remove_hook(entity, fragment) + end + end + end + end + + if new_chunk then + local new_entities = new_chunk.__entities + local new_component_storages = new_chunk.__component_storages + local new_component_fragments = new_chunk.__component_fragments + + local new_place = #new_entities + 1 + new_entities[new_place] = entity + + 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] + new_cs[new_place] = old_cs[old_place] + end + end + + __detach_entity(entity) + + __entity_chunks[entity_index] = new_chunk + __entity_places[entity_index] = new_place + else + __detach_entity(entity) + end + + __structural_changes = __structural_changes + 1 + end + + __defer_commit() + return true, false end ---@param query evolved.query