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