mirror of
				https://github.com/Ukendio/jecs.git
				synced 2025-11-03 18:39:19 +00:00 
			
		
		
		
	Add component registration and metadata API
This commit is contained in:
		
							parent
							
								
									6ec8ed69e9
								
							
						
					
					
						commit
						a6c53c9de8
					
				
					 2 changed files with 179 additions and 99 deletions
				
			
		
							
								
								
									
										238
									
								
								jecs.luau
									
									
									
									
									
								
							
							
						
						
									
										238
									
								
								jecs.luau
									
									
									
									
									
								
							| 
						 | 
					@ -116,13 +116,47 @@ 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({})
 | 
					local NULL_ARRAY = table.freeze({}) :: Column
 | 
				
			||||||
 | 
					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 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
 | 
				
			||||||
| 
						 | 
					@ -529,62 +563,64 @@ 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 not idr then
 | 
						if idr then
 | 
				
			||||||
		local flags = ECS_ID_MASK
 | 
							return idr
 | 
				
			||||||
		local relation = id
 | 
					 | 
				
			||||||
		local target = 0
 | 
					 | 
				
			||||||
		local is_pair = ECS_IS_PAIR(id)
 | 
					 | 
				
			||||||
		if is_pair then
 | 
					 | 
				
			||||||
			relation = entity_index_get_alive(entity_index, ECS_PAIR_FIRST(id)) :: i53
 | 
					 | 
				
			||||||
			assert(relation and entity_index_is_alive(
 | 
					 | 
				
			||||||
				entity_index, relation), ECS_INTERNAL_ERROR)
 | 
					 | 
				
			||||||
			target = entity_index_get_alive(entity_index, ECS_PAIR_SECOND(id)) :: i53
 | 
					 | 
				
			||||||
			assert(target and entity_index_is_alive(
 | 
					 | 
				
			||||||
				entity_index, target), ECS_INTERNAL_ERROR)
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local cleanup_policy = world_target(world, relation, EcsOnDelete, 0)
 | 
					 | 
				
			||||||
		local cleanup_policy_target = world_target(world, relation, EcsOnDeleteTarget, 0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local has_delete = false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if cleanup_policy == EcsDelete or cleanup_policy_target == EcsDelete then
 | 
					 | 
				
			||||||
			has_delete = true
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local on_add, on_change, on_remove = world_get(world,
 | 
					 | 
				
			||||||
			relation, EcsOnAdd, EcsOnChange, EcsOnRemove)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local is_tag = not world_has_one_inline(world,
 | 
					 | 
				
			||||||
			relation, EcsComponent)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if is_tag and is_pair then
 | 
					 | 
				
			||||||
			is_tag = not world_has_one_inline(world, target, EcsComponent)
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		flags = bit32.bor(
 | 
					 | 
				
			||||||
			flags,
 | 
					 | 
				
			||||||
			if has_delete then ECS_ID_DELETE else 0,
 | 
					 | 
				
			||||||
			if is_tag then ECS_ID_IS_TAG else 0
 | 
					 | 
				
			||||||
		)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		idr = {
 | 
					 | 
				
			||||||
			size = 0,
 | 
					 | 
				
			||||||
			cache = {},
 | 
					 | 
				
			||||||
			counts = {},
 | 
					 | 
				
			||||||
			flags = flags,
 | 
					 | 
				
			||||||
			hooks = {
 | 
					 | 
				
			||||||
				on_add = on_add,
 | 
					 | 
				
			||||||
				on_change = on_change,
 | 
					 | 
				
			||||||
				on_remove = on_remove,
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		component_index[id] = idr
 | 
					 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local flags = ECS_ID_MASK
 | 
				
			||||||
 | 
						local relation = id
 | 
				
			||||||
 | 
						local target = 0
 | 
				
			||||||
 | 
						local is_pair = ECS_IS_PAIR(id)
 | 
				
			||||||
 | 
						if is_pair then
 | 
				
			||||||
 | 
							relation = entity_index_get_alive(entity_index, ECS_PAIR_FIRST(id)) :: i53
 | 
				
			||||||
 | 
							assert(relation and entity_index_is_alive(
 | 
				
			||||||
 | 
								entity_index, relation), ECS_INTERNAL_ERROR)
 | 
				
			||||||
 | 
							target = entity_index_get_alive(entity_index, ECS_PAIR_SECOND(id)) :: i53
 | 
				
			||||||
 | 
							assert(target and entity_index_is_alive(
 | 
				
			||||||
 | 
								entity_index, target), ECS_INTERNAL_ERROR)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local cleanup_policy = world_target(world, relation, EcsOnDelete, 0)
 | 
				
			||||||
 | 
						local cleanup_policy_target = world_target(world, relation, EcsOnDeleteTarget, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local has_delete = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if cleanup_policy == EcsDelete or cleanup_policy_target == EcsDelete then
 | 
				
			||||||
 | 
							has_delete = true
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local on_add, on_change, on_remove = world_get(world,
 | 
				
			||||||
 | 
							relation, EcsOnAdd, EcsOnChange, EcsOnRemove)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local is_tag = not world_has_one_inline(world,
 | 
				
			||||||
 | 
							relation, EcsComponent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if is_tag and is_pair then
 | 
				
			||||||
 | 
							is_tag = not world_has_one_inline(world, target, EcsComponent)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						flags = bit32.bor(
 | 
				
			||||||
 | 
							flags,
 | 
				
			||||||
 | 
							if has_delete then ECS_ID_DELETE else 0,
 | 
				
			||||||
 | 
							if is_tag then ECS_ID_IS_TAG else 0
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						idr = {
 | 
				
			||||||
 | 
							size = 0,
 | 
				
			||||||
 | 
							cache = {},
 | 
				
			||||||
 | 
							counts = {},
 | 
				
			||||||
 | 
							flags = flags,
 | 
				
			||||||
 | 
							hooks = {
 | 
				
			||||||
 | 
								on_add = on_add,
 | 
				
			||||||
 | 
								on_change = on_change,
 | 
				
			||||||
 | 
								on_remove = on_remove,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						} :: ecs_id_record_t
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						component_index[id] = idr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return idr
 | 
						return idr
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -621,7 +657,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,
 | 
				
			||||||
| 
						 | 
					@ -670,7 +706,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] = {}
 | 
						world.archetype_edges[archetype.id] = {} :: Map<i53, ecs_archetype_t>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return archetype
 | 
						return archetype
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -747,17 +783,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 = edge[id]
 | 
						local to: ecs_archetype_t = edge[id]
 | 
				
			||||||
	if not to then
 | 
						if to == nil 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 :: ecs_archetype_t
 | 
						return to
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function find_archetype_with(world, id, from)
 | 
					local function find_archetype_with(world, id, from): ecs_archetype_t
 | 
				
			||||||
	local id_types = from.types
 | 
						local id_types = from.types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local at = find_insert(id_types, id)
 | 
						local at = find_insert(id_types, id)
 | 
				
			||||||
| 
						 | 
					@ -767,7 +803,7 @@ local function find_archetype_with(world, id, from)
 | 
				
			||||||
	return archetype_ensure(world, dst)
 | 
						return archetype_ensure(world, dst)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function archetype_traverse_add(world, id, from: ecs_archetype_t)
 | 
					local function archetype_traverse_add(world, id, from: ecs_archetype_t): 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
 | 
				
			||||||
| 
						 | 
					@ -830,7 +866,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.records[id]
 | 
							local tr = (to :: ecs_archetype_t).records[id]
 | 
				
			||||||
		local column = from.columns[tr]
 | 
							local column = from.columns[tr]
 | 
				
			||||||
		column[record.row] = data
 | 
							column[record.row] = data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -984,8 +1020,8 @@ local function world_clear(world: ecs_world_t, entity: i53)
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if idr_t then
 | 
						if idr_t then
 | 
				
			||||||
		local queue
 | 
							local queue: { i53 }
 | 
				
			||||||
		local ids
 | 
							local ids: Map<i53, boolean>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		local count = 0
 | 
							local count = 0
 | 
				
			||||||
		local archetype_ids = idr_t.cache
 | 
							local archetype_ids = idr_t.cache
 | 
				
			||||||
| 
						 | 
					@ -1175,8 +1211,8 @@ local function world_delete(world: ecs_world_t, entity: i53)
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if idr_t then
 | 
						if idr_t then
 | 
				
			||||||
		local children
 | 
							local children: { i53 }
 | 
				
			||||||
		local ids
 | 
							local ids: Map<i53, boolean>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		local count = 0
 | 
							local count = 0
 | 
				
			||||||
		local archetype_ids = idr_t.cache
 | 
							local archetype_ids = idr_t.cache
 | 
				
			||||||
| 
						 | 
					@ -1240,7 +1276,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) ~= 0 then
 | 
							if (bit32.band(flags, ECS_ID_DELETE) :: number) ~= 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
 | 
				
			||||||
| 
						 | 
					@ -1754,7 +1790,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 = {}
 | 
							on_create_action = {} :: Map<i53, { ecs_observer_t }>
 | 
				
			||||||
		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]
 | 
				
			||||||
| 
						 | 
					@ -1765,7 +1801,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 = {}
 | 
							on_delete_action = {} :: Map<i53, { ecs_observer_t }>
 | 
				
			||||||
		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]
 | 
				
			||||||
| 
						 | 
					@ -2168,12 +2204,12 @@ local function world_query(world: ecs_world_t, ...)
 | 
				
			||||||
			return q
 | 
								return q
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if idr == nil or map.size < idr.size then
 | 
							if idr == nil or (map.size :: number) < (idr.size :: number) then
 | 
				
			||||||
			idr = map
 | 
								idr = map
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if not idr then
 | 
						if idr == nil then
 | 
				
			||||||
		return q
 | 
							return q
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2306,7 +2342,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 = 0,
 | 
							max_component_id = ecs_max_component_id,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		observable = {} :: Observable,
 | 
							observable = {} :: Observable,
 | 
				
			||||||
	}, World) :: any
 | 
						}, World) :: any
 | 
				
			||||||
