clear pair chunks when pair fragments are destroyed

This commit is contained in:
BlackMATov
2025-07-11 18:44:01 +07:00
parent f7e4dfb30c
commit d86c85d522
2 changed files with 340 additions and 78 deletions

View File

@@ -910,7 +910,202 @@ do
end end
end end
do
do
local p, s = evo.id(2)
local e = evo.builder()
:set(s)
:set(evo.pair(p, s), 42)
:spawn()
evo.destroy(p)
assert(evo.alive(e))
assert(evo.has(e, s))
assert(not evo.has(e, evo.pair(p, s)))
assert(evo.get(e, evo.pair(p, s)) == nil)
end
do
local p, s = evo.id(2)
local e = evo.builder()
:set(p)
:set(evo.pair(p, s), 42)
:spawn()
evo.destroy(s)
assert(evo.alive(e))
assert(evo.has(e, p))
assert(not evo.has(e, evo.pair(p, s)))
assert(evo.get(e, evo.pair(p, s)) == nil)
end
do
local p, s = evo.id(2)
evo.set(p, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY)
local e = evo.builder()
:set(evo.pair(p, s), 42)
:spawn()
evo.destroy(p)
assert(not evo.alive(e))
end
do
local p, s = evo.id(2)
evo.set(s, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY)
local e = evo.builder()
:set(evo.pair(p, s), 42)
:spawn()
evo.destroy(s)
assert(not evo.alive(e))
end
end
do
local p1, s1 = evo.id(2)
local e0 = evo.builder()
:destruction_policy(evo.DESTRUCTION_POLICY_DESTROY_ENTITY)
:spawn()
local e1 = evo.builder()
:set(evo.pair(p1, e0), 11)
:destruction_policy(evo.DESTRUCTION_POLICY_DESTROY_ENTITY)
:spawn()
local e2 = evo.builder()
:set(evo.pair(e1, s1), 22)
:destruction_policy(evo.DESTRUCTION_POLICY_DESTROY_ENTITY)
:spawn()
local e3 = evo.builder()
:set(evo.pair(e2, e2), 33)
:spawn()
evo.destroy(e0)
assert(not evo.alive(e1))
assert(not evo.alive(e2))
assert(not evo.alive(e3))
end
do
do
local f, p, s = evo.id(3)
local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn()
evo.destroy(f)
assert(evo.get(e, f) == nil)
assert(evo.get(e, evo.pair(p, s)) == 42)
end
do
local f, p, s = evo.id(3)
local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn()
evo.destroy(p)
assert(evo.get(e, f) == 21)
assert(evo.get(e, evo.pair(p, s)) == nil)
end
do
local f, p, s = evo.id(3)
local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn()
evo.destroy(s)
assert(evo.get(e, f) == 21)
assert(evo.get(e, evo.pair(p, s)) == nil)
end
do
local f, p, s = evo.id(3)
local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn()
evo.destroy(p, s)
assert(evo.get(e, f) == 21)
assert(evo.get(e, evo.pair(p, s)) == nil)
end
do
local f, p, s = evo.id(3)
local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn()
evo.destroy(f, p, s)
assert(evo.alive(e) and evo.empty(e))
end
end
do
do
local f, p, s = evo.id(3)
evo.set(p, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY)
local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn()
evo.destroy(p)
assert(not evo.alive(e))
end
do
local f, p, s = evo.id(3)
evo.set(s, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY)
local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn()
evo.destroy(p)
assert(evo.get(e, f) == 21)
assert(evo.get(e, evo.pair(p, s)) == nil)
end
do
local f, p, s = evo.id(3)
evo.set(p, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY)
local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn()
evo.destroy(s)
assert(evo.get(e, f) == 21)
assert(evo.get(e, evo.pair(p, s)) == nil)
end
do
local f, p, s = evo.id(3)
evo.set(s, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY)
local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn()
evo.destroy(s)
assert(not evo.alive(e))
end
end
do
local f, p, s = evo.id(3)
local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn()
evo.destroy(evo.pair(p, s))
evo.destroy(evo.pair(evo.ANY, s))
evo.destroy(evo.pair(p, evo.ANY))
evo.destroy(evo.pair(evo.ANY, evo.ANY))
assert(evo.get(e, f) == 21)
assert(evo.get(e, evo.pair(p, s)) == 42)
end
-- TODO: -- TODO:
-- How should required fragments work with pairs? -- How should required fragments work with pairs?
-- How can we set defaults for paired fragments? -- How can we set defaults for paired fragments?
-- Prevent setting wildcard pairs to entities! -- Prevent setting wildcard pairs to entities!
-- Should we have destruction policies analog for pairs?
-- Do not forget to purge chunks with pairs!
-- Should we call hooks for pairs?

