Remove drain and next

This commit is contained in:
Ukendio 2024-09-29 03:49:55 +02:00
parent d6932ff80e
commit 25dd6394ba
2 changed files with 57 additions and 114 deletions

View file

@ -611,13 +611,7 @@ end
local function archetype_ensure_edge(world, edges, id): GraphEdge
local edge = edges[id]
if not edge then
edge = {
from = nil :: any,
to = nil :: any,
id = id,
prev = nil,
next = nil,
} :: GraphEdge
edge = {} :: GraphEdge
edges[id] = edge
end
@ -1095,9 +1089,6 @@ local EMPTY_QUERY = {
iter = function()
return NOOP
end,
drain = ARM,
next = NOOP,
replace = NOOP,
with = ARM,
without = ARM,
archetypes = function()
@ -1107,16 +1098,9 @@ local EMPTY_QUERY = {
setmetatable(EMPTY_QUERY, EMPTY_QUERY)
local function query_iter(query)
local function query_iter_init(query)
local world_query_iter_next
if query.should_drain then
world_query_iter_next = query.iter_next
if world_query_iter_next then
return world_query_iter_next
end
end
local compatible_archetypes = query.compatible_archetypes
local lastArchetype = 1
local archetype = compatible_archetypes[1]
@ -1363,19 +1347,16 @@ local function query_iter(query)
end
end
query.iter_next = world_query_iter_next
query.next = world_query_iter_next
return world_query_iter_next
end
local function query_drain(query)
local query_iter_next = query_iter(query)
query.next = query_iter_next
query.should_drain = true
return query
end
local function query_next(query)
error("Did you forget to call drain?")
local function query_iter(query)
local query_next = query.next
if not query_next then
query_next = query_iter_init(query)
end
return query_next
end
local function query_without(query, ...)
@ -1440,55 +1421,6 @@ local function query_with(query, ...)
return query
end
local function columns_replace_values(row, columns, ...)
for i, column in columns do
column[row] = select(i, ...)
end
end
local function query_replace(query, fn: (...any) -> ...any)
local compatible_archetypes = query.compatible_archetypes
local ids = query.ids
local A, B, C, D, E = unpack(ids, 1, 5)
local queryOutput = {}
for i, archetype in compatible_archetypes do
local columns = archetype.columns
local records = archetype.records
for row in archetype.entities do
if not B then
local va = columns[records[A].column]
local pa = fn(va[row])
va[row] = pa
elseif not C then
local va = columns[records[A].column]
local vb = columns[records[B].column]
va[row], vb[row] = fn(va[row], vb[row])
elseif not D then
local va = columns[records[A].column]
local vb = columns[records[B].column]
local vc = columns[records[C].column]
va[row], vb[row], vc[row] = fn(va[row], vb[row], vc[row])
elseif not E then
local va = columns[records[A].column]
local vb = columns[records[B].column]
local vc = columns[records[C].column]
local vd = columns[records[D].column]
va[row], vb[row], vc[row], vd[row] = fn(va[row], vb[row], vc[row], vd[row])
else
for j, id in ids do
local tr = records[id]
queryOutput[j] = columns[tr.column][row]
end
columns_replace_values(row, columns, fn(unpack(queryOutput)))
end
end
end
end
-- Meant for directly iterating over archetypes to minimize
-- function call overhead. Should not be used unless iterating over
-- hundreds of thousands of entities in bulk.
@ -1499,13 +1431,10 @@ end
local Query = {}
Query.__index = Query
Query.__iter = query_iter
Query.iter = query_iter
Query.iter = query_iter_init
Query.without = query_without
Query.with = query_with
Query.archetypes = query_archetypes
Query.drain = query_drain
Query.next = query_next
Query.replace = query_replace
local function world_query(world: World, ...)
local compatible_archetypes = {}

View file

@ -328,7 +328,7 @@ TEST("world:query()", function()
world:set(eAB, B, true)
-- Should drain the iterator
local q = world:query(A):drain()
local q = world:query(A)
local i = 0
local j = 0
@ -403,9 +403,9 @@ TEST("world:query()", function()
world:set(eAB, A, true)
world:set(eAB, B, true)
local q = world:query(A):drain()
local it = world:query(A):iter()
local e, data = q.next()
local e, data = it()
while e do
if e == eA then
CHECK(data)
@ -415,7 +415,7 @@ TEST("world:query()", function()
CHECK(false)
end
e, data = q.next()
e, data = it()
end
CHECK(true)
end
@ -689,9 +689,10 @@ TEST("world:query()", function()
world:add(e1, A)
local query = world:query(B)
CHECK(query:next() == nil)
CHECK(query:replace() == nil)
CHECK(query:without() == query)
CHECK(query:with() == query)
-- They always return the same EMPTY_LIST
CHECK(query:archetypes() == world:query(B):archetypes())
end
do
@ -711,26 +712,6 @@ TEST("world:query()", function()
end
end
do CASE "replace"
local world = jecs.World.new()
local A = world:component()
local B = world:component()
local C = world:component()
local e = world:entity()
world:set(e, A, 1)
world:set(e, B, true)
world:set(e, C, "hello ")
world:query(A, B, C):replace(function(a, b, c)
return a * 2, not b, c.."world"
end)
CHECK(world:get(e, A) == 2)
CHECK(world:get(e, B) == false)
CHECK(world:get(e, C) == "hello world")
end
do CASE "without"
do
-- REGRESSION TEST
@ -1104,9 +1085,9 @@ local function ChangeTracker<T>(world, T: Entity<T>): Tracker<T>
local function changes_added()
added = true
local q = world:query(T):without(PreviousT):drain()
local it = world:query(T):without(PreviousT):iter()
return function()
local id, data = q.next()
local id, data = it()
if not id then
return nil
end
@ -1120,10 +1101,10 @@ local function ChangeTracker<T>(world, T: Entity<T>): Tracker<T>
end
local function changes_changed()
local q = world:query(T, PreviousT):drain()
local it = world:query(T, PreviousT):iter()
return function()
local id, new, old = q.next()
local id, new, old = it()
while true do
if not id then
return nil
@ -1137,7 +1118,7 @@ local function ChangeTracker<T>(world, T: Entity<T>): Tracker<T>
break
end
id, new, old = q.next()
id, new, old = it()
end
add[id] = new
@ -1149,9 +1130,9 @@ local function ChangeTracker<T>(world, T: Entity<T>): Tracker<T>
local function changes_removed()
removed = true
local q = world:query(PreviousT):without(T):drain()
local it = world:query(PreviousT):without(T):iter()
return function()
local id = q.next()
local id = it()
if id then
world:remove(id, PreviousT)
end
@ -1705,4 +1686,37 @@ TEST("scheduler", function()
CHECK(systems[1].callback == camera)
end
end)
TEST("repro", function()
do CASE ""
local world = world_new()
local reproEntity = world:component()
local components = { Cooldown = world:component() }
world:set(reproEntity, components.Cooldown, 2)
local function updateCooldowns(dt: number)
local toRemove = {}
for id, cooldown in world:query(components.Cooldown):iter() do
cooldown -= dt
if cooldown <= 0 then
table.insert(toRemove, id)
print('removing')
-- world:remove(id, components.Cooldown)
else
world:set(id, components.Cooldown, cooldown)
end
end
for _, id in toRemove do
world:remove(id, components.Cooldown)
CHECK(not world:get(id, components.Cooldown))
end
end
updateCooldowns(1.5)
updateCooldowns(1.5)
end
end)
FINISH()