| 
						 | 
					@ -2323,6 +2359,21 @@ 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)
 | 
				
			||||||
| 
						 | 
					@ -2350,22 +2401,25 @@ end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
World.new = world_new
 | 
					World.new = world_new
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type Entity<T = unknown> = { __T: T }
 | 
					export type Entity<T = any> = { __T: T }
 | 
				
			||||||
export type Id<T = unknown> = { __T: T }
 | 
					export type Id<T = any> = { __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) -> (),
 | 
				
			||||||
| 
						 | 
					@ -2399,20 +2453,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: (self: World, id: Entity, relation: Id, index: number?) -> Entity?,
 | 
						target: <T>(self: World, id: Entity, relation: Id<T>, 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) -> (),
 | 
						add: <T>(self: World, id: Entity, component: Id<T>) -> (),
 | 
				
			||||||
	--- 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: (self: World, id: Entity) -> (),
 | 
						clear: <T>(self: World, id: Id<T>) -> (),
 | 
				
			||||||
	--- Removes a component from the given entity
 | 
						--- Removes a component from the given entity
 | 
				
			||||||
	remove: (self: World, id: Entity, component: Id) -> (),
 | 
						remove: <T>(self: World, id: Entity, component: Id<T>) -> (),
 | 
				
			||||||
	--- 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?))
 | 
				
			||||||
