mirror of
				https://github.com/Ukendio/jecs.git
				synced 2025-11-04 02:49:18 +00:00 
			
		
		
		
	Compare commits
	
		
			1 commit
		
	
	
		
			0355cc11b7
			...
			f3ad0415c7
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
							 | 
						f3ad0415c7 | 
					 6 changed files with 132 additions and 254 deletions
				
			
		| 
						 | 
					@ -101,50 +101,45 @@ local function observers_add(world: jecs.World & { [string]: any }): PatchedWorl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	world.added = function(_, component, fn)
 | 
						world.added = function(_, component, fn)
 | 
				
			||||||
		local listeners = signals.added[component]
 | 
							local listeners = signals.added[component]
 | 
				
			||||||
		local component_index = world.component_index :: jecs.ComponentIndex
 | 
					 | 
				
			||||||
		assert(component_index[component] == nil, "You cannot use hooks on components you intend to use this signal with")
 | 
					 | 
				
			||||||
		if not listeners then
 | 
							if not listeners then
 | 
				
			||||||
			listeners = {}
 | 
								listeners = {}
 | 
				
			||||||
			signals.added[component] = listeners
 | 
								signals.added[component] = listeners
 | 
				
			||||||
			local function on_add(entity: number, id: number, value: any)
 | 
								local idr = jecs.id_record_ensure(world, component)
 | 
				
			||||||
				for _, listener in listeners :: any do
 | 
								idr.hooks.on_add = function(entity)
 | 
				
			||||||
					listener(entity, id, value)
 | 
									for _, listener in listeners do
 | 
				
			||||||
 | 
										listener(entity, component)
 | 
				
			||||||
 | 
									end
 | 
				
			||||||
			end
 | 
								end
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
			world:set(component, jecs.OnAdd, on_add)		end
 | 
					 | 
				
			||||||
		table.insert(listeners, fn)
 | 
							table.insert(listeners, fn)
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	world.changed = function(_, component, fn)
 | 
						world.changed = function(_, component, fn)
 | 
				
			||||||
		local listeners = signals.emplaced[component]
 | 
							local listeners = signals.emplaced[component]
 | 
				
			||||||
		local component_index = world.component_index :: jecs.ComponentIndex
 | 
					 | 
				
			||||||
		assert(component_index[component] == nil, "You cannot use hooks on components you intend to use this signal with")
 | 
					 | 
				
			||||||
		if not listeners then
 | 
							if not listeners then
 | 
				
			||||||
			listeners = {}
 | 
								listeners = {}
 | 
				
			||||||
			signals.emplaced[component] = listeners
 | 
								signals.emplaced[component] = listeners
 | 
				
			||||||
			local function on_change(entity: number, id: number, value: any)
 | 
								local idr = jecs.id_record_ensure(world, component)
 | 
				
			||||||
				for _, listener in listeners :: any do
 | 
								idr.hooks.on_change = function(entity, value)
 | 
				
			||||||
					listener(entity, id, value)
 | 
									for _, listener in listeners do
 | 
				
			||||||
 | 
										listener(entity, component, value)
 | 
				
			||||||
				end
 | 
									end
 | 
				
			||||||
			end
 | 
								end
 | 
				
			||||||
			world:set(component, jecs.OnChange, on_change)
 | 
					 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
		table.insert(listeners, fn)
 | 
							table.insert(listeners, fn)
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	world.removed = function(_, component, fn)
 | 
						world.removed = function(_, component, fn)
 | 
				
			||||||
		local listeners = signals.removed[component]
 | 
							local listeners = signals.removed[component]
 | 
				
			||||||
		local component_index = world.component_index :: jecs.ComponentIndex
 | 
					 | 
				
			||||||
		assert(component_index[component] == nil, "You cannot use hooks on components you intend to use this signal with")
 | 
					 | 
				
			||||||
		if not listeners then
 | 
							if not listeners then
 | 
				
			||||||
			listeners = {}
 | 
								listeners = {}
 | 
				
			||||||
			signals.removed[component] = listeners
 | 
								signals.removed[component] = listeners
 | 
				
			||||||
			local function on_remove(entity: number, id: number, value: any)
 | 
								local idr = jecs.id_record_ensure(world, component)
 | 
				
			||||||
				for _, listener in listeners :: any do
 | 
								idr.hooks.on_remove = function(entity)
 | 
				
			||||||
					listener(entity, id, value)
 | 
									for _, listener in listeners do
 | 
				
			||||||
 | 
										listener(entity, component)
 | 
				
			||||||
				end
 | 
									end
 | 
				
			||||||
			end
 | 
								end
 | 
				
			||||||
			world:set(component, jecs.OnRemove, on_remove)
 | 
					 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
		table.insert(listeners, fn)
 | 
							table.insert(listeners, fn)
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
| 
						 | 
					@ -155,7 +150,7 @@ local function observers_add(world: jecs.World & { [string]: any }): PatchedWorl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	world.monitor = monitors_new
 | 
						world.monitor = monitors_new
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return world :: PatchedWorld
 | 
						return world
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return observers_add
 | 
					return observers_add
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,10 +14,6 @@ A (component) ID can be marked with `Tag´ in which the component will never con
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Hooks are part of the "interface" of a component. You could consider hooks as the counterpart to OOP methods in ECS. They define the behavior of a component, but can only be invoked through mutations on the component data. You can only configure a single `OnAdd`, `OnRemove` and `OnSet` hook per component, just like you can only have a single constructor and destructor.
 | 
					Hooks are part of the "interface" of a component. You could consider hooks as the counterpart to OOP methods in ECS. They define the behavior of a component, but can only be invoked through mutations on the component data. You can only configure a single `OnAdd`, `OnRemove` and `OnSet` hook per component, just like you can only have a single constructor and destructor.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
::: warning
 | 
					 | 
				
			||||||
Hooks, added to a component that has already been added to other entities/components, will not be called.
 | 
					 | 
				
			||||||
:::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Examples
 | 
					## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
::: code-group
 | 
					::: code-group
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										163
									
								
								jecs.luau
									
									
									
									
									
								
							
							
						
						
									
										163
									
								
								jecs.luau
									
									
									
									
									
								
							| 
						 | 
					@ -45,9 +45,9 @@ type ecs_id_record_t = {
 | 
				
			||||||
	flags: number,
 | 
						flags: number,
 | 
				
			||||||
	size: number,
 | 
						size: number,
 | 
				
			||||||
	hooks: {
 | 
						hooks: {
 | 
				
			||||||
		on_add: ((entity: i53, id: i53, data: any?) -> ())?,
 | 
							on_add: ((entity: i53, data: any?) -> ())?,
 | 
				
			||||||
		on_change: ((entity: i53, id: i53, data: any) -> ())?,
 | 
							on_change: ((entity: i53, data: any) -> ())?,
 | 
				
			||||||
		on_remove: ((entity: i53, id: i53) -> ())?,
 | 
							on_remove: ((entity: i53) -> ())?,
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -116,47 +116,13 @@ local ECS_ID_MASK =                          	    0b00
 | 
				
			||||||
local ECS_ENTITY_MASK =              bit32.lshift(1, 24)
 | 
					local ECS_ENTITY_MASK =              bit32.lshift(1, 24)
 | 
				
			||||||
local ECS_GENERATION_MASK =          bit32.lshift(1, 16)
 | 
					local ECS_GENERATION_MASK =          bit32.lshift(1, 16)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local NULL_ARRAY = table.freeze({}) :: Column
 | 
					local NULL_ARRAY = table.freeze({})
 | 
				
			||||||
local NULL = newproxy(false)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local ECS_INTERNAL_ERROR = [[
 | 
					local ECS_INTERNAL_ERROR = [[
 | 
				
			||||||
	This is an internal error, please file a bug report via the following link:
 | 
						This is an internal error, please file a bug report via the following link:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	https://github.com/Ukendio/jecs/issues/new?template=BUG-REPORT.md
 | 
						https://github.com/Ukendio/jecs/issues/new?template=BUG-REPORT.md
 | 
				
			||||||
]]
 | 
					]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local ecs_metadata: Map<i53, Map<i53, any>> = {}
 | 
					 | 
				
			||||||
local ecs_max_component_id = 0
 | 
					 | 
				
			||||||
local ecs_max_tag_id = EcsRest
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function ECS_COMPONENT()
 | 
					 | 
				
			||||||
	ecs_max_component_id += 1
 | 
					 | 
				
			||||||
	if ecs_max_component_id > HI_COMPONENT_ID then
 | 
					 | 
				
			||||||
		error("Too many components")
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
	return ecs_max_component_id
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function ECS_TAG()
 | 
					 | 
				
			||||||
	ecs_max_tag_id += 1
 | 
					 | 
				
			||||||
	return ecs_max_tag_id
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function ECS_META(id: i53, ty: i53, value: any?)
 | 
					 | 
				
			||||||
	local bundle = ecs_metadata[id]
 | 
					 | 
				
			||||||
	if bundle == nil then
 | 
					 | 
				
			||||||
		bundle = {}
 | 
					 | 
				
			||||||
		ecs_metadata[id] = bundle
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
	bundle[ty] = if value == nil then NULL else value
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function ECS_META_RESET()
 | 
					 | 
				
			||||||
	ecs_metadata = {}
 | 
					 | 
				
			||||||
	ecs_max_component_id = 0
 | 
					 | 
				
			||||||
	ecs_max_tag_id = EcsRest
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function ECS_COMBINE(id: number, generation: number): i53
 | 
					local function ECS_COMBINE(id: number, generation: number): i53
 | 
				
			||||||
	return id + (generation * ECS_ENTITY_MASK)
 | 
						return id + (generation * ECS_ENTITY_MASK)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -500,14 +466,6 @@ local function world_has_one_inline(world: ecs_world_t, entity: i53, id: i53): b
 | 
				
			||||||
	return records[id] ~= nil
 | 
						return records[id] ~= nil
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function ecs_is_tag(world: ecs_world_t, entity: i53): boolean
 | 
					 | 
				
			||||||
	local idr = world.component_index[entity]
 | 
					 | 
				
			||||||
	if idr then
 | 
					 | 
				
			||||||
		return bit32.band(idr.flags, ECS_ID_IS_TAG) ~= 0
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
	return not world_has_one_inline(world, entity, EcsComponent)
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function world_has(world: ecs_world_t, entity: i53,
 | 
					local function world_has(world: ecs_world_t, entity: i53,
 | 
				
			||||||
	a: i53, b: i53?, c: i53?, d: i53?, e: i53?): boolean
 | 
						a: i53, b: i53?, c: i53?, d: i53?, e: i53?): boolean
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -571,12 +529,9 @@ end
 | 
				
			||||||
local function id_record_ensure(world: ecs_world_t, id: number): ecs_id_record_t
 | 
					local function id_record_ensure(world: ecs_world_t, id: number): ecs_id_record_t
 | 
				
			||||||
	local component_index = world.component_index
 | 
						local component_index = world.component_index
 | 
				
			||||||
	local entity_index = world.entity_index
 | 
						local entity_index = world.entity_index
 | 
				
			||||||
	local idr: ecs_id_record_t? = component_index[id]
 | 
						local idr: ecs_id_record_t = component_index[id]
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if idr then
 | 
					 | 
				
			||||||
		return idr
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if not idr then
 | 
				
			||||||
		local flags = ECS_ID_MASK
 | 
							local flags = ECS_ID_MASK
 | 
				
			||||||
		local relation = id
 | 
							local relation = id
 | 
				
			||||||
		local target = 0
 | 
							local target = 0
 | 
				
			||||||
| 
						 | 
					@ -625,9 +580,10 @@ local function id_record_ensure(world: ecs_world_t, id: number): ecs_id_record_t
 | 
				
			||||||
				on_change = on_change,
 | 
									on_change = on_change,
 | 
				
			||||||
				on_remove = on_remove,
 | 
									on_remove = on_remove,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
	} :: ecs_id_record_t
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		component_index[id] = idr
 | 
							component_index[id] = idr
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return idr
 | 
						return idr
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -665,7 +621,7 @@ local function archetype_create(world: ecs_world_t, id_types: { i24 }, ty, prev:
 | 
				
			||||||
	local columns = (table.create(length) :: any) :: { Column }
 | 
						local columns = (table.create(length) :: any) :: { Column }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local records: { number } = {}
 | 
						local records: { number } = {}
 | 
				
			||||||
	local counts: { number } = {}
 | 
						local counts: {number} = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local archetype: ecs_archetype_t = {
 | 
						local archetype: ecs_archetype_t = {
 | 
				
			||||||
		columns = columns,
 | 
							columns = columns,
 | 
				
			||||||
| 
						 | 
					@ -714,7 +670,7 @@ local function archetype_create(world: ecs_world_t, id_types: { i24 }, ty, prev:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	world.archetype_index[ty] = archetype
 | 
						world.archetype_index[ty] = archetype
 | 
				
			||||||
	world.archetypes[archetype_id] = archetype
 | 
						world.archetypes[archetype_id] = archetype
 | 
				
			||||||
	world.archetype_edges[archetype.id] = {} :: Map<i53, ecs_archetype_t>
 | 
						world.archetype_edges[archetype.id] = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return archetype
 | 
						return archetype
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -791,17 +747,17 @@ local function archetype_traverse_remove(
 | 
				
			||||||
	local edges = world.archetype_edges
 | 
						local edges = world.archetype_edges
 | 
				
			||||||
	local edge = edges[from.id]
 | 
						local edge = edges[from.id]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local to: ecs_archetype_t = edge[id]
 | 
						local to = edge[id]
 | 
				
			||||||
	if to == nil then
 | 
						if not to then
 | 
				
			||||||
		to = find_archetype_without(world, from, id)
 | 
							to = find_archetype_without(world, from, id)
 | 
				
			||||||
		edge[id] = to
 | 
							edge[id] = to
 | 
				
			||||||
		edges[to.id][id] = from
 | 
							edges[to.id][id] = from
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return to
 | 
						return to :: ecs_archetype_t
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function find_archetype_with(world, id, from): ecs_archetype_t
 | 
					local function find_archetype_with(world, id, from)
 | 
				
			||||||
	local id_types = from.types
 | 
						local id_types = from.types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local at = find_insert(id_types, id)
 | 
						local at = find_insert(id_types, id)
 | 
				
			||||||
| 
						 | 
					@ -811,7 +767,7 @@ local function find_archetype_with(world, id, from): ecs_archetype_t
 | 
				
			||||||
	return archetype_ensure(world, dst)
 | 
						return archetype_ensure(world, dst)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function archetype_traverse_add(world, id, from: ecs_archetype_t): ecs_archetype_t
 | 
					local function archetype_traverse_add(world, id, from: ecs_archetype_t)
 | 
				
			||||||
	from = from or world.ROOT_ARCHETYPE
 | 
						from = from or world.ROOT_ARCHETYPE
 | 
				
			||||||
	if from.records[id] then
 | 
						if from.records[id] then
 | 
				
			||||||
		return from
 | 
							return from
 | 
				
			||||||
| 
						 | 
					@ -857,7 +813,7 @@ local function world_add(
 | 
				
			||||||
	local on_add = idr.hooks.on_add
 | 
						local on_add = idr.hooks.on_add
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if on_add then
 | 
						if on_add then
 | 
				
			||||||
		on_add(entity, id)
 | 
							on_add(entity)
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -874,7 +830,7 @@ local function world_set(world: ecs_world_t, entity: i53, id: i53, data: unknown
 | 
				
			||||||
	local idr_hooks = idr.hooks
 | 
						local idr_hooks = idr.hooks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if from == to then
 | 
						if from == to then
 | 
				
			||||||
		local tr = (to :: ecs_archetype_t).records[id]
 | 
							local tr = to.records[id]
 | 
				
			||||||
		local column = from.columns[tr]
 | 
							local column = from.columns[tr]
 | 
				
			||||||
		column[record.row] = data
 | 
							column[record.row] = data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -882,7 +838,7 @@ local function world_set(world: ecs_world_t, entity: i53, id: i53, data: unknown
 | 
				
			||||||
		-- and just set the data directly.
 | 
							-- and just set the data directly.
 | 
				
			||||||
		local on_change = idr_hooks.on_change
 | 
							local on_change = idr_hooks.on_change
 | 
				
			||||||
		if on_change then
 | 
							if on_change then
 | 
				
			||||||
			on_change(entity, id, data)
 | 
								on_change(entity, data)
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					@ -905,7 +861,7 @@ local function world_set(world: ecs_world_t, entity: i53, id: i53, data: unknown
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local on_add = idr_hooks.on_add
 | 
						local on_add = idr_hooks.on_add
 | 
				
			||||||
	if on_add then
 | 
						if on_add then
 | 
				
			||||||
		on_add(entity, id, data)
 | 
							on_add(entity, data)
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -937,7 +893,7 @@ local function world_remove(world: ecs_world_t, entity: i53, id: i53)
 | 
				
			||||||
		local idr = world.component_index[id]
 | 
							local idr = world.component_index[id]
 | 
				
			||||||
		local on_remove = idr.hooks.on_remove
 | 
							local on_remove = idr.hooks.on_remove
 | 
				
			||||||
		if on_remove then
 | 
							if on_remove then
 | 
				
			||||||
			on_remove(entity, id)
 | 
								on_remove(entity)
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		local to = archetype_traverse_remove(world, id, record.archetype)
 | 
							local to = archetype_traverse_remove(world, id, record.archetype)
 | 
				
			||||||
| 
						 | 
					@ -989,7 +945,7 @@ local function archetype_delete(world: ecs_world_t, archetype: ecs_archetype_t,
 | 
				
			||||||
		local idr = component_index[id]
 | 
							local idr = component_index[id]
 | 
				
			||||||
		local on_remove = idr.hooks.on_remove
 | 
							local on_remove = idr.hooks.on_remove
 | 
				
			||||||
		if on_remove then
 | 
							if on_remove then
 | 
				
			||||||
			on_remove(delete, id)
 | 
								on_remove(delete)
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1028,8 +984,8 @@ local function world_clear(world: ecs_world_t, entity: i53)
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if idr_t then
 | 
						if idr_t then
 | 
				
			||||||
		local queue: { i53 }
 | 
							local queue
 | 
				
			||||||
		local ids: Map<i53, boolean>
 | 
							local ids
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		local count = 0
 | 
							local count = 0
 | 
				
			||||||
		local archetype_ids = idr_t.cache
 | 
							local archetype_ids = idr_t.cache
 | 
				
			||||||
| 
						 | 
					@ -1049,7 +1005,7 @@ local function world_clear(world: ecs_world_t, entity: i53)
 | 
				
			||||||
					continue
 | 
										continue
 | 
				
			||||||
				end
 | 
									end
 | 
				
			||||||
				if not ids then
 | 
									if not ids then
 | 
				
			||||||
					ids = {} :: { [i53]: boolean }
 | 
										ids = {}
 | 
				
			||||||
				end
 | 
									end
 | 
				
			||||||
				ids[id] = true
 | 
									ids[id] = true
 | 
				
			||||||
				removal_queued = true
 | 
									removal_queued = true
 | 
				
			||||||
| 
						 | 
					@ -1060,7 +1016,7 @@ local function world_clear(world: ecs_world_t, entity: i53)
 | 
				
			||||||
			end
 | 
								end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if not queue then
 | 
								if not queue then
 | 
				
			||||||
				queue = {} :: { i53 }
 | 
									queue = {}
 | 
				
			||||||
			end
 | 
								end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			local n = #entities
 | 
								local n = #entities
 | 
				
			||||||
| 
						 | 
					@ -1219,8 +1175,8 @@ local function world_delete(world: ecs_world_t, entity: i53)
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if idr_t then
 | 
						if idr_t then
 | 
				
			||||||
		local children: { i53 }
 | 
							local children
 | 
				
			||||||
		local ids: Map<i53, boolean>
 | 
							local ids
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		local count = 0
 | 
							local count = 0
 | 
				
			||||||
		local archetype_ids = idr_t.cache
 | 
							local archetype_ids = idr_t.cache
 | 
				
			||||||
| 
						 | 
					@ -1250,7 +1206,7 @@ local function world_delete(world: ecs_world_t, entity: i53)
 | 
				
			||||||
					break
 | 
										break
 | 
				
			||||||
				else
 | 
									else
 | 
				
			||||||
					if not ids then
 | 
										if not ids then
 | 
				
			||||||
						ids = {} :: { [i53]: boolean }
 | 
											ids = {}
 | 
				
			||||||
					end
 | 
										end
 | 
				
			||||||
					ids[id] = true
 | 
										ids[id] = true
 | 
				
			||||||
					removal_queued = true
 | 
										removal_queued = true
 | 
				
			||||||
| 
						 | 
					@ -1261,7 +1217,7 @@ local function world_delete(world: ecs_world_t, entity: i53)
 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
			end
 | 
								end
 | 
				
			||||||
			if not children then
 | 
								if not children then
 | 
				
			||||||
				children = {} :: { i53 }
 | 
									children = {}
 | 
				
			||||||
			end
 | 
								end
 | 
				
			||||||
			local n = #entities
 | 
								local n = #entities
 | 
				
			||||||
			table.move(entities, 1, n, count + 1, children)
 | 
								table.move(entities, 1, n, count + 1, children)
 | 
				
			||||||
| 
						 | 
					@ -1284,7 +1240,7 @@ local function world_delete(world: ecs_world_t, entity: i53)
 | 
				
			||||||
	if idr_r then
 | 
						if idr_r then
 | 
				
			||||||
		local archetype_ids = idr_r.cache
 | 
							local archetype_ids = idr_r.cache
 | 
				
			||||||
		local flags = idr_r.flags
 | 
							local flags = idr_r.flags
 | 
				
			||||||
		if (bit32.band(flags, ECS_ID_DELETE) :: number) ~= 0 then
 | 
							if bit32.band(flags, ECS_ID_DELETE) ~= 0 then
 | 
				
			||||||
			for archetype_id in archetype_ids do
 | 
								for archetype_id in archetype_ids do
 | 
				
			||||||
				local idr_r_archetype = archetypes[archetype_id]
 | 
									local idr_r_archetype = archetypes[archetype_id]
 | 
				
			||||||
				local entities = idr_r_archetype.entities
 | 
									local entities = idr_r_archetype.entities
 | 
				
			||||||
| 
						 | 
					@ -1798,7 +1754,7 @@ local function query_cached(query: ecs_query_data_t)
 | 
				
			||||||
	local observable = world.observable :: ecs_observable_t
 | 
						local observable = world.observable :: ecs_observable_t
 | 
				
			||||||
	local on_create_action = observable[EcsOnArchetypeCreate]
 | 
						local on_create_action = observable[EcsOnArchetypeCreate]
 | 
				
			||||||
	if not on_create_action then
 | 
						if not on_create_action then
 | 
				
			||||||
		on_create_action = {} :: Map<i53, { ecs_observer_t }>
 | 
							on_create_action = {}
 | 
				
			||||||
		observable[EcsOnArchetypeCreate] = on_create_action
 | 
							observable[EcsOnArchetypeCreate] = on_create_action
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
	local query_cache_on_create = on_create_action[A]
 | 
						local query_cache_on_create = on_create_action[A]
 | 
				
			||||||
| 
						 | 
					@ -1809,7 +1765,7 @@ local function query_cached(query: ecs_query_data_t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local on_delete_action = observable[EcsOnArchetypeDelete]
 | 
						local on_delete_action = observable[EcsOnArchetypeDelete]
 | 
				
			||||||
	if not on_delete_action then
 | 
						if not on_delete_action then
 | 
				
			||||||
		on_delete_action = {} :: Map<i53, { ecs_observer_t }>
 | 
							on_delete_action = {}
 | 
				
			||||||
		observable[EcsOnArchetypeDelete] = on_delete_action
 | 
							observable[EcsOnArchetypeDelete] = on_delete_action
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
	local query_cache_on_delete = on_delete_action[A]
 | 
						local query_cache_on_delete = on_delete_action[A]
 | 
				
			||||||
| 
						 | 
					@ -2212,12 +2168,12 @@ local function world_query(world: ecs_world_t, ...)
 | 
				
			||||||
			return q
 | 
								return q
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if idr == nil or (map.size :: number) < (idr.size :: number) then
 | 
							if idr == nil or map.size < idr.size then
 | 
				
			||||||
			idr = map
 | 
								idr = map
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if idr == nil then
 | 
						if not idr then
 | 
				
			||||||
		return q
 | 
							return q
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2350,7 +2306,7 @@ local function world_new()
 | 
				
			||||||
		ROOT_ARCHETYPE = (nil :: any) :: Archetype,
 | 
							ROOT_ARCHETYPE = (nil :: any) :: Archetype,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		max_archetype_id = 0,
 | 
							max_archetype_id = 0,
 | 
				
			||||||
		max_component_id = ecs_max_component_id,
 | 
							max_component_id = 0,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		observable = {} :: Observable,
 | 
							observable = {} :: Observable,
 | 
				
			||||||
	}, World) :: any
 | 
						}, World) :: any
 | 
				
			||||||
| 
						 | 
					@ -2367,21 +2323,6 @@ local function world_new()
 | 
				
			||||||
		entity_index_new_id(entity_index)
 | 
							entity_index_new_id(entity_index)
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i = EcsRest + 1, ecs_max_tag_id do
 | 
					 | 
				
			||||||
		-- Initialize built-in components
 | 
					 | 
				
			||||||
		entity_index_new_id(entity_index)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i, bundle in ecs_metadata do
 | 
					 | 
				
			||||||
		for ty, value in bundle do
 | 
					 | 
				
			||||||
			if value == NULL then
 | 
					 | 
				
			||||||
				world_add(self, i, ty)
 | 
					 | 
				
			||||||
			else
 | 
					 | 
				
			||||||
				world_set(self, i, ty, value)
 | 
					 | 
				
			||||||
			end
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	world_add(self, EcsName, EcsComponent)
 | 
						world_add(self, EcsName, EcsComponent)
 | 
				
			||||||
	world_add(self, EcsOnChange, EcsComponent)
 | 
						world_add(self, EcsOnChange, EcsComponent)
 | 
				
			||||||
	world_add(self, EcsOnAdd, EcsComponent)
 | 
						world_add(self, EcsOnAdd, EcsComponent)
 | 
				
			||||||
| 
						 | 
					@ -2409,25 +2350,22 @@ end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
World.new = world_new
 | 
					World.new = world_new
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type Entity<T = any> = { __T: T }
 | 
					export type Entity<T = unknown> = { __T: T }
 | 
				
			||||||
export type Id<T = any> = { __T: T }
 | 
					export type Id<T = unknown> = { __T: T }
 | 
				
			||||||
export type Pair<P, O> = Id<P>
 | 
					export type Pair<P, O> = Id<P>
 | 
				
			||||||
type ecs_id_t<T=unknown> = Id<T> | Pair<T, "Tag"> | Pair<"Tag", T>
 | 
					type ecs_id_t<T=unknown> = Id<T> | Pair<T, "Tag"> | Pair<"Tag", T>
 | 
				
			||||||
export type Item<T...> = (self: Query<T...>) -> (Entity, T...)
 | 
					export type Item<T...> = (self: Query<T...>) -> (Entity, T...)
 | 
				
			||||||
export type Iter<T...> = (query: Query<T...>) -> () -> (Entity, T...)
 | 
					export type Iter<T...> = (query: Query<T...>) -> () -> (Entity, T...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type Query<T...> = typeof(setmetatable(
 | 
					export type Query<T...> = typeof(setmetatable({}, {
 | 
				
			||||||
	{} :: {
 | 
						__iter = (nil :: any) :: Iter<T...>,
 | 
				
			||||||
 | 
					})) & {
 | 
				
			||||||
	iter: Iter<T...>,
 | 
						iter: Iter<T...>,
 | 
				
			||||||
	with: (self: Query<T...>, ...Id) -> Query<T...>,
 | 
						with: (self: Query<T...>, ...Id) -> Query<T...>,
 | 
				
			||||||
	without: (self: Query<T...>, ...Id) -> Query<T...>,
 | 
						without: (self: Query<T...>, ...Id) -> Query<T...>,
 | 
				
			||||||
	archetypes: (self: Query<T...>) -> { Archetype },
 | 
						archetypes: (self: Query<T...>) -> { Archetype },
 | 
				
			||||||
	cached: (self: Query<T...>) -> Query<T...>,
 | 
						cached: (self: Query<T...>) -> Query<T...>,
 | 
				
			||||||
	},
 | 
					}
 | 
				
			||||||
	{} :: {
 | 
					 | 
				
			||||||
		__iter: Iter<T...>
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type Observer = {
 | 
					export type Observer = {
 | 
				
			||||||
 	callback: (archetype: Archetype) -> (),
 | 
					 	callback: (archetype: Archetype) -> (),
 | 
				
			||||||
| 
						 | 
					@ -2461,20 +2399,20 @@ 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), 0)`, you will obtain the parent entity.
 | 
						--- `world:target(id, ChildOf(parent), 0)`, you will obtain the parent entity.
 | 
				
			||||||
	target: <T>(self: World, id: Entity, relation: Id<T>, index: number?) -> Entity?,
 | 
						target: (self: World, id: Entity, relation: Id, index: 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) -> (),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	--- Adds a component to the entity with no value
 | 
						--- Adds a component to the entity with no value
 | 
				
			||||||
	add: <T>(self: World, id: Entity, component: Id<T>) -> (),
 | 
						add: <T>(self: World, id: Entity, component: Id) -> (),
 | 
				
			||||||
	--- Assigns a value to a component on the given entity
 | 
						--- Assigns a value to a component on the given entity
 | 
				
			||||||
	set: <T>(self: World, id: Entity, component: Id<T>, data: T) -> (),
 | 
						set: <T>(self: World, id: Entity, component: Id<T>, data: T) -> (),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cleanup: (self: World) -> (),
 | 
						cleanup: (self: World) -> (),
 | 
				
			||||||
	-- Clears an entity from the world
 | 
						-- Clears an entity from the world
 | 
				
			||||||
	clear: <T>(self: World, id: Id<T>) -> (),
 | 
						clear: (self: World, id: Entity) -> (),
 | 
				
			||||||
	--- Removes a component from the given entity
 | 
						--- Removes a component from the given entity
 | 
				
			||||||
	remove: <T>(self: World, id: Entity, component: Id<T>) -> (),
 | 
						remove: (self: World, id: Entity, component: Id) -> (),
 | 
				
			||||||
	--- Retrieves the value of up to 4 components. These values may be nil.
 | 
						--- Retrieves the value of up to 4 components. These values may be nil.
 | 
				
			||||||
	get: (<A>(self: World, id: Entity, Id<A>) -> A?)
 | 
						get: (<A>(self: World, id: Entity, Id<A>) -> A?)
 | 
				
			||||||
		& (<A, B>(self: World, id: Entity, Id<A>, Id<B>) -> (A?, B?))
 | 
							& (<A, B>(self: World, id: Entity, Id<A>, Id<B>) -> (A?, B?))
 | 
				
			||||||
