Add case for when component is not found in archetype (#25)

* Add case for when component is not found in archetype

* Check only destination archetype first

* Omit onNotifyAdd
This commit is contained in:
Marcus 2024-05-08 00:57:22 +02:00 committed by GitHub
parent e8b78f7b50
commit 91d3fcabc3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 50 additions and 25 deletions

3
.gitignore vendored
View file

@ -50,6 +50,3 @@ WallyPatches
roblox.toml
sourcemap.json
drafts/*.lua
*.code-workspace
roblox.yml

View file

@ -287,23 +287,31 @@ local function archetypeTraverseAdd(world: World, componentId: i53, from: Archet
return add
end
local function ensureRecord(entityIndex, entityId: i53): Record
local function ensureRecord(world, entityId: i53): Record
local entityIndex = world.entityIndex
local record = entityIndex[entityId]
if not record then
record = {}
entityIndex[entityId] = record
if record then
return record
end
return record :: Record
local ROOT = world.ROOT_ARCHETYPE
local row = #ROOT.entities + 1
ROOT.entities[row] = entityId
record = {
archetype = ROOT,
row = row
}
entityIndex[entityId] = record
return record
end
function World.add(world: World, entityId: i53, componentId: i53)
local record = ensureRecord(world.entityIndex, entityId)
local record = ensureRecord(world, entityId)
local from = record.archetype
local to = archetypeTraverseAdd(world, componentId, from)
if from then
if from and not (from == world.ROOT_ARCHETYPE) then
moveEntity(world.entityIndex, entityId, record, to)
else
if #to.types > 0 then
@ -315,19 +323,20 @@ end
-- Symmetric like `World.add` but idempotent
function World.set(world: World, entityId: i53, componentId: i53, data: unknown)
local record = ensureRecord(world.entityIndex, entityId)
local record = ensureRecord(world, entityId)
local from = record.archetype
local to = archetypeTraverseAdd(world, componentId, from)
if from == to then
local archetypeRecord = from.records[componentId]
if archetypeRecord then
-- If the archetypes are the same it can avoid moving the entity
-- and just set the data directly.
local archetypeRecord = to.records[componentId]
from.columns[archetypeRecord][record.row] = data
-- Should fire an OnSet event here.
return
end
local to = archetypeTraverseAdd(world, componentId, from)
if from then
-- If there was a previous archetype, then the entity needs to move the archetype
moveEntity(world.entityIndex, entityId, record, to)
@ -335,22 +344,25 @@ function World.set(world: World, entityId: i53, componentId: i53, data: unknown)
if #to.types > 0 then
-- When there is no previous archetype it should create the archetype
newEntity(entityId, record, to)
onNotifyAdd(world, to, from, record.row, {componentId})
--onNotifyAdd(world, to, from, record.row, {componentId})
end
end
local archetypeRecord = to.records[componentId]
archetypeRecord = to.records[componentId]
to.columns[archetypeRecord][record.row] = data
end
local function archetypeTraverseRemove(world: World, componentId: i53, archetype: Archetype?): Archetype
local from = (archetype or world.ROOT_ARCHETYPE) :: Archetype
local function archetypeTraverseRemove(world: World, componentId: i53, from: Archetype): Archetype
local edge = ensureEdge(from, componentId)
local remove = edge.remove
if not remove then
local to = table.clone(from.types)
table.remove(to, table.find(to, componentId))
local at = table.find(to, componentId)
if not at then
return from
end
table.remove(to, at)
remove = ensureArchetype(world, to, from)
edge.remove = remove :: never
end
@ -359,13 +371,12 @@ local function archetypeTraverseRemove(world: World, componentId: i53, archetype
end
function World.remove(world: World, entityId: i53, componentId: i53)
local entityIndex = world.entityIndex
local record = ensureRecord(entityIndex, entityId)
local record = ensureRecord(world, entityId)
local sourceArchetype = record.archetype
local destinationArchetype = archetypeTraverseRemove(world, componentId, sourceArchetype)
if sourceArchetype and not (sourceArchetype == destinationArchetype) then
moveEntity(entityIndex, entityId, record, destinationArchetype)
moveEntity(world.entityIndex, entityId, record, destinationArchetype)
end
end
@ -732,4 +743,4 @@ return table.freeze({
ON_ADD = ON_ADD;
ON_REMOVE = ON_REMOVE;
ON_SET = ON_SET;
})
})

View file

@ -35,14 +35,17 @@ TEST("world:query", function()
local world = jecs.World.new()
local A = world:component()
local B = world:component()
local C = world:component()
local entities = {}
for i = 1, N do
local id = world:entity()
world:set(id, A, true)
-- specifically put them in disorder to track regression
-- https://github.com/Ukendio/jecs/pull/15
world:set(id, B, true)
if i > 5 then world:remove(id, B, true) end
world:set(id, A, true)
if i > 5 then world:remove(id, B) end
entities[i] = id
end
@ -110,6 +113,20 @@ TEST("world:query", function()
CHECK(world:get(id, Health) == nil)
end
do CASE "show allow remove that doesn't exist on entity"
local world = jecs.World.new()
local Health = world:entity()
local Poison = world:component()
local id = world:entity()
world:set(id, Health, 50)
world:remove(id, Poison)
CHECK(world:get(id, Poison) == nil)
CHECK(world:get(id, Health) == 50)
end
do CASE "Should allow iterating the whole world"
local world = jecs.World.new()