mirror of
				https://github.com/Ukendio/jecs.git
				synced 2025-11-03 18:39:19 +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
				
			
		| 
						 | 
				
			
			@ -2443,10 +2443,9 @@ local function world_new()
 | 
			
		|||
						if not idr then
 | 
			
		||||
							idr = component_index[wc]
 | 
			
		||||
						end
 | 
			
		||||
					end
 | 
			
		||||
 | 
			
		||||
						edge[id] = to
 | 
			
		||||
						archetype_edges[(to :: Archetype).id][id] = src
 | 
			
		||||
					end
 | 
			
		||||
				else
 | 
			
		||||
					if bit32.btest(idr.flags, ECS_ID_IS_EXCLUSIVE) then
 | 
			
		||||
						local on_remove = idr.on_remove
 | 
			
		||||
| 
						 | 
				
			
			@ -2544,10 +2543,9 @@ local function world_new()
 | 
			
		|||
					if not idr then
 | 
			
		||||
						idr = component_index[wc]
 | 
			
		||||
					end
 | 
			
		||||
				end
 | 
			
		||||
 | 
			
		||||
					edge[id] = to
 | 
			
		||||
					archetype_edges[(to :: Archetype).id][id] = src
 | 
			
		||||
				end
 | 
			
		||||
			else
 | 
			
		||||
				if bit32.btest(idr.flags, ECS_ID_IS_EXCLUSIVE) then
 | 
			
		||||
					local on_remove = idr.on_remove
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
{
 | 
			
		||||
	"name": "@rbxts/jecs",
 | 
			
		||||
	"version": "0.9.0-rc.9",
 | 
			
		||||
	"version": "0.9.0-rc.10",
 | 
			
		||||
	"description": "Stupidly fast Entity Component System",
 | 
			
		||||
	"main": "jecs.luau",
 | 
			
		||||
	"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 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()
 | 
			
		||||
	local world = jecs.world()
 | 
			
		||||
	local r = world:entity()
 | 
			
		||||
| 
						 | 
				
			
			@ -319,7 +338,25 @@ TEST("repro", function()
 | 
			
		|||
end)
 | 
			
		||||
 | 
			
		||||
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 A = world:component()
 | 
			
		||||
		world:add(A, jecs.Exclusive)
 | 
			
		||||
| 
						 | 
				
			
			@ -2044,6 +2081,140 @@ TEST("world:remove()", function()
 | 
			
		|||
end)
 | 
			
		||||
 | 
			
		||||
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"
 | 
			
		||||
		local world = jecs.world()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
[package]
 | 
			
		||||
name = "ukendio/jecs"
 | 
			
		||||
version = "0.9.0-rc.9"
 | 
			
		||||
version = "0.9.0-rc.10"
 | 
			
		||||
registry = "https://github.com/UpliftGames/wally-index"
 | 
			
		||||
realm = "shared"
 | 
			
		||||
license = "MIT"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue