mirror of
https://github.com/Ukendio/jecs.git
synced 2025-09-14 04:29:18 +00:00
Fix backwards edge traversal for exclusive relationships
This commit is contained in:
parent
8f95309871
commit
1d650d12e9
4 changed files with 178 additions and 9 deletions
10
jecs.luau
10
jecs.luau
|
@ -2443,10 +2443,9 @@ local function world_new()
|
||||||
if not idr then
|
if not idr then
|
||||||
idr = component_index[wc]
|
idr = component_index[wc]
|
||||||
end
|
end
|
||||||
|
edge[id] = to
|
||||||
|
archetype_edges[(to :: Archetype).id][id] = src
|
||||||
end
|
end
|
||||||
|
|
||||||
edge[id] = to
|
|
||||||
archetype_edges[(to :: Archetype).id][id] = src
|
|
||||||
else
|
else
|
||||||
if bit32.btest(idr.flags, ECS_ID_IS_EXCLUSIVE) then
|
if bit32.btest(idr.flags, ECS_ID_IS_EXCLUSIVE) then
|
||||||
local on_remove = idr.on_remove
|
local on_remove = idr.on_remove
|
||||||
|
@ -2544,10 +2543,9 @@ local function world_new()
|
||||||
if not idr then
|
if not idr then
|
||||||
idr = component_index[wc]
|
idr = component_index[wc]
|
||||||
end
|
end
|
||||||
|
edge[id] = to
|
||||||
|
archetype_edges[(to :: Archetype).id][id] = src
|
||||||
end
|
end
|
||||||
|
|
||||||
edge[id] = to
|
|
||||||
archetype_edges[(to :: Archetype).id][id] = src
|
|
||||||
else
|
else
|
||||||
if bit32.btest(idr.flags, ECS_ID_IS_EXCLUSIVE) then
|
if bit32.btest(idr.flags, ECS_ID_IS_EXCLUSIVE) then
|
||||||
local on_remove = idr.on_remove
|
local on_remove = idr.on_remove
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@rbxts/jecs",
|
"name": "@rbxts/jecs",
|
||||||
"version": "0.9.0-rc.9",
|
"version": "0.9.0-rc.10",
|
||||||
"description": "Stupidly fast Entity Component System",
|
"description": "Stupidly fast Entity Component System",
|
||||||
"main": "jecs.luau",
|
"main": "jecs.luau",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
173
test/tests.luau
173
test/tests.luau
|
@ -24,6 +24,25 @@ type Id<T=unknown> = jecs.Id<T>
|
||||||
local entity_visualiser = require("@tools/entity_visualiser")
|
local entity_visualiser = require("@tools/entity_visualiser")
|
||||||
local dwi = entity_visualiser.stringify
|
local dwi = entity_visualiser.stringify
|
||||||
|
|
||||||
|
TEST("", function()
|
||||||
|
local world = jecs.world()
|
||||||
|
local a = world:entity()
|
||||||
|
local b = world:entity()
|
||||||
|
local c = world:entity()
|
||||||
|
|
||||||
|
world:add(a, pair(ChildOf, b))
|
||||||
|
world:add(a, pair(ChildOf, c))
|
||||||
|
|
||||||
|
CHECK(not world:has(a, pair(ChildOf, b)))
|
||||||
|
CHECK(world:has(a, pair(ChildOf, c)))
|
||||||
|
|
||||||
|
|
||||||
|
world:remove(a, pair(ChildOf, c))
|
||||||
|
|
||||||
|
CHECK(not world:has(a, pair(ChildOf, b)))
|
||||||
|
CHECK(not world:has(a, pair(ChildOf, c)))
|
||||||
|
|
||||||
|
end)
|
||||||
TEST("ardi", function()
|
TEST("ardi", function()
|
||||||
local world = jecs.world()
|
local world = jecs.world()
|
||||||
local r = world:entity()
|
local r = world:entity()
|
||||||
|
@ -319,7 +338,25 @@ TEST("repro", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:add()", function()
|
TEST("world:add()", function()
|
||||||
do CASE "exclusive relations"
|
do CASE "Removing exclusive pair should traverse backwards on edge"
|
||||||
|
local world = jecs.world()
|
||||||
|
local a = world:entity()
|
||||||
|
local b = world:entity()
|
||||||
|
local c = world:entity()
|
||||||
|
|
||||||
|
world:add(a, pair(ChildOf, b))
|
||||||
|
world:add(a, pair(ChildOf, c))
|
||||||
|
|
||||||
|
CHECK(not world:has(a, pair(ChildOf, b)))
|
||||||
|
CHECK(world:has(a, pair(ChildOf, c)))
|
||||||
|
|
||||||
|
world:remove(a, pair(ChildOf, c))
|
||||||
|
|
||||||
|
CHECK(not world:has(a, pair(ChildOf, b)))
|
||||||
|
CHECK(not world:has(a, pair(ChildOf, c)))
|
||||||
|
CHECK(not world:target(a, ChildOf))
|
||||||
|
end
|
||||||
|
do CASE "Exclusive relations"
|
||||||
local world = jecs.world()
|
local world = jecs.world()
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
world:add(A, jecs.Exclusive)
|
world:add(A, jecs.Exclusive)
|
||||||
|
@ -2044,6 +2081,140 @@ TEST("world:remove()", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:set()", function()
|
TEST("world:set()", function()
|
||||||
|
do CASE "Removing exclusive pair should traverse backwards on edge"
|
||||||
|
local world = jecs.world()
|
||||||
|
local a = world:entity()
|
||||||
|
local b = world:entity()
|
||||||
|
local c = world:entity()
|
||||||
|
|
||||||
|
local BattleLink = world:component()
|
||||||
|
world:add(BattleLink, jecs.Exclusive)
|
||||||
|
|
||||||
|
world:set(a, pair(BattleLink, b), {
|
||||||
|
timestamp = 1,
|
||||||
|
transform = vector.create(1, 2, 3)
|
||||||
|
})
|
||||||
|
world:set(a, pair(BattleLink, c), {
|
||||||
|
timestamp = 2,
|
||||||
|
transform = vector.create(1, 2, 3)
|
||||||
|
})
|
||||||
|
|
||||||
|
CHECK(not world:has(a, pair(BattleLink, b)))
|
||||||
|
CHECK(world:has(a, pair(BattleLink, c)))
|
||||||
|
|
||||||
|
world:remove(a, pair(BattleLink, c))
|
||||||
|
|
||||||
|
CHECK(not world:has(a, pair(BattleLink, b)))
|
||||||
|
CHECK(not world:has(a, pair(BattleLink, c)))
|
||||||
|
CHECK(not world:target(a, BattleLink))
|
||||||
|
end
|
||||||
|
|
||||||
|
do CASE "Exclusive relations"
|
||||||
|
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:set(e, pair(A, B), true)
|
||||||
|
world:set(e, pair(A, C), true)
|
||||||
|
|
||||||
|
CHECK(world:has(e, pair(A, B)) == false)
|
||||||
|
CHECK(world:has(e, pair(A, C)) == true)
|
||||||
|
|
||||||
|
-- We have to test the path that checks the uncached method
|
||||||
|
local e1 = world:entity()
|
||||||
|
|
||||||
|
world:set(e1, pair(A, B), true)
|
||||||
|
world:set(e1, pair(A, C), true)
|
||||||
|
|
||||||
|
CHECK(world:has(e1, pair(A, B)) == false)
|
||||||
|
CHECK(world:has(e1, pair(A, C)) == true)
|
||||||
|
end
|
||||||
|
|
||||||
|
do CASE "exclusive relations invoke hooks"
|
||||||
|
local world = jecs.world()
|
||||||
|
local A = world:component()
|
||||||
|
local B = world:component()
|
||||||
|
local C = world:component()
|
||||||
|
|
||||||
|
local e_ptr: jecs.Entity = (jecs.Rest :: any) + 1
|
||||||
|
|
||||||
|
world:add(A, jecs.Exclusive)
|
||||||
|
local on_remove_call = false
|
||||||
|
world:set(A, jecs.OnRemove, function(e, id)
|
||||||
|
on_remove_call = true
|
||||||
|
end)
|
||||||
|
|
||||||
|
local on_add_call_count = 0
|
||||||
|
world:set(A, jecs.OnAdd, function(e, id)
|
||||||
|
on_add_call_count += 1
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
local e = world:entity()
|
||||||
|
CHECK(e == e_ptr)
|
||||||
|
world:set(e, pair(A, B))
|
||||||
|
CHECK(on_add_call_count == 1)
|
||||||
|
world:set(e, pair(A, C))
|
||||||
|
CHECK(on_add_call_count == 2)
|
||||||
|
CHECK(on_remove_call)
|
||||||
|
|
||||||
|
CHECK(world:has(e, pair(A, B)) == false)
|
||||||
|
CHECK(world:has(e, pair(A, C)) == true)
|
||||||
|
|
||||||
|
-- We have to ensure that it actually invokes hooks everytime it
|
||||||
|
-- traverses the archetype
|
||||||
|
e = world:entity()
|
||||||
|
world:add(e, pair(A, B))
|
||||||
|
CHECK(on_add_call_count == 3)
|
||||||
|
world:add(e, pair(A, C))
|
||||||
|
CHECK(on_add_call_count == 4)
|
||||||
|
CHECK(on_remove_call)
|
||||||
|
|
||||||
|
CHECK(world:has(e, pair(A, B)) == false)
|
||||||
|
CHECK(world:has(e, pair(A, C)) == true)
|
||||||
|
end
|
||||||
|
|
||||||
|
do CASE "exclusive relations invoke on_remove hooks that should allow side effects"
|
||||||
|
local world = jecs.world()
|
||||||
|
local A = world:component()
|
||||||
|
local B = world:component()
|
||||||
|
local C = world:component()
|
||||||
|
local D = world:component()
|
||||||
|
|
||||||
|
world:add(A, jecs.Exclusive)
|
||||||
|
local call_count = 0
|
||||||
|
world:set(A, jecs.OnRemove, function(e, id)
|
||||||
|
call_count += 1
|
||||||
|
if call_count == 1 then
|
||||||
|
world:set(e, C, true)
|
||||||
|
else
|
||||||
|
world:set(e, D, true)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
local e = world:entity()
|
||||||
|
world:set(e, pair(A, B), true)
|
||||||
|
world:set(e, pair(A, C), true)
|
||||||
|
|
||||||
|
CHECK(world:has(e, pair(A, B)) == false)
|
||||||
|
CHECK(world:has(e, pair(A, C)) == true)
|
||||||
|
CHECK(world:has(e, C))
|
||||||
|
|
||||||
|
|
||||||
|
-- We have to ensure that it actually invokes hooks everytime it
|
||||||
|
-- traverses the archetype
|
||||||
|
e = world:entity()
|
||||||
|
world:set(e, pair(A, B), true)
|
||||||
|
world:set(e, pair(A, C), true)
|
||||||
|
|
||||||
|
CHECK(world:has(e, pair(A, B)) == false)
|
||||||
|
CHECK(world:has(e, pair(A, C)) == true)
|
||||||
|
CHECK(world:has(e, D))
|
||||||
|
end
|
||||||
do CASE "archetype move"
|
do CASE "archetype move"
|
||||||
local world = jecs.world()
|
local world = jecs.world()
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ukendio/jecs"
|
name = "ukendio/jecs"
|
||||||
version = "0.9.0-rc.9"
|
version = "0.9.0-rc.10"
|
||||||
registry = "https://github.com/UpliftGames/wally-index"
|
registry = "https://github.com/UpliftGames/wally-index"
|
||||||
realm = "shared"
|
realm = "shared"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
Loading…
Reference in a new issue