mirror of
				https://github.com/Ukendio/jecs.git
				synced 2025-11-04 02:49:18 +00:00 
			
		
		
		
	Support setting signal on cached Relation
	
		
			
	
		
	
	
		
	
		
			Some checks are pending
		
		
	
	
	
				
					
				
			
		
			Some checks are pending
		
		
	
	
This commit is contained in:
		
							parent
							
								
									4153a7cdfe
								
							
						
					
					
						commit
						add9ad3939
					
				
					 5 changed files with 64 additions and 85 deletions
				
			
		
							
								
								
									
										26
									
								
								jecs.luau
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								jecs.luau
									
									
									
									
									
								
							| 
						 | 
					@ -24,10 +24,10 @@ export type Archetype = {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type QueryInner = {
 | 
					export type QueryInner = {
 | 
				
			||||||
	compatible_archetypes: { Archetype },
 | 
						compatible_archetypes: { Archetype },
 | 
				
			||||||
	ids: { i53 },
 | 
						ids: { Id },
 | 
				
			||||||
	filter_with: { i53 },
 | 
						filter_with: { Id },
 | 
				
			||||||
	filter_without: { i53 },
 | 
						filter_without: { Id },
 | 
				
			||||||
	next: () -> (number, ...any),
 | 
						next: () -> (Entity, ...any),
 | 
				
			||||||
	world: World,
 | 
						world: World,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,6 +54,8 @@ export type Query<T...> = typeof(setmetatable(
 | 
				
			||||||
		archetypes: (self: Query<T...>) -> { Archetype },
 | 
							archetypes: (self: Query<T...>) -> { Archetype },
 | 
				
			||||||
		cached: (self: Query<T...>) -> Query<T...>,
 | 
							cached: (self: Query<T...>) -> Query<T...>,
 | 
				
			||||||
		ids: { Id<any> },
 | 
							ids: { Id<any> },
 | 
				
			||||||
 | 
							patch: (self: Query<T...>, fn: (T...) -> (T...)) -> (),
 | 
				
			||||||
 | 
							view: (self: Query<T...>) -> View<T...>,
 | 
				
			||||||
		-- world: World
 | 
							-- world: World
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	{} :: {
 | 
						{} :: {
 | 
				
			||||||
| 
						 | 
					@ -68,6 +70,12 @@ export type Observer = {
 | 
				
			||||||
	query: QueryInner,
 | 
						query: QueryInner,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type observer = {
 | 
				
			||||||
 | 
						callback: (archetype: archetype) -> (),
 | 
				
			||||||
 | 
						query: query,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type archetype = {
 | 
					type archetype = {
 | 
				
			||||||
	id: number,
 | 
						id: number,
 | 
				
			||||||
	types: { i53 },
 | 
						types: { i53 },
 | 
				
			||||||
| 
						 | 
					@ -504,7 +512,7 @@ local function ecs_pair_second(world: world, e: i53)
 | 
				
			||||||
	return ecs_get_alive(world, obj)
 | 
						return ecs_get_alive(world, obj)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function query_match(query: QueryInner, archetype: archetype)
 | 
					local function query_match(query: query, archetype: archetype)
 | 
				
			||||||
	local columns_map = archetype.columns_map
 | 
						local columns_map = archetype.columns_map
 | 
				
			||||||
	local with = query.filter_with
 | 
						local with = query.filter_with
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -526,7 +534,7 @@ local function query_match(query: QueryInner, archetype: archetype)
 | 
				
			||||||
	return true
 | 
						return true
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function find_observers(world: world, event: i53, component: i53): { Observer }?
 | 
					local function find_observers(world: world, event: i53, component: i53): { observer }?
 | 
				
			||||||
	local cache = world.observable[event]
 | 
						local cache = world.observable[event]
 | 
				
			||||||
	if not cache then
 | 
						if not cache then
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
| 
						 | 
					@ -2615,7 +2623,7 @@ local function world_new()
 | 
				
			||||||
				table.insert(listeners, existing_hook)
 | 
									table.insert(listeners, existing_hook)
 | 
				
			||||||
			end
 | 
								end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			local idr = world.component_index[component]
 | 
								local idr = component_index[ECS_PAIR(component, EcsWildcard)] or component_index[component]
 | 
				
			||||||
			if idr then
 | 
								if idr then
 | 
				
			||||||
				idr.on_add = on_add
 | 
									idr.on_add = on_add
 | 
				
			||||||
			else
 | 
								else
 | 
				
			||||||
| 
						 | 
					@ -2650,7 +2658,7 @@ local function world_new()
 | 
				
			||||||
				table.insert(listeners, existing_hook)
 | 
									table.insert(listeners, existing_hook)
 | 
				
			||||||
			end
 | 
								end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			local idr = world.component_index[component]
 | 
								local idr = component_index[ECS_PAIR(component, EcsWildcard)] or component_index[component]
 | 
				
			||||||
			if idr then
 | 
								if idr then
 | 
				
			||||||
				idr.on_change = on_change
 | 
									idr.on_change = on_change
 | 
				
			||||||
			else
 | 
								else
 | 
				
			||||||
| 
						 | 
					@ -2681,7 +2689,7 @@ local function world_new()
 | 
				
			||||||
				table.insert(listeners, existing_hook)
 | 
									table.insert(listeners, existing_hook)
 | 
				
			||||||
			end
 | 
								end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			local idr = world.component_index[component]
 | 
								local idr = component_index[ECS_PAIR(component, EcsWildcard)] or component_index[component]
 | 
				
			||||||
			if idr then
 | 
								if idr then
 | 
				
			||||||
				idr.on_remove = on_remove
 | 
									idr.on_remove = on_remove
 | 
				
			||||||
			else
 | 
								else
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	"name": "@rbxts/jecs",
 | 
						"name": "@rbxts/jecs",
 | 
				
			||||||
	"version": "0.9.0-rc.7",
 | 
						"version": "0.9.0-rc.8",
 | 
				
			||||||
	"description": "Stupidly fast Entity Component System",
 | 
						"description": "Stupidly fast Entity Component System",
 | 
				
			||||||
	"main": "jecs.luau",
 | 
						"main": "jecs.luau",
 | 
				
			||||||
	"repository": {
 | 
						"repository": {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1064,40 +1064,77 @@ end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST("world:added", function()
 | 
					TEST("world:added", function()
 | 
				
			||||||
	local world = jecs.world()
 | 
						local world = jecs.world()
 | 
				
			||||||
 | 
					 | 
				
			||||||
	do CASE "Should work even if set after the component has been used"
 | 
						do CASE "Should work even if set after the component has been used"
 | 
				
			||||||
		local A = world:component()
 | 
							local A = world:component()
 | 
				
			||||||
 | 
					 | 
				
			||||||
		world:set(world:entity(), A, 2)
 | 
							world:set(world:entity(), A, 2)
 | 
				
			||||||
		local ran = false
 | 
							local ran = false
 | 
				
			||||||
		world:added(A, function()
 | 
							world:added(A, function()
 | 
				
			||||||
			ran = true
 | 
								ran = true
 | 
				
			||||||
		end)
 | 
							end)
 | 
				
			||||||
 | 
					 | 
				
			||||||
		local entity = world:entity()
 | 
							local entity = world:entity()
 | 
				
			||||||
		world:set(entity, A, 3)
 | 
							world:set(entity, A, 3)
 | 
				
			||||||
 | 
							CHECK(ran)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						do CASE "Should work even if set after the pair has been used"
 | 
				
			||||||
 | 
							local A = world:component()
 | 
				
			||||||
 | 
							local B = world:component()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							world:set(world:entity(), A, 2)
 | 
				
			||||||
 | 
							world:set(world:entity(), pair(A, B), 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							world:added(A, function()
 | 
				
			||||||
 | 
								ran = true
 | 
				
			||||||
 | 
							end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							local entity = world:entity()
 | 
				
			||||||
 | 
							world:set(entity, pair(A, B), 3)
 | 
				
			||||||
 | 
							CHECK(ran)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						do CASE "Should allow setting signal after Relation has been used as a component"
 | 
				
			||||||
 | 
							local A = world:component()
 | 
				
			||||||
 | 
							local B = world:component()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							world:add(world:entity(), A)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							world:added(A, function()
 | 
				
			||||||
 | 
								ran = true
 | 
				
			||||||
 | 
							end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							world:add(world:entity(), pair(A, B))
 | 
				
			||||||
 | 
							CHECK(ran)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						do CASE "Should invoke signal for the Relation being set as a key despite a pair with Relation having been cached"
 | 
				
			||||||
 | 
							local A = world:component()
 | 
				
			||||||
 | 
							local B = world:component()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							world:add(world:entity(), pair(A, B))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							world:added(A, function()
 | 
				
			||||||
 | 
								ran = true
 | 
				
			||||||
 | 
							end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							world:add(world:entity(), A)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		CHECK(ran)
 | 
							CHECK(ran)
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	do CASE "Should not override hook"
 | 
						do CASE "Should not override hook"
 | 
				
			||||||
		local A = world:component()
 | 
							local A = world:component()
 | 
				
			||||||
 | 
					 | 
				
			||||||
		local count = 1
 | 
							local count = 1
 | 
				
			||||||
		local function counter()
 | 
							local function counter()
 | 
				
			||||||
			count += 1
 | 
								count += 1
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
 | 
					 | 
				
			||||||
		world:set(A, jecs.OnAdd, counter)
 | 
							world:set(A, jecs.OnAdd, counter)
 | 
				
			||||||
		world:added(A, counter)
 | 
							world:added(A, counter)
 | 
				
			||||||
		world:set(world:entity(), A, false)
 | 
							world:set(world:entity(), A, false)
 | 
				
			||||||
		CHECK(count == (1 + 2))
 | 
							CHECK(count == (1 + 2))
 | 
				
			||||||
		world:set(world:entity(), A, false)
 | 
							world:set(world:entity(), A, false)
 | 
				
			||||||
 | 
					 | 
				
			||||||
		CHECK(count == (1 + (2 * 2)))
 | 
							CHECK(count == (1 + (2 * 2)))
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
end)
 | 
					end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST("world:range()", function()
 | 
					TEST("world:range()", function()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
[package]
 | 
					[package]
 | 
				
			||||||
name = "ukendio/jecs"
 | 
					name = "ukendio/jecs"
 | 
				
			||||||
version = "0.9.0-rc.7"
 | 
					version = "0.9.0-rc.8"
 | 
				
			||||||
registry = "https://github.com/UpliftGames/wally-index"
 | 
					registry = "https://github.com/UpliftGames/wally-index"
 | 
				
			||||||
realm = "shared"
 | 
					realm = "shared"
 | 
				
			||||||
license = "MIT"
 | 
					license = "MIT"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,66 +0,0 @@
 | 
				
			||||||
local jecs = require("@jecs")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local world = jecs.world()
 | 
					 | 
				
			||||||
local pair = jecs.pair
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local IsA = world:entity()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local traits = {
 | 
					 | 
				
			||||||
	IsA = IsA
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
world:set(IsA, jecs.OnAdd, function(component, id)
 | 
					 | 
				
			||||||
	local second = jecs.pair_second(world, id)
 | 
					 | 
				
			||||||
	assert(second ~= component, "circular")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	local is_tag = jecs.is_tag(world, second)
 | 
					 | 
				
			||||||
	world:added(component, function(entity, _, value)
 | 
					 | 
				
			||||||
		if is_tag then
 | 
					 | 
				
			||||||
			world:add(entity, second)
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			world:set(entity, second, value)
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
	end)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	world:removed(component, function(entity)
 | 
					 | 
				
			||||||
		world:remove(entity, second)
 | 
					 | 
				
			||||||
	end)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if not is_tag then
 | 
					 | 
				
			||||||
		world:changed(component, function(entity, _, value)
 | 
					 | 
				
			||||||
			world:set(entity, second, value)
 | 
					 | 
				
			||||||
		end)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	local r = jecs.record(world, second) :: jecs.Record
 | 
					 | 
				
			||||||
	local archetype = r.archetype
 | 
					 | 
				
			||||||
	if not archetype then
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
	local types = archetype.types
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, id in types do
 | 
					 | 
				
			||||||
		if jecs.is_tag(world, id) then
 | 
					 | 
				
			||||||
			world:add(component, id)
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			local metadata = world:get(second, id)
 | 
					 | 
				
			||||||
			if not world:has(component, id) then
 | 
					 | 
				
			||||||
				world:set(component, id, metadata)
 | 
					 | 
				
			||||||
			end
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	-- jecs.bulk_insert(world, component, ids, values)
 | 
					 | 
				
			||||||
end)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local Witch = world:entity()
 | 
					 | 
				
			||||||
local Werewolf = world:entity()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local WereWitch = world:entity()
 | 
					 | 
				
			||||||
world:add(WereWitch, pair(IsA, Witch))
 | 
					 | 
				
			||||||
world:add(WereWitch, pair(IsA, Werewolf))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local e = world:entity()
 | 
					 | 
				
			||||||
world:add(e, WereWitch)
 | 
					 | 
				
			||||||
print(world:has(e, pair(IsA, Witch))) -- false
 | 
					 | 
				
			||||||
print(world:has(e, Witch)) -- true
 | 
					 | 
				
			||||||
		Loading…
	
		Reference in a new issue