mirror of
				https://github.com/Ukendio/jecs.git
				synced 2025-11-03 02:29:16 +00:00 
			
		
		
		
	Compare commits
	
		
			3 commits
		
	
	
		
			4336e65633
			...
			a3939cf083
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
							 | 
						a3939cf083 | ||
| 
							 | 
						9d83c3bc13 | ||
| 
							 | 
						e073a570b7 | 
					 6 changed files with 2531 additions and 596 deletions
				
			
		| 
						 | 
				
			
			@ -28,22 +28,22 @@ local B6 = ecr.component()
 | 
			
		|||
local B7 = ecr.component()
 | 
			
		||||
local B8 = ecr.component()
 | 
			
		||||
 | 
			
		||||
local C1 = ecs:entity()
 | 
			
		||||
local C2 = ecs:entity()
 | 
			
		||||
local C3 = ecs:entity()
 | 
			
		||||
local C4 = ecs:entity()
 | 
			
		||||
local C5 = ecs:entity()
 | 
			
		||||
local C6 = ecs:entity()
 | 
			
		||||
local C7 = ecs:entity()
 | 
			
		||||
local C8 = ecs:entity()
 | 
			
		||||
local E1 = mcs:entity()
 | 
			
		||||
local E2 = mcs:entity()
 | 
			
		||||
local E3 = mcs:entity()
 | 
			
		||||
local E4 = mcs:entity()
 | 
			
		||||
local E5 = mcs:entity()
 | 
			
		||||
local E6 = mcs:entity()
 | 
			
		||||
local E7 = mcs:entity()
 | 
			
		||||
local E8 = mcs:entity()
 | 
			
		||||
local C1 = ecs:component()
 | 
			
		||||
local C2 = ecs:component()
 | 
			
		||||
local C3 = ecs:component()
 | 
			
		||||
local C4 = ecs:component()
 | 
			
		||||
local C5 = ecs:component()
 | 
			
		||||
local C6 = ecs:component()
 | 
			
		||||
local C7 = ecs:component()
 | 
			
		||||
local C8 = ecs:component()
 | 
			
		||||
local E1 = mcs:component()
 | 
			
		||||
local E2 = mcs:component()
 | 
			
		||||
local E3 = mcs:component()
 | 
			
		||||
local E4 = mcs:component()
 | 
			
		||||
local E5 = mcs:component()
 | 
			
		||||
local E6 = mcs:component()
 | 
			
		||||
local E7 = mcs:component()
 | 
			
		||||
local E8 = mcs:component()
 | 
			
		||||
 | 
			
		||||
local registry2 = ecr.registry()
 | 
			
		||||
