diff --git a/README.md b/README.md index 90be204..02164d9 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ registry.has -> entity -> entity -> (boolean) registry.has_all -> entity -> entity... -> (boolean) registry.has_any -> entity -> entity... -> (boolean) registry.apply -> entity -> entity -> (any -> any) -> (boolean) -registry.batch_apply -> query -> entity -> (any -> any) -> (boolean) +registry.batch_apply -> query -> entity -> (any -> any) -> (integer) registry.assign -> entity -> entity -> any -> (boolean) registry.batch_assign -> query -> entity -> any -> (integer) registry.insert -> entity -> entity -> any -> (boolean) diff --git a/develop/untests/registry_untests.lua b/develop/untests/registry_untests.lua index efc73cd..a30f9c4 100644 --- a/develop/untests/registry_untests.lua +++ b/develop/untests/registry_untests.lua @@ -506,6 +506,64 @@ do end end +do + local f1, f2 = evo.registry.entity(), evo.registry.entity() + + do + local e1 = evo.registry.entity():set(f1, 10) + local e2 = evo.registry.entity():set(f1, 15) + local e3 = evo.registry.entity():set(f1, 20):set(f2, 40) + local e4 = evo.registry.entity():set(f1, 25):set(f2, 45) + + local q = evo.registry.query(f1) + + assert(2 == evo.registry.batch_insert(q, f2, 42)) + assert(e1:get(f1) == 10 and e2:get(f1) == 15 and e3:get(f1) == 20 and e4:get(f1) == 25) + assert(e1:get(f2) == 42 and e2:get(f2) == 42 and e3:get(f2) == 40 and e4:get(f2) == 45) + end +end + +do + local f1, f2, f3 = evo.registry.entity(), evo.registry.entity(), evo.registry.entity() + + do + local e1 = evo.registry.entity():set(f1, 10) + local e2 = evo.registry.entity():set(f1, 15) + local e3 = evo.registry.entity():set(f1, 20):set(f2, 40) + local e4 = evo.registry.entity():set(f1, 25):set(f2, 45):set(f3, 55) + local e5 = evo.registry.entity():set(f3, 65) + + do + local q = evo.registry.query(f2) + + assert(2 == evo.registry.batch_remove(q, f1)) + assert(e1.__chunk == evo.registry.chunk(f1)) + assert(e2.__chunk == evo.registry.chunk(f1)) + assert(e3.__chunk == evo.registry.chunk(f2)) + assert(e4.__chunk == evo.registry.chunk(f2, f3)) + assert(e5.__chunk == evo.registry.chunk(f3)) + assert(e1:get(f1) == 10 and e2:get(f1) == 15) + assert(e3:get(f2) == 40 and e4:get(f2) == 45 and e4:get(f3) == 55) + + assert(2 == evo.registry.batch_remove(q, f2)) + assert(e1.__chunk == evo.registry.chunk(f1)) + assert(e2.__chunk == evo.registry.chunk(f1)) + assert(e3.__chunk == nil) + assert(e4.__chunk == evo.registry.chunk(f3)) + assert(e5.__chunk == evo.registry.chunk(f3)) + assert(e1:get(f1) == 10 and e2:get(f1) == 15) + assert(e3:get(f2) == nil and e4:get(f2) == nil and e4:get(f3) == 55) + assert(e5:get(f3) == 65) + end + + do + local q = evo.registry.query(f3) + assert(0 == evo.registry.batch_remove(q, f1)) + assert(2 == evo.registry.batch_remove(q, f3)) + end + end +end + for _ = 1, 100 do local insert_fragments = {} ---@type evolved.entity[] local insert_fragment_count = math.random(0, 10) diff --git a/evolved/registry.lua b/evolved/registry.lua index 9912b3b..070e922 100644 --- a/evolved/registry.lua +++ b/evolved/registry.lua @@ -620,26 +620,31 @@ function registry.insert(entity, fragment, component) return false end - local old_index_in_chunk = entity.__index_in_chunk - local new_index_in_chunk = #new_chunk.__entities + 1 - - __changes = __changes + 1 - new_chunk.__changes = new_chunk.__changes + 1 - - new_chunk.__entities[new_index_in_chunk] = entity - new_chunk.__components[fragment][new_index_in_chunk] = component - - if old_chunk ~= nil then - for old_f, old_cs in pairs(old_chunk.__components) do - local new_cs = new_chunk.__components[old_f] - new_cs[new_index_in_chunk] = old_cs[old_index_in_chunk] - end - - __detach_entity(entity) + do + __changes = __changes + 1 end - entity.__chunk = new_chunk - entity.__index_in_chunk = new_index_in_chunk + if new_chunk ~= nil then + local old_index_in_chunk = entity.__index_in_chunk + local new_index_in_chunk = #new_chunk.__entities + 1 + + new_chunk.__changes = new_chunk.__changes + 1 + new_chunk.__entities[new_index_in_chunk] = entity + new_chunk.__components[fragment][new_index_in_chunk] = component + + if old_chunk ~= nil then + for old_f, old_cs in pairs(old_chunk.__components) do + local new_cs = new_chunk.__components[old_f] + new_cs[new_index_in_chunk] = old_cs[old_index_in_chunk] + end + __detach_entity(entity) + end + + entity.__chunk = new_chunk + entity.__index_in_chunk = new_index_in_chunk + else + __detach_entity(entity) + end return true end @@ -649,9 +654,68 @@ end ---@param component any ---@return integer inserted_count function registry.batch_insert(query, fragment, component) - error('not impl yet', 2) -end + component = component == nil and true or component + ---@type evolved.chunk[] + local chunks = {} + + for chunk in registry.execute(query) do + if not __chunk_has_fragment(chunk, fragment) then + chunks[#chunks + 1] = chunk + end + end + + local inserted_count = 0 + + for _, old_chunk in ipairs(chunks) do + local new_chunk = __chunk_with_fragment(old_chunk, fragment) + + do + local changes = #old_chunk.__entities + + __changes = __changes + changes + inserted_count = inserted_count + changes + + old_chunk.__changes = old_chunk.__changes + changes + + if new_chunk ~= nil then + new_chunk.__changes = new_chunk.__changes + changes + end + end + + if new_chunk ~= nil then + for _, entity in ipairs(old_chunk.__entities) do + local new_index_in_chunk = #new_chunk.__entities + 1 + + new_chunk.__entities[new_index_in_chunk] = entity + new_chunk.__components[fragment][new_index_in_chunk] = component + + entity.__chunk = new_chunk + entity.__index_in_chunk = new_index_in_chunk + end + + for old_f, old_cs in pairs(old_chunk.__components) do + local new_cs = new_chunk.__components[old_f] + for i = 1, #old_cs do new_cs[#new_cs + 1] = old_cs[i] end + end + else + for _, entity in ipairs(old_chunk.__entities) do + entity.__chunk = nil + entity.__index_in_chunk = 0 + end + end + + do + old_chunk.__entities = {} + + for old_f, _ in pairs(old_chunk.__components) do + old_chunk.__components[old_f] = {} + end + end + end + + return inserted_count +end ---@param entity evolved.entity ---@param ... evolved.entity fragments @@ -668,39 +732,96 @@ function registry.remove(entity, ...) return false end - if new_chunk == nil then - __detach_entity(entity) - return true + do + __changes = __changes + 1 end - local old_index_in_chunk = entity.__index_in_chunk - local new_index_in_chunk = #new_chunk.__entities + 1 + if new_chunk ~= nil then + local old_index_in_chunk = entity.__index_in_chunk + local new_index_in_chunk = #new_chunk.__entities + 1 - __changes = __changes + 1 - new_chunk.__changes = new_chunk.__changes + 1 + new_chunk.__changes = new_chunk.__changes + 1 + new_chunk.__entities[new_index_in_chunk] = entity - new_chunk.__entities[new_index_in_chunk] = entity - - if old_chunk ~= nil then - for new_f, new_cs in pairs(new_chunk.__components) do - local old_cs = old_chunk.__components[new_f] - new_cs[new_index_in_chunk] = old_cs[old_index_in_chunk] + if old_chunk ~= nil then + for new_f, new_cs in pairs(new_chunk.__components) do + local old_cs = old_chunk.__components[new_f] + new_cs[new_index_in_chunk] = old_cs[old_index_in_chunk] + end + __detach_entity(entity) end + entity.__chunk = new_chunk + entity.__index_in_chunk = new_index_in_chunk + else __detach_entity(entity) end - entity.__chunk = new_chunk - entity.__index_in_chunk = new_index_in_chunk - return true end ---@param query evolved.query ---@param ... evolved.entity fragments ----@return boolean removed_count +---@return integer removed_count function registry.batch_remove(query, ...) - error('not impl yet', 2) + ---@type evolved.chunk[] + local chunks = {} + + for chunk in registry.execute(query) do + if __chunk_has_any_fragments(chunk, ...) then + chunks[#chunks + 1] = chunk + end + end + + local removed_count = 0 + + for _, old_chunk in ipairs(chunks) do + local new_chunk = __chunk_without_fragments(old_chunk, ...) + + do + local changes = #old_chunk.__entities + + __changes = __changes + changes + removed_count = removed_count + changes + + old_chunk.__changes = old_chunk.__changes + changes + + if new_chunk ~= nil then + new_chunk.__changes = new_chunk.__changes + changes + end + end + + if new_chunk ~= nil then + for _, entity in ipairs(old_chunk.__entities) do + local new_index_in_chunk = #new_chunk.__entities + 1 + + new_chunk.__entities[new_index_in_chunk] = entity + + entity.__chunk = new_chunk + entity.__index_in_chunk = new_index_in_chunk + end + + for new_f, new_cs in pairs(new_chunk.__components) do + local old_cs = old_chunk.__components[new_f] + for i = 1, #old_cs do new_cs[#new_cs + 1] = old_cs[i] end + end + else + for _, entity in ipairs(old_chunk.__entities) do + entity.__chunk = nil + entity.__index_in_chunk = 0 + end + end + + do + old_chunk.__entities = {} + + for old_f, _ in pairs(old_chunk.__components) do + old_chunk.__components[old_f] = {} + end + end + end + + return removed_count end ---@param entity evolved.entity @@ -719,7 +840,7 @@ function registry.detach(entity) end ---@param query evolved.query ----@return boolean detached_count +---@return integer detached_count function registry.batch_detach(query) error('not impl yet', 2) end @@ -854,7 +975,9 @@ function registry.execute(query) end end - return matched_chunk + if #matched_chunk.__entities > 0 then + return matched_chunk + end end end end