mirror of
https://github.com/Ukendio/jecs.git
synced 2025-07-01 04:49:16 +00:00
Fix export
This commit is contained in:
parent
4c105fa72c
commit
77bbe3e285
4 changed files with 31 additions and 337 deletions
43
lib/init.lua
43
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,
|
||||
|
|
|
@ -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
|
|
@ -11,9 +11,6 @@
|
|||
},
|
||||
"ReplicatedStorage": {
|
||||
"$className": "ReplicatedStorage",
|
||||
"DevPackages": {
|
||||
"$path": "DevPackages"
|
||||
},
|
||||
"Lib": {
|
||||
"$path": "lib"
|
||||
},
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue