Add iter method

This commit is contained in:
Ukendio 2024-05-03 18:14:10 +02:00
parent c5daddd505
commit 97acbd66ab
3 changed files with 101 additions and 4 deletions

View file

@ -173,8 +173,8 @@ local World = {}
World.__index = World
function World.new()
local self = setmetatable({
entityIndex = {},
componentIndex = {},
entityIndex = {} :: EntityIndex,
componentIndex = {} :: ComponentIndex,
archetypes = {},
archetypeIndex = {},
ROOT_ARCHETYPE = (nil :: any) :: Archetype,
@ -285,10 +285,10 @@ local function archetypeTraverseAdd(world: World, componentId: i53, from: Archet
return edge.add
end
local function ensureRecord(entityIndex, entityId: i53): Record
local function ensureRecord(entityIndex: EntityIndex, entityId: i53): Record
local id = entityId
if not entityIndex[id] then
entityIndex[id] = {}
entityIndex[id] = {} :: Record
end
return entityIndex[id] :: Record
end
@ -632,6 +632,37 @@ function World.observer(world: World, ...)
}
end
function World.__iter(world: World): () -> (number?, unknown?)
local entityIndex = world.entityIndex
local last
return function()
local entity, record = next(entityIndex, last)
if not entity then
return
end
last = entity
local archetype = record.archetype
if not archetype then
-- Returns only the entity id as an entity without data should not return
-- data and allow the user to get an error if they don't handle the case.
return entity
end
local row = record.row
local types = archetype.types
local columns = archetype.columns
local entityData = {}
for i, column in columns do
-- We use types because the key should be the component ID not the column index
entityData[types[i]] = column[row]
end
return entity, entityData
end
end
return table.freeze({
World = World,
ON_ADD = ON_ADD,

View file

@ -299,5 +299,38 @@ return function()
expect(world:get(id, Poison)).to.never.be.ok()
expect(world:get(id, Health)).to.never.be.ok()
end)
it("should allow iterating the whole world", function()
local world = jecs.World.new()
local A, B = world:entity(), world:entity()
local eA = world:entity()
world:set(eA, A, true)
local eB = world:entity()
world:set(eB, B, true)
local eAB = world:entity()
world:set(eAB, A, true)
world:set(eAB, B, true)
local count = 0
for id, data in world do
count += 1
if id == eA then
expect(data[A]).to.be.ok()
expect(data[B]).to.never.be.ok()
elseif id == eB then
expect(data[B]).to.be.ok()
expect(data[A]).to.never.be.ok()
elseif id == eAB then
expect(data[A]).to.be.ok()
expect(data[B]).to.be.ok()
else
error("unknown entity", id)
end
end
expect(count).to.equal(3)
end)
end)
end

View file

@ -110,6 +110,39 @@ TEST("world:query", function()
CHECK(world:get(id, Health) == nil)
end
do CASE "Should allow iterating the whole world"
local world = jecs.World.new()
local A, B = world:entity(), world:entity()
local eA = world:entity()
world:set(eA, A, true)
local eB = world:entity()
world:set(eB, B, true)
local eAB = world:entity()
world:set(eAB, A, true)
world:set(eAB, B, true)
local count = 0
for id, data in world do
count += 1
if id == eA then
CHECK(data[A] == true)
CHECK(data[B] == nil)
elseif id == eB then
CHECK(data[B] == true)
CHECK(data[A] == nil)
elseif id == eAB then
CHECK(data[A] == true)
CHECK(data[B] == true)
else
error("unknown entity", id)
end
end
CHECK(count == 3)
end
end)
FINISH()