From 77bbe3e285469dc725d8411a978cab58d511c7da Mon Sep 17 00:00:00 2001 From: Ukendio Date: Fri, 10 May 2024 21:45:24 +0200 Subject: [PATCH] Fix export --- lib/init.lua | 43 +++++-- lib/init.spec.lua | 320 ---------------------------------------------- test.project.json | 3 - tests/world.lua | 2 +- 4 files changed, 31 insertions(+), 337 deletions(-) delete mode 100644 lib/init.spec.lua diff --git a/lib/init.lua b/lib/init.lua index ea1154b..b8ebf09 100644 --- a/lib/init.lua +++ b/lib/init.lua @@ -48,7 +48,8 @@ local HI_COMPONENT_ID = 256 local ON_ADD = HI_COMPONENT_ID + 1 local ON_REMOVE = HI_COMPONENT_ID + 2 local ON_SET = HI_COMPONENT_ID + 3 -local REST = HI_COMPONENT_ID + 4 +local WILDCARD = HI_COMPONENT_ID + 4 +local REST = HI_COMPONENT_ID + 5 local function transitionArchetype( entityIndex: EntityIndex, @@ -205,7 +206,7 @@ end local FLAGS_PAIR = 0x8 -local function addFlags(flags) +local function ADD_FLAGS(flags) local typeFlags = 0x0 if flags.isPair then typeFlags = bit32.bor(typeFlags, FLAGS_PAIR) -- HIGHEST bit in the ID. @@ -231,17 +232,16 @@ local ECS_ENTITY_MASK = bit32.lshift(1, 24) -- ECS_GENERATION_MASK (0xFFFFull << 24) local ECS_GENERATION_MASK = bit32.lshift(1, 16) -local function newId(source: number, target: number) +local function NEW_ID(source: number, target: number) local e = source * 2^28 + target * ECS_ID_FLAGS_MASK return e end -local function isPair(e: number) +local function ECS_IS_PAIR(e: number) return (e % 2^4) // FLAGS_PAIR ~= 0 end -function separate(entity: number) - local _typeFlags = entity % 0x10 +function SEPARATE(entity: number) local _typeFlags = entity % 0x10 entity //= ECS_ID_FLAGS_MASK return entity // ECS_ENTITY_MASK, entity % ECS_GENERATION_MASK, _typeFlags end @@ -258,9 +258,9 @@ local function ECS_ID(e: i53) end local function ECS_GENERATION_INC(e: i53) - local id, generation, flags = separate(e) + local id, generation, flags = SEPARATE(e) - return newId(id, generation + 1) + flags + return NEW_ID(id, generation + 1) + flags end -- gets the high ID @@ -274,7 +274,7 @@ end 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 }) + local id = NEW_ID(ECS_PAIR_SECOND(target), ECS_PAIR_SECOND(source)) + ADD_FLAGS({ isPair = true }) return id end @@ -283,16 +283,16 @@ local function getAlive(entityIndex: EntityIndex, id: i53) end local function ecs_get_source(entityIndex, e) - assert(isPair(e)) + assert(ECS_IS_PAIR(e)) return getAlive(entityIndex, ECS_PAIR_FIRST(e)) end local function ecs_get_target(entityIndex, e) - assert(isPair(e)) + assert(ECS_IS_PAIR(e)) return getAlive(entityIndex, ECS_PAIR_SECOND(e)) end local function nextEntityId(entityIndex, index: i24) - local id = newId(index, 0) + local id = NEW_ID(index, 0) entityIndex.sparse[id] = { dense = index } :: Record @@ -583,6 +583,7 @@ function World.query(world: World, ...: i53): Query local firstArchetypeMap local componentIndex = world.componentIndex + local entityIndex = world.entityIndex for _, componentId in components do local map = componentIndex[componentId] @@ -602,6 +603,22 @@ function World.query(world: World, ...: i53): Query local skip = false for i, componentId in components do + if ECS_IS_PAIR(componentId) then + local source = ecs_get_source(entityIndex, componentId) + local target = ecs_get_target(entityIndex, componentId) + + if target == WILDCARD then + local matched = false + for c in archetypeRecords do + if ecs_get_source(entityIndex, c) == source then + matched = true + end + end + if matched then + fr + end + end + end local index = archetypeRecords[componentId] if not index then skip = true @@ -768,7 +785,7 @@ return table.freeze({ ON_REMOVE = ON_REMOVE; ON_SET = ON_SET; ECS_ID = ECS_ID, - IS_PAIR = isPair, + ECS_IS_PAIR = ECS_IS_PAIR, ECS_PAIR = ECS_PAIR, ECS_GENERATION = ECS_GENERATION, ECS_GENERATION_INC = ECS_GENERATION_INC, diff --git a/lib/init.spec.lua b/lib/init.spec.lua deleted file mode 100644 index 553c9a4..0000000 --- a/lib/init.spec.lua +++ /dev/null @@ -1,320 +0,0 @@ -local jecs = require(script.Parent) -local world = jecs.World.new() - -local A, B, C, D = world:entity(), world:entity(), world:entity(), world:entity() -local E, F, G, H = world:entity(), world:entity(), world:entity(), world:entity() -print("A", A) -print("B", B) -print("C", C) -print("D", D) -print("E", E) -print("F", F) -print("G", G) -print("H", H) - -local common = 0 -local N = 2^16-2 -local archetypes = {} -local function flip() - return math.random() >= 0.5 -end - -local amountOfCombination = 0 -for i = 1, N do - local entity = world:entity() - local combination = "" - - if flip() then - combination ..= "2_" - world:set(entity, B, { value = true}) - end - if flip() then - combination ..= "3_" - world:set(entity, C, { value = true}) - end - if flip() then - combination ..= "4_" - world:set(entity, D, { value = true}) - end - if flip() then - combination ..= "5_" - world:set(entity, E, { value = true}) - end - if flip() then - combination ..= "6_" - world:set(entity, F, { value = true}) - end - if flip() then - combination ..= "7_" - world:set(entity, G, { value = true}) - end - if flip() then - combination ..= "8" - world:set(entity, H, { value = true}) - end - - if #combination == 7 then - combination = "1_" .. combination - common += 1 - world:set(entity, A, { value = true}) - end - - if combination:find("2") - and combination:find("3") - and combination:find("4") - and combination:find("6") - then - amountOfCombination += 1 - end - archetypes[combination] = true -end - -return function() - describe("World", function() - it("should add component", function() - local id = world:entity() - world:set(id, A, true) - world:set(id, B, 1) - - local id1 = world:entity() - world:set(id1, A, "hello") - expect(world:get(id, A)).to.equal(true) - expect(world:get(id, B)).to.equal(1) - expect(world:get(id1, A)).to.equal("hello") - end) - - it("should remove component", function() - local Tag = world:entity() - local entities = {} - for i = 1, 10 do - local entity = world:entity() - entities[i] = entity - world:set(entity, Tag) - end - - for i = 1, 10 do - local entity = entities[i] - expect(world:get(entity, Tag)).to.equal(nil) - world:remove(entity, Tag) - end - - end) - - it("should override component data", function() - - local id = world:entity() - world:set(id, A, true) - expect(world:get(id, A)).to.equal(true) - - world:set(id, A, false) - expect(world:get(id, A)).to.equal(false) - - end) - - it("should not query a removed component", function() - local Tag = world:entity() - local AnotherTag = world:entity() - - local entity = world:entity() - world:set(entity, Tag) - world:set(entity, AnotherTag) - world:remove(entity, AnotherTag) - - local added = 0 - for e, t, a in world:query(Tag, AnotherTag) do - added += 1 - end - expect(added).to.equal(0) - end) - - it("should query correct number of compatible archetypes", function() - local added = 0 - for _ in world:query(B, C, D, F) do - added += 1 - end - expect(added).to.equal(amountOfCombination) - end) - - it("should not query poisoned players", function() - local Player = world:entity() - local Health = world:entity() - local Poison = world:entity() - - local one = world:entity() - world:set(one, Player, { name = "alice"}) - world:set(one, Health, 100) - world:set(one, Poison) - - local two = world:entity() - world:set(two, Player, { name = "bob"}) - world:set(two, Health, 90) - - local withoutCount = 0 - for _id, _player in world:query(Player):without(Poison) do - withoutCount += 1 - end - - expect(withoutCount).to.equal(1) - end) - - it("should allow calling world:entity before world:component", function() - for _ = 1, 256 do - world:entity() - end - expect(world:component()).to.be.ok() - end) - - it("should skip iteration", function() - local Position, Velocity = world:entity(), world:entity() - local e = world:entity() - world:set(e, Position, Vector3.zero) - world:set(e, Velocity, Vector3.one) - local added = 0 - for i in world:query(Position):without(Velocity) do - added += 1 - end - expect(added).to.equal(0) - end) - - it("should query all matching entities", function() - - 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 - local i = table.find(entities, id) - expect(i).to.be.ok() - table.remove(entities, i) - end - - expect(#entities).to.equal(0) - end) - - it("should query all matching entities when irrelevant component is removed", function() - - - 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 - local i = table.find(entities, id) - expect(i).to.be.ok() - table.remove(entities, i) - end - - expect(added).to.equal(N) - end) - - it("should query all entities without B", function() - 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 - local i = table.find(entities, id) - expect(i).to.be.ok() - table.remove(entities, i) - end - - expect(#entities).to.equal(0) - end) - - it("should allow setting components in arbitrary order", function() - 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) - - expect(world:get(id, Poison)).to.equal(5) - end) - - it("Should allow deleting components", function() - 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) - - expect(world:get(id, Poison)).to.never.be.ok() - expect(world:get(id, Health)).to.never.be.ok() - end) - - it("should allow iterating the whole world", function() - 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 - expect(data[A]).to.be.ok() - expect(data[B]).to.never.be.ok() - elseif id == eB then - expect(data[B]).to.be.ok() - expect(data[A]).to.never.be.ok() - elseif id == eAB then - expect(data[A]).to.be.ok() - expect(data[B]).to.be.ok() - else - error("unknown entity", id) - end - end - - expect(count).to.equal(3) - end) - end) -end \ No newline at end of file diff --git a/test.project.json b/test.project.json index b931a84..a6f15d4 100644 --- a/test.project.json +++ b/test.project.json @@ -11,9 +11,6 @@ }, "ReplicatedStorage": { "$className": "ReplicatedStorage", - "DevPackages": { - "$path": "DevPackages" - }, "Lib": { "$path": "lib" }, diff --git a/tests/world.lua b/tests/world.lua index d95097d..1b8a3ea 100644 --- a/tests/world.lua +++ b/tests/world.lua @@ -2,7 +2,7 @@ local testkit = require("../testkit") 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 IS_PAIR = jecs.ECS_IS_PAIR local ECS_PAIR = jecs.ECS_PAIR local getAlive = jecs.getAlive local ecs_get_source = jecs.ecs_get_source