| 
						 | 
					@ -2431,9 +2485,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: (self: World, id: Id) -> () -> Entity,
 | 
						each: <T>(self: World, id: Id<T>) -> () -> Entity,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	children: (self: World, id: Id) -> () -> Entity,
 | 
						children: <T>(self: World, id: Id<T>) -> () -> 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>)
 | 
				
			||||||
| 
						 | 
					@ -2461,10 +2515,14 @@ 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>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	OnAdd = EcsOnAdd :: Entity<(entity: Entity) -> ()>,
 | 
						OnAdd = EcsOnAdd :: Entity<(entity: Entity) -> ()>,
 | 
				
			||||||
	OnRemove = EcsOnRemove :: Entity<(entity: Entity) -> ()>,
 | 
						OnRemove = EcsOnRemove :: Entity<(entity: Entity) -> ()>,
 | 
				
			||||||
| 
						 | 
					@ -2487,8 +2545,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>,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -62,8 +62,8 @@ end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST("world:children()", function()
 | 
					TEST("world:children()", function()
 | 
				
			||||||
	local world = jecs.world()
 | 
						local world = jecs.world()
 | 
				
			||||||
	local C = world:component()
 | 
						local C = jecs.component()
 | 
				
			||||||
	local T = world:entity()
 | 
						local T = jecs.tag()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local e1 = world:entity()
 | 
						local e1 = world:entity()
 | 
				
			||||||
	world:set(e1, C, true)
 | 
						world:set(e1, C, true)
 | 
				
			||||||
| 
						 | 
					@ -95,6 +95,8 @@ 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()
 | 
				
			||||||
| 
						 | 
					@ -195,6 +197,26 @@ 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()
 | 
				
			||||||
| 
						 | 
					@ -720,21 +742,21 @@ TEST("world:query()", function()
 | 
				
			||||||
		local i = 0
 | 
							local i = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		local iter = 0
 | 
							local iter = 0
 | 
				
			||||||
		for _, e in q:iter() do
 | 
							for _ 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 _, e in q:iter() do
 | 
							for _ in q:iter() do
 | 
				
			||||||
			i=2
 | 
								i=2
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
		CHECK(i == 2)
 | 
							CHECK(i == 2)
 | 
				
			||||||
		for _, e in q :: any do
 | 
							for _ in q do
 | 
				
			||||||
			i=3
 | 
								i=3
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
		CHECK(i == 3)
 | 
							CHECK(i == 3)
 | 
				
			||||||
		for _, e in q :: any do
 | 
							for _ in q do
 | 
				
			||||||
			i=4
 | 
								i=4
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
		CHECK(i == 4)
 | 
							CHECK(i == 4)
 | 
				
			||||||
| 
						 | 
					@ -746,8 +768,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()
 | 
							local A = world:component() :: jecs.Entity<string>
 | 
				
			||||||
		local B = world:component()
 | 
							local B = world:component() :: jecs.Entity<string>
 | 
				
			||||||
		local e = world:entity()
 | 
							local e = world:entity()
 | 
				
			||||||
		world:add(e, A)
 | 
							world:add(e, A)
 | 
				
			||||||
		world:add(e, B)
 | 
							world:add(e, B)
 | 
				
			||||||
| 
						 | 
					@ -756,7 +778,7 @@ TEST("world:query()", function()
 | 
				
			||||||
		for x in q:iter() do
 | 
							for x in q:iter() do
 | 
				
			||||||
			counter += 1
 | 
								counter += 1
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
		for x in q:iter() do
 | 
							for x, tets in q do
 | 
				
			||||||
			counter += 1
 | 
								counter += 1
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
		CHECK(counter == 2)
 | 
							CHECK(counter == 2)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue