Traverse refs for deletion

This commit is contained in:
Ukendio 2024-10-01 17:30:51 +02:00
parent 8aace714fc
commit ca3cc06400
2 changed files with 103 additions and 59 deletions

View file

@ -26,8 +26,7 @@ type GraphEdges = Map<i53, GraphEdge>
type GraphNode = { type GraphNode = {
add: GraphEdges, add: GraphEdges,
remove: GraphEdges, remove: GraphEdges,
add_ref: GraphEdge?, refs: GraphEdge
remove_ref: GraphEdge?,
} }
export type Archetype = { export type Archetype = {
@ -521,7 +520,7 @@ local function archetype_create(world: World, types: { i24 }, ty, prev: i53?): A
local archetype: Archetype = { local archetype: Archetype = {
columns = columns, columns = columns,
node = { add = {}, remove = {} }, node = { add = {}, remove = {}, refs = {} },
entities = {}, entities = {},
id = archetype_id, id = archetype_id,
records = records, records = records,
@ -624,17 +623,20 @@ local function archetype_ensure_edge(world, edges, id): GraphEdge
return edge return edge
end end
local function init_edge_for_add(world, archetype, edge, id, to) local function init_edge_for_add(world, archetype, edge: GraphEdge, id, to)
archetype_init_edge(archetype, edge, id, to) archetype_init_edge(archetype, edge, id, to)
archetype_ensure_edge(world, archetype.node.add, id) archetype_ensure_edge(world, archetype.node.add, id)
if archetype ~= to then if archetype ~= to then
local to_add_ref = to.node.add_ref local to_refs = to.node.refs
edge.next = to_add_ref local next_edge = to_refs.next
edge.prev = nil
if to_add_ref then to_refs.next = edge
to_add_ref.prev = edge edge.prev = to_refs
end edge.next = next_edge
to.node.add_ref = edge
if next_edge then
next_edge.prev = edge
end
end end
end end
@ -642,21 +644,16 @@ local function init_edge_for_remove(world, archetype, edge, id, to)
archetype_init_edge(archetype, edge, id, to) archetype_init_edge(archetype, edge, id, to)
archetype_ensure_edge(world, archetype.node.remove, id) archetype_ensure_edge(world, archetype.node.remove, id)
if archetype ~= to then if archetype ~= to then
local to_remove_ref = to.node.remove_ref local to_refs = to.node.refs
local prev local prev_edge = to_refs.prev
if to_remove_ref then
prev = to_remove_ref.prev
to_remove_ref.prev = edge
edge.next = to_remove_ref
else
to.node.remove_ref = edge
edge.next = nil
end
edge.prev = prev to_refs.prev = edge
if prev then edge.next = to_refs
prev.next = edge edge.prev = prev_edge
end
if prev_edge then
prev_edge.next = edge
end
end end
end end
@ -871,52 +868,47 @@ local function archetype_clear_edges(archetype: Archetype)
local node = archetype.node local node = archetype.node
local add = node.add local add = node.add
local remove = node.remove local remove = node.remove
for _, edge in add do local node_refs = node.refs
for id, edge in add do
archetype_disconnect_edge(edge) archetype_disconnect_edge(edge)
add[id] = nil
end end
for _, edge in remove do for id, edge in remove do
archetype_disconnect_edge(edge) archetype_disconnect_edge(edge)
end remove[id] = nil
local node_add_ref = node.add_ref
if node_add_ref then
local current = node_add_ref.next
while current do
local edge = current
current = current.next
local node_add = edge.from.node.add
if node_add then
archetype_remove_edge(node_add, edge.id, edge)
end
end
end end
local node_remove_ref = node.remove_ref local cur = node_refs.next
if node_remove_ref then while cur do
local current = node_remove_ref.prev local edge = cur
while current do local next_edge = edge.next
local edge = current archetype_remove_edge(edge.from.node.add, edge.id, edge)
current = current.prev cur = next_edge
local node_remove = edge.from.node.remove end
if node_remove then
archetype_remove_edge(node_remove, edge.id, edge)
end
end
end
node.add = nil :: any cur = node_refs.prev
node.remove = nil :: any while cur do
node.add_ref = nil :: any local edge = cur
node.remove_ref = nil :: any local next_edge = edge.prev
archetype_remove_edge(edge.from.node.remove, edge.id, edge)
cur = next_edge
end
node_refs.next = nil
node_refs.prev = nil
end end
local function archetype_destroy(world: World, archetype: Archetype) local function archetype_destroy(world: World, archetype: Archetype)
if archetype == world.ROOT_ARCHETYPE then
return
end
local component_index = world.componentIndex local component_index = world.componentIndex
archetype_clear_edges(archetype) archetype_clear_edges(archetype)
local archetype_id = archetype.id local archetype_id = archetype.id
if archetype ~= world.ROOT_ARCHETYPE then world.archetypes[archetype_id] = nil
world.archetypes[archetype_id] = nil world.archetypeIndex[archetype.type] = nil
world.archetypeIndex[archetype.type] = nil
end
local records = archetype.records local records = archetype.records
for id in records do for id in records do

View file

@ -71,6 +71,58 @@ local function name(world, e)
return world:get(e, jecs.Name) return world:get(e, jecs.Name)
end end
TEST("world:cleanup()", function()
local world = world_new()
local A = world:component()
local B = world:component()
local C = world:component()
local e1 = world:entity()
local e2 = world:entity()
local e3 = world:entity()
world:set(e1, A, true)
world:set(e2, A, true)
world:set(e2, B, true)
world:set(e3, A, true)
world:set(e3, B, true)
world:set(e3, C, true)
local archetypeIndex = world.archetypeIndex
CHECK(#archetypeIndex["1"].entities == 1)
CHECK(#archetypeIndex["1_2"].entities == 1)
CHECK(#archetypeIndex["1_2_3"].entities == 1)
world:delete(e1)
world:delete(e2)
world:delete(e3)
world:cleanup()
archetypeIndex = world.archetypeIndex
CHECK(archetypeIndex["1"] == nil)
CHECK(archetypeIndex["1_2"] == nil)
CHECK(archetypeIndex["1_2_3"] == nil)
local e4 = world:entity()
world:set(e4, A, true)
CHECK(#archetypeIndex["1"].entities == 1)
CHECK(archetypeIndex["1_2"] == nil)
CHECK(archetypeIndex["1_2_3"] == nil)
world:set(e4, B, true)
CHECK(#archetypeIndex["1"].entities == 0)
CHECK(#archetypeIndex["1_2"].entities == 1)
CHECK(archetypeIndex["1_2_3"] == nil)
world:set(e4, C, true)
CHECK(#archetypeIndex["1"].entities == 0)
CHECK(#archetypeIndex["1_2"].entities == 0)
CHECK(#archetypeIndex["1_2_3"].entities == 1)
end)
TEST("world:entity()", function() TEST("world:entity()", function()
do CASE "unique IDs" do CASE "unique IDs"
local world = jecs.World.new() local world = jecs.World.new()