mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-25 01:20:04 +00:00
Add nth parameter to world:target
This commit is contained in:
parent
411138e1f7
commit
54b67ebdf3
3 changed files with 214 additions and 89 deletions
4
demo/src/ReplicatedStorage/ecs_init.luau
Normal file
4
demo/src/ReplicatedStorage/ecs_init.luau
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
_G.JECS_DEBUG = true
|
||||||
|
_G.JECS_HI_COMPONENT_ID = 32
|
||||||
|
require(game:GetService("ReplicatedStorage").ecs)
|
||||||
|
return
|
111
src/init.luau
111
src/init.luau
|
@ -382,11 +382,8 @@ local function world_has(world: World, entity: number, ...: i53): boolean
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TODO:
|
|
||||||
-- should have an additional `nth` parameter which selects the nth target
|
|
||||||
-- this is important when an entity can have multiple relationships with the same target
|
|
||||||
local function world_target(world: World, entity: i53,
|
local function world_target(world: World, entity: i53,
|
||||||
relation: i24--[[, nth: number]]): i24?
|
relation: i24, index: number): i24?
|
||||||
|
|
||||||
local record = world.entityIndex.sparse[entity]
|
local record = world.entityIndex.sparse[entity]
|
||||||
local archetype = record.archetype
|
local archetype = record.archetype
|
||||||
|
@ -404,7 +401,24 @@ local function world_target(world: World, entity: i53,
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
return ecs_pair_second(world, archetype.types[tr.column])
|
local count = tr.count
|
||||||
|
if index >= count then
|
||||||
|
index = index + count + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local nth = archetype.types[index + tr.column]
|
||||||
|
|
||||||
|
if not nth then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return ecs_pair_second(world, nth)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ECS_ID_IS_WILDCARD(e: i53): boolean
|
||||||
|
local first = ECS_ENTITY_T_HI(e)
|
||||||
|
local second = ECS_ENTITY_T_LO(e)
|
||||||
|
return first == EcsWildcard or second == EcsWildcard
|
||||||
end
|
end
|
||||||
|
|
||||||
local function id_record_ensure(world: World, id: number): ArchetypeMap
|
local function id_record_ensure(world: World, id: number): ArchetypeMap
|
||||||
|
@ -415,8 +429,8 @@ local function id_record_ensure(world: World, id: number): ArchetypeMap
|
||||||
local flags = ECS_ID_MASK
|
local flags = ECS_ID_MASK
|
||||||
local relation = ECS_ENTITY_T_HI(id)
|
local relation = ECS_ENTITY_T_HI(id)
|
||||||
|
|
||||||
local cleanup_policy = world_target(world, relation, EcsOnDelete)
|
local cleanup_policy = world_target(world, relation, EcsOnDelete, 0)
|
||||||
local cleanup_policy_target = world_target(world, relation, EcsOnDeleteTarget)
|
local cleanup_policy_target = world_target(world, relation, EcsOnDeleteTarget, 0)
|
||||||
|
|
||||||
local has_delete = false
|
local has_delete = false
|
||||||
|
|
||||||
|
@ -426,14 +440,14 @@ local function id_record_ensure(world: World, id: number): ArchetypeMap
|
||||||
|
|
||||||
local on_add, on_set, on_remove = world_get(world, relation, EcsOnAdd, EcsOnSet, EcsOnRemove)
|
local on_add, on_set, on_remove = world_get(world, relation, EcsOnAdd, EcsOnSet, EcsOnRemove)
|
||||||
|
|
||||||
local has_tag = world_has_one_inline(world, id, EcsTag)
|
local is_tag = not world_has_one_inline(world, relation, EcsComponent)
|
||||||
|
|
||||||
flags = bit32.bor(flags,
|
flags = bit32.bor(flags,
|
||||||
if on_add then ECS_ID_HAS_ON_ADD else 0,
|
if on_add then ECS_ID_HAS_ON_ADD else 0,
|
||||||
if on_remove then ECS_ID_HAS_ON_REMOVE else 0,
|
if on_remove then ECS_ID_HAS_ON_REMOVE else 0,
|
||||||
if on_set then ECS_ID_HAS_ON_SET else 0,
|
if on_set then ECS_ID_HAS_ON_SET else 0,
|
||||||
if has_delete then ECS_ID_DELETE else 0,
|
if has_delete then ECS_ID_DELETE else 0,
|
||||||
if has_tag then ECS_ID_IS_TAG else 0
|
if is_tag then ECS_ID_IS_TAG else 0
|
||||||
)
|
)
|
||||||
|
|
||||||
idr = {
|
idr = {
|
||||||
|
@ -447,13 +461,6 @@ local function id_record_ensure(world: World, id: number): ArchetypeMap
|
||||||
return idr
|
return idr
|
||||||
end
|
end
|
||||||
|
|
||||||
local function ECS_ID_IS_WILDCARD(e: i53): boolean
|
|
||||||
assert(ECS_IS_PAIR(e))
|
|
||||||
local first = ECS_ENTITY_T_HI(e)
|
|
||||||
local second = ECS_ENTITY_T_LO(e)
|
|
||||||
return first == EcsWildcard or second == EcsWildcard
|
|
||||||
end
|
|
||||||
|
|
||||||
local function archetype_create(world: World, types: { i24 }, prev: Archetype?): Archetype
|
local function archetype_create(world: World, types: { i24 }, prev: Archetype?): Archetype
|
||||||
local ty = hash(types)
|
local ty = hash(types)
|
||||||
|
|
||||||
|
@ -465,29 +472,39 @@ local function archetype_create(world: World, types: { i24 }, prev: Archetype?):
|
||||||
|
|
||||||
local records: { ArchetypeRecord } = {}
|
local records: { ArchetypeRecord } = {}
|
||||||
for i, componentId in types do
|
for i, componentId in types do
|
||||||
local tr = { column = i, count = 1 }
|
|
||||||
local idr = id_record_ensure(world, componentId)
|
local idr = id_record_ensure(world, componentId)
|
||||||
idr.cache[id] = tr
|
local tr = { column = i, count = 1 }
|
||||||
idr.size += 1
|
idr.cache[id] = tr
|
||||||
records[componentId] = tr
|
idr.size += 1
|
||||||
|
records[componentId] = tr
|
||||||
|
|
||||||
if ECS_IS_PAIR(componentId) then
|
if ECS_IS_PAIR(componentId) then
|
||||||
local relation = ecs_pair_first(world, componentId)
|
local relation = ecs_pair_first(world, componentId)
|
||||||
local object = ecs_pair_second(world, componentId)
|
local object = ecs_pair_second(world, componentId)
|
||||||
|
|
||||||
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)
|
||||||
|
local r_tr = idr_r.cache[id]
|
||||||
|
if not r_tr then
|
||||||
|
r_tr = { column = i, count = 1 }
|
||||||
|
idr_r.cache[id] = r_tr
|
||||||
|
idr_r.size += 1
|
||||||
|
records[r] = r_tr
|
||||||
|
else
|
||||||
|
r_tr.count += 1
|
||||||
|
end
|
||||||
|
|
||||||
local o = ECS_PAIR(EcsWildcard, object)
|
local t = ECS_PAIR(EcsWildcard, object)
|
||||||
local idr_o = id_record_ensure(world, o)
|
local idr_t = id_record_ensure(world, t)
|
||||||
|
local t_tr = idr_t.cache[id]
|
||||||
records[r] = tr
|
if not t_tr then
|
||||||
records[o] = tr
|
t_tr = { column = i, count = 1 }
|
||||||
|
idr_t.cache[id] = t_tr
|
||||||
idr_r.cache[id] = tr
|
idr_t.size += 1
|
||||||
idr_o.cache[id] = tr
|
records[t] = t_tr
|
||||||
|
else
|
||||||
idr_r.size += 1
|
t_tr.count += 1
|
||||||
idr_o.size += 1
|
end
|
||||||
end
|
end
|
||||||
if bit32.band(idr.flags, ECS_ID_IS_TAG) == 0 then
|
if bit32.band(idr.flags, ECS_ID_IS_TAG) == 0 then
|
||||||
columns[i] = {}
|
columns[i] = {}
|
||||||
|
@ -519,7 +536,7 @@ local function world_entity(world: World): i53
|
||||||
end
|
end
|
||||||
|
|
||||||
local function world_parent(world: World, entity: i53)
|
local function world_parent(world: World, entity: i53)
|
||||||
return world_target(world, entity, EcsChildOf)
|
return world_target(world, entity, EcsChildOf, 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function archetype_ensure(world: World, types, prev): Archetype
|
local function archetype_ensure(world: World, types, prev): Archetype
|
||||||
|
@ -747,7 +764,9 @@ do
|
||||||
column_count: number, types: { i53 }, entity: i53)
|
column_count: number, types: { i53 }, entity: i53)
|
||||||
|
|
||||||
for i, column in columns do
|
for i, column in columns do
|
||||||
column[column_count] = nil
|
if column ~= NULL_ARRAY then
|
||||||
|
column[column_count] = nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -755,8 +774,10 @@ do
|
||||||
column_count: number, row, types, entity)
|
column_count: number, row, types, entity)
|
||||||
|
|
||||||
for i, column in columns do
|
for i, column in columns do
|
||||||
column[row] = column[column_count]
|
if column ~= NULL_ARRAY then
|
||||||
column[column_count] = nil
|
column[row] = column[column_count]
|
||||||
|
column[column_count] = nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local function archetype_delete(world: World,
|
local function archetype_delete(world: World,
|
||||||
|
@ -1449,9 +1470,7 @@ if _G.__JECS_DEBUG == true then
|
||||||
return world_query(world, ...)
|
return world_query(world, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
World.set = function(world: World, entity: i53,
|
World.set = function(world: World, entity: i53, id: i53, value: any): ()
|
||||||
id: i53, value: any): ()
|
|
||||||
|
|
||||||
local idr = world.componentIndex[id]
|
local idr = world.componentIndex[id]
|
||||||
local flags = idr.flags
|
local flags = idr.flags
|
||||||
local id_is_tag = bit32.band(flags, ECS_ID_IS_TAG) ~= 0
|
local id_is_tag = bit32.band(flags, ECS_ID_IS_TAG) ~= 0
|
||||||
|
@ -1475,7 +1494,7 @@ if _G.__JECS_DEBUG == true then
|
||||||
world_add(world, entity, id)
|
world_add(world, entity, id)
|
||||||
end
|
end
|
||||||
|
|
||||||
World.get = function(world: World, entity: i53, id: i53, ...: i53)
|
World.get = function(world: World, entity: i53, ...)
|
||||||
local length = select("#", ...)
|
local length = select("#", ...)
|
||||||
ASSERT(length > 4, "world:get does not support more than 4 components")
|
ASSERT(length > 4, "world:get does not support more than 4 components")
|
||||||
for i = 1, length do
|
for i = 1, length do
|
||||||
|
@ -1484,15 +1503,12 @@ if _G.__JECS_DEBUG == true then
|
||||||
local flags = idr.flags
|
local flags = idr.flags
|
||||||
local id_is_tag = bit32.band(flags, ECS_ID_IS_TAG) ~= 0
|
local id_is_tag = bit32.band(flags, ECS_ID_IS_TAG) ~= 0
|
||||||
if id_is_tag then
|
if id_is_tag then
|
||||||
|
local name = world_get_one_inline(world, id, EcsName) or `${id}`
|
||||||
throw(`cannot get component ({name}) value because it is a tag. If this was intentional, use "world:has(entity, {name})"`)
|
throw(`cannot get component ({name}) value because it is a tag. If this was intentional, use "world:has(entity, {name})"`)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if value ~= nil then
|
|
||||||
local name = world_get_one_inline(world, id, EcsName) or `${id}`
|
|
||||||
throw(`You provided a value when none was expected. Did you mean to use "world:add(entity, {name})"`)
|
|
||||||
end
|
|
||||||
|
|
||||||
return world_get(world, entity, id)
|
return world_get(world, entity, ...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1518,6 +1534,11 @@ function World.new()
|
||||||
entity_index_new_id(self.entityIndex, i)
|
entity_index_new_id(self.entityIndex, i)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
world_add(self, EcsOnSet, EcsComponent)
|
||||||
|
world_add(self, EcsOnAdd, EcsComponent)
|
||||||
|
world_add(self, EcsOnRemove, EcsComponent)
|
||||||
|
world_add(self, EcsRest, EcsComponent)
|
||||||
|
world_add(self, EcsName, EcsComponent)
|
||||||
world_add(self, EcsChildOf, ECS_PAIR(EcsOnDeleteTarget, EcsDelete))
|
world_add(self, EcsChildOf, ECS_PAIR(EcsOnDeleteTarget, EcsDelete))
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
@ -1563,7 +1584,7 @@ export type World = {
|
||||||
component: <T>(self: World) -> Entity<T>,
|
component: <T>(self: World) -> Entity<T>,
|
||||||
--- Gets the target of an relationship. For example, when a user calls
|
--- Gets the target of an relationship. For example, when a user calls
|
||||||
--- `world:target(id, ChildOf(parent))`, you will obtain the parent entity.
|
--- `world:target(id, ChildOf(parent))`, you will obtain the parent entity.
|
||||||
target: (self: World, id: Entity, relation: Entity) -> Entity?,
|
target: (self: World, id: Entity, relation: Entity, nth: number) -> Entity?,
|
||||||
--- Deletes an entity and all it's related components and relationships.
|
--- Deletes an entity and all it's related components and relationships.
|
||||||
delete: (self: World, id: Entity) -> (),
|
delete: (self: World, id: Entity) -> (),
|
||||||
|
|
||||||
|
|
188
test/tests.luau
188
test/tests.luau
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
local jecs = require("@jecs")
|
local jecs = require("@jecs")
|
||||||
local testkit = require("@testkit")
|
local testkit = require("@testkit")
|
||||||
local BENCH, START = testkit.benchmark()
|
local BENCH, START = testkit.benchmark()
|
||||||
|
@ -9,6 +10,7 @@ local pair = jecs.pair
|
||||||
local getAlive = jecs.entity_index_get_alive
|
local getAlive = jecs.entity_index_get_alive
|
||||||
local ecs_pair_first = jecs.pair_first
|
local ecs_pair_first = jecs.pair_first
|
||||||
local ecs_pair_second = jecs.pair_second
|
local ecs_pair_second = jecs.pair_second
|
||||||
|
local world_new = jecs.World.new
|
||||||
|
|
||||||
local TEST, CASE, CHECK, FINISH, SKIP = testkit.test()
|
local TEST, CASE, CHECK, FINISH, SKIP = testkit.test()
|
||||||
local function CHECK_NO_ERR<T...>(s: string, fn: (T...) -> (), ...: T...)
|
local function CHECK_NO_ERR<T...>(s: string, fn: (T...) -> (), ...: T...)
|
||||||
|
@ -138,24 +140,50 @@ TEST("world:set()", function()
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "arbitrary order"
|
do CASE "arbitrary order"
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
local Health = world:entity()
|
local Health = world:entity()
|
||||||
local Poison = world:component()
|
local Poison = world:component()
|
||||||
|
|
||||||
local id = world:entity()
|
local id = world:entity()
|
||||||
world:set(id, Poison, 5)
|
world:set(id, Poison, 5)
|
||||||
world:set(id, Health, 50)
|
world:set(id, Health, 50)
|
||||||
|
|
||||||
CHECK(world:get(id, Poison) == 5)
|
CHECK(world:get(id, Poison) == 5)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
do CASE "pairs"
|
||||||
|
local world = jecs.World.new()
|
||||||
|
|
||||||
|
local C1 = world:component()
|
||||||
|
local C2 = world:component()
|
||||||
|
local T1 = world:entity()
|
||||||
|
local T2 = world:entity()
|
||||||
|
|
||||||
|
local e = world:entity()
|
||||||
|
|
||||||
|
world:set(e, pair(C1, C2), true)
|
||||||
|
world:set(e, pair(C1, T1), true)
|
||||||
|
world:set(e, pair(T1, C1), true)
|
||||||
|
world:set(e, pair(T1, T2), true)
|
||||||
|
|
||||||
|
CHECK(world:get(e, pair(C1, C2)))
|
||||||
|
CHECK(world:get(e, pair(C1, T1)))
|
||||||
|
CHECK(not world:get(e, pair(T1, C1)))
|
||||||
|
CHECK(not world:get(e, pair(T1, T2)))
|
||||||
|
|
||||||
|
local e2 = world:entity()
|
||||||
|
|
||||||
|
world:set(e2, pair(jecs.ChildOf, e), true)
|
||||||
|
CHECK(not world:get(e2, pair(jecs.ChildOf, e)))
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:remove()", function()
|
TEST("world:remove()", function()
|
||||||
do CASE "should allow remove a component that doesn't exist on entity"
|
do CASE "should allow remove a component that doesn't exist on entity"
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
local Health = world:entity()
|
local Health = world:component()
|
||||||
local Poison = world:component()
|
local Poison = world:component()
|
||||||
|
|
||||||
local id = world:entity()
|
local id = world:entity()
|
||||||
|
@ -213,14 +241,35 @@ end)
|
||||||
TEST("world:query()", function()
|
TEST("world:query()", function()
|
||||||
do CASE "tag"
|
do CASE "tag"
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local A = world:component()
|
local A = world:entity()
|
||||||
world:add(A, jecs.Tag)
|
|
||||||
local e = world:entity()
|
local e = world:entity()
|
||||||
world:set(e, A, "test")
|
world:set(e, A, "test")
|
||||||
for id, a in world:query(A) do
|
for id, a in world:query(A) do
|
||||||
CHECK(a == nil)
|
CHECK(a == nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
do CASE "pairs"
|
||||||
|
local world = jecs.World.new()
|
||||||
|
|
||||||
|
local C1 = world:component()
|
||||||
|
local C2 = world:component()
|
||||||
|
local T1 = world:entity()
|
||||||
|
local T2 = world:entity()
|
||||||
|
|
||||||
|
local e = world:entity()
|
||||||
|
|
||||||
|
world:set(e, pair(C1, C2), true)
|
||||||
|
world:set(e, pair(C1, T1), true)
|
||||||
|
world:set(e, pair(T1, C1), true)
|
||||||
|
world:set(e, pair(T1, T2), true)
|
||||||
|
|
||||||
|
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(d == nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
do CASE "query single component"
|
do CASE "query single component"
|
||||||
do
|
do
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
@ -405,8 +454,8 @@ TEST("world:query()", function()
|
||||||
|
|
||||||
do CASE("should allow querying for relations")
|
do CASE("should allow querying for relations")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local Eats = world:entity()
|
local Eats = world:component()
|
||||||
local Apples = world:entity()
|
local Apples = world:component()
|
||||||
local bob = world:entity()
|
local bob = world:entity()
|
||||||
|
|
||||||
world:set(bob, pair(Eats, Apples), true)
|
world:set(bob, pair(Eats, Apples), true)
|
||||||
|
@ -418,7 +467,7 @@ TEST("world:query()", function()
|
||||||
|
|
||||||
do CASE("should allow wildcards in queries")
|
do CASE("should allow wildcards in queries")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local Eats = world:entity()
|
local Eats = world:component()
|
||||||
local Apples = world:entity()
|
local Apples = world:entity()
|
||||||
local bob = world:entity()
|
local bob = world:entity()
|
||||||
|
|
||||||
|
@ -437,7 +486,7 @@ TEST("world:query()", function()
|
||||||
|
|
||||||
do CASE("should match against multiple pairs")
|
do CASE("should match against multiple pairs")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local Eats = world:entity()
|
local Eats = world:component()
|
||||||
local Apples = world:entity()
|
local Apples = world:entity()
|
||||||
local Oranges = world:entity()
|
local Oranges = world:entity()
|
||||||
local bob = world:entity()
|
local bob = world:entity()
|
||||||
|
@ -697,32 +746,32 @@ TEST("world:clear()", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:has()", function()
|
TEST("world:has()", function()
|
||||||
do CASE "should find Tag on entity"
|
do CASE "should find Tag on entity"
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
local Tag = world:component()
|
local Tag = world:entity()
|
||||||
|
|
||||||
local e = world:entity()
|
local e = world:entity()
|
||||||
world:add(e, Tag)
|
world:add(e, Tag)
|
||||||
|
|
||||||
CHECK(world:has(e, Tag))
|
CHECK(world:has(e, Tag))
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "should return false when missing one tag"
|
do CASE "should return false when missing one tag"
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
local A = world:component()
|
local A = world:entity()
|
||||||
local B = world:component()
|
local B = world:entity()
|
||||||
local C = world:component()
|
local C = world:entity()
|
||||||
local D = world:component()
|
local D = world:entity()
|
||||||
|
|
||||||
local e = world:entity()
|
local e = world:entity()
|
||||||
world:add(e, A)
|
world:add(e, A)
|
||||||
world:add(e, C)
|
world:add(e, C)
|
||||||
world:add(e, D)
|
world:add(e, D)
|
||||||
|
|
||||||
CHECK(world:has(e, A, B, C, D) == false)
|
CHECK(world:has(e, A, B, C, D) == false)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:component()", function()
|
TEST("world:component()", function()
|
||||||
|
@ -738,10 +787,8 @@ TEST("world:component()", function()
|
||||||
do CASE "tag"
|
do CASE "tag"
|
||||||
local world = jecs.World.new() :: World
|
local world = jecs.World.new() :: World
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
local B = world:component()
|
local B = world:entity()
|
||||||
local C = world:component()
|
local C = world:entity()
|
||||||
world:add(B, jecs.Tag)
|
|
||||||
world:add(C, jecs.Tag)
|
|
||||||
local e = world:entity()
|
local e = world:entity()
|
||||||
world:set(e, A, "test")
|
world:set(e, A, "test")
|
||||||
world:add(e, B, "test")
|
world:add(e, B, "test")
|
||||||
|
@ -758,7 +805,7 @@ TEST("world:delete", function()
|
||||||
do CASE("should allow deleting components")
|
do CASE("should allow deleting components")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
local Health = world:entity()
|
local Health = world:component()
|
||||||
local Poison = world:component()
|
local Poison = world:component()
|
||||||
|
|
||||||
local id = world:entity()
|
local id = world:entity()
|
||||||
|
@ -900,13 +947,66 @@ TEST("world:delete", function()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
TEST("world:target", function()
|
||||||
|
do CASE "nth index"
|
||||||
|
local world = world_new()
|
||||||
|
local A = world:component()
|
||||||
|
local B = world:component()
|
||||||
|
local C = world:component()
|
||||||
|
local D = world:component()
|
||||||
|
local e = world:entity()
|
||||||
|
|
||||||
|
world:add(e, pair(A, B))
|
||||||
|
world:add(e, pair(A, C))
|
||||||
|
world:add(e, pair(B, C))
|
||||||
|
world:add(e, pair(B, D))
|
||||||
|
world:add(e, pair(C, D))
|
||||||
|
|
||||||
|
CHECK(pair(A, B) < pair(A, C))
|
||||||
|
|
||||||
|
CHECK(world:target(e, A, 0) == B)
|
||||||
|
CHECK(world:target(e, A, 1) == C)
|
||||||
|
CHECK(world:target(e, B, 0) == C)
|
||||||
|
CHECK(world:target(e, B, 1) == D)
|
||||||
|
CHECK(world:target(e, C, 0) == D)
|
||||||
|
CHECK(world:target(e, C, 1) == nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
do CASE "loop until no target"
|
||||||
|
local world = world_new()
|
||||||
|
|
||||||
|
local ROOT = world:entity()
|
||||||
|
local e1 = world:entity()
|
||||||
|
local targets = {}
|
||||||
|
|
||||||
|
for i = 1, 10 do
|
||||||
|
local target = world:entity()
|
||||||
|
targets[i] = target
|
||||||
|
world:add(e1, pair(ROOT, target))
|
||||||
|
end
|
||||||
|
|
||||||
|
local i = 0
|
||||||
|
local target = world:target(e1, ROOT, 0)
|
||||||
|
while target do
|
||||||
|
i+=1
|
||||||
|
CHECK(targets[i] == target)
|
||||||
|
target = world:target(e1, ROOT, i)
|
||||||
|
end
|
||||||
|
|
||||||
|
CHECK(i == 10)
|
||||||
|
end
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
TEST("world:contains", function()
|
TEST("world:contains", function()
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
local id = world:entity()
|
local id = world:entity()
|
||||||
CHECK(world:contains(id))
|
CHECK(world:contains(id))
|
||||||
world:delete(id)
|
|
||||||
CHECK(not world:contains(id))
|
do CASE "should not exist after delete"
|
||||||
|
world:delete(id)
|
||||||
|
CHECK(not world:contains(id))
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
type Tracker<T> = { track: (world: World, fn: (changes: {
|
type Tracker<T> = { track: (world: World, fn: (changes: {
|
||||||
added: () -> () -> (number, T),
|
added: () -> () -> (number, T),
|
||||||
|
@ -1434,7 +1534,7 @@ TEST("scheduler", function()
|
||||||
local DependsOn = components.DependsOn
|
local DependsOn = components.DependsOn
|
||||||
|
|
||||||
local Physics = scheduler.phase(Heartbeat)
|
local Physics = scheduler.phase(Heartbeat)
|
||||||
CHECK(world:target(Physics, DependsOn) == Heartbeat)
|
CHECK(world:target(Physics, DependsOn, 0) == Heartbeat)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "user-defined sub phases"
|
do CASE "user-defined sub phases"
|
||||||
|
@ -1447,7 +1547,7 @@ TEST("scheduler", function()
|
||||||
local A = scheduler.phase(phases.Heartbeat)
|
local A = scheduler.phase(phases.Heartbeat)
|
||||||
local B = scheduler.phase(A)
|
local B = scheduler.phase(A)
|
||||||
|
|
||||||
CHECK(world:target(B, DependsOn) == A)
|
CHECK(world:target(B, DependsOn, 0) == A)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "phase order"
|
do CASE "phase order"
|
||||||
|
|
Loading…
Reference in a new issue