mirror of
https://github.com/Ukendio/jecs.git
synced 2025-06-20 00:09:18 +00:00
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:
parent
e8b78f7b50
commit
91d3fcabc3
3 changed files with 50 additions and 25 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -50,6 +50,3 @@ WallyPatches
|
|||
roblox.toml
|
||||
sourcemap.json
|
||||
drafts/*.lua
|
||||
|
||||
*.code-workspace
|
||||
roblox.yml
|
||||
|
|
51
lib/init.lua
51
lib/init.lua
|
@ -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;
|
||||
})
|
||||
})
|
|
@ -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()
|
||||
|
||||
|
|
Loading…
Reference in a new issue