jecs/tools/lifetime_tracker.luau
2025-03-12 15:30:56 +01:00

193 lines
5.2 KiB
Text

local jecs = require("@jecs")
local ECS_GENERATION = jecs.ECS_GENERATION
local ECS_ID = jecs.ECS_ID
local __ = jecs.Wildcard
local pair = jecs.pair
local testkit = require("@testkit")
local BENCH, START = testkit.benchmark()
local it = testkit.test()
local TEST, CASE = it.TEST, it.CASE
local CHECK, FINISH = it.CHECK, it.FINISH
local SKIP, FOCUS = it.SKIP, it.FOCUS
local CHECK_EXPECT_ERR = it.CHECK_EXPECT_ERR
local c = {
white_underline = function(s: any)
return `\27[1;4m{s}\27[0m`
end,
white = function(s: any)
return `\27[37;1m{s}\27[0m`
end,
green = function(s: any)
return `\27[32;1m{s}\27[0m`
end,
red = function(s: any)
return `\27[31;1m{s}\27[0m`
end,
yellow = function(s: any)
return `\27[33;1m{s}\27[0m`
end,
red_highlight = function(s: any)
return `\27[41;1;30m{s}\27[0m`
end,
green_highlight = function(s: any)
return `\27[42;1;30m{s}\27[0m`
end,
gray = function(s: any)
return `\27[30;1m{s}\27[0m`
end,
}
local function pe(e: any)
local gen = ECS_GENERATION(e)
return c.green(`e{ECS_ID(e)}`)..c.yellow(`v{gen}`)
end
function print_centered_entity(entity, width: number)
local entity_str = tostring(entity)
local entity_length = #entity_str
-- Calculate total padding needed to center the string
local padding_total = width - 2 - entity_length -- Subtract 2 for the `| |` characters
-- Calculate padding for the left and right
local padding_left = math.floor(padding_total / 2)
local padding_right = padding_total - padding_left
-- Build the centered string
local centered_str = string.rep(" ", padding_left) .. entity_str .. string.rep(" ", padding_right)
-- Print with pipes around the centered string
print("|" .. centered_str .. "|")
end
local function lifetime_tracker_add(world: jecs.World)
local entity_index = world.entity_index
local dense_array = entity_index.dense_array
local world_delete = world.delete
local world_entity = world.entity
local component_index = world.component_index
local ENTITY_RANGE = (jecs.Rest :: any) + 1
local w = setmetatable({}, { __index = world })
w.delete = function(self, e)
print("Entity deleted:", e)
for child in world:each(pair(__, e)) do
end
return world_delete(world, e)
end
w.entity = function(self)
local e = world_entity(world)
print("Entity created:", pe(e))
return e
end
w.print_entities = function(self)
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 sep = "|--------|"
print("|-alive--|")
for i = 1, #alive do
local e = pe(alive[i])
print_centered_entity(e, 32)
print(sep)
end
print("\n")
print("|--dead--|")
for i = 1, #dead do
print_centered_entity(pe(dead[i]), 32)
print(sep)
end
end
local timelines = {}
w.print_snapshot = function(self)
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 id > alive_count 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("-------------------------------------------------------------------")
end
return w
end
return lifetime_tracker_add