| 
						 | 
					@ -2493,9 +2431,9 @@ export type World = {
 | 
				
			||||||
	--- Checks if the world contains the given entity
 | 
						--- Checks if the world contains the given entity
 | 
				
			||||||
	contains:(self: World, entity: Entity) -> boolean,
 | 
						contains:(self: World, entity: Entity) -> boolean,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	each: <T>(self: World, id: Id<T>) -> () -> Entity,
 | 
						each: (self: World, id: Id) -> () -> Entity,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	children: <T>(self: World, id: Id<T>) -> () -> Entity,
 | 
						children: (self: World, id: Id) -> () -> Entity,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	--- Searches the world for entities that match a given query
 | 
						--- Searches the world for entities that match a given query
 | 
				
			||||||
	query: (<A>(World, Id<A>) -> Query<A>)
 | 
						query: (<A>(World, Id<A>) -> Query<A>)
 | 
				
			||||||
| 
						 | 
					@ -2523,15 +2461,10 @@ export type World = {
 | 
				
			||||||
-- 		return first
 | 
					-- 		return first
 | 
				
			||||||
-- 	end
 | 
					-- 	end
 | 
				
			||||||
-- end
 | 
					-- end
 | 
				
			||||||
--
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
return {
 | 
					return {
 | 
				
			||||||
	World = World :: { new: () -> World },
 | 
						World = World :: { new: () -> World },
 | 
				
			||||||
	world = world_new :: () -> World,
 | 
						world = world_new :: () -> World,
 | 
				
			||||||
	component = (ECS_COMPONENT :: any) :: <T>() -> Entity<T>,
 | 
					 | 
				
			||||||
	tag = (ECS_TAG :: any) :: <T>() -> Entity<T>,
 | 
					 | 
				
			||||||
	meta = (ECS_META :: any) :: <T>(id: Entity, id: Id<T>, value: T) -> Entity<T>,
 | 
					 | 
				
			||||||
	is_tag = (ecs_is_tag :: any) :: <T>(World, Id<T>) -> boolean,
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	OnAdd = EcsOnAdd :: Entity<(entity: Entity) -> ()>,
 | 
						OnAdd = EcsOnAdd :: Entity<(entity: Entity) -> ()>,
 | 
				
			||||||
	OnRemove = EcsOnRemove :: Entity<(entity: Entity) -> ()>,
 | 
						OnRemove = EcsOnRemove :: Entity<(entity: Entity) -> ()>,
 | 
				
			||||||
| 
						 | 
					@ -2554,8 +2487,8 @@ return {
 | 
				
			||||||
	ECS_GENERATION_INC = ECS_GENERATION_INC,
 | 
						ECS_GENERATION_INC = ECS_GENERATION_INC,
 | 
				
			||||||
	ECS_GENERATION = ECS_GENERATION,
 | 
						ECS_GENERATION = ECS_GENERATION,
 | 
				
			||||||
	ECS_ID_IS_WILDCARD = ECS_ID_IS_WILDCARD,
 | 
						ECS_ID_IS_WILDCARD = ECS_ID_IS_WILDCARD,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ECS_ID_DELETE = ECS_ID_DELETE,
 | 
						ECS_ID_DELETE = ECS_ID_DELETE,
 | 
				
			||||||
	ECS_META_RESET = ECS_META_RESET,
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	IS_PAIR = (ECS_IS_PAIR :: any) :: <P, O>(pair: Pair<P, O>) -> boolean,
 | 
						IS_PAIR = (ECS_IS_PAIR :: any) :: <P, O>(pair: Pair<P, O>) -> boolean,
 | 
				
			||||||
	pair_first = (ecs_pair_first :: any) :: <P, O>(world: World, pair: Pair<P, O>) -> Id<P>,
 | 
						pair_first = (ecs_pair_first :: any) :: <P, O>(world: World, pair: Pair<P, O>) -> Id<P>,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -82,25 +82,6 @@ TEST("addons/observers", function()
 | 
				
			||||||
		world:set(e, A, true)
 | 
							world:set(e, A, true)
 | 
				
			||||||
		CHECK(count == 3)
 | 
							CHECK(count == 3)
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					 | 
				
			||||||
	do CASE "Call on pairs"
 | 
					 | 
				
			||||||
		local A = world:component()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local callcount = 0
 | 
					 | 
				
			||||||
		world:added(A, function(entity)
 | 
					 | 
				
			||||||
			callcount += 1
 | 
					 | 
				
			||||||
		end)
 | 
					 | 
				
			||||||
		world:added(A, function(entity)
 | 
					 | 
				
			||||||
			callcount += 1
 | 
					 | 
				
			||||||
		end)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local e = world:entity()
 | 
					 | 
				
			||||||
		local e1 = world:entity()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		world:add(e1, jecs.pair(A, e))
 | 
					 | 
				
			||||||
		world:add(e, jecs.pair(A, e1))
 | 
					 | 
				
			||||||
		CHECK(callcount == 4)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
end)
 | 
					end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return FINISH()
 | 
					return FINISH()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,7 +22,6 @@ type Entity<T=nil> = jecs.Entity<T>
 | 
				
			||||||
type Id<T=unknown> = jecs.Id<T>
 | 
					type Id<T=unknown> = jecs.Id<T>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local entity_visualiser = require("@tools/entity_visualiser")
 | 
					local entity_visualiser = require("@tools/entity_visualiser")
 | 
				
			||||||
local lifetime_tracker_add = require("@tools/lifetime_tracker")
 | 
					 | 
				
			||||||
local dwi = entity_visualiser.stringify
 | 
					local dwi = entity_visualiser.stringify
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST("world:add()", function()
 | 
					TEST("world:add()", function()
 | 
				
			||||||
| 
						 | 
					@ -63,8 +62,8 @@ end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST("world:children()", function()
 | 
					TEST("world:children()", function()
 | 
				
			||||||
	local world = jecs.world()
 | 
						local world = jecs.world()
 | 
				
			||||||
	local C = jecs.component()
 | 
						local C = world:component()
 | 
				
			||||||
	local T = jecs.tag()
 | 
						local T = world:entity()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local e1 = world:entity()
 | 
						local e1 = world:entity()
 | 
				
			||||||
	world:set(e1, C, true)
 | 
						world:set(e1, C, true)
 | 
				
			||||||
| 
						 | 
					@ -96,8 +95,6 @@ TEST("world:children()", function()
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	CHECK(count == 1)
 | 
						CHECK(count == 1)
 | 
				
			||||||
 | 
					 | 
				
			||||||
	jecs.ECS_META_RESET()
 | 
					 | 
				
			||||||
end)
 | 
					end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST("world:clear()", function()
 | 
					TEST("world:clear()", function()
 | 
				
			||||||
| 
						 | 
					@ -198,26 +195,6 @@ TEST("world:clear()", function()
 | 
				
			||||||
end)
 | 
					end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST("world:component()", function()
 | 
					TEST("world:component()", function()
 | 
				
			||||||
	do CASE "allow IDs to be registered"
 | 
					 | 
				
			||||||
		local A = jecs.component()
 | 
					 | 
				
			||||||
		local B = jecs.component()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local world = jecs.world()
 | 
					 | 
				
			||||||
		local C = world:component()
 | 
					 | 
				
			||||||
		CHECK((A :: any) == 1)
 | 
					 | 
				
			||||||
		CHECK((B :: any) == 2)
 | 
					 | 
				
			||||||
		CHECK((C :: any) == 3)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local e = world:entity()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		world:set(e, A, "foo")
 | 
					 | 
				
			||||||
		world:set(e, B, "foo")
 | 
					 | 
				
			||||||
		world:set(e, C, "foo")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		CHECK(world:has(e, A, B, C))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		jecs.ECS_META_RESET() -- Reset the ECS metadata because they may have side effects
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
	do CASE "only components should have EcsComponent trait"
 | 
						do CASE "only components should have EcsComponent trait"
 | 
				
			||||||
		local world = jecs.world()
 | 
							local world = jecs.world()
 | 
				
			||||||
		local A = world:component()
 | 
							local A = world:component()
 | 
				
			||||||
| 
						 | 
					@ -247,7 +224,6 @@ TEST("world:component()", function()
 | 
				
			||||||
end)
 | 
					end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST("world:contains()", function()
 | 
					TEST("world:contains()", function()
 | 
				
			||||||
	local tag = jecs.tag()
 | 
					 | 
				
			||||||
	local world = jecs.world()
 | 
						local world = jecs.world()
 | 
				
			||||||
	local id = world:entity()
 | 
						local id = world:entity()
 | 
				
			||||||
	CHECK(world:contains(id))
 | 
						CHECK(world:contains(id))
 | 
				
			||||||
| 
						 | 
					@ -257,9 +233,6 @@ TEST("world:contains()", function()
 | 
				
			||||||
		world:delete(id)
 | 
							world:delete(id)
 | 
				
			||||||
		CHECK(not world:contains(id))
 | 
							CHECK(not world:contains(id))
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					 | 
				
			||||||
	CHECK(world:contains(tag))
 | 
					 | 
				
			||||||
	jecs.ECS_META_RESET()
 | 
					 | 
				
			||||||
end)
 | 
					end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST("world:delete()", function()
 | 
					TEST("world:delete()", function()
 | 
				
			||||||
| 
						 | 
					@ -633,7 +606,6 @@ end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST("world:entity()", function()
 | 
					TEST("world:entity()", function()
 | 
				
			||||||
	local N = 2^8
 | 
						local N = 2^8
 | 
				
			||||||
 | 
					 | 
				
			||||||
	do CASE "unique IDs"
 | 
						do CASE "unique IDs"
 | 
				
			||||||
		local world = jecs.world()
 | 
							local world = jecs.world()
 | 
				
			||||||
		local set = {}
 | 
							local set = {}
 | 
				
			||||||
| 
						 | 
					@ -748,21 +720,21 @@ TEST("world:query()", function()
 | 
				
			||||||
		local i = 0
 | 
							local i = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		local iter = 0
 | 
							local iter = 0
 | 
				
			||||||
		for _ in q:iter() do
 | 
							for _, e in q:iter() do
 | 
				
			||||||
			iter += 1
 | 
								iter += 1
 | 
				
			||||||
			i=1
 | 
								i=1
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
		CHECK (iter == 1)
 | 
							CHECK (iter == 1)
 | 
				
			||||||
		CHECK(i == 1)
 | 
							CHECK(i == 1)
 | 
				
			||||||
		for _ in q:iter() do
 | 
							for _, e in q:iter() do
 | 
				
			||||||
			i=2
 | 
								i=2
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
		CHECK(i == 2)
 | 
							CHECK(i == 2)
 | 
				
			||||||
		for _ in q do
 | 
							for _, e in q :: any do
 | 
				
			||||||
			i=3
 | 
								i=3
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
		CHECK(i == 3)
 | 
							CHECK(i == 3)
 | 
				
			||||||
		for _ in q do
 | 
							for _, e in q :: any do
 | 
				
			||||||
			i=4
 | 
								i=4
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
		CHECK(i == 4)
 | 
							CHECK(i == 4)
 | 
				
			||||||
| 
						 | 
					@ -774,8 +746,8 @@ TEST("world:query()", function()
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
	do CASE "multiple iter"
 | 
						do CASE "multiple iter"
 | 
				
			||||||
		local world = jecs.World.new()
 | 
							local world = jecs.World.new()
 | 
				
			||||||
		local A = world:component() :: jecs.Entity<string>
 | 
							local A = world:component()
 | 
				
			||||||
		local B = world:component() :: jecs.Entity<string>
 | 
							local B = world:component()
 | 
				
			||||||
		local e = world:entity()
 | 
							local e = world:entity()
 | 
				
			||||||
		world:add(e, A)
 | 
							world:add(e, A)
 | 
				
			||||||
		world:add(e, B)
 | 
							world:add(e, B)
 | 
				
			||||||
| 
						 | 
					@ -1433,6 +1405,8 @@ TEST("#adding a recycled target", function()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
end)
 | 
					end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST("#repro2", function()
 | 
					TEST("#repro2", function()
 | 
				
			||||||
	local world = jecs.world()
 | 
						local world = jecs.world()
 | 
				
			||||||
	local Lifetime = world:component() :: Id<number>
 | 
						local Lifetime = world:component() :: Id<number>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -226,8 +226,6 @@ local function CHECK<T>(value: T, stack: number?): T?
 | 
				
			||||||
	return value
 | 
						return value
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local test_focused = false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function TEST(name: string, fn: () -> ())
 | 
					local function TEST(name: string, fn: () -> ())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	test = {
 | 
						test = {
 | 
				
			||||||
| 
						 | 
					@ -240,10 +238,6 @@ local function TEST(name: string, fn: () -> ())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local t = test
 | 
						local t = test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if check_for_focused and not test_focused then
 | 
					 | 
				
			||||||
		test.focus = true
 | 
					 | 
				
			||||||
		test_focused = true
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
	table.insert(tests, t)
 | 
						table.insert(tests, t)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -251,10 +245,15 @@ local function FOCUS()
 | 
				
			||||||
	assert(test, "no active test")
 | 
						assert(test, "no active test")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	check_for_focused = true
 | 
						check_for_focused = true
 | 
				
			||||||
	test_focused = false
 | 
						if test.case then
 | 
				
			||||||
 | 
							test.case.focus = true
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							test.focus = true
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function FINISH(): number
 | 
					local function FINISH(): number
 | 
				
			||||||
 | 
						local success = true
 | 
				
			||||||
	local total_cases = 0
 | 
						local total_cases = 0
 | 
				
			||||||
	local passed_cases = 0
 | 
						local passed_cases = 0
 | 
				
			||||||
	local passed_focus_cases = 0
 | 
						local passed_focus_cases = 0
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue