diff --git a/.github/workflows/styling.yaml b/.github/workflows/styling.yaml deleted file mode 100644 index 4d28cab..0000000 --- a/.github/workflows/styling.yaml +++ /dev/null @@ -1,20 +0,0 @@ -name: Styling - -on: [push, pull_request, workflow_dispatch] - -jobs: - run: - name: Run Stylua - runs-on: ubuntu-latest - - steps: - - name: Checkout Project - uses: actions/checkout@v4 - - - name: Run Stylua - uses: JohnnyMorganz/stylua-action@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - version: latest # NOTE: we recommend pinning to a specific version in case of formatting changes - # CLI arguments - args: --check jecs.luau diff --git a/jecs.luau b/jecs.luau index 5d9f31f..4ca2baf 100644 --- a/jecs.luau +++ b/jecs.luau @@ -307,7 +307,7 @@ local function archetype_move(entity_index: EntityIndex, to: Archetype, dst_row: local src_entities = from.entities local last = #src_entities - local types = from.types + local id_types = from.types local records = to.records for i, column in src_columns do @@ -316,12 +316,13 @@ local function archetype_move(entity_index: EntityIndex, to: Archetype, dst_row: end -- Retrieves the new column index from the source archetype's record from each component -- We have to do this because the columns are tightly packed and indexes may not correspond to each other. - local tr = records[types[i]] + local tr = records[id_types[i]] -- Sometimes target column may not exist, e.g. when you remove a component. if tr then dst_columns[tr.column][dst_row] = column[src_row] end + -- If the entity is the last row in the archetype then swapping it would be meaningless. if src_row ~= last then -- Swap rempves columns to ensure there are no holes in the archetype. @@ -525,7 +526,8 @@ local function id_record_ensure(world: World, id: number): IdRecord if not idr then local flags = ECS_ID_MASK local relation = id - if ECS_IS_PAIR(id) then + local is_pair = ECS_IS_PAIR(id) + if is_pair then relation = ecs_pair_first(world, id) end @@ -542,6 +544,10 @@ local function id_record_ensure(world: World, id: number): IdRecord local is_tag = not world_has_one_inline(world, relation, EcsComponent) + if is_tag and is_pair then + is_tag = not world_has_one_inline(world, ecs_pair_second(world, id), EcsComponent) + end + flags = bit32.bor( flags, if on_add then ECS_ID_HAS_ON_ADD else 0, @@ -585,8 +591,6 @@ local function archetype_append_to_records( end end - - local function create_observer_uni(world: World, component: number, event): ecs_partial_t local map = world.observerable[event] if not map then @@ -670,18 +674,18 @@ local function world_parent(world: World, entity: i53) return world_target(world, entity, EcsChildOf, 0) end -local function archetype_ensure(world: World, types): Archetype - if #types < 1 then +local function archetype_ensure(world: World, id_types): Archetype + if #id_types < 1 then return world.ROOT_ARCHETYPE end - local ty = hash(types) + local ty = hash(id_types) local archetype = world.archetypeIndex[ty] if archetype then return archetype end - return archetype_create(world, types, ty) + return archetype_create(world, id_types, ty) end local function find_insert(id_types: { i53 }, toAdd: i53): number @@ -963,7 +967,7 @@ end local function archetype_delete(world: World, archetype: Archetype, row: number, destruct: boolean?) local entityIndex = world.entity_index local columns = archetype.columns - local types = archetype.types + local id_types = archetype.types local entities = archetype.entities local column_count = #entities local last = #entities @@ -982,7 +986,7 @@ local function archetype_delete(world: World, archetype: Archetype, row: number, -- TODO: if last == 0 then deactivate table - for _, id in types do + for _, id in id_types do local on_remove: (entity: i53) -> () = world_get_one_inline(world, id, EcsOnRemove) if on_remove then on_remove(delete) @@ -990,9 +994,9 @@ local function archetype_delete(world: World, archetype: Archetype, row: number, end if row == last then - archetype_fast_delete_last(columns, column_count, types, delete) + archetype_fast_delete_last(columns, column_count, id_types, delete) else - archetype_fast_delete(columns, column_count, row, types, delete) + archetype_fast_delete(columns, column_count, row, id_types, delete) end end @@ -1886,23 +1890,21 @@ function World.new() return self end -export type Id = Entity | Pair, Entity> +export type Id = Entity -export type Pair = number & { - __relation: First, -} +type function ecs_entity_t(entity) + return entity:components()[2]:readproperty(types.singleton("__T")) +end --- type function _Pair(first, second) --- local thing = first:components()[2] +export type function Pair(first, second) + local thing = first:components()[2] --- if thing:readproperty(types.singleton("__T")):is("nil") then --- return second --- else --- return first --- end --- end - --- type TestPair = _Pair, Entity> + if thing:readproperty(types.singleton("__T")):is("nil") then + return second + else + return first + end +end type Item = (self: Query) -> (Entity, T...) @@ -1997,7 +1999,14 @@ export type World = { children: (self: World, id: Id) -> () -> Entity, --- Searches the world for entities that match a given query - query: (self: World, a: { __T: A }) -> Query, + query: ((World, A) -> Query>) + & ((World, A, B) -> Query, ecs_entity_t>) + & ((World, A, B, C) -> Query, ecs_entity_t, ecs_entity_t>) + & ((World, A, B, C, D) -> Query, ecs_entity_t, ecs_entity_t, ecs_entity_t>) + & ((World, A, B, C, D, E) -> Query, ecs_entity_t, ecs_entity_t, ecs_entity_t, ecs_entity_t>) + & ((World, A, B, C, D, E, F) -> Query, ecs_entity_t, ecs_entity_t, ecs_entity_t, ecs_entity_t, ecs_entity_t>) + & ((World, A, B, C, D, E, F, G) -> Query, ecs_entity_t, ecs_entity_t, ecs_entity_t, ecs_entity_t, ecs_entity_t, ecs_entity_t>) + & ((World, A, B, C, D, E, F, G, H) -> Query, ecs_entity_t, ecs_entity_t, ecs_entity_t, ecs_entity_t, ecs_entity_t, ecs_entity_t, ecs_entity_t>) } return { diff --git a/test/tests.luau b/test/tests.luau index 04f0aef..1037dce 100644 --- a/test/tests.luau +++ b/test/tests.luau @@ -169,8 +169,7 @@ TEST("world:entity()", function() CHECK(ECS_GENERATION(e) == 1) -- 1 end - do - CASE("pairs") + do CASE("pairs") local world = jecs.World.new() local _e = world:entity() local e2 = world:entity() @@ -179,11 +178,17 @@ TEST("world:entity()", function() -- Incomplete pair, must have a bit flag that notes it is a pair CHECK(IS_PAIR(world:entity()) == false) - local pair = pair(e2, e3) - CHECK(IS_PAIR(pair) == true) + local p = pair(e2, e3) + CHECK(IS_PAIR(p) == true) - CHECK(ecs_pair_first(world, pair) == e2) - CHECK(ecs_pair_second(world, pair) == e3) + CHECK(ecs_pair_first(world, p) == e2) + CHECK(ecs_pair_second(world, p) == e3) + + world:delete(e2) + local e2v2 = world:entity() + CHECK(IS_PAIR(e2v2) == false) + + CHECK(IS_PAIR(pair(e2v2, e3)) == true) end do CASE "Recycling" @@ -285,7 +290,7 @@ TEST("world:set()", function() CHECK(world:get(e, pair(C1, C2))) CHECK(world:get(e, pair(C1, T1))) - CHECK(not world:get(e, pair(T1, C1))) + CHECK(world:get(e, pair(T1, C1))) CHECK(not world:get(e, pair(T1, T2))) local e2 = world:entity() @@ -421,7 +426,7 @@ TEST("world:query()", function() for id, a, b, c, d in world:query(pair(C1, C2), pair(C1, T1), pair(T1, C1), pair(T1, T2)) do CHECK(a == true) CHECK(b == true) - CHECK(c == nil) + CHECK(c == true) CHECK(d == nil) end end