Add entity visualiser
Some checks are pending
analysis / Run Luau Analyze (push) Waiting to run
deploy-docs / build (push) Waiting to run
deploy-docs / Deploy (push) Blocked by required conditions
publish-npm / publish (push) Waiting to run
unit-testing / Run Luau Tests (push) Waiting to run

This commit is contained in:
Ukendio 2025-03-12 16:30:19 +01:00
parent d15266b6d5
commit de8e263828
6 changed files with 123 additions and 72 deletions

View file

@ -1,9 +1,9 @@
{ {
"aliases": { "aliases": {
"jecs": "jecs", "jecs": "jecs",
"testkit": "test/testkit", "testkit": "tools/testkit",
"mirror": "mirror", "mirror": "mirror",
"tools": "tools" "tools": "tools",
}, },
"languageMode": "strict" "languageMode": "strict"
} }

View file

@ -1,4 +1,6 @@
local jecs = require("@jecs") local jecs = require("@jecs")
local pair = jecs.pair
local ChildOf = jecs.ChildOf
local lifetime_tracker_add = require("@tools/lifetime_tracker") local lifetime_tracker_add = require("@tools/lifetime_tracker")
local world = lifetime_tracker_add(jecs.world()) local world = lifetime_tracker_add(jecs.world())
world:print_snapshot() world:print_snapshot()
@ -8,13 +10,15 @@ world:delete(e)
world:print_snapshot() world:print_snapshot()
local e2 = world:entity() local e2 = world:entity()
world:add(e2, pair(ChildOf, e1))
local e3 = world:entity() local e3 = world:entity()
world:add(e3, pair(ChildOf, e1))
world:print_snapshot() world:print_snapshot()
world:delete(e1) world:delete(e1)
world:delete(e2) world:delete(e2)
world:delete(e3) world:delete(e3)
world:print_snapshot() world:print_snapshot()
world:print_entities() world:print_entity_index()
world:entity() world:entity()
world:entity() world:entity()
world:print_snapshot() world:print_snapshot()

33
tools/ansi.luau Normal file
View file

@ -0,0 +1,33 @@
return {
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,
}

View file

@ -0,0 +1,50 @@
local jecs = require("@jecs")
local ECS_GENERATION = jecs.ECS_GENERATION
local ECS_ID = jecs.ECS_ID
local ansi = require("@tools/ansi")
local function pe(e: any)
local gen = ECS_GENERATION(e)
return ansi.green(`e{ECS_ID(e)}`)..ansi.yellow(`v{gen}`)
end
local function name(world: jecs.World, id: any)
return world:get(id, jecs.Name) or `${id}`
end
local function components(world: jecs.World, entity: any)
local r = jecs.entity_index_try_get(world.entity_index, entity)
if not r then
return false
end
local archetype = r.archetype
local row = r.row
print(`Entity {pe(entity)}`)
print("-----------------------------------------------------")
for i, column in archetype.columns do
local component = archetype.types[i]
local n
if jecs.IS_PAIR(component) then
n = `({name(world, jecs.pair_first(world, component))}, {name(world, jecs.pair_second(world, component))})`
else
n = name(world, component)
end
local data = column[row] or "TAG"
print(`| {n} | {data} |`)
end
print("-----------------------------------------------------")
return true
end
local world = jecs.world()
local A = world:component()
world:set(A, jecs.Name, "A")
local e = world:entity()
world:set(e, A, true)
components(world, e)
return {
components = components,
prettify = pe
}

View file

@ -4,70 +4,19 @@ local ECS_ID = jecs.ECS_ID
local __ = jecs.Wildcard local __ = jecs.Wildcard
local pair = jecs.pair local pair = jecs.pair
local testkit = require("@testkit") local pe = require("@tools/entity_visualiser").prettify
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) function print_centered_entity(entity, width: number)
local entity_str = tostring(entity) local entity_str = tostring(entity)
local entity_length = #entity_str local entity_length = #entity_str
-- Calculate total padding needed to center the string local padding_total = width - 2 - entity_length
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_left = math.floor(padding_total / 2)
local padding_right = padding_total - padding_left local padding_right = padding_total - padding_left
-- Build the centered string
local centered_str = string.rep(" ", padding_left) .. entity_str .. string.rep(" ", padding_right) local centered_str = string.rep(" ", padding_left) .. entity_str .. string.rep(" ", padding_right)
-- Print with pipes around the centered string
print("|" .. centered_str .. "|") print("|" .. centered_str .. "|")
end end
@ -82,36 +31,51 @@ local function lifetime_tracker_add(world: jecs.World)
local w = setmetatable({}, { __index = world }) local w = setmetatable({}, { __index = world })
w.delete = function(self, e) w.delete = function(self, e)
print("Entity deleted:", e) print(`*deleting {pe(e)}`)
for child in world:each(pair(__, e)) do
local idr_t = component_index[pair(__, e)]
if idr_t then
print(`{pe(e)} has the following dependencies:`)
for archetype_id in idr_t.cache do
local archetype = world.archetypes[archetype_id]
local entities = {}
for _, dependency in archetype.entities do
print(` {pe(dependency)}`)
end
end
end end
return world_delete(world, e)
world_delete(world, e)
print(`*deleted {pe(e)}`)
end end
w.entity = function(self) w.entity = function(self)
local e = world_entity(world) local e = world_entity(world)
print("Entity created:", pe(e)) print(`*created {pe(e)}`)
return e return e
end end
w.print_entities = function(self) w.print_entity_index = function(self)
local max_id = entity_index.max_id local max_id = entity_index.max_id
local alive_count = entity_index.alive_count local alive_count = entity_index.alive_count
local alive = table.move(dense_array, 1+jecs.Rest::any, alive_count, 1, {}) 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 dead = table.move(dense_array, alive_count + 1, max_id, 1, {})
local sep = "|--------|" local sep = "|--------|"
print("|-alive--|") if #alive > 0 then
for i = 1, #alive do print("|-alive--|")
local e = pe(alive[i]) for i = 1, #alive do
print_centered_entity(e, 32) local e = pe(alive[i])
print(sep) print_centered_entity(e, 32)
print(sep)
end
print("\n")
end end
print("\n")
print("|--dead--|") if #dead > 0 then
for i = 1, #dead do print("|--dead--|")
print_centered_entity(pe(dead[i]), 32) for i = 1, #dead do
print(sep) print_centered_entity(pe(dead[i]), 32)
print(sep)
end
end end
end end
local timelines = {} local timelines = {}