diff --git a/jecs.luau b/jecs.luau index 3dc222a..e760217 100755 --- a/jecs.luau +++ b/jecs.luau @@ -1,3 +1,4 @@ + --!optimize 2 --!native --!strict @@ -186,9 +187,10 @@ local ECS_ENTITY_MASK = bit32.lshift(1, 24) local ECS_GENERATION_MASK = bit32.lshift(1, 16) local ECS_PAIR_OFFSET = 2^48 -local ECS_ID_DELETE = 0b01 -local ECS_ID_IS_TAG = 0b10 -local ECS_ID_MASK = 0b00 +local ECS_ID_DELETE = 0b0001 +local ECS_ID_IS_TAG = 0b0010 +local ECS_ID_IS_EXCLUSIVE = 0b0100 +local ECS_ID_MASK = 0b0000 local HI_COMPONENT_ID = 256 local EcsOnAdd = HI_COMPONENT_ID + 1 @@ -204,7 +206,8 @@ local EcsRemove = HI_COMPONENT_ID + 10 local EcsName = HI_COMPONENT_ID + 11 local EcsOnArchetypeCreate = HI_COMPONENT_ID + 12 local EcsOnArchetypeDelete = HI_COMPONENT_ID + 13 -local EcsRest = HI_COMPONENT_ID + 14 +local EcsExclusive = HI_COMPONENT_ID + 14 +local EcsRest = HI_COMPONENT_ID + 15 local NULL_ARRAY = table.freeze({}) :: Column local NULL = newproxy(false) @@ -673,6 +676,7 @@ local function id_record_ensure(world: World, id: Entity): ComponentRecord local is_pair = ECS_IS_PAIR(id :: number) local has_delete = false + local is_exclusive = false if is_pair then relation = entity_index_get_alive(entity_index, ECS_PAIR_FIRST(id :: number)) :: i53 @@ -687,6 +691,10 @@ local function id_record_ensure(world: World, id: Entity): ComponentRecord if cleanup_policy_target == EcsDelete then has_delete = true end + + if world_has_one_inline(world, relation, EcsExclusive) then + is_exclusive = true + end else local cleanup_policy = world_target(world, relation, EcsOnDelete, 0) @@ -708,7 +716,8 @@ local function id_record_ensure(world: World, id: Entity): ComponentRecord flags = bit32.bor( flags, if has_delete then ECS_ID_DELETE else 0, - if is_tag then ECS_ID_IS_TAG else 0 + if is_tag then ECS_ID_IS_TAG else 0, + if is_exclusive then ECS_ID_IS_EXCLUSIVE else 0 ) idr = { @@ -919,9 +928,20 @@ end local function find_archetype_with(world: World, id: Id, from: Archetype): Archetype local id_types = from.types + local dst = table.clone(id_types) + + if ECS_IS_PAIR(id::number) then + local first = ECS_PAIR_FIRST(id::number) + local idr = world.component_index[ECS_PAIR(first, EcsWildcard)] + if idr and bit32.btest(idr.flags, EcsExclusive) then + local cr = idr.records[from.id] + dst[cr] = id + return archetype_ensure(world, dst) + end + end local at = find_insert(id_types :: { number } , id :: number) - local dst = table.clone(id_types) + table.insert(dst, at, id) return archetype_ensure(world, dst) @@ -2903,6 +2923,7 @@ return { Delete = (EcsDelete :: any) :: Entity, Remove = (EcsRemove :: any) :: Entity, Name = (EcsName :: any) :: Entity, + Exclusive = EcsExclusive :: Entity, Rest = (EcsRest :: any) :: Entity, pair = (ECS_PAIR :: any) :: (first: Id

, second: Id) -> Pair, diff --git a/test/tests.luau b/test/tests.luau index 6387d03..ff8d7f8 100755 --- a/test/tests.luau +++ b/test/tests.luau @@ -24,9 +24,22 @@ type Id = jecs.Id local entity_visualiser = require("@tools/entity_visualiser") local dwi = entity_visualiser.stringify -TEST("repro", function() +FOCUS() +TEST("exclusive", function() + local world = jecs.world() + local A = world:component() + world:add(A, jecs.Exclusive) + local B = world:component() + local C = world:component() + local e = world:entity() + world:add(e, pair(A, B)) + world:add(e, pair(A, C)) + + CHECK(world:has(e, pair(A, B)) == false) + CHECK(world:has(e, pair(A, C)) == true) + -- print(jecs.entity_index_try_get(world.entity_index, e).archetype.type) end) TEST("bulk", function()