mirror of
				https://github.com/Ukendio/jecs.git
				synced 2025-10-31 01:09:16 +00:00 
			
		
		
		
	Archetype deletion (#126)
* initial commit * cleanup edges * rename ptr to edge
This commit is contained in:
		
							parent
							
								
									ca00d4c0c1
								
							
						
					
					
						commit
						2ed869ba93
					
				
					 3 changed files with 300 additions and 105 deletions
				
			
		
							
								
								
									
										389
									
								
								src/init.luau
									
									
									
									
									
								
							
							
						
						
									
										389
									
								
								src/init.luau
									
									
									
									
									
								
							|  | @ -11,16 +11,30 @@ type ArchetypeId = number | ||||||
| 
 | 
 | ||||||
| type Column = { any } | type Column = { any } | ||||||
| 
 | 
 | ||||||
| type ArchetypeEdge = { | type Map<K, V> = {[K]: V} | ||||||
| 	add: Archetype, | 
 | ||||||
| 	remove: Archetype, | type GraphEdge = { | ||||||
|  |     from: Archetype, | ||||||
|  |     to: Archetype?, | ||||||
|  |     prev: GraphEdge?, | ||||||
|  |     next: GraphEdge?, | ||||||
|  |     id: number | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type GraphEdges = Map<i53, GraphEdge> | ||||||
|  | 
 | ||||||
|  | type GraphNode = { | ||||||
|  |     add: GraphEdges, | ||||||
|  | 	remove: GraphEdges, | ||||||
|  | 	add_ref: GraphEdge?, | ||||||
|  | 	remove_ref: GraphEdge? | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export type Archetype = { | export type Archetype = { | ||||||
| 	id: number, | 	id: number, | ||||||
| 	edges: { [i53]: ArchetypeEdge }, | 	node: GraphNode, | ||||||
| 	types: Ty, | 	types: Ty, | ||||||
| 	type: string | number, | 	type: string, | ||||||
| 	entities: { number }, | 	entities: { number }, | ||||||
| 	columns: { Column }, | 	columns: { Column }, | ||||||
| 	records: { ArchetypeRecord }, | 	records: { ArchetypeRecord }, | ||||||
|  | @ -28,27 +42,26 @@ export type Archetype = { | ||||||
| type Record = { | type Record = { | ||||||
| 	archetype: Archetype, | 	archetype: Archetype, | ||||||
| 	row: number, | 	row: number, | ||||||
| 	dense: i24, | 	dense: i24 | ||||||
| 	componentRecord: ArchetypeMap, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type EntityIndex = { dense: { [i24]: i53 }, sparse: { [i53]: Record } } | type EntityIndex = { | ||||||
|  |     dense: Map<i24, i53>, | ||||||
|  |     sparse: Map<i53, Record> | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| type ArchetypeRecord = { | type ArchetypeRecord = { | ||||||
| 	count: number, | 	count: number, | ||||||
| 	column: number, | 	column: number, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type ArchetypeMap = { | type IdRecord = { | ||||||
| 	cache: { ArchetypeRecord }, | 	cache: { ArchetypeRecord }, | ||||||
| 	flags: number, | 	flags: number, | ||||||
| 	first: ArchetypeMap, |  | ||||||
| 	second: ArchetypeMap, |  | ||||||
| 	parent: ArchetypeMap, |  | ||||||
| 	size: number, | 	size: number, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type ComponentIndex = { [i24]: ArchetypeMap } | type ComponentIndex = Map<i53, IdRecord> | ||||||
| 
 | 
 | ||||||
| type Archetypes = { [ArchetypeId]: Archetype } | type Archetypes = { [ArchetypeId]: Archetype } | ||||||
| 
 | 
 | ||||||
|  | @ -420,7 +433,7 @@ local function ECS_ID_IS_WILDCARD(e: i53): boolean | ||||||
| 	return first == EcsWildcard or second == EcsWildcard | 	return first == EcsWildcard or second == EcsWildcard | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| local function id_record_ensure(world: World, id: number): ArchetypeMap | local function id_record_ensure(world: World, id: number): IdRecord | ||||||
| 	local componentIndex = world.componentIndex | 	local componentIndex = world.componentIndex | ||||||
| 	local idr = componentIndex[id] | 	local idr = componentIndex[id] | ||||||
| 
 | 
 | ||||||
|  | @ -453,14 +466,20 @@ local function id_record_ensure(world: World, id: number): ArchetypeMap | ||||||
| 			size = 0, | 			size = 0, | ||||||
| 			cache = {}, | 			cache = {}, | ||||||
| 			flags = flags, | 			flags = flags, | ||||||
| 		} :: ArchetypeMap | 		} :: IdRecord | ||||||
| 		componentIndex[id] = idr | 		componentIndex[id] = idr | ||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
| 	return idr | 	return idr | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| local function archetype_append_to_records(idr: ArchetypeMap, archetype_id, records, id, index) | local function archetype_append_to_records( | ||||||
|  |     idr: IdRecord, | ||||||
|  |     archetype_id: number, | ||||||
|  |     records: Map<i53, ArchetypeRecord>, | ||||||
|  |     id: number, | ||||||
|  |     index: number | ||||||
|  | ) | ||||||
|     local tr = idr.cache[archetype_id] |     local tr = idr.cache[archetype_id] | ||||||
|     if not tr then |     if not tr then | ||||||
|         tr = { column = index, count = 1} |         tr = { column = index, count = 1} | ||||||
|  | @ -472,7 +491,7 @@ local function archetype_append_to_records(idr: ArchetypeMap, archetype_id, reco | ||||||
|     end |     end | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| local function archetype_create(world: World, types: { i24 }, prev: Archetype?): Archetype | local function archetype_create(world: World, types: { i24 }, prev: i53?): Archetype | ||||||
| 	local ty = hash(types) | 	local ty = hash(types) | ||||||
| 
 | 
 | ||||||
| 	local archetype_id = (world.nextArchetypeId :: number) + 1 | 	local archetype_id = (world.nextArchetypeId :: number) + 1 | ||||||
|  | @ -507,7 +526,7 @@ local function archetype_create(world: World, types: { i24 }, prev: Archetype?): | ||||||
| 
 | 
 | ||||||
| 	local archetype: Archetype = { | 	local archetype: Archetype = { | ||||||
| 		columns = columns, | 		columns = columns, | ||||||
| 		edges = {}, | 		node = { add = {}, remove = {} }, | ||||||
| 		entities = {}, | 		entities = {}, | ||||||
| 		id = archetype_id, | 		id = archetype_id, | ||||||
| 		records = records, | 		records = records, | ||||||
|  | @ -531,7 +550,7 @@ local function world_parent(world: World, entity: i53) | ||||||
| 	return world_target(world, entity, EcsChildOf, 0) | 	return world_target(world, entity, EcsChildOf, 0) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| local function archetype_ensure(world: World, types, prev): Archetype | local function archetype_ensure(world: World, types): Archetype | ||||||
| 	if #types < 1 then | 	if #types < 1 then | ||||||
| 		return world.ROOT_ARCHETYPE | 		return world.ROOT_ARCHETYPE | ||||||
| 	end | 	end | ||||||
|  | @ -542,7 +561,7 @@ local function archetype_ensure(world: World, types, prev): Archetype | ||||||
| 		return archetype | 		return archetype | ||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
| 	return archetype_create(world, types, prev) | 	return archetype_create(world, types) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| local function find_insert(types: { i53 }, toAdd: i53): number | local function find_insert(types: { i53 }, toAdd: i53): number | ||||||
|  | @ -572,32 +591,125 @@ local function find_archetype_with(world: World, node: Archetype, id: i53): Arch | ||||||
| 	end | 	end | ||||||
| 	table.insert(dst, at, id) | 	table.insert(dst, at, id) | ||||||
| 
 | 
 | ||||||
| 	return archetype_ensure(world, dst, node) | 	return archetype_ensure(world, dst) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| local function edge_ensure(archetype: Archetype, id: i53): ArchetypeEdge | local function find_archetype_without(world: World, node: Archetype, id: i53): Archetype | ||||||
| 	local edges = archetype.edges | 	local types = node.types | ||||||
| 	local edge = edges[id] | 	local at = table.find(types, id) | ||||||
| 	if not edge then | 	if at == nil then | ||||||
| 		edge = {} :: any | 		return node | ||||||
| 		edges[id] = edge |  | ||||||
| 	end | 	end | ||||||
| 	return edge | 
 | ||||||
|  | 	local dst = table.clone(types) | ||||||
|  | 	table.remove(dst, at) | ||||||
|  | 
 | ||||||
|  | 	return archetype_ensure(world, dst) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| local function archetype_traverse_add(world: World, id: i53, from: Archetype): Archetype | local function archetype_init_edge(archetype: Archetype, | ||||||
| 	from = from or world.ROOT_ARCHETYPE |     edge: GraphEdge, id: i53, to: Archetype) | ||||||
|  |     edge.from = archetype | ||||||
|  |     edge.to = to | ||||||
|  |     edge.id = id | ||||||
|  | end | ||||||
| 
 | 
 | ||||||
| 	local edge = edge_ensure(from, id) | local function archetype_ensure_edge(world, edges, id): GraphEdge | ||||||
| 	local add = edge.add |     local edge = edges[id] | ||||||
| 	if not add then |     if not edge then | ||||||
| 		-- Save an edge using the component ID to the archetype to allow |         edge = { | ||||||
| 		-- faster traversals to adjacent archetypes. |             from = nil :: any, | ||||||
| 		add = find_archetype_with(world, from, id) |             to = nil :: any, | ||||||
| 		edge.add = add :: never |             id = id, | ||||||
| 	end |             prev = nil, | ||||||
|  |             next = nil, | ||||||
|  |         } :: GraphEdge | ||||||
|  |         edges[id] = edge | ||||||
|  |     end | ||||||
| 
 | 
 | ||||||
| 	return add |     return edge | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local function init_edge_for_add(world, archetype, edge, id, to) | ||||||
|  |     archetype_init_edge(archetype, edge, id, to) | ||||||
|  |     archetype_ensure_edge(world, archetype.node.add, id) | ||||||
|  |     if archetype ~= to then | ||||||
|  |         local to_add_ref = to.node.add_ref | ||||||
|  |         edge.next = to_add_ref | ||||||
|  |         edge.prev = nil | ||||||
|  |         if to_add_ref then | ||||||
|  |             to_add_ref.prev = edge | ||||||
|  |         end | ||||||
|  |         to.node.add_ref = edge | ||||||
|  |     end | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local function init_edge_for_remove(world, archetype, edge, id, to) | ||||||
|  |     archetype_init_edge(archetype, edge, id, to) | ||||||
|  |     archetype_ensure_edge(world, archetype.node.remove, id) | ||||||
|  |     if archetype ~= to then | ||||||
|  |         local to_remove_ref = to.node.remove_ref | ||||||
|  |         local prev | ||||||
|  |         if to_remove_ref then | ||||||
|  |             prev = to_remove_ref.prev | ||||||
|  |             to_remove_ref.prev = edge | ||||||
|  |             edge.next = to_remove_ref | ||||||
|  |         else | ||||||
|  |             to.node.remove_ref = edge | ||||||
|  |             edge.next = nil | ||||||
|  |         end | ||||||
|  | 
 | ||||||
|  |         edge.prev = prev | ||||||
|  |         if prev then | ||||||
|  |             prev.next = edge | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local function create_edge_for_add(world: World, node: Archetype, | ||||||
|  |     edge: GraphEdge, id: i53): Archetype | ||||||
|  | 
 | ||||||
|  |     local to = find_archetype_with(world, node, id) | ||||||
|  |     init_edge_for_add(world, node, edge, id, to) | ||||||
|  |     return to | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local function create_edge_for_remove(world: World, node: Archetype, | ||||||
|  |     edge: GraphEdge, id: i53): Archetype | ||||||
|  | 
 | ||||||
|  |     local to = find_archetype_without(world, node, id) | ||||||
|  |     init_edge_for_remove(world, node, edge, id, to) | ||||||
|  |     return to | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | local function archetype_traverse_add(world: World, id: i53, | ||||||
|  |     from: Archetype): Archetype | ||||||
|  | 
 | ||||||
|  |     from = from or world.ROOT_ARCHETYPE | ||||||
|  |     local edge = archetype_ensure_edge( | ||||||
|  |         world, from.node.add, id) | ||||||
|  | 
 | ||||||
|  |     local to = edge.to | ||||||
|  |     if not to then | ||||||
|  |         to = create_edge_for_add(world, from, edge, id) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     return to :: Archetype | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local function archetype_traverse_remove(world: World, id: i53, from: Archetype): Archetype | ||||||
|  |     from = from or world.ROOT_ARCHETYPE | ||||||
|  | 
 | ||||||
|  |     local edge = archetype_ensure_edge( | ||||||
|  |         world, from.node.add, id) | ||||||
|  | 
 | ||||||
|  |     local to = edge.to | ||||||
|  |     if not to then | ||||||
|  |         to = create_edge_for_remove(world, from, edge, id) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     return to :: Archetype | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| local function invoke_hook(world: World, hook_id: number, id: i53, entity: i53, data: any?) | local function invoke_hook(world: World, hook_id: number, id: i53, entity: i53, data: any?) | ||||||
|  | @ -700,24 +812,6 @@ local function world_component(world: World): i53 | ||||||
| 	return id | 	return id | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| local function archetype_traverse_remove(world: World, id: i53, from: Archetype): Archetype |  | ||||||
| 	local edge = edge_ensure(from, id) |  | ||||||
| 
 |  | ||||||
| 	local remove = edge.remove |  | ||||||
| 	if not remove then |  | ||||||
| 		local to = table.clone(from.types) :: { i53 } |  | ||||||
| 		local at = table.find(to, id) |  | ||||||
| 		if not at then |  | ||||||
| 			return from |  | ||||||
| 		end |  | ||||||
| 		table.remove(to, at) |  | ||||||
| 		remove = archetype_ensure(world, to, from) |  | ||||||
| 		edge.remove = remove :: any |  | ||||||
| 	end |  | ||||||
| 
 |  | ||||||
| 	return remove |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| local function world_remove(world: World, entity: i53, id: i53) | local function world_remove(world: World, entity: i53, id: i53) | ||||||
| 	local entity_index = world.entityIndex | 	local entity_index = world.entityIndex | ||||||
| 	local record = entity_index.sparse[entity] | 	local record = entity_index.sparse[entity] | ||||||
|  | @ -755,30 +849,118 @@ local function world_clear(world: World, entity: i53) | ||||||
| 	entity_move(world.entityIndex, entity, record, ROOT_ARCHETYPE) | 	entity_move(world.entityIndex, entity, record, ROOT_ARCHETYPE) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| local world_delete: (world: World, entity: i53) -> () | local function archetype_fast_delete_last(columns: { Column }, | ||||||
|  |     column_count: number, types: { i53 }, entity: i53) | ||||||
|  | 
 | ||||||
|  |    	for i, column in columns do | ||||||
|  |         if column ~= NULL_ARRAY then | ||||||
|  |       		column[column_count] = nil | ||||||
|  |         end | ||||||
|  |    	end | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local function archetype_fast_delete(columns: { Column }, | ||||||
|  |     column_count: number, row, types, entity) | ||||||
|  | 
 | ||||||
|  |    	for i, column in columns do | ||||||
|  |         if column ~= NULL_ARRAY then | ||||||
|  |       		column[row] = column[column_count] | ||||||
|  |             column[column_count] = nil | ||||||
|  |         end | ||||||
|  |    	end | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local function archetype_disconnect_edge(edge: GraphEdge) | ||||||
|  |     local edge_next = edge.next | ||||||
|  |     local edge_prev = edge.prev | ||||||
|  |     if edge_next then | ||||||
|  |         edge_next.prev = edge_prev | ||||||
|  |     end | ||||||
|  |     if edge_prev then | ||||||
|  |         edge_prev.next = edge_next | ||||||
|  |     end | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local function archetype_remove_edge(edges: Map<i53, GraphEdge>, | ||||||
|  |     id: i53, edge: GraphEdge) | ||||||
|  | 
 | ||||||
|  |     archetype_disconnect_edge(edge) | ||||||
|  |     edges[id] = nil | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local function archetype_clear_edges(archetype: Archetype) | ||||||
|  |     local node = archetype.node | ||||||
|  |     local add = node.add | ||||||
|  |     local remove = node.remove | ||||||
|  |     for _, edge in add do | ||||||
|  |         archetype_disconnect_edge(edge) | ||||||
|  |     end | ||||||
|  |     for _, edge in remove do | ||||||
|  |         archetype_disconnect_edge(edge) | ||||||
|  |     end | ||||||
|  |     local node_add_ref = node.add_ref | ||||||
|  |     if node_add_ref then | ||||||
|  |         local current = node_add_ref.next | ||||||
|  |         while current do | ||||||
|  |             local edge = current | ||||||
|  |             current = current.next | ||||||
|  |             local node_add = edge.from.node.add | ||||||
|  |             if node_add then | ||||||
|  |                 archetype_remove_edge(node_add, edge.id, edge) | ||||||
|  |             end | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     local node_remove_ref = node.remove_ref | ||||||
|  |     if node_remove_ref then | ||||||
|  |         local current = node_remove_ref.prev | ||||||
|  |         while current do | ||||||
|  |             local edge = current | ||||||
|  |             current = current.prev | ||||||
|  |             local node_remove = edge.from.node.remove | ||||||
|  |             if node_remove then | ||||||
|  |                 archetype_remove_edge(node_remove, edge.id, edge) | ||||||
|  |             end | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     node.add = nil :: any | ||||||
|  |     node.remove = nil :: any | ||||||
|  |     node.add_ref = nil :: any | ||||||
|  |     node.remove_ref = nil :: any | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local function archetype_destroy(world: World, archetype: Archetype) | ||||||
|  |     local component_index = world.componentIndex | ||||||
|  |     archetype_clear_edges(archetype) | ||||||
|  |     local archetype_id = archetype.id | ||||||
|  |     world.archetypes[archetype_id] = nil | ||||||
|  |     world.archetypeIndex[archetype.type] = nil | ||||||
|  |     local records = archetype.records | ||||||
|  | 
 | ||||||
|  |     for id in records do | ||||||
|  |         local idr = component_index[id] | ||||||
|  |         idr.cache[archetype_id] = nil | ||||||
|  |         idr.size -= 1 | ||||||
|  |         records[id] = nil | ||||||
|  |         if idr.size == 0 then | ||||||
|  |             component_index[id] = nil | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local function world_cleanup(world) | ||||||
|  |     for _, archetype in world.archetypes do | ||||||
|  |         if #archetype.entities == 0 then | ||||||
|  |             archetype_destroy(world, archetype) | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local world_delete: (world: World, entity: i53, destruct: boolean?) -> () | ||||||
| do | do | ||||||
|     local function archetype_fast_delete_last(columns: { Column }, |  | ||||||
|         column_count: number, types: { i53 }, entity: i53) |  | ||||||
| 
 |  | ||||||
|     	for i, column in columns do |  | ||||||
|             if column ~= NULL_ARRAY then |  | ||||||
|           		column[column_count] = nil |  | ||||||
|             end |  | ||||||
|     	end |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     local function archetype_fast_delete(columns: { Column }, |  | ||||||
|         column_count: number, row, types, entity) |  | ||||||
| 
 |  | ||||||
|     	for i, column in columns do |  | ||||||
|             if column ~= NULL_ARRAY then |  | ||||||
|           		column[row] = column[column_count] |  | ||||||
|                 column[column_count] = nil |  | ||||||
|             end |  | ||||||
|     	end |  | ||||||
|     end |  | ||||||
|     local function archetype_delete(world: World, |     local function archetype_delete(world: World, | ||||||
|         archetype: Archetype, row: number) |         archetype: Archetype, row: number, destruct: boolean?) | ||||||
| 
 | 
 | ||||||
|     	local entityIndex = world.entityIndex |     	local entityIndex = world.entityIndex | ||||||
|     	local columns = archetype.columns |     	local columns = archetype.columns | ||||||
|  | @ -812,33 +994,31 @@ do | ||||||
|     	end |     	end | ||||||
| 
 | 
 | ||||||
|     	local component_index = world.componentIndex |     	local component_index = world.componentIndex | ||||||
|  | 
 | ||||||
|     	local archetypes = world.archetypes |     	local archetypes = world.archetypes | ||||||
| 
 | 
 | ||||||
|     	local idr = component_index[delete] |         local idr = component_index[delete] | ||||||
|     	if idr then |        	if idr then | ||||||
|     		local children = {} |       		local children = {} | ||||||
|     		for archetype_id in idr.cache do |       		for archetype_id in idr.cache do | ||||||
|     			local idr_archetype = archetypes[archetype_id] |      			local idr_archetype = archetypes[archetype_id] | ||||||
| 
 | 
 | ||||||
|     			for i, child in idr_archetype.entities do |      			for i, child in idr_archetype.entities do | ||||||
|     				table.insert(children, child) |     				table.insert(children, child) | ||||||
|     			end |      			end | ||||||
|     		end |       		end | ||||||
|     		local flags = idr.flags |       		local flags = idr.flags | ||||||
|     		if bit32.band(flags, ECS_ID_DELETE) ~= 0 then |       		if bit32.band(flags, ECS_ID_DELETE) ~= 0 then | ||||||
|     			for _, child in children do |      			for _, child in children do | ||||||
|     				-- Cascade deletion to children |     				-- Cascade deletion to children | ||||||
|     				world_delete(world, child) |     				world_delete(world, child) | ||||||
|     			end |      			end | ||||||
|     		else |       		else | ||||||
|     			for _, child in children do |      			for _, child in children do | ||||||
|     				world_remove(world, child, delete) |     				world_remove(world, child, delete) | ||||||
|     			end |      			end | ||||||
|     		end |       		end | ||||||
| 
 |        	end | ||||||
|     		component_index[delete] = nil |  | ||||||
|     	end |  | ||||||
| 
 |  | ||||||
|     	-- TODO: iterate each linked record. |     	-- TODO: iterate each linked record. | ||||||
|     	-- local r = ECS_PAIR(delete, EcsWildcard) |     	-- local r = ECS_PAIR(delete, EcsWildcard) | ||||||
|     	-- local idr_r = component_index[r] |     	-- local idr_r = component_index[r] | ||||||
|  | @ -890,7 +1070,7 @@ do | ||||||
|     					if bit32.band(flags, ECS_ID_DELETE) ~= 0 then |     					if bit32.band(flags, ECS_ID_DELETE) ~= 0 then | ||||||
|     						for _, child in children do |     						for _, child in children do | ||||||
|     							-- Cascade deletions of it has Delete as component trait |     							-- Cascade deletions of it has Delete as component trait | ||||||
|     							world_delete(world, child) |     							world_delete(world, child, destruct) | ||||||
|     						end |     						end | ||||||
|     					else |     					else | ||||||
|     						local object = ECS_ENTITY_T_LO(id) |     						local object = ECS_ENTITY_T_LO(id) | ||||||
|  | @ -903,11 +1083,10 @@ do | ||||||
|     				end |     				end | ||||||
|     			end |     			end | ||||||
|     		end |     		end | ||||||
|     		component_index[o] = nil |  | ||||||
|     	end |     	end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     function world_delete(world: World, entity: i53) |     function world_delete(world: World, entity: i53, destruct: boolean?) | ||||||
|     	local entityIndex = world.entityIndex |     	local entityIndex = world.entityIndex | ||||||
| 
 | 
 | ||||||
|     	local record = entityIndex.sparse[entity] |     	local record = entityIndex.sparse[entity] | ||||||
|  | @ -921,11 +1100,12 @@ do | ||||||
|     	if archetype then |     	if archetype then | ||||||
|     		-- In the future should have a destruct mode for |     		-- In the future should have a destruct mode for | ||||||
|     		-- deleting archetypes themselves. Maybe requires recycling |     		-- deleting archetypes themselves. Maybe requires recycling | ||||||
|     		archetype_delete(world, archetype, row) |     		archetype_delete(world, archetype, row, destruct) | ||||||
|     	end |     	end | ||||||
| 
 | 
 | ||||||
|     	record.archetype = nil :: any |     	record.archetype = nil :: any | ||||||
|     	entityIndex.sparse[entity] = nil |     	entityIndex.sparse[entity] = nil | ||||||
|  | 
 | ||||||
|     end |     end | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  | @ -1368,7 +1548,7 @@ local function world_query(world: World, ...) | ||||||
| 
 | 
 | ||||||
| 	local archetypes = world.archetypes | 	local archetypes = world.archetypes | ||||||
| 
 | 
 | ||||||
| 	local idr: ArchetypeMap | 	local idr: IdRecord | ||||||
| 	local componentIndex = world.componentIndex | 	local componentIndex = world.componentIndex | ||||||
| 
 | 
 | ||||||
| 	for _, id in ids do | 	for _, id in ids do | ||||||
|  | @ -1435,6 +1615,7 @@ World.has = world_has | ||||||
| World.target = world_target | World.target = world_target | ||||||
| World.parent = world_parent | World.parent = world_parent | ||||||
| World.contains = world_contains | World.contains = world_contains | ||||||
|  | World.cleanup = world_cleanup | ||||||
| 
 | 
 | ||||||
| if _G.__JECS_DEBUG then | if _G.__JECS_DEBUG then | ||||||
|     -- taken from https://github.com/centau/ecr/blob/main/src/ecr.luau |     -- taken from https://github.com/centau/ecr/blob/main/src/ecr.luau | ||||||
|  |  | ||||||
							
								
								
									
										14
									
								
								test/memory.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								test/memory.luau
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | ||||||
|  | local testkit = require("@testkit") | ||||||
|  | local jecs = require("@jecs") | ||||||
|  | 
 | ||||||
|  | local world = jecs.World.new() | ||||||
|  | 
 | ||||||
|  | local A = world:component() | ||||||
|  | local B = world:component() | ||||||
|  | 
 | ||||||
|  | local e = world:entity() | ||||||
|  | world:add(e, A) | ||||||
|  | world:add(e, B) | ||||||
|  | local archetype_id = world.archetypeIndex["1_2"].id | ||||||
|  | world:delete(e) | ||||||
|  | testkit.print(world) | ||||||
|  | @ -842,7 +842,7 @@ TEST("world:delete", function() | ||||||
| 		CHECK(world:get(id1, Health) == 50) | 		CHECK(world:get(id1, Health) == 50) | ||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
| 	do CASE "delete entities using another Entity as component" | 	do CASE "delete entities using another Entity as component with Delete cleanup action" | ||||||
|         local world = jecs.World.new() |         local world = jecs.World.new() | ||||||
| 
 | 
 | ||||||
| 		local Health = world:entity() | 		local Health = world:entity() | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue