mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-24 17:10:03 +00:00
Fix nth count for target
This commit is contained in:
parent
f3befa3adb
commit
59e7fd1f41
2 changed files with 102 additions and 54 deletions
109
jecs.luau
109
jecs.luau
|
@ -135,7 +135,11 @@ local ECS_ENTITY_MASK = bit32.lshift(1, 24)
|
||||||
local ECS_GENERATION_MASK = bit32.lshift(1, 16)
|
local ECS_GENERATION_MASK = bit32.lshift(1, 16)
|
||||||
|
|
||||||
local NULL_ARRAY = table.freeze({})
|
local NULL_ARRAY = table.freeze({})
|
||||||
|
local ECS_INTERNAL_ERROR = [[
|
||||||
|
This is an internal error, please file a bug report via the following link:
|
||||||
|
|
||||||
|
https://github.com/Ukendio/jecs/issues/new?template=BUG-REPORT.md
|
||||||
|
]]
|
||||||
|
|
||||||
local function ECS_COMBINE(id: number, generation: number): i53
|
local function ECS_COMBINE(id: number, generation: number): i53
|
||||||
return id + (generation * ECS_ENTITY_MASK)
|
return id + (generation * ECS_ENTITY_MASK)
|
||||||
|
@ -180,6 +184,14 @@ local function ECS_PAIR(pred: i53, obj: i53): i53
|
||||||
return obj + (pred * ECS_ENTITY_MASK) + ECS_PAIR_OFFSET
|
return obj + (pred * ECS_ENTITY_MASK) + ECS_PAIR_OFFSET
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function ECS_PAIR_FIRST(e: i53): i24
|
||||||
|
return (e - ECS_PAIR_OFFSET) // ECS_ENTITY_MASK
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ECS_PAIR_SECOND(e: i53): i24
|
||||||
|
return (e - ECS_PAIR_OFFSET) % ECS_ENTITY_MASK
|
||||||
|
end
|
||||||
|
|
||||||
local function entity_index_try_get_any(
|
local function entity_index_try_get_any(
|
||||||
entity_index: ecs_entity_index_t,
|
entity_index: ecs_entity_index_t,
|
||||||
entity: number
|
entity: number
|
||||||
|
@ -217,16 +229,39 @@ local function entity_index_try_get_fast(entity_index: ecs_entity_index_t, entit
|
||||||
return r
|
return r
|
||||||
end
|
end
|
||||||
|
|
||||||
local function entity_index_get_alive(index: ecs_entity_index_t, id: i24): i53
|
local function entity_index_is_alive(entity_index: ecs_entity_index_t, entity: i53)
|
||||||
local r = entity_index_try_get_any(index, id)
|
return entity_index_try_get(entity_index, entity) ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function entity_index_get_alive(index: ecs_entity_index_t, entity: i53): i53
|
||||||
|
local r = entity_index_try_get_any(index, entity)
|
||||||
if r then
|
if r then
|
||||||
return index.dense_array[r.dense]
|
return index.dense_array[r.dense]
|
||||||
end
|
end
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
local function entity_index_is_alive(entity_index: ecs_entity_index_t, entity: i53)
|
local function ecs_get_alive(world, entity)
|
||||||
return entity_index_try_get(entity_index, entity) ~= nil
|
if entity == 0 then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local eindex = world.entity_index
|
||||||
|
|
||||||
|
if entity_index_is_alive(eindex, entity) then
|
||||||
|
return entity
|
||||||
|
end
|
||||||
|
|
||||||
|
if entity > ECS_ENTITY_MASK then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local current = entity_index_get_alive(eindex, entity)
|
||||||
|
if not current or not entity_index_is_alive(eindex, current) then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
return current
|
||||||
end
|
end
|
||||||
|
|
||||||
local function entity_index_new_id(entity_index: ecs_entity_index_t): i53
|
local function entity_index_new_id(entity_index: ecs_entity_index_t): i53
|
||||||
|
@ -251,13 +286,13 @@ local function entity_index_new_id(entity_index: ecs_entity_index_t): i53
|
||||||
end
|
end
|
||||||
|
|
||||||
local function ecs_pair_first(world: ecs_world_t, e: i53)
|
local function ecs_pair_first(world: ecs_world_t, e: i53)
|
||||||
local pred = (e - ECS_PAIR_OFFSET) // ECS_ENTITY_MASK
|
local pred = ECS_PAIR_FIRST(e)
|
||||||
return entity_index_get_alive(world.entity_index, pred)
|
return ecs_get_alive(world, pred)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function ecs_pair_second(world: ecs_world_t, e: i53)
|
local function ecs_pair_second(world: ecs_world_t, e: i53)
|
||||||
local obj = (e - ECS_PAIR_OFFSET) % ECS_ENTITY_MASK
|
local obj = ECS_PAIR_SECOND(e)
|
||||||
return entity_index_get_alive(world.entity_index, obj)
|
return ecs_get_alive(world, obj)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function query_match(query: ecs_query_data_t,
|
local function query_match(query: ecs_query_data_t,
|
||||||
|
@ -433,24 +468,6 @@ local function world_get(world: ecs_world_t, entity: i53,
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function world_get_one_inline(world: ecs_world_t, entity: i53, id: i53): any
|
|
||||||
local record = entity_index_try_get_fast(world.entity_index, entity)
|
|
||||||
if not record then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local archetype = record.archetype
|
|
||||||
if not archetype then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local tr = archetype.records[id]
|
|
||||||
if not tr then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
return archetype.columns[tr][record.row]
|
|
||||||
end
|
|
||||||
|
|
||||||
local function world_has_one_inline(world: ecs_world_t, entity: i53, id: i53): boolean
|
local function world_has_one_inline(world: ecs_world_t, entity: i53, id: i53): boolean
|
||||||
local record = entity_index_try_get_fast(world.entity_index, entity)
|
local record = entity_index_try_get_fast(world.entity_index, entity)
|
||||||
if not record then
|
if not record then
|
||||||
|
@ -501,30 +518,23 @@ local function world_target(world: ecs_world_t, entity: i53, relation: i24, inde
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local idr = world.component_index[ECS_PAIR(relation, EcsWildcard)]
|
local r = ECS_PAIR(relation, EcsWildcard)
|
||||||
if not idr then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local archetype_id = archetype.id
|
local count = archetype.counts[r]
|
||||||
local count = idr.counts[archetype.id]
|
|
||||||
if not count then
|
if not count then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if nth > count then
|
if nth >= count then
|
||||||
nth = nth + count
|
nth = nth + count + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
local tr = idr.cache[archetype_id]
|
nth = archetype.types[nth + archetype.records[r]]
|
||||||
|
|
||||||
nth = archetype.types[nth + tr]
|
|
||||||
|
|
||||||
if not nth then
|
if not nth then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
return ecs_pair_second(world, nth)
|
return ECS_PAIR_SECOND(nth)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function ECS_ID_IS_WILDCARD(e: i53): boolean
|
local function ECS_ID_IS_WILDCARD(e: i53): boolean
|
||||||
|
@ -535,14 +545,21 @@ end
|
||||||
|
|
||||||
local function id_record_ensure(world: ecs_world_t, id: number): ecs_id_record_t
|
local function id_record_ensure(world: ecs_world_t, id: number): ecs_id_record_t
|
||||||
local component_index = world.component_index
|
local component_index = world.component_index
|
||||||
|
local entity_index = world.entity_index
|
||||||
local idr: ecs_id_record_t = component_index[id]
|
local idr: ecs_id_record_t = component_index[id]
|
||||||
|
|
||||||
if not idr then
|
if not idr then
|
||||||
local flags = ECS_ID_MASK
|
local flags = ECS_ID_MASK
|
||||||
local relation = id
|
local relation = id
|
||||||
|
local target = 0
|
||||||
local is_pair = ECS_IS_PAIR(id)
|
local is_pair = ECS_IS_PAIR(id)
|
||||||
if is_pair then
|
if is_pair then
|
||||||
relation = ecs_pair_first(world, id)
|
relation = entity_index_get_alive(entity_index, ECS_PAIR_FIRST(id))
|
||||||
|
assert(relation ~= 0 and entity_index_is_alive(
|
||||||
|
entity_index, relation), ECS_INTERNAL_ERROR)
|
||||||
|
target = entity_index_get_alive(entity_index, ECS_PAIR_SECOND(id))
|
||||||
|
assert(target ~= 0 and entity_index_is_alive(
|
||||||
|
entity_index, target), ECS_INTERNAL_ERROR)
|
||||||
end
|
end
|
||||||
|
|
||||||
local cleanup_policy = world_target(world, relation, EcsOnDelete, 0)
|
local cleanup_policy = world_target(world, relation, EcsOnDelete, 0)
|
||||||
|
@ -559,7 +576,7 @@ local function id_record_ensure(world: ecs_world_t, id: number): ecs_id_record_t
|
||||||
local is_tag = not world_has_one_inline(world, relation, EcsComponent)
|
local is_tag = not world_has_one_inline(world, relation, EcsComponent)
|
||||||
|
|
||||||
if is_tag and is_pair then
|
if is_tag and is_pair then
|
||||||
is_tag = not world_has_one_inline(world, ecs_pair_second(world, id), EcsComponent)
|
is_tag = not world_has_one_inline(world, target, EcsComponent)
|
||||||
end
|
end
|
||||||
|
|
||||||
flags = bit32.bor(
|
flags = bit32.bor(
|
||||||
|
@ -624,6 +641,7 @@ local function archetype_create(world: ecs_world_t, id_types: { i24 }, ty, prev:
|
||||||
local records: { number } = {}
|
local records: { number } = {}
|
||||||
local counts: {number} = {}
|
local counts: {number} = {}
|
||||||
|
|
||||||
|
local entity_index = world.entity_index
|
||||||
local archetype: ecs_archetype_t = {
|
local archetype: ecs_archetype_t = {
|
||||||
columns = columns,
|
columns = columns,
|
||||||
entities = {},
|
entities = {},
|
||||||
|
@ -643,9 +661,10 @@ local function archetype_create(world: ecs_world_t, id_types: { i24 }, ty, prev:
|
||||||
archetype_append_to_records(idr, archetype, component_id, i)
|
archetype_append_to_records(idr, archetype, component_id, i)
|
||||||
|
|
||||||
if ECS_IS_PAIR(component_id) then
|
if ECS_IS_PAIR(component_id) then
|
||||||
local relation = ecs_pair_first(world, component_id)
|
local relation = ECS_PAIR_FIRST(component_id)
|
||||||
local object = ecs_pair_second(world, component_id)
|
relation = entity_index_get_alive(entity_index, relation)
|
||||||
|
local object = ECS_PAIR_SECOND(component_id)
|
||||||
|
object = entity_index_get_alive(entity_index, object)
|
||||||
local r = ECS_PAIR(relation, EcsWildcard)
|
local r = ECS_PAIR(relation, EcsWildcard)
|
||||||
local idr_r = id_record_ensure(world, r)
|
local idr_r = id_record_ensure(world, r)
|
||||||
archetype_append_to_records(idr_r, archetype, r, i)
|
archetype_append_to_records(idr_r, archetype, r, i)
|
||||||
|
@ -2481,7 +2500,7 @@ return {
|
||||||
Name = EcsName :: Entity<string>,
|
Name = EcsName :: Entity<string>,
|
||||||
Rest = EcsRest :: Entity,
|
Rest = EcsRest :: Entity,
|
||||||
|
|
||||||
pair = ECS_PAIR :: <P, O>(first: P, second: O) -> Pair<P, O>,
|
pair = (ECS_PAIR :: any) :: <P, O>(first: P, second: O) -> Pair<P, O>,
|
||||||
|
|
||||||
-- Inwards facing API for testing
|
-- Inwards facing API for testing
|
||||||
ECS_ID = ECS_ENTITY_T_LO,
|
ECS_ID = ECS_ENTITY_T_LO,
|
||||||
|
|
|
@ -126,23 +126,51 @@ TEST("#repro2", function()
|
||||||
local entity = world:entity()
|
local entity = world:entity()
|
||||||
world:set(entity, pair(Lifetime, Particle), 1)
|
world:set(entity, pair(Lifetime, Particle), 1)
|
||||||
world:set(entity, pair(Lifetime, Beam), 2)
|
world:set(entity, pair(Lifetime, Beam), 2)
|
||||||
|
world:set(entity, pair(4, 5), 6) -- noise
|
||||||
|
|
||||||
|
local entity_visualizer = require("@tools/entity_visualiser")
|
||||||
|
entity_visualizer.components(world, entity)
|
||||||
|
|
||||||
for e in world:each(pair(Lifetime, __)) do
|
for e in world:each(pair(Lifetime, __)) do
|
||||||
local i = 0
|
local i = 0
|
||||||
local nth = world:target(e, Lifetime, i)
|
local nth = world:target(e, Lifetime, i)
|
||||||
while nth do
|
while nth do
|
||||||
|
entity_visualizer.components(world, e)
|
||||||
|
|
||||||
local data = world:get(e, pair(Lifetime, nth))
|
local data = world:get(e, pair(Lifetime, nth))
|
||||||
if nth == Particle then
|
data -= 1
|
||||||
CHECK(data == 1)
|
if data <= 0 then
|
||||||
elseif nth == Beam then
|
world:remove(e, pair(Lifetime, nth))
|
||||||
CHECK(data == 2)
|
else
|
||||||
else
|
world:set(e, pair(Lifetime, nth), data)
|
||||||
CHECK(false)
|
end
|
||||||
end
|
|
||||||
i += 1
|
i += 1
|
||||||
nth = world:target(e, Lifetime, i)
|
nth = world:target(e, Lifetime, i)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
CHECK(not world:has(entity, pair(Lifetime, Particle)))
|
||||||
|
CHECK(world:get(entity, pair(Lifetime, Beam)) == 1)
|
||||||
|
end)
|
||||||
|
|
||||||
|
local lifetime_tracker_add = require("@tools/lifetime_tracker")
|
||||||
|
|
||||||
|
TEST("another", function()
|
||||||
|
local world = world_new()
|
||||||
|
world = lifetime_tracker_add(world, {padding_enabled=false})
|
||||||
|
local e1 = world:entity()
|
||||||
|
local e2 = world:entity()
|
||||||
|
local e3 = world:entity()
|
||||||
|
world:delete(e2)
|
||||||
|
world:print_entity_index()
|
||||||
|
print(pair(e1, e2))
|
||||||
|
print(pair(e2, e3))
|
||||||
|
local e2_e3 = pair(e2, e3)
|
||||||
|
CHECK(jecs.pair_first(world, e2_e3) == 0)
|
||||||
|
CHECK(jecs.pair_second(world, e2_e3) == e3)
|
||||||
|
CHECK_EXPECT_ERR(function()
|
||||||
|
world:add(e1, pair(e2, e3))
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("#repro", function()
|
TEST("#repro", function()
|
||||||
|
@ -851,8 +879,9 @@ TEST("world:query()", function()
|
||||||
local bob = world:entity()
|
local bob = world:entity()
|
||||||
|
|
||||||
world:delete(Apples)
|
world:delete(Apples)
|
||||||
|
CHECK_EXPECT_ERR(function()
|
||||||
world:set(bob, pair(Eats, Apples), "bob eats apples")
|
world:set(bob, pair(Eats, Apples), "bob eats apples")
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
do
|
do
|
||||||
|
|
Loading…
Reference in a new issue