mirror of
				https://github.com/Ukendio/jecs.git
				synced 2025-11-04 02:49:18 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			219 lines
		
	
	
	
		
			5.8 KiB
		
	
	
	
		
			Text
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			219 lines
		
	
	
	
		
			5.8 KiB
		
	
	
	
		
			Text
		
	
	
		
			Executable file
		
	
	
	
	
local jecs = require("@jecs")
 | 
						|
local ECS_GENERATION = jecs.ECS_GENERATION
 | 
						|
local ECS_ID = jecs.ECS_ID
 | 
						|
local __ = jecs.Wildcard
 | 
						|
local pair = jecs.pair
 | 
						|
 | 
						|
local prettify = require("@tools/entity_visualiser").prettify
 | 
						|
 | 
						|
local pe = prettify
 | 
						|
 | 
						|
function print_centered_entity(entity, width: number)
 | 
						|
	local entity_str = tostring(entity)
 | 
						|
	local entity_length = #entity_str
 | 
						|
 | 
						|
	local padding_total = width - 2 - entity_length
 | 
						|
 | 
						|
	local padding_left = math.floor(padding_total / 2)
 | 
						|
	local padding_right = padding_total - padding_left
 | 
						|
 | 
						|
	local centered_str = string.rep(" ", padding_left) .. entity_str .. string.rep(" ", padding_right)
 | 
						|
 | 
						|
	print("|" .. centered_str .. "|")
 | 
						|
end
 | 
						|
 | 
						|
local function name(world, e)
 | 
						|
	return world:get(world, e, jecs.Name) or pe(e)
 | 
						|
end
 | 
						|
local padding_enabled = false
 | 
						|
local function pad()
 | 
						|
	if padding_enabled then
 | 
						|
		print("------------------")
 | 
						|
	end
 | 
						|
end
 | 
						|
 | 
						|
type PatchedWorld = jecs.World & {
 | 
						|
	print_entity_index: (world: PatchedWorld) -> (),
 | 
						|
	print_snapshot: (world: PatchedWorld) -> (),
 | 
						|
}
 | 
						|
 | 
						|
