mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-25 09:30:03 +00:00
Add tests for relations
This commit is contained in:
parent
77c7a862ea
commit
2bff9b4551
3 changed files with 205 additions and 306 deletions
325
lib/init.lua
325
lib/init.lua
|
@ -199,27 +199,168 @@ function World.new()
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
local function emit(world, eventDescription)
|
local FLAGS_PAIR = 0x8
|
||||||
local event = eventDescription.event
|
|
||||||
|
|
||||||
table.insert(world.hooks[event], {
|
local function addFlags(flags)
|
||||||
archetype = eventDescription.archetype;
|
local typeFlags = 0x0
|
||||||
ids = eventDescription.ids;
|
if flags.isPair then
|
||||||
offset = eventDescription.offset;
|
typeFlags = bit32.bor(typeFlags, FLAGS_PAIR) -- HIGHEST bit in the ID.
|
||||||
otherArchetype = eventDescription.otherArchetype;
|
end
|
||||||
})
|
if false then
|
||||||
|
typeFlags = bit32.bor(typeFlags, 0x4) -- Set the second flag to true
|
||||||
|
end
|
||||||
|
if false then
|
||||||
|
typeFlags = bit32.bor(typeFlags, 0x2) -- Set the third flag to true
|
||||||
|
end
|
||||||
|
if false then
|
||||||
|
typeFlags = bit32.bor(typeFlags, 0x1) -- LAST BIT in the ID.
|
||||||
|
end
|
||||||
|
|
||||||
|
return typeFlags
|
||||||
end
|
end
|
||||||
|
|
||||||
local function onNotifyAdd(world, archetype, otherArchetype, row: number, added: Ty)
|
local ECS_ID_FLAGS_MASK = 0x10
|
||||||
if #added > 0 then
|
|
||||||
emit(world, {
|
-- ECS_ENTITY_MASK (0xFFFFFFFFull << 28)
|
||||||
archetype = archetype;
|
local ECS_ENTITY_MASK = bit32.lshift(1, 24)
|
||||||
event = ON_ADD;
|
|
||||||
ids = added;
|
-- ECS_GENERATION_MASK (0xFFFFull << 24)
|
||||||
offset = row;
|
local ECS_GENERATION_MASK = bit32.lshift(1, 16)
|
||||||
otherArchetype = otherArchetype;
|
|
||||||
})
|
local function newId(source: number, target: number)
|
||||||
|
local e = source * 2^28 + target * ECS_ID_FLAGS_MASK
|
||||||
|
return e
|
||||||
|
end
|
||||||
|
|
||||||
|
local function isPair(e: number)
|
||||||
|
return (e % 2^4) // FLAGS_PAIR ~= 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function separate(entity: number)
|
||||||
|
local _typeFlags = entity % 0x10
|
||||||
|
entity //= ECS_ID_FLAGS_MASK
|
||||||
|
return entity // ECS_ENTITY_MASK, entity % ECS_GENERATION_MASK, _typeFlags
|
||||||
|
end
|
||||||
|
|
||||||
|
-- HIGH 24 bits LOW 24 bits
|
||||||
|
local function ECS_GENERATION(e: i53)
|
||||||
|
e //= 0x10
|
||||||
|
return e % ECS_GENERATION_MASK
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ECS_ID(e: i53)
|
||||||
|
e //= 0x10
|
||||||
|
return e // ECS_ENTITY_MASK
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ECS_GENERATION_INC(e: i53)
|
||||||
|
local id, generation, flags = separate(e)
|
||||||
|
|
||||||
|
return newId(id, generation + 1) + flags
|
||||||
|
end
|
||||||
|
|
||||||
|
-- gets the high ID
|
||||||
|
local function ECS_PAIR_FIRST(entity: i53): i24
|
||||||
|
entity //= 0x10
|
||||||
|
local first = entity % ECS_ENTITY_MASK
|
||||||
|
return first
|
||||||
|
end
|
||||||
|
|
||||||
|
-- gets the low ID
|
||||||
|
local ECS_PAIR_SECOND = ECS_ID
|
||||||
|
|
||||||
|
local function ECS_PAIR(source: number, target: number)
|
||||||
|
local id = newId(ECS_PAIR_SECOND(target), ECS_PAIR_SECOND(source)) + addFlags({ isPair = true })
|
||||||
|
return id
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getAlive(entityIndex, id)
|
||||||
|
return entityIndex.dense[id]
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ecs_pair_first(entityIndex, e)
|
||||||
|
assert(isPair(e))
|
||||||
|
return getAlive(entityIndex, ECS_PAIR_FIRST(e))
|
||||||
|
end
|
||||||
|
local function ecs_pair_second(entityIndex, e)
|
||||||
|
assert(isPair(e))
|
||||||
|
return getAlive(entityIndex, ECS_PAIR_SECOND(e))
|
||||||
|
end
|
||||||
|
|
||||||
|
function World.component(world: World)
|
||||||
|
local componentId = world.nextComponentId + 1
|
||||||
|
if componentId > HI_COMPONENT_ID then
|
||||||
|
-- IDs are partitioned into ranges because component IDs are not nominal,
|
||||||
|
-- so it needs to error when IDs intersect into the entity range.
|
||||||
|
error("Too many components, consider using world:entity() instead to create components.")
|
||||||
end
|
end
|
||||||
|
world.nextComponentId = componentId
|
||||||
|
return componentId
|
||||||
|
end
|
||||||
|
|
||||||
|
function World.entity(world: World)
|
||||||
|
local nextEntityId = world.nextEntityId + 1
|
||||||
|
world.nextEntityId = nextEntityId
|
||||||
|
local index = nextEntityId + REST
|
||||||
|
local id = newId(index, 0)
|
||||||
|
local entityIndex = world.entityIndex
|
||||||
|
entityIndex.sparse[id] = {
|
||||||
|
dense = index
|
||||||
|
} :: Record
|
||||||
|
entityIndex.dense[index] = id
|
||||||
|
|
||||||
|
return id
|
||||||
|
end
|
||||||
|
|
||||||
|
-- should reuse this logic in World.set instead of swap removing in transition archetype
|
||||||
|
local function destructColumns(columns, count, row)
|
||||||
|
if row == count then
|
||||||
|
for _, column in columns do
|
||||||
|
column[count] = nil
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for _, column in columns do
|
||||||
|
column[row] = column[count]
|
||||||
|
column[count] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function archetypeDelete(entityIndex, record: Record, entityId: i53, destruct: boolean)
|
||||||
|
local sparse, dense = entityIndex.sparse, entityIndex.dense
|
||||||
|
local archetype = record.archetype
|
||||||
|
local row = record.row
|
||||||
|
local entities = archetype.entities
|
||||||
|
local last = #entities
|
||||||
|
|
||||||
|
local entityToMove = entities[last]
|
||||||
|
|
||||||
|
if row ~= last then
|
||||||
|
dense[record.dense] = entityToMove
|
||||||
|
sparse[entityToMove] = record
|
||||||
|
end
|
||||||
|
|
||||||
|
sparse[entityId] = nil
|
||||||
|
dense[#dense] = nil
|
||||||
|
|
||||||
|
entities[row], entities[last] = entities[last], nil
|
||||||
|
|
||||||
|
local columns = archetype.columns
|
||||||
|
|
||||||
|
if not destruct then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
destructColumns(columns, last, row)
|
||||||
|
end
|
||||||
|
|
||||||
|
function World.delete(world: World, entityId: i53)
|
||||||
|
local entityIndex = world.entityIndex
|
||||||
|
local record = entityIndex.sparse[entityId]
|
||||||
|
if not record then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
archetypeDelete(entityIndex, record, entityId, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
export type World = typeof(World.new())
|
export type World = typeof(World.new())
|
||||||
|
@ -300,22 +441,8 @@ local function archetypeTraverseAdd(world: World, componentId: i53, from: Archet
|
||||||
return add
|
return add
|
||||||
end
|
end
|
||||||
|
|
||||||
local function ensureRecord(entityIndex: EntityIndex, entityId: i53): Record
|
|
||||||
local sparse = entityIndex.sparse
|
|
||||||
local dense = entityIndex.dense
|
|
||||||
local page = sparse[entityId]
|
|
||||||
if not page then
|
|
||||||
local i = #dense + 1
|
|
||||||
page = { dense = i } :: Record
|
|
||||||
sparse[entityId] = page
|
|
||||||
dense[i] = entityId
|
|
||||||
end
|
|
||||||
|
|
||||||
return page
|
|
||||||
end
|
|
||||||
|
|
||||||
function World.set(world: World, entityId: i53, componentId: i53, data: unknown)
|
function World.set(world: World, entityId: i53, componentId: i53, data: unknown)
|
||||||
local record = ensureRecord(world.entityIndex, entityId)
|
local record = world.entityIndex.sparse[entityId]
|
||||||
local from = record.archetype
|
local from = record.archetype
|
||||||
local to = archetypeTraverseAdd(world, componentId, from)
|
local to = archetypeTraverseAdd(world, componentId, from)
|
||||||
|
|
||||||
|
@ -335,7 +462,6 @@ function World.set(world: World, entityId: i53, componentId: i53, data: unknown)
|
||||||
if #to.types > 0 then
|
if #to.types > 0 then
|
||||||
-- When there is no previous archetype it should create the archetype
|
-- When there is no previous archetype it should create the archetype
|
||||||
newEntity(entityId, record, to)
|
newEntity(entityId, record, to)
|
||||||
onNotifyAdd(world, to, from, record.row, {componentId})
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -360,7 +486,7 @@ end
|
||||||
|
|
||||||
function World.remove(world: World, entityId: i53, componentId: i53)
|
function World.remove(world: World, entityId: i53, componentId: i53)
|
||||||
local entityIndex = world.entityIndex
|
local entityIndex = world.entityIndex
|
||||||
local record = ensureRecord(entityIndex, entityId)
|
local record = entityIndex.sparse[entityId]
|
||||||
local sourceArchetype = record.archetype
|
local sourceArchetype = record.archetype
|
||||||
local destinationArchetype = archetypeTraverseRemove(world, componentId, sourceArchetype)
|
local destinationArchetype = archetypeTraverseRemove(world, componentId, sourceArchetype)
|
||||||
|
|
||||||
|
@ -579,129 +705,6 @@ function World.query(world: World, ...: i53): Query
|
||||||
return setmetatable({}, preparedQuery) :: any
|
return setmetatable({}, preparedQuery) :: any
|
||||||
end
|
end
|
||||||
|
|
||||||
function World.component(world: World)
|
|
||||||
local componentId = world.nextComponentId + 1
|
|
||||||
if componentId > HI_COMPONENT_ID then
|
|
||||||
-- IDs are partitioned into ranges because component IDs are not nominal,
|
|
||||||
-- so it needs to error when IDs intersect into the entity range.
|
|
||||||
error("Too many components, consider using world:entity() instead to create components.")
|
|
||||||
end
|
|
||||||
world.nextComponentId = componentId
|
|
||||||
return componentId
|
|
||||||
end
|
|
||||||
|
|
||||||
function World.entity(world: World)
|
|
||||||
local nextEntityId = world.nextEntityId + 1
|
|
||||||
world.nextEntityId = nextEntityId
|
|
||||||
return nextEntityId + REST
|
|
||||||
end
|
|
||||||
|
|
||||||
-- should reuse this logic in World.set instead of swap removing in transition archetype
|
|
||||||
local function destructColumns(columns, count, row)
|
|
||||||
if row == count then
|
|
||||||
for _, column in columns do
|
|
||||||
column[count] = nil
|
|
||||||
end
|
|
||||||
else
|
|
||||||
for _, column in columns do
|
|
||||||
column[row] = column[count]
|
|
||||||
column[count] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function archetypeDelete(entityIndex, record: Record, entityId: i53, destruct: boolean)
|
|
||||||
local sparse, dense = entityIndex.sparse, entityIndex.dense
|
|
||||||
local archetype = record.archetype
|
|
||||||
local row = record.row
|
|
||||||
local entities = archetype.entities
|
|
||||||
local last = #entities
|
|
||||||
|
|
||||||
local entityToMove = entities[last]
|
|
||||||
|
|
||||||
if row ~= last then
|
|
||||||
dense[record.dense] = entityToMove
|
|
||||||
sparse[entityToMove] = record
|
|
||||||
end
|
|
||||||
|
|
||||||
sparse[entityId] = nil
|
|
||||||
dense[#dense] = nil
|
|
||||||
|
|
||||||
entities[row], entities[last] = entities[last], nil
|
|
||||||
|
|
||||||
local columns = archetype.columns
|
|
||||||
|
|
||||||
if not destruct then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
destructColumns(columns, last, row)
|
|
||||||
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
function World.delete(world: World, entityId: i53)
|
|
||||||
local entityIndex = world.entityIndex
|
|
||||||
local record = entityIndex.sparse[entityId]
|
|
||||||
if not record then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
archetypeDelete(entityIndex, record, entityId, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
function World.observer(world: World, ...)
|
|
||||||
local componentIds = {...}
|
|
||||||
local idsCount = #componentIds
|
|
||||||
local hooks = world.hooks
|
|
||||||
|
|
||||||
return {
|
|
||||||
event = function(event)
|
|
||||||
local hook = hooks[event]
|
|
||||||
hooks[event] = nil
|
|
||||||
|
|
||||||
local last, change
|
|
||||||
return function()
|
|
||||||
last, change = next(hook, last)
|
|
||||||
if not last then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local matched = false
|
|
||||||
local ids = change.ids
|
|
||||||
|
|
||||||
while not matched do
|
|
||||||
local skip = false
|
|
||||||
for _, id in ids do
|
|
||||||
if not table.find(componentIds, id) then
|
|
||||||
skip = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if skip then
|
|
||||||
last, change = next(hook, last)
|
|
||||||
ids = change.ids
|
|
||||||
continue
|
|
||||||
end
|
|
||||||
|
|
||||||
matched = true
|
|
||||||
end
|
|
||||||
|
|
||||||
local queryOutput = table.create(idsCount)
|
|
||||||
local row = change.offset
|
|
||||||
local archetype = change.archetype
|
|
||||||
local columns = archetype.columns
|
|
||||||
local archetypeRecords = archetype.records
|
|
||||||
for index, id in componentIds do
|
|
||||||
queryOutput[index] = columns[archetypeRecords[id]][row]
|
|
||||||
end
|
|
||||||
|
|
||||||
return archetype.entities[row], unpack(queryOutput, 1, idsCount)
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function World.__iter(world: World): () -> (number?, unknown?)
|
function World.__iter(world: World): () -> (number?, unknown?)
|
||||||
local dense = world.entityIndex.dense
|
local dense = world.entityIndex.dense
|
||||||
local sparse = world.entityIndex.sparse
|
local sparse = world.entityIndex.sparse
|
||||||
|
@ -740,4 +743,12 @@ return table.freeze({
|
||||||
ON_ADD = ON_ADD;
|
ON_ADD = ON_ADD;
|
||||||
ON_REMOVE = ON_REMOVE;
|
ON_REMOVE = ON_REMOVE;
|
||||||
ON_SET = ON_SET;
|
ON_SET = ON_SET;
|
||||||
|
ECS_ID = ECS_ID,
|
||||||
|
IS_PAIR = isPair,
|
||||||
|
ECS_PAIR = ECS_PAIR,
|
||||||
|
ECS_GENERATION = ECS_GENERATION,
|
||||||
|
ECS_GENERATION_INC = ECS_GENERATION_INC,
|
||||||
|
getAlive = getAlive,
|
||||||
|
ecs_pair_first = ecs_pair_first,
|
||||||
|
ecs_pair_second = ecs_pair_second
|
||||||
})
|
})
|
||||||
|
|
148
tests/test1.lua
148
tests/test1.lua
|
@ -1,148 +0,0 @@
|
||||||
local testkit = require("../testkit")
|
|
||||||
local jecs = require("../lib/init")
|
|
||||||
|
|
||||||
local TEST, CASE, CHECK, FINISH, SKIP = testkit.test()
|
|
||||||
|
|
||||||
local N = 10
|
|
||||||
|
|
||||||
TEST("world:query", function()
|
|
||||||
do CASE "should query all matching entities"
|
|
||||||
|
|
||||||
local world = jecs.World.new()
|
|
||||||
local A = world:component()
|
|
||||||
local B = world:component()
|
|
||||||
|
|
||||||
local entities = {}
|
|
||||||
for i = 1, N do
|
|
||||||
local id = world:entity()
|
|
||||||
|
|
||||||
|
|
||||||
world:set(id, A, true)
|
|
||||||
if i > 5 then world:set(id, B, true) end
|
|
||||||
entities[i] = id
|
|
||||||
end
|
|
||||||
|
|
||||||
for id in world:query(A) do
|
|
||||||
table.remove(entities, CHECK(table.find(entities, id)))
|
|
||||||
end
|
|
||||||
|
|
||||||
CHECK(#entities == 0)
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "should query all matching entities when irrelevant component is removed"
|
|
||||||
|
|
||||||
local world = jecs.World.new()
|
|
||||||
local A = world:component()
|
|
||||||
local B = world:component()
|
|
||||||
|
|
||||||
local entities = {}
|
|
||||||
for i = 1, N do
|
|
||||||
local id = world:entity()
|
|
||||||
|
|
||||||
world:set(id, A, true)
|
|
||||||
world:set(id, B, true)
|
|
||||||
if i > 5 then world:remove(id, B, true) end
|
|
||||||
entities[i] = id
|
|
||||||
end
|
|
||||||
|
|
||||||
local added = 0
|
|
||||||
for id in world:query(A) do
|
|
||||||
added += 1
|
|
||||||
table.remove(entities, CHECK(table.find(entities, id)))
|
|
||||||
end
|
|
||||||
|
|
||||||
CHECK(added == N)
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "should query all entities without B"
|
|
||||||
|
|
||||||
local world = jecs.World.new()
|
|
||||||
local A = world:component()
|
|
||||||
local B = world:component()
|
|
||||||
|
|
||||||
local entities = {}
|
|
||||||
for i = 1, N do
|
|
||||||
local id = world:entity()
|
|
||||||
|
|
||||||
world:set(id, A, true)
|
|
||||||
if i < 5 then
|
|
||||||
entities[i] = id
|
|
||||||
else
|
|
||||||
world:set(id, B, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
for id in world:query(A):without(B) do
|
|
||||||
table.remove(entities, CHECK(table.find(entities, id)))
|
|
||||||
end
|
|
||||||
|
|
||||||
CHECK(#entities == 0)
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "should allow setting components in arbitrary order"
|
|
||||||
local world = jecs.World.new()
|
|
||||||
|
|
||||||
local Health = world:entity()
|
|
||||||
local Poison = world:component()
|
|
||||||
|
|
||||||
local id = world:entity()
|
|
||||||
world:set(id, Poison, 5)
|
|
||||||
world:set(id, Health, 50)
|
|
||||||
|
|
||||||
CHECK(world:get(id, Poison) == 5)
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "Should allow deleting components"
|
|
||||||
local world = jecs.World.new()
|
|
||||||
|
|
||||||
local Health = world:entity()
|
|
||||||
local Poison = world:component()
|
|
||||||
|
|
||||||
local id = world:entity()
|
|
||||||
world:set(id, Poison, 5)
|
|
||||||
world:set(id, Health, 50)
|
|
||||||
world:delete(id)
|
|
||||||
|
|
||||||
CHECK(world:get(id, Poison) == nil)
|
|
||||||
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()
|
|
|
@ -1,5 +1,12 @@
|
||||||
local testkit = require("../testkit")
|
local testkit = require("../testkit")
|
||||||
local jecs = require("../lib/init")
|
local jecs = require("../lib/init")
|
||||||
|
local ECS_ID, ECS_GENERATION = jecs.ECS_ID, jecs.ECS_GENERATION
|
||||||
|
local ECS_GENERATION_INC = jecs.ECS_GENERATION_INC
|
||||||
|
local IS_PAIR = jecs.IS_PAIR
|
||||||
|
local ECS_PAIR = jecs.ECS_PAIR
|
||||||
|
local getAlive = jecs.getAlive
|
||||||
|
local ecs_pair_first = jecs.ecs_pair_first
|
||||||
|
local ecs_pair_second = jecs.ecs_pair_second
|
||||||
|
|
||||||
local TEST, CASE, CHECK, FINISH, SKIP = testkit.test()
|
local TEST, CASE, CHECK, FINISH, SKIP = testkit.test()
|
||||||
|
|
||||||
|
@ -127,7 +134,7 @@ TEST("world", function()
|
||||||
CHECK(world:get(id, Poison) == 5)
|
CHECK(world:get(id, Poison) == 5)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Should allow deleting components"
|
do CASE "should allow deleting components"
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
local Health = world:entity()
|
local Health = world:entity()
|
||||||
|
@ -149,6 +156,35 @@ TEST("world", function()
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
do CASE "should increment generation"
|
||||||
|
local world = jecs.World.new()
|
||||||
|
local e = world:entity()
|
||||||
|
local REST = 256 + 4
|
||||||
|
CHECK(ECS_ID(e) == 1 + REST)
|
||||||
|
CHECK(ECS_GENERATION(e) == 0) -- 0
|
||||||
|
e = ECS_GENERATION_INC(e)
|
||||||
|
CHECK(ECS_GENERATION(e) == 1) -- 1
|
||||||
|
end
|
||||||
|
|
||||||
|
do CASE "relations"
|
||||||
|
local world = jecs.World.new()
|
||||||
|
local _e = world:entity()
|
||||||
|
local e2 = world:entity()
|
||||||
|
local e3 = world:entity()
|
||||||
|
local REST = 256 + 4
|
||||||
|
CHECK(ECS_ID(e2) == 2 + REST)
|
||||||
|
CHECK(ECS_ID(e3) == 3 + REST)
|
||||||
|
CHECK(ECS_GENERATION(e2) == 0)
|
||||||
|
CHECK(ECS_GENERATION(e3) == 0)
|
||||||
|
|
||||||
|
CHECK(IS_PAIR(world:entity()) == false)
|
||||||
|
|
||||||
|
local pair = ECS_PAIR(e2, e3)
|
||||||
|
CHECK(IS_PAIR(pair) == true)
|
||||||
|
CHECK(ecs_pair_first(world.entityIndex, pair) == e2)
|
||||||
|
CHECK(ecs_pair_second(world.entityIndex, pair) == e3)
|
||||||
|
end
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
FINISH()
|
FINISH()
|
Loading…
Reference in a new issue