return {
 | 
			
		||||
| 
						 | 
				
			
			@ -52,48 +52,30 @@ return {
 | 
			
		|||
	end,
 | 
			
		||||
 | 
			
		||||
	Functions = {
 | 
			
		||||
		Matter = function()
 | 
			
		||||
			local e = newWorld:spawn()
 | 
			
		||||
		Mirror = function()
 | 
			
		||||
			local e = mcs:entity()
 | 
			
		||||
			for i = 1, 5000 do
 | 
			
		||||
				newWorld:insert(
 | 
			
		||||
					e,
 | 
			
		||||
					A1({ value = true }),
 | 
			
		||||
					A2({ value = true }),
 | 
			
		||||
					A3({ value = true }),
 | 
			
		||||
					A4({ value = true }),
 | 
			
		||||
					A5({ value = true }),
 | 
			
		||||
					A6({ value = true }),
 | 
			
		||||
					A7({ value = true }),
 | 
			
		||||
					A8({ value = true })
 | 
			
		||||
				)
 | 
			
		||||
				mcs:set(e, E1, false)
 | 
			
		||||
				mcs:set(e, E2, false)
 | 
			
		||||
				mcs:set(e, E3, false)
 | 
			
		||||
				mcs:set(e, E4, false)
 | 
			
		||||
				mcs:set(e, E5, false)
 | 
			
		||||
				mcs:set(e, E6, false)
 | 
			
		||||
				mcs:set(e, E7, false)
 | 
			
		||||
				mcs:set(e, E8, false)
 | 
			
		||||
			end
 | 
			
		||||
		end,
 | 
			
		||||
 | 
			
		||||
		ECR = function()
 | 
			
		||||
			local e = registry2.create()
 | 
			
		||||
			for i = 1, 5000 do
 | 
			
		||||
				registry2:set(e, B1, { value = false })
 | 
			
		||||
				registry2:set(e, B2, { value = false })
 | 
			
		||||
				registry2:set(e, B3, { value = false })
 | 
			
		||||
				registry2:set(e, B4, { value = false })
 | 
			
		||||
				registry2:set(e, B5, { value = false })
 | 
			
		||||
				registry2:set(e, B6, { value = false })
 | 
			
		||||
				registry2:set(e, B7, { value = false })
 | 
			
		||||
				registry2:set(e, B8, { value = false })
 | 
			
		||||
			end
 | 
			
		||||
		end,
 | 
			
		||||
 | 
			
		||||
		Jecs = function()
 | 
			
		||||
			local e = ecs:entity()
 | 
			
		||||
			for i = 1, 5000 do
 | 
			
		||||
				ecs:set(e, C1, { value = false })
 | 
			
		||||
				ecs:set(e, C2, { value = false })
 | 
			
		||||
				ecs:set(e, C3, { value = false })
 | 
			
		||||
				ecs:set(e, C4, { value = false })
 | 
			
		||||
				ecs:set(e, C5, { value = false })
 | 
			
		||||
				ecs:set(e, C6, { value = false })
 | 
			
		||||
				ecs:set(e, C7, { value = false })
 | 
			
		||||
				ecs:set(e, C8, { value = false })
 | 
			
		||||
				ecs:set(e, C1, false)
 | 
			
		||||
				ecs:set(e, C2, false)
 | 
			
		||||
				ecs:set(e, C3, false)
 | 
			
		||||
				ecs:set(e, C4, false)
 | 
			
		||||
				ecs:set(e, C5, false)
 | 
			
		||||
				ecs:set(e, C6, false)
 | 
			
		||||
				ecs:set(e, C7, false)
 | 
			
		||||
				ecs:set(e, C8, false)
 | 
			
		||||
			end
 | 
			
		||||
		end,
 | 
			
		||||
	},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										49
									
								
								benches/visual/remove.bench.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								benches/visual/remove.bench.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,49 @@
 | 
			
		|||
--!optimize 2
 | 
			
		||||
--!native
 | 
			
		||||
 | 
			
		||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
local Matter = require(ReplicatedStorage.DevPackages.Matter)
 | 
			
		||||
local ecr = require(ReplicatedStorage.DevPackages.ecr)
 | 
			
		||||
local jecs = require(ReplicatedStorage.Lib)
 | 
			
		||||
local pair = jecs.pair
 | 
			
		||||
local ecs = jecs.World.new()
 | 
			
		||||
local mirror = require(ReplicatedStorage.mirror)
 | 
			
		||||
local mcs = mirror.World.new()
 | 
			
		||||
 | 
			
		||||
local C1 = ecs:component()
 | 
			
		||||
local C2 = ecs:entity()
 | 
			
		||||
ecs:add(C2, pair(jecs.OnDeleteTarget, jecs.Delete))
 | 
			
		||||
local C3 = ecs:entity()
 | 
			
		||||
ecs:add(C3, pair(jecs.OnDeleteTarget, jecs.Delete))
 | 
			
		||||
local C4 = ecs:entity()
 | 
			
		||||
ecs:add(C4, pair(jecs.OnDeleteTarget, jecs.Delete))
 | 
			
		||||
local E1 = mcs:component()
 | 
			
		||||
local E2 = mcs:entity()
 | 
			
		||||
mcs:add(E2, pair(jecs.OnDeleteTarget, jecs.Delete))
 | 
			
		||||
local E3 = mcs:entity()
 | 
			
		||||
mcs:add(E3, pair(jecs.OnDeleteTarget, jecs.Delete))
 | 
			
		||||
local E4 = mcs:entity()
 | 
			
		||||
mcs:add(E4, pair(jecs.OnDeleteTarget, jecs.Delete))
 | 
			
		||||
 | 
			
		||||
return {
 | 
			
		||||
	ParameterGenerator = function()
 | 
			
		||||
	end,
 | 
			
		||||
 | 
			
		||||
	Functions = {
 | 
			
		||||
		Mirror = function()
 | 
			
		||||
			local m = mcs:entity()
 | 
			
		||||
			for i = 1, 100 do
 | 
			
		||||
				mcs:add(m, E3)
 | 
			
		||||
				mcs:remove(m, E3)
 | 
			
		||||
			end
 | 
			
		||||
		end,
 | 
			
		||||
 | 
			
		||||
		Jecs = function()
 | 
			
		||||
			local j = ecs:entity()
 | 
			
		||||
			for i = 1, 100 do
 | 
			
		||||
				ecs:add(j, C3)
 | 
			
		||||
				ecs:remove(j, C3)
 | 
			
		||||
			end
 | 
			
		||||
		end,
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										134
									
								
								jecs.luau
									
									
									
									
									
								
							
							
						
						
									
										134
									
								
								jecs.luau
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -90,60 +90,35 @@ local EcsOnArchetypeCreate =        HI_COMPONENT_ID + 12
 | 
			
		|||
local EcsOnArchetypeDelete =        HI_COMPONENT_ID + 13
 | 
			
		||||
local EcsRest =                     HI_COMPONENT_ID + 14
 | 
			
		||||
 | 
			
		||||
local ECS_PAIR_FLAG =                                0x8
 | 
			
		||||
local ECS_ID_FLAGS_MASK =                           0x10
 | 
			
		||||
local ECS_ENTITY_MASK =              bit32.lshift(1, 24)
 | 
			
		||||
local ECS_GENERATION_MASK =          bit32.lshift(1, 16)
 | 
			
		||||
 | 
			
		||||
local ECS_ID_DELETE =                        0b0000_0001
 | 
			
		||||
local ECS_ID_IS_TAG =                        0b0000_0010
 | 
			
		||||
local ECS_ID_HAS_ON_ADD =                    0b0000_0100
 | 
			
		||||
local ECS_ID_HAS_ON_SET =                    0b0000_1000
 | 
			
		||||
local ECS_ID_HAS_ON_REMOVE =                 0b0001_0000
 | 
			
		||||
local ECS_ID_MASK =                          0b0000_0000
 | 
			
		||||
-- stylua: ignore end
 | 
			
		||||
local NULL_ARRAY = table.freeze({}) :: Column
 | 
			
		||||
 | 
			
		||||
local function FLAGS_ADD(is_pair: boolean): number
 | 
			
		||||
	local flags = 0x0
 | 
			
		||||
local ECS_ENTITY_MASK =              bit32.lshift(1, 24)
 | 
			
		||||
local ECS_GENERATION_MASK =          bit32.lshift(1, 16)
 | 
			
		||||
 | 
			
		||||
	if is_pair then
 | 
			
		||||
		flags = bit32.bor(flags, ECS_PAIR_FLAG) -- HIGHEST bit in the ID.
 | 
			
		||||
	end
 | 
			
		||||
	if false then
 | 
			
		||||
		flags = bit32.bor(flags, 0x4) -- Set the second flag to true
 | 
			
		||||
	end
 | 
			
		||||
	if false then
 | 
			
		||||
		flags = bit32.bor(flags, 0x2) -- Set the third flag to true
 | 
			
		||||
	end
 | 
			
		||||
	if false then
 | 
			
		||||
		flags = bit32.bor(flags, 0x1) -- LAST BIT in the ID.
 | 
			
		||||
	end
 | 
			
		||||
local NULL_ARRAY = table.freeze({})
 | 
			
		||||
 | 
			
		||||
	return flags
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function ECS_COMBINE(source: number, target: number): i53
 | 
			
		||||
	return (source * 268435456) + (target * ECS_ID_FLAGS_MASK)
 | 
			
		||||
 | 
			
		||||
local function ECS_COMBINE(id: number, generation: number): i53
 | 
			
		||||
	return id + (generation * ECS_ENTITY_MASK)
 | 
			
		||||
end
 | 
			
		||||
local ECS_PAIR_OFFSET = 2^48
 | 
			
		||||
 | 
			
		||||
local function ECS_IS_PAIR(e: number): boolean
 | 
			
		||||
	return if e > ECS_ENTITY_MASK then (e % ECS_ID_FLAGS_MASK) // ECS_PAIR_FLAG ~= 0 else false
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- HIGH 24 bits LOW 24 bits
 | 
			
		||||
local function ECS_GENERATION(e: i53): i24
 | 
			
		||||
	return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) % ECS_GENERATION_MASK else 0
 | 
			
		||||
	return e > ECS_PAIR_OFFSET
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function ECS_GENERATION_INC(e: i53)
 | 
			
		||||
	if e > ECS_ENTITY_MASK then
 | 
			
		||||
		local flags = e // ECS_ID_FLAGS_MASK
 | 
			
		||||
		local id = flags // ECS_ENTITY_MASK
 | 
			
		||||
		local generation = flags % ECS_GENERATION_MASK
 | 
			
		||||
		local id = e % ECS_ENTITY_MASK
 | 
			
		||||
		local generation = e // ECS_ENTITY_MASK
 | 
			
		||||
 | 
			
		||||
		local next_gen = generation + 1
 | 
			
		||||
		if next_gen > ECS_GENERATION_MASK then
 | 
			
		||||
		if next_gen >= ECS_GENERATION_MASK then
 | 
			
		||||
			return id
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -152,22 +127,23 @@ local function ECS_GENERATION_INC(e: i53)
 | 
			
		|||
	return ECS_COMBINE(e, 1)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- FIRST gets the high ID
 | 
			
		||||
local function ECS_ENTITY_T_HI(e: i53): i24
 | 
			
		||||
	return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) % ECS_ENTITY_MASK else e
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- SECOND
 | 
			
		||||
local function ECS_ENTITY_T_LO(e: i53): i24
 | 
			
		||||
	return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) // ECS_ENTITY_MASK else e
 | 
			
		||||
	return e % ECS_ENTITY_MASK
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function _STRIP_GENERATION(e: i53): i24
 | 
			
		||||
	return ECS_ENTITY_T_LO(e)
 | 
			
		||||
local function ECS_GENERATION(e: i53)
 | 
			
		||||
	return e // ECS_ENTITY_MASK
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function ECS_ENTITY_T_HI(e: i53): i24
 | 
			
		||||
	return e // ECS_ENTITY_MASK
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function ECS_PAIR(pred: i53, obj: i53): i53
 | 
			
		||||
	return ECS_COMBINE(ECS_ENTITY_T_LO(pred), ECS_ENTITY_T_LO(obj)) + FLAGS_ADD(--[[isPair]] true) :: i53
 | 
			
		||||
	pred %= ECS_ENTITY_MASK
 | 
			
		||||
	obj %= ECS_ENTITY_MASK
 | 
			
		||||
 | 
			
		||||
	return obj + (pred * 2^24) + ECS_PAIR_OFFSET
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function entity_index_try_get_any(entity_index: EntityIndex, entity: number): Record?
 | 
			
		||||
| 
						 | 
				
			
			@ -236,14 +212,14 @@ local function entity_index_new_id(entity_index: EntityIndex): i53
 | 
			
		|||
	return id
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- ECS_PAIR_FIRST, gets the relationship target / obj / HIGH bits
 | 
			
		||||
local function ecs_pair_first(world, e)
 | 
			
		||||
	return entity_index_get_alive(world.entity_index, ECS_ENTITY_T_LO(e))
 | 
			
		||||
	local pred = (e - ECS_PAIR_OFFSET) // ECS_ENTITY_MASK
 | 
			
		||||
	return entity_index_get_alive(world.entity_index, pred)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- ECS_PAIR_SECOND gets the relationship / pred / LOW bits
 | 
			
		||||
local function ecs_pair_second(world, e)
 | 
			
		||||
	return entity_index_get_alive(world.entity_index, ECS_ENTITY_T_HI(e))
 | 
			
		||||
	local obj = (e - ECS_PAIR_OFFSET) % ECS_ENTITY_MASK
 | 
			
		||||
	return entity_index_get_alive(world.entity_index, obj)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function query_match(query, archetype: Archetype)
 | 
			
		||||
| 
						 | 
				
			
			@ -476,8 +452,8 @@ local function world_target(world: World, entity: i53, relation: i24, index: num
 | 
			
		|||
		return nil
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	if nth >= count then
 | 
			
		||||
		nth = nth + count + 1
 | 
			
		||||
	if nth > count then
 | 
			
		||||
		nth = nth + count
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local tr = idr.cache[archetype_id]
 | 
			
		||||
| 
						 | 
				
			
			@ -860,16 +836,16 @@ local function world_set(world: World, entity: i53, id: i53, data: unknown): ()
 | 
			
		|||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local on_add = idr_hooks.on_add
 | 
			
		||||
	if on_add then
 | 
			
		||||
		on_add(entity)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local tr = to.records[id]
 | 
			
		||||
	local column = to.columns[tr]
 | 
			
		||||
 | 
			
		||||
	column[record.row] = data
 | 
			
		||||
 | 
			
		||||
	local on_add = idr_hooks.on_add
 | 
			
		||||
	if on_add then
 | 
			
		||||
		on_add(entity)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local on_set = idr_hooks.on_set
 | 
			
		||||
	if on_set then
 | 
			
		||||
		on_set(entity, data)
 | 
			
		||||
| 
						 | 
				
			
			@ -899,15 +875,16 @@ local function world_remove(world: World, entity: i53, id: i53)
 | 
			
		|||
	if not from then
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
	local to = archetype_traverse_remove(world, id, from)
 | 
			
		||||
 | 
			
		||||
	if from ~= to then
 | 
			
		||||
	if from.records[id] then
 | 
			
		||||
		local idr = world.component_index[id]
 | 
			
		||||
		local on_remove = idr.hooks.on_remove
 | 
			
		||||
		if on_remove then
 | 
			
		||||
			on_remove(entity)
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		local to = archetype_traverse_remove(world, id, record.archetype)
 | 
			
		||||
 | 
			
		||||
		entity_move(entity_index, entity, record, to)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			@ -2413,51 +2390,44 @@ export type World = {
 | 
			
		|||
	observable: any,
 | 
			
		||||
 | 
			
		||||
	--- Creates a new entity
 | 
			
		||||
	entity: (self: World) -> Entity,
 | 
			
		||||
	entity: (self: World, id: Entity?) -> Entity,
 | 
			
		||||
	--- Creates a new entity located in the first 256 ids.
 | 
			
		||||
	--- These should be used for static components for fast access.
 | 
			
		||||
	component: <T>(self: World) -> Entity<T>,
 | 
			
		||||
	--- Gets the target of an relationship. For example, when a user calls
 | 
			
		||||
	--- `world:target(id, ChildOf(parent), 0)`, you will obtain the parent entity.
 | 
			
		||||
	target: <T, U>(self: World, id: Entity<T>, relation: Entity<U>, index: number?) -> Entity?,
 | 
			
		||||
	target: (self: World, id: Entity, relation: Id, index: number?) -> Entity?,
 | 
			
		||||
	--- Deletes an entity and all it's related components and relationships.
 | 
			
		||||
	delete: <T>(self: World, id: Entity<T>) -> (),
 | 
			
		||||
	delete: (self: World, id: Entity) -> (),
 | 
			
		||||
 | 
			
		||||
	--- Adds a component to the entity with no value
 | 
			
		||||
	add: <T, U>(self: World, id: Entity<T>, component: Id<U>) -> (),
 | 
			
		||||
	add: <T>(self: World, id: Entity, component: Id) -> (),
 | 
			
		||||
	--- Assigns a value to a component on the given entity
 | 
			
		||||
	set: <T, U>(self: World, id: Entity<T>, component: Id<U>, data: U) -> (),
 | 
			
		||||
	set: <T>(self: World, id: Entity, component: Id<T>, data: T) -> (),
 | 
			
		||||
 | 
			
		||||
	cleanup: (self: World) -> (),
 | 
			
		||||
	-- Clears an entity from the world
 | 
			
		||||
	clear: <T>(self: World, id: Entity<T>) -> (),
 | 
			
		||||
	clear: (self: World, id: Entity) -> (),
 | 
			
		||||
	--- Removes a component from the given entity
 | 
			
		||||
	remove: <T, U>(self: World, id: Entity<T>, component: Id<U>) -> (),
 | 
			
		||||
	remove: (self: World, id: Entity, component: Id) -> (),
 | 
			
		||||
	--- Retrieves the value of up to 4 components. These values may be nil.
 | 
			
		||||
	get: (<T, A>(self: World, id: Entity<T>, Id<A>) -> A?)
 | 
			
		||||
		& (<T, A, B>(self: World, id: Entity<T>, Id<A>, Id<B>) -> (A?, B?))
 | 
			
		||||
		& (<T, A, B, C>(self: World, id: Entity<T>, Id<A>, Id<B>, Id<C>) -> (A?, B?, C?))
 | 
			
		||||
		& <T, A, B, C, D>(self: World, id: Entity<T>, Id<A>, Id<B>, Id<C>, Id<D>) -> (A?, B?, C?, D?),
 | 
			
		||||
	get: (<A>(self: World, id: Entity, Id<A>) -> A?)
 | 
			
		||||
		& (<A, B>(self: World, id: Entity, Id<A>, Id<B>) -> (A?, B?))
 | 
			
		||||
		& (<A, B, C>(self: World, id: Entity, Id<A>, Id<B>, Id<C>) -> (A?, B?, C?))
 | 
			
		||||
		& <A, B, C, D>(self: World, id: Entity, Id<A>, Id<B>, Id<C>, Id<D>) -> (A?, B?, C?, D?),
 | 
			
		||||
 | 
			
		||||
	--- Returns whether the entity has the ID.
 | 
			
		||||
	has: (<T, U>(self: World, entity: Entity<T>, ...Id<U>) -> boolean)
 | 
			
		||||
		& (<T, U, V>(self: World, entity: Entity<T>, Id<U>, Id<V>) -> boolean)
 | 
			
		||||
		& (<T, U, V, W>(self: World, entity: Entity<T>, Id<U>, Id<V>, Id<W>) -> boolean)
 | 
			
		||||
		& (<T, U, V, W, X>(self: World, entity: Entity<T>, Id<U>, Id<V>, Id<W>, Id<X>) -> boolean)
 | 
			
		||||
		& (<T, U, V, W, X, Y>(self: World, entity: Entity<T>, Id<U>, Id<V>, Id<W>, Id<X>, Id<Y>) -> boolean)
 | 
			
		||||
		& (<T, U, V, W, X, Y, Z>(self: World, entity: Entity<T>, Id<U>, Id<V>, Id<W>, Id<X>, Id<Y>, Id<Z>) -> boolean)
 | 
			
		||||
		& (<T, U, V, W, X, Y, Z, A>(self: World, entity: Entity<T>, Id<U>, Id<V>, Id<W>, Id<X>, Id<Y>, Id<Z>, Id<A>) -> boolean)
 | 
			
		||||
		& (<T, U, V, W, X, Y, Z, A>(self: World, entity: Entity<T>, Id<U>, Id<V>, Id<W>, Id<X>, Id<Y>, Id<Z>, Id<A>, ...unknown) -> boolean),
 | 
			
		||||
	has: (self: World, entity: Entity, ...Id) -> boolean,
 | 
			
		||||
 | 
			
		||||
	--- Get parent (target of ChildOf relationship) for entity. If there is no ChildOf relationship pair, it will return nil.
 | 
			
		||||
	parent: <T>(self: World, entity: Entity<T>) -> Entity,
 | 
			
		||||
	parent:(self: World, entity: Entity) -> Entity,
 | 
			
		||||
 | 
			
		||||
	--- Checks if the world contains the given entity
 | 
			
		||||
	contains: <T>(self: World, entity: Entity<T>) -> 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
 | 
			
		||||
	query: (<A>(World, Id<A>) -> Query<A>)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2765
									
								
								mirror.luau
									
									
									
									
									
								
							
							
						
						
									
										2765
									
								
								mirror.luau
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -124,15 +124,15 @@ TEST("#repro", function()
 | 
			
		|||
		local tgts = {}
 | 
			
		||||
		local pairwildcard = pair(relation, jecs.Wildcard)
 | 
			
		||||
		for _, archetype in world:query(pairwildcard):archetypes() do
 | 
			
		||||
		local tr = archetype.records[pairwildcard]
 | 
			
		||||
		local count = archetype.counts[pairwildcard]
 | 
			
		||||
		local types = archetype.types
 | 
			
		||||
		for _, entity in archetype.entities do
 | 
			
		||||
		    for i = 0, count - 1 do
 | 
			
		||||
		    local tgt = jecs.pair_second(world, types[i + tr])
 | 
			
		||||
		    table.insert(tgts, tgt)
 | 
			
		||||
		    end
 | 
			
		||||
		end
 | 
			
		||||
			local tr = archetype.records[pairwildcard]
 | 
			
		||||
			local count = archetype.counts[pairwildcard]
 | 
			
		||||
			local types = archetype.types
 | 
			
		||||
			for _, entity in archetype.entities do
 | 
			
		||||
			    for i = 0, count - 1 do
 | 
			
		||||
				    local tgt = jecs.pair_second(world, types[i + tr])
 | 
			
		||||
				    table.insert(tgts, tgt)
 | 
			
		||||
			    end
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
		return tgts
 | 
			
		||||
	end
 | 
			
		||||
| 
						 | 
				
			
			@ -148,22 +148,24 @@ TEST("#repro", function()
 | 
			
		|||
	local e1 = world:entity()
 | 
			
		||||
	local e2 = world:entity()
 | 
			
		||||
	local e3 = world:entity()
 | 
			
		||||
	print(e1, e2, e3)
 | 
			
		||||
	setAttacksAndEats(e3, e1)
 | 
			
		||||
	setAttacksAndEats(e3, e2)
 | 
			
		||||
	setAttacksAndEats(e1, e2)
 | 
			
		||||
	print("---------------- delete e2 ---------------")
 | 
			
		||||
	local d = dwi(world)
 | 
			
		||||
	for archetype_id in world.component_index[pair(jecs.Wildcard, e2)].cache do
 | 
			
		||||
		local archetype = world.archetypes[archetype_id].type
 | 
			
		||||
		testkit.print(archetype)
 | 
			
		||||
	end
 | 
			
		||||
	world:delete(e2)
 | 
			
		||||
	print("-----------------------------")
 | 
			
		||||
	testkit.print(d.tbl(e1).types)
 | 
			
		||||
	-- testkit.print(d.tbl(e3).types)
 | 
			
		||||
	-- testkit.print(getTargets(Attacks))
 | 
			
		||||
	-- testkit.print(getTargets(Eats))
 | 
			
		||||
	local types1 = { pair(Attacks, e1), pair(Eats, e1) }
 | 
			
		||||
	table.sort(types1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	CHECK(d.tbl(e1).type == "")
 | 
			
		||||
	CHECK(d.tbl(e3).type == table.concat(types1, "_"))
 | 
			
		||||
 | 
			
		||||
	for _, entity in getTargets(Attacks) do
 | 
			
		||||
		CHECK(entity == e1)
 | 
			
		||||
	end
 | 
			
		||||
	for _, entity in getTargets(Eats) do
 | 
			
		||||
		CHECK(entity == e1)
 | 
			
		||||
	end
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
TEST("archetype", function()
 | 
			
		||||
| 
						 | 
				
			
			@ -235,6 +237,9 @@ TEST("world:cleanup()", function()
 | 
			
		|||
	CHECK(#archetype_index["1_2_3"].entities == 1)
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
local pe = require("@tools/entity_visualiser").prettify
 | 
			
		||||
local lifetime_tracker_add = require("@tools/lifetime_tracker")
 | 
			
		||||
 | 
			
		||||
TEST("world:entity()", function()
 | 
			
		||||
	do
 | 
			
		||||
		CASE("unique IDs")
 | 
			
		||||
| 
						 | 
				
			
			@ -1370,10 +1375,15 @@ TEST("world:target", function()
 | 
			
		|||
		world:add(e, pair(C, D))
 | 
			
		||||
 | 
			
		||||
		CHECK(pair(A, B) < pair(A, C))
 | 
			
		||||
		CHECK(pair(A, E) < pair(B, C))
 | 
			
		||||
		CHECK(pair(A, C) < pair(A, D))
 | 
			
		||||
		CHECK(pair(C, A) < pair(C, D))
 | 
			
		||||
 | 
			
		||||
		local records = debug_world_inspect(world).records(e)
 | 
			
		||||
		CHECK(jecs.pair_first(world, pair(B, C)) == B)
 | 
			
		||||
		local r = jecs.entity_index_try_get(world.entity_index, e)
 | 
			
		||||
		local archetype = r.archetype
 | 
			
		||||
		local counts = archetype.counts
 | 
			
		||||
		CHECK(counts[pair(A, __)] == 4)
 | 
			
		||||
		CHECK(records[pair(B, C)] > records[pair(A, E)])
 | 
			
		||||
		CHECK(world:target(e, A, 0) == B)
 | 
			
		||||
		CHECK(world:target(e, A, 1) == C)
 | 
			
		||||
| 
						 | 
				
			
			@ -1383,6 +1393,28 @@ TEST("world:target", function()
 | 
			
		|||
		CHECK(world:target(e, B, 1) == D)
 | 
			
		||||
		CHECK(world:target(e, C, 0) == D)
 | 
			
		||||
		CHECK(world:target(e, C, 1) == nil)
 | 
			
		||||
 | 
			
		||||
		-- for id in archetype.records do
 | 
			
		||||
		-- 	local f = world:get(ecs_pair_first(world, id), jecs.Name)
 | 
			
		||||
		-- 	local s = world:get(ecs_pair_second(world, id), jecs.Name)
 | 
			
		||||
		-- 	print(`({f}, {s})`)
 | 
			
		||||
		-- end
 | 
			
		||||
		--
 | 
			
		||||
 | 
			
		||||
		CHECK(archetype.records[pair(A, B)] == 1)
 | 
			
		||||
		CHECK(archetype.records[pair(A, C)] == 2)
 | 
			
		||||
		CHECK(archetype.records[pair(A, D)] == 3)
 | 
			
		||||
		CHECK(archetype.records[pair(A, E)] == 4)
 | 
			
		||||
		-- print("(A, B)", archetype.records[pair(A, B)])
 | 
			
		||||
		-- print("(A, C)", archetype.records[pair(A, C)])
 | 
			
		||||
		-- print("(A, D)", archetype.records[pair(A, D)])
 | 
			
		||||
		-- print("(A, E)", archetype.records[pair(A, E)])
 | 
			
		||||
 | 
			
		||||
		-- print(pair(A, D), pair(B, C))
 | 
			
		||||
		-- print("(B, C)", archetype.records[pair(B, C)])
 | 
			
		||||
 | 
			
		||||
		CHECK(world:target(e, C, 0) == D)
 | 
			
		||||
		CHECK(world:target(e, C, 1) == nil)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	do
 | 
			
		||||
| 
						 | 
				
			
			@ -1498,7 +1530,7 @@ TEST("Hooks", function()
 | 
			
		|||
			world:set(e, A, true)
 | 
			
		||||
			world:remove(e, A)
 | 
			
		||||
			CHECK(not world:get(e, A))
 | 
			
		||||
			CHECK(not world:get(e, B))
 | 
			
		||||
			CHECK(world:get(e, B))
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
end)
 | 
			
		||||
| 
						 | 
				
			
			@ -1566,18 +1598,18 @@ TEST("repro", function()
 | 
			
		|||
	do CASE "#1"
 | 
			
		||||
		local world = world_new()
 | 
			
		||||
		local reproEntity = world:component()
 | 
			
		||||
		local components = { Cooldown = world:component() :: jecs.Id<number> }
 | 
			
		||||
		local components = { Cooldown = world:component() :: jecs.Entity<number> }
 | 
			
		||||
		world:set(reproEntity, components.Cooldown, 2)
 | 
			
		||||
 | 
			
		||||
		local function updateCooldowns(dt: number)
 | 
			
		||||
			local toRemove = {}
 | 
			
		||||
 | 
			
		||||
			for id, cooldown in world:query(components.Cooldown):iter() do
 | 
			
		||||
			local it = world:query(components.Cooldown):iter()
 | 
			
		||||
			for id, cooldown in it do
 | 
			
		||||
				cooldown -= dt
 | 
			
		||||
 | 
			
		||||
				if cooldown <= 0 then
 | 
			
		||||
					table.insert(toRemove, id)
 | 
			
		||||
					print("removing")
 | 
			
		||||
					-- world:remove(id, components.Cooldown)
 | 
			
		||||
				else
 | 
			
		||||
					world:set(id, components.Cooldown, cooldown)
 | 
			
		||||
| 
						 | 
				
			
			@ -1626,6 +1658,10 @@ TEST("wildcard query", function()
 | 
			
		|||
 | 
			
		||||
		local entity = world:entity()
 | 
			
		||||
 | 
			
		||||
		local p = pair(Relation, A)
 | 
			
		||||
		CHECK(jecs.pair_first(world, p) == Relation)
 | 
			
		||||
		CHECK(jecs.pair_second(world, p) == A)
 | 
			
		||||
		local w = dwi(world)
 | 
			
		||||
		world:add(entity, pair(Relation, A))
 | 
			
		||||
 | 
			
		||||
		local counter = 0
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,7 +45,10 @@ local function lifetime_tracker_add(world: jecs.World, opt)
 | 
			
		|||
	padding_enabled = opt.padding_enabled
 | 
			
		||||
 | 
			
		||||
	local world_entity = world.entity
 | 
			
		||||
	w.entity = function(self)
 | 
			
		||||
	w.entity = function(self, entity)
 | 
			
		||||
		if entity then
 | 
			
		||||
			return world_entity(world, entity)
 | 
			
		||||
		end
 | 
			
		||||
		local will_recycle = entity_index.max_id ~= entity_index.alive_count
 | 
			
		||||
		local e = world_entity(world)
 | 
			
		||||
		if will_recycle then
 | 
			
		||||
| 
						 | 
				
			
			@ -108,7 +111,7 @@ local function lifetime_tracker_add(world: jecs.World, opt)
 | 
			
		|||
				local entity = dense_array[i]
 | 
			
		||||
				local id = ECS_ID(entity)
 | 
			
		||||
				local status = "alive"
 | 
			
		||||
				if id > alive_count then
 | 
			
		||||
				if not world:contains(entity) then
 | 
			
		||||
					status = "dead"
 | 
			
		||||
				end
 | 
			
		||||
				data[id] = status
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue