Add backwards iteration (#61)

* Iterate backwards

* Should test for removing as well

* Add test

* Add faíling test

* Add to changelog
This commit is contained in:
Marcus 2024-07-06 16:36:00 +02:00 committed by GitHub
parent 6559c56d47
commit c0e73273d1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 93 additions and 34 deletions

View file

@ -10,12 +10,16 @@ The format is based on [Keep a Changelog][kac], and this project adheres to
## [Unreleased]
### Changed
- Iterator now goes backwards instead to prevent common cases of iterator invalidation
## [0.2.1] - 2024-07-06
### Added
* Added `jecs.Component` built-in component which will be added to ids created with `world:component()`.
* used to find every component id with `query(jecs.Component)
- Added `jecs.Component` built-in component which will be added to ids created with `world:component()`.
- Used to find every component id with `query(jecs.Component)
## [0.2.0] - 2024-07-03

View file

@ -184,13 +184,9 @@ local function nextEntityId(entityIndex: EntityIndex, index: i24): i53
return id
end
local function transitionArchetype(
entityIndex: EntityIndex,
to: Archetype,
destinationRow: i24,
from: Archetype,
sourceRow: i24
)
local function transitionArchetype(entityIndex: EntityIndex, to: Archetype,
destinationRow: i24, from: Archetype, sourceRow: i24)
local columns = from.columns
local sourceEntities = from.entities
local destinationEntities = to.entities
@ -690,11 +686,11 @@ local function preparedQuery(compatibleArchetypes: { Archetype },
local queryOutput = {}
local i = 1
local entities = archetype.entities
local i = #entities
local function queryNext(): ...any
local entityId = archetype.entities[i]
local entityId = entities[i]
while entityId == nil do
lastArchetype += 1
archetype = compatibleArchetypes[lastArchetype]
@ -703,12 +699,13 @@ local function preparedQuery(compatibleArchetypes: { Archetype },
return
end
i = 1
entityId = archetype.entities[1]
entities = archetype.entities
i = #entities
entityId = entities[i]
end
local row = i
i+=1
i-=1
local columns = archetype.columns
local tr = indices[lastArchetype]
@ -788,9 +785,10 @@ local function preparedQuery(compatibleArchetypes: { Archetype },
local it = {
__iter = function()
i = 1
lastArchetype = 1
archetype = compatibleArchetypes[1]
entities = archetype.entities
i = #entities
return queryNext
end,

View file

@ -25,30 +25,23 @@ local N = 10
type World = jecs.WorldShim
TEST("world", function()
do CASE("should be iterable")
do CASE("should find every component id")
local world = jecs.World.new() :: World
local A = world:component()
local B = world:component()
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)
world:entity()
world:entity()
world:entity()
for id, data in world do
if id == eA then
CHECK(data[A] == true)
CHECK(data[B] == nil)
elseif id == eB then
CHECK(data[A] == nil)
CHECK(data[B] == true)
elseif id == eAB then
CHECK(data[A] == true)
CHECK(data[B] == true)
local count = 0
for componentId in world:query(jecs.Component) do
if componentId ~= A and componentId ~= B then
error("found entity")
end
count += 1
end
CHECK(count == 2)
end
do CASE("should remove its components")
@ -395,6 +388,70 @@ TEST("world", function()
end
CHECK(count == 2)
end
do CASE "should be able to add/remove matching entity during iteration"
local world = jecs.World.new()
local Name = world:component()
for i = 1, 5 do
local e = world:entity()
world:set(e, Name, tostring(e))
end
local count = 0
for id, name in world:query(Name) do
count += 1
CHECK(id == tonumber(name))
world:remove(id, Name)
local e = world:entity()
world:set(e, Name, tostring(e))
end
CHECK(count == 5)
end
do CASE "should allow adding a matching entity during iteration"
local world = jecs.World.new()
local A = world:component()
local B = world:component()
local e1 = world:entity()
local e2 = world:entity()
world:add(e1, A)
world:add(e2, A)
world:add(e2, B)
local count = 0
for id in world:query(A) do
local e = world:entity()
world:add(e, A)
world:add(e, B)
count += 1
end
CHECK(count == 3)
end
do CASE "should not iterate same entity when adding component"
local world = jecs.World.new()
local A = world:component()
local B = world:component()
local e1 = world:entity()
local e2 = world:entity()
world:add(e1, A)
world:add(e2, A)
world:add(e2, B)
local count = 0
for id in world:query(A) do
world:add(id, B)
count += 1
end
print(count)
CHECK(count == 2)
end
end)
FINISH()