local function lifetime_tracker_add(world: jecs.World, opt): PatchedWorld
 | 
						|
	local entity_index = world.entity_index
 | 
						|
	local dense_array = entity_index.dense_array
 | 
						|
	local component_index = world.component_index
 | 
						|
 | 
						|
	local ENTITY_RANGE = (jecs.Rest :: any) + 1
 | 
						|
 | 
						|
	padding_enabled = opt.padding_enabled
 | 
						|
 | 
						|
	local world_entity = world.entity
 | 
						|
	world.entity = function(_, 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
 | 
						|
			print(`*recycled {pe(e)}`)
 | 
						|
		else
 | 
						|
			print(`*created {pe(e)}`)
 | 
						|
		end
 | 
						|
		pad()
 | 
						|
		return e
 | 
						|
	end
 | 
						|
	world.print_entity_index = function()
 | 
						|
		local max_id = entity_index.max_id
 | 
						|
		local alive_count = entity_index.alive_count
 | 
						|
		local range_begin = entity_index.range_begin or jecs.Rest + 1
 | 
						|
		local alive = table.move(dense_array, range_begin :: any, alive_count, 1, {})
 | 
						|
		local dead = table.move(dense_array, alive_count + 1, max_id, 1, {})
 | 
						|
 | 
						|
		local sep = "|--------|"
 | 
						|
		if #alive > 0 then
 | 
						|
			print("|-alive--|")
 | 
						|
			for i = 1, #alive do
 | 
						|
				local e = pe(alive[i])
 | 
						|
				print_centered_entity(e, 32)
 | 
						|
				print(sep)
 | 
						|
			end
 | 
						|
			print("\n")
 | 
						|
		end
 | 
						|
 | 
						|
		if #dead > 0 then
 | 
						|
			print("|--dead--|")
 | 
						|
			for i = 1, #dead do
 | 
						|
				print_centered_entity(pe(dead[i]), 32)
 | 
						|
				print(sep)
 | 
						|
			end
 | 
						|
		end
 | 
						|
		pad()
 | 
						|
	end
 | 
						|
	local timelines = {}
 | 
						|
	world.print_snapshot = function(_)
 | 
						|
		local timeline = #timelines + 1
 | 
						|
		local entity_column_width = 10
 | 
						|
		local status_column_width = 8
 | 
						|
 | 
						|
		local header = string.format("| %-" .. entity_column_width .. "s |", "Entity")
 | 
						|
		for i = 1, timeline do
 | 
						|
			header = header .. string.format(" %-" .. status_column_width .. "s |", string.format("T%d", i))
 | 
						|
		end
 | 
						|
 | 
						|
		local max_id = entity_index.max_id
 | 
						|
		local alive_count = entity_index.alive_count
 | 
						|
		local alive = table.move(dense_array, 1 + jecs.Rest :: any, alive_count, 1, {})
 | 
						|
		local dead = table.move(dense_array, alive_count + 1, max_id, 1, {})
 | 
						|
 | 
						|
		local data = {}
 | 
						|
		print("-------------------------------------------------------------------")
 | 
						|
		print(header)
 | 
						|
 | 
						|
		-- Store the snapshot data for this timeline
 | 
						|
		for i = ENTITY_RANGE, max_id do
 | 
						|
			if dense_array[i] then
 | 
						|
				local entity = dense_array[i]
 | 
						|
				local id = ECS_ID(entity)
 | 
						|
				local status = "alive"
 | 
						|
				if not world:contains(entity) then
 | 
						|
					status = "dead"
 | 
						|
				end
 | 
						|
				data[id] = status
 | 
						|
			end
 | 
						|
		end
 | 
						|
 | 
						|
		table.insert(timelines, data)
 | 
						|
 | 
						|
		-- Create a table to hold entity data for sorting
 | 
						|
		local entities = {}
 | 
						|
		for i = ENTITY_RANGE, max_id do
 | 
						|
			if dense_array[i] then
 | 
						|
				local entity = dense_array[i]
 | 
						|
				local id = ECS_ID(entity)
 | 
						|
				-- Push entity and id into the new `entities` table
 | 
						|
				table.insert(entities, { entity = entity, id = id })
 | 
						|
			end
 | 
						|
		end
 | 
						|
 | 
						|
		-- Sort the entities by ECS_ID
 | 
						|
		table.sort(entities, function(a, b)
 | 
						|
			return a.id < b.id
 | 
						|
		end)
 | 
						|
 | 
						|
		-- Print the sorted rows
 | 
						|
		for _, entity_data in ipairs(entities) do
 | 
						|
			local entity = entity_data.entity
 | 
						|
			local id = entity_data.id
 | 
						|
			local status = "alive"
 | 
						|
			if id > alive_count then
 | 
						|
				status = "dead"
 | 
						|
			end
 | 
						|
			local row = string.format("| %-" .. entity_column_width .. "s     |", pe(entity))
 | 
						|
			for j = 1, timeline do
 | 
						|
				local timeline_data = timelines[j]
 | 
						|
				local entity_data = timeline_data[id]
 | 
						|
				if entity_data then
 | 
						|
					row = row .. string.format(" %-" .. status_column_width .. "s |", entity_data)
 | 
						|
				else
 | 
						|
					row = row .. string.format(" %-" .. status_column_width .. "s |", "-")
 | 
						|
				end
 | 
						|
			end
 | 
						|
			print(row)
 | 
						|
		end
 | 
						|
		print("-------------------------------------------------------------------")
 | 
						|
		pad()
 | 
						|
	end
 | 
						|
	local world_add = world.add
 | 
						|
	local relations = {}
 | 
						|
	world.add = function(_, entity: any, component: any)
 | 
						|
		world_add(world, entity, component)
 | 
						|
		if jecs.IS_PAIR(component) then
 | 
						|
			local relation = jecs.pair_first(world, component)
 | 
						|
			local target = jecs.pair_second(world, component)
 | 
						|
			print(`*added ({pe(relation)}, {pe(target)}) to {pe(entity)}`)
 | 
						|
			pad()
 | 
						|
		end
 | 
						|
	end
 | 
						|
 | 
						|
	local world_delete = world.delete
 | 
						|
	world.delete = function(world, e)
 | 
						|
		world_delete(world, e)
 | 
						|
 | 
						|
		local idr_t = component_index[pair(__, e)]
 | 
						|
		if idr_t then
 | 
						|
			for archetype_id in idr_t.cache do
 | 
						|
				local archetype = world.archetypes[archetype_id]
 | 
						|
				for _, id in archetype.types do
 | 
						|
					if not jecs.IS_PAIR(id) then
 | 
						|
						continue
 | 
						|
					end
 | 
						|
					local object = jecs.pair_second(world, id)
 | 
						|
					if object ~= e then
 | 
						|
						continue
 | 
						|
					end
 | 
						|
					local id_record = component_index[id]
 | 
						|
					local flags = id_record.flags
 | 
						|
					local flags_delete_mask: number = bit32.band(flags, jecs.ECS_ID_DELETE)
 | 
						|
					if flags_delete_mask ~= 0 then
 | 
						|
						for _, entity in archetype.entities do
 | 
						|
							print(`*deleted dependant {pe(entity)} of {pe(e)}`)
 | 
						|
							pad()
 | 
						|
						end
 | 
						|
						break
 | 
						|
					else
 | 
						|
						for _, entity in archetype.entities do
 | 
						|
							print(
 | 
						|
								`*removed dependency ({pe(jecs.pair_first(world, id))}, {pe(object)}) from {pe(entity)}`
 | 
						|
							)
 | 
						|
						end
 | 
						|
					end
 | 
						|
				end
 | 
						|
			end
 | 
						|
		end
 | 
						|
 | 
						|
		print(`*deleted {pe(e)}`)
 | 
						|
		pad()
 | 
						|
	end
 | 
						|
	return world
 | 
						|
end
 | 
						|
 | 
						|
return lifetime_tracker_add
 |