View File

@@ -150,6 +150,8 @@ local __defer_bytecode = {} ---@type any[]
local __root_chunks = {} ---@type table<evolved.fragment, evolved.chunk> local __root_chunks = {} ---@type table<evolved.fragment, evolved.chunk>
local __major_chunks = {} ---@type table<evolved.fragment, evolved.assoc_list> local __major_chunks = {} ---@type table<evolved.fragment, evolved.assoc_list>
local __minor_chunks = {} ---@type table<evolved.fragment, evolved.assoc_list> local __minor_chunks = {} ---@type table<evolved.fragment, evolved.assoc_list>
local __primary_chunks = {} ---@type table<evolved.fragment, evolved.assoc_list>
local __secondary_chunks = {} ---@type table<evolved.fragment, evolved.assoc_list>
local __pinned_chunks = {} ---@type table<evolved.chunk, integer> local __pinned_chunks = {} ---@type table<evolved.chunk, integer>
@@ -1204,6 +1206,7 @@ local __new_chunk
local __update_chunk_tags local __update_chunk_tags
local __update_chunk_flags local __update_chunk_flags
local __trace_major_chunks local __trace_major_chunks
local __trace_minor_chunks
local __update_major_chunks_hook local __update_major_chunks_hook
local __update_major_chunks_trace local __update_major_chunks_trace
@@ -1365,6 +1368,27 @@ function __new_chunk(chunk_parent, chunk_fragment)
__assoc_list_insert(minor_chunks, chunk) __assoc_list_insert(minor_chunks, chunk)
end end
for i = 1, chunk_pair_count do
local pair = chunk_pair_list[i]
local primary, secondary = __evolved_unpair(pair)
local primary_chunks = __primary_chunks[primary]
local secondary_chunks = __secondary_chunks[secondary]
if not primary_chunks then
primary_chunks = __assoc_list_new(4)
__primary_chunks[primary] = primary_chunks
end
if not secondary_chunks then
secondary_chunks = __assoc_list_new(4)
__secondary_chunks[secondary] = secondary_chunks
end
__assoc_list_insert(primary_chunks, chunk)
__assoc_list_insert(secondary_chunks, chunk)
end
__update_chunk_tags(chunk) __update_chunk_tags(chunk)
__update_chunk_flags(chunk) __update_chunk_flags(chunk)
@@ -1488,7 +1512,7 @@ function __update_chunk_flags(chunk)
end end
---@param major evolved.fragment ---@param major evolved.fragment
---@param trace fun(chunk: evolved.chunk, ...: any): boolean ---@param trace fun(chunk: evolved.chunk, ...: any)
---@param ... any additional trace arguments ---@param ... any additional trace arguments
function __trace_major_chunks(major, trace, ...) function __trace_major_chunks(major, trace, ...)
---@type evolved.chunk[] ---@type evolved.chunk[]
@@ -1512,10 +1536,11 @@ function __trace_major_chunks(major, trace, ...)
while chunk_stack_size > 0 do while chunk_stack_size > 0 do
local chunk = chunk_stack[chunk_stack_size] local chunk = chunk_stack[chunk_stack_size]
trace(chunk, ...)
chunk_stack[chunk_stack_size] = nil chunk_stack[chunk_stack_size] = nil
chunk_stack_size = chunk_stack_size - 1 chunk_stack_size = chunk_stack_size - 1
if trace(chunk, ...) then
local chunk_child_list = chunk.__child_list local chunk_child_list = chunk.__child_list
local chunk_child_count = chunk.__child_count local chunk_child_count = chunk.__child_count
@@ -1525,6 +1550,67 @@ function __trace_major_chunks(major, trace, ...)
chunk_stack_size = chunk_stack_size + chunk_child_count chunk_stack_size = chunk_stack_size + chunk_child_count
end end
__release_table(__table_pool_tag.chunk_list, chunk_stack, true)
end
---@param minor evolved.fragment
---@param trace fun(chunk: evolved.chunk, ...: any)
---@param ... any additional trace arguments
function __trace_minor_chunks(minor, trace, ...)
---@type evolved.chunk[]
local chunk_stack = __acquire_table(__table_pool_tag.chunk_list)
local chunk_stack_size = 0
do
local minor_chunks = __minor_chunks[minor]
local minor_chunk_list = minor_chunks and minor_chunks.__item_list --[=[@as evolved.chunk[]]=]
local minor_chunk_count = minor_chunks and minor_chunks.__item_count or 0 --[[@as integer]]
if minor_chunk_count > 0 then
__lua_table_move(
minor_chunk_list, 1, minor_chunk_count,
chunk_stack_size + 1, chunk_stack)
chunk_stack_size = chunk_stack_size + minor_chunk_count
end
end
do
local primary_chunks = __primary_chunks[minor]
local primary_chunk_list = primary_chunks and primary_chunks.__item_list --[=[@as evolved.chunk[]]=]
local primary_chunk_count = primary_chunks and primary_chunks.__item_count or 0 --[[@as integer]]
if primary_chunk_count > 0 then
__lua_table_move(
primary_chunk_list, 1, primary_chunk_count,
chunk_stack_size + 1, chunk_stack)
chunk_stack_size = chunk_stack_size + primary_chunk_count
end
end
do
local secondary_chunks = __secondary_chunks[minor]
local secondary_chunk_list = secondary_chunks and secondary_chunks.__item_list --[=[@as evolved.chunk[]]=]
local secondary_chunk_count = secondary_chunks and secondary_chunks.__item_count or 0 --[[@as integer]]
if secondary_chunk_count > 0 then
__lua_table_move(
secondary_chunk_list, 1, secondary_chunk_count,
chunk_stack_size + 1, chunk_stack)
chunk_stack_size = chunk_stack_size + secondary_chunk_count
end
end
while chunk_stack_size > 0 do
local chunk = chunk_stack[chunk_stack_size]
trace(chunk, ...)
chunk_stack[chunk_stack_size] = nil
chunk_stack_size = chunk_stack_size - 1
end end
__release_table(__table_pool_tag.chunk_list, chunk_stack, true) __release_table(__table_pool_tag.chunk_list, chunk_stack, true)
@@ -1536,11 +1622,9 @@ function __update_major_chunks_hook(major)
end end
---@param chunk evolved.chunk ---@param chunk evolved.chunk
---@return boolean
function __update_major_chunks_trace(chunk) function __update_major_chunks_trace(chunk)
__update_chunk_tags(chunk) __update_chunk_tags(chunk)
__update_chunk_flags(chunk) __update_chunk_flags(chunk)
return true
end end
--- ---
@@ -1646,18 +1730,6 @@ local function __chunk_without_fragment(chunk, fragment)
sib_chunk = sib_chunk.__parent sib_chunk = sib_chunk.__parent
end end
local ini_fragment_list = chunk.__fragment_list
local ini_fragment_count = chunk.__fragment_count
local lst_fragment_index = sib_chunk and sib_chunk.__fragment_count + 2 or 2
for ini_fragment_index = lst_fragment_index, ini_fragment_count do
local ini_fragment = ini_fragment_list[ini_fragment_index]
if not __is_pair(ini_fragment) then
sib_chunk = __chunk_with_fragment(sib_chunk, ini_fragment)
end
end
if sib_chunk then if sib_chunk then
chunk.__without_fragment_edges[fragment] = sib_chunk chunk.__without_fragment_edges[fragment] = sib_chunk
sib_chunk.__with_fragment_edges[fragment] = chunk sib_chunk.__with_fragment_edges[fragment] = chunk
@@ -1678,20 +1750,16 @@ local function __chunk_without_fragment(chunk, fragment)
sib_chunk = sib_chunk.__parent sib_chunk = sib_chunk.__parent
end end
local ini_fragment_list = chunk.__fragment_list local ini_pair_list = chunk.__pair_list
local ini_fragment_count = chunk.__fragment_count local ini_pair_count = chunk.__pair_count
local lst_fragment_index = sib_chunk and sib_chunk.__fragment_count + 2 or 2 local lst_pair_index = sib_chunk and sib_chunk.__pair_count + 2 or 2
for ini_fragment_index = lst_fragment_index, ini_fragment_count do for ini_pair_index = lst_pair_index, ini_pair_count do
local ini_fragment = ini_fragment_list[ini_fragment_index] local ini_pair = ini_pair_list[ini_pair_index]
if not __is_pair(ini_fragment) then local _, ini_secondary_index = __evolved_unpack(ini_pair)
sib_chunk = __chunk_with_fragment(sib_chunk, ini_fragment) if ini_secondary_index ~= secondary_index then
else sib_chunk = __chunk_with_fragment(sib_chunk, ini_pair)
local _, ini_secondary = __evolved_unpair(ini_fragment)
if secondary ~= ini_secondary then
sib_chunk = __chunk_with_fragment(sib_chunk, ini_fragment)
end
end end
end end
@@ -1715,20 +1783,16 @@ local function __chunk_without_fragment(chunk, fragment)
sib_chunk = sib_chunk.__parent sib_chunk = sib_chunk.__parent
end end
local ini_fragment_list = chunk.__fragment_list local ini_pair_list = chunk.__pair_list
local ini_fragment_count = chunk.__fragment_count local ini_pair_count = chunk.__pair_count
local lst_fragment_index = sib_chunk and sib_chunk.__fragment_count + 2 or 2 local lst_pair_index = sib_chunk and sib_chunk.__pair_count + 2 or 2
for ini_fragment_index = lst_fragment_index, ini_fragment_count do for ini_pair_index = lst_pair_index, ini_pair_count do
local ini_fragment = ini_fragment_list[ini_fragment_index] local ini_pair = ini_pair_list[ini_pair_index]
if not __is_pair(ini_fragment) then local ini_primary_index, _ = __evolved_unpack(ini_pair)
sib_chunk = __chunk_with_fragment(sib_chunk, ini_fragment) if ini_primary_index ~= primary_index then
else sib_chunk = __chunk_with_fragment(sib_chunk, ini_pair)
local ini_primary, _ = __evolved_unpair(ini_fragment)
if primary ~= ini_primary then
sib_chunk = __chunk_with_fragment(sib_chunk, ini_fragment)
end
end end
end end
@@ -2889,7 +2953,7 @@ local function __destroy_entity_list(entity_list, entity_count)
for i = 1, entity_count do for i = 1, entity_count do
local entity = entity_list[i] local entity = entity_list[i]
local entity_index = entity % 0x100000 local entity_index = entity % 2 ^ 20
if __freelist_ids[entity_index] ~= entity then if __freelist_ids[entity_index] ~= entity then
-- this entity is not alive, nothing to purge -- this entity is not alive, nothing to purge
@@ -2990,22 +3054,16 @@ local function __destroy_fragment_list(fragment_list, fragment_count)
destroy_entity_policy_fragment_count = destroy_entity_policy_fragment_count + 1 destroy_entity_policy_fragment_count = destroy_entity_policy_fragment_count + 1
destroy_entity_policy_fragment_list[destroy_entity_policy_fragment_count] = processing_fragment destroy_entity_policy_fragment_list[destroy_entity_policy_fragment_count] = processing_fragment
local minor_chunks = __minor_chunks[processing_fragment] __trace_minor_chunks(processing_fragment, function(chunk)
local minor_chunk_list = minor_chunks and minor_chunks.__item_list --[=[@as evolved.chunk[]]=] local chunk_entity_list = chunk.__entity_list
local minor_chunk_count = minor_chunks and minor_chunks.__item_count or 0 --[[@as integer]] local chunk_entity_count = chunk.__entity_count
for minor_chunk_index = 1, minor_chunk_count do
local minor_chunk = minor_chunk_list[minor_chunk_index]
local minor_chunk_entity_list = minor_chunk.__entity_list
local minor_chunk_entity_count = minor_chunk.__entity_count
__lua_table_move( __lua_table_move(
minor_chunk_entity_list, 1, minor_chunk_entity_count, chunk_entity_list, 1, chunk_entity_count,
processing_fragment_stack_size + 1, processing_fragment_stack) processing_fragment_stack_size + 1, processing_fragment_stack)
processing_fragment_stack_size = processing_fragment_stack_size + minor_chunk_entity_count processing_fragment_stack_size = processing_fragment_stack_size + chunk_entity_count
end end)
elseif processing_fragment_destruction_policy == __DESTRUCTION_POLICY_REMOVE_FRAGMENT then elseif processing_fragment_destruction_policy == __DESTRUCTION_POLICY_REMOVE_FRAGMENT then
remove_fragment_policy_fragment_count = remove_fragment_policy_fragment_count + 1 remove_fragment_policy_fragment_count = remove_fragment_policy_fragment_count + 1
remove_fragment_policy_fragment_list[remove_fragment_policy_fragment_count] = processing_fragment remove_fragment_policy_fragment_list[remove_fragment_policy_fragment_count] = processing_fragment
@@ -3023,14 +3081,7 @@ local function __destroy_fragment_list(fragment_list, fragment_count)
for i = 1, destroy_entity_policy_fragment_count do for i = 1, destroy_entity_policy_fragment_count do
local fragment = destroy_entity_policy_fragment_list[i] local fragment = destroy_entity_policy_fragment_list[i]
local minor_chunks = __minor_chunks[fragment] __trace_minor_chunks(fragment, __chunk_clear)
local minor_chunk_list = minor_chunks and minor_chunks.__item_list --[=[@as evolved.chunk[]]=]
local minor_chunk_count = minor_chunks and minor_chunks.__item_count or 0 --[[@as integer]]
for minor_chunk_index = 1, minor_chunk_count do
local minor_chunk = minor_chunk_list[minor_chunk_index]
__chunk_clear(minor_chunk)
end
end end
__release_table(__table_pool_tag.fragment_list, destroy_entity_policy_fragment_list) __release_table(__table_pool_tag.fragment_list, destroy_entity_policy_fragment_list)
@@ -3042,14 +3093,10 @@ local function __destroy_fragment_list(fragment_list, fragment_count)
for i = 1, remove_fragment_policy_fragment_count do for i = 1, remove_fragment_policy_fragment_count do
local fragment = remove_fragment_policy_fragment_list[i] local fragment = remove_fragment_policy_fragment_list[i]
local minor_chunks = __minor_chunks[fragment] __trace_minor_chunks(fragment, __chunk_remove,
local minor_chunk_list = minor_chunks and minor_chunks.__item_list --[=[@as evolved.chunk[]]=] fragment,
local minor_chunk_count = minor_chunks and minor_chunks.__item_count or 0 --[[@as integer]] __evolved_pair(fragment, __ANY),
__evolved_pair(__ANY, fragment))
for minor_chunk_index = 1, minor_chunk_count do
local minor_chunk = minor_chunk_list[minor_chunk_index]
__chunk_remove(minor_chunk, fragment)
end
end end
__release_table(__table_pool_tag.fragment_list, remove_fragment_policy_fragment_list) __release_table(__table_pool_tag.fragment_list, remove_fragment_policy_fragment_list)
@@ -5368,6 +5415,10 @@ function __evolved_destroy(...)
__evolved_defer() __evolved_defer()
do do
local minor_chunks = __minor_chunks
local primary_chunks = __primary_chunks
local secondary_chunks = __secondary_chunks
local purging_entity_list = __acquire_table(__table_pool_tag.entity_list) local purging_entity_list = __acquire_table(__table_pool_tag.entity_list)
local purging_entity_count = 0 local purging_entity_count = 0
@@ -5377,14 +5428,19 @@ function __evolved_destroy(...)
for argument_index = 1, argument_count do for argument_index = 1, argument_count do
---@type evolved.entity ---@type evolved.entity
local entity = __lua_select(argument_index, ...) local entity = __lua_select(argument_index, ...)
local entity_index = entity % 0x100000 local entity_index = entity % 2 ^ 20
if __is_pair(entity) then if __is_pair(entity) then
-- pairs cannot be destroyed, nothing to do -- pairs cannot be destroyed, nothing to do
elseif __freelist_ids[entity_index] ~= entity then elseif __freelist_ids[entity_index] ~= entity then
-- this entity is not alive, nothing to destroy -- this entity is not alive, nothing to destroy
else else
if not __minor_chunks[entity] then local is_fragment =
minor_chunks[entity] or
primary_chunks[entity] or
secondary_chunks[entity]
if not is_fragment then
purging_entity_count = purging_entity_count + 1 purging_entity_count = purging_entity_count + 1
purging_entity_list[purging_entity_count] = entity purging_entity_list[purging_entity_count] = entity
else else
@@ -5555,6 +5611,10 @@ function __evolved_batch_destroy(...)
__evolved_defer() __evolved_defer()
do do
local minor_chunks = __minor_chunks
local primary_chunks = __primary_chunks
local secondary_chunks = __secondary_chunks
local clearing_chunk_list = __acquire_table(__table_pool_tag.chunk_list) local clearing_chunk_list = __acquire_table(__table_pool_tag.chunk_list)
local clearing_chunk_count = 0 local clearing_chunk_count = 0
@@ -5567,9 +5627,11 @@ function __evolved_batch_destroy(...)
for argument_index = 1, argument_count do for argument_index = 1, argument_count do
---@type evolved.query ---@type evolved.query
local query = __lua_select(argument_index, ...) local query = __lua_select(argument_index, ...)
local query_index = query % 0x100000 local query_index = query % 2 ^ 20
if __freelist_ids[query_index] ~= query then if __is_pair(query) then
-- pairs cannot be used as queries
elseif __freelist_ids[query_index] ~= query then
-- this query is not alive, nothing to destroy -- this query is not alive, nothing to destroy
else else
for chunk, entity_list, entity_count in __evolved_execute(query) do for chunk, entity_list, entity_count in __evolved_execute(query) do
@@ -5579,7 +5641,12 @@ function __evolved_batch_destroy(...)
for i = 1, entity_count do for i = 1, entity_count do
local entity = entity_list[i] local entity = entity_list[i]
if not __minor_chunks[entity] then local is_fragment =
minor_chunks[entity] or
primary_chunks[entity] or
secondary_chunks[entity]
if not is_fragment then
purging_entity_count = purging_entity_count + 1 purging_entity_count = purging_entity_count + 1
purging_entity_list[purging_entity_count] = entity purging_entity_list[purging_entity_count] = entity
else else