mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-24 17:10:03 +00:00
Compare commits
9 commits
cdbc053792
...
0a4d608b44
Author | SHA1 | Date | |
---|---|---|---|
|
0a4d608b44 | ||
|
b26fc39fce | ||
|
0e4f40ced7 | ||
|
de8e263828 | ||
|
d15266b6d5 | ||
|
18019679d5 | ||
|
925864dd2b | ||
|
ac79638599 | ||
|
f7573e8824 |
11 changed files with 564 additions and 69 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -65,3 +65,7 @@ drafts/
|
|||
|
||||
# Luau tools
|
||||
profile.*
|
||||
|
||||
# Patch files
|
||||
|
||||
*.patch
|
||||
|
|
5
.luaurc
5
.luaurc
|
@ -1,8 +1,9 @@
|
|||
{
|
||||
"aliases": {
|
||||
"jecs": "jecs",
|
||||
"testkit": "test/testkit",
|
||||
"mirror": "mirror"
|
||||
"testkit": "tools/testkit",
|
||||
"mirror": "mirror",
|
||||
"tools": "tools",
|
||||
},
|
||||
"languageMode": "strict"
|
||||
}
|
||||
|
|
|
@ -5,41 +5,60 @@ 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 newWorld = Matter.World.new()
|
||||
local ecs = jecs.World.new()
|
||||
local mirror = require(ReplicatedStorage.mirror)
|
||||
local mcs = mirror.World.new()
|
||||
|
||||
local A, B = Matter.component(), Matter.component()
|
||||
local C, D = ecs:component(), ecs:component()
|
||||
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))
|
||||
|
||||
local registry2 = ecr.registry()
|
||||
|
||||
return {
|
||||
ParameterGenerator = function()
|
||||
local matter_entities = {}
|
||||
local jecs_entities = {}
|
||||
local entities = {
|
||||
matter = matter_entities,
|
||||
jecs = jecs_entities,
|
||||
}
|
||||
local j = ecs:entity()
|
||||
ecs:set(j, C1, true)
|
||||
local m = mcs:entity()
|
||||
mcs:set(m, E1, true)
|
||||
for i = 1, 1000 do
|
||||
table.insert(matter_entities, newWorld:spawn(A(), B()))
|
||||
local e = ecs:entity()
|
||||
ecs:set(e, C, {})
|
||||
ecs:set(e, D, {})
|
||||
table.insert(jecs_entities, e)
|
||||
local friend1 = ecs:entity()
|
||||
local friend2 = mcs:entity()
|
||||
|
||||
ecs:add(friend1, pair(C2, j))
|
||||
ecs:add(friend1, pair(C3, j))
|
||||
ecs:add(friend1, pair(C4, j))
|
||||
|
||||
mcs:add(friend2, pair(E2, m))
|
||||
mcs:add(friend2, pair(E3, m))
|
||||
mcs:add(friend2, pair(E4, m))
|
||||
end
|
||||
return entities
|
||||
return {
|
||||
m = m,
|
||||
j = j,
|
||||
}
|
||||
end,
|
||||
|
||||
Functions = {
|
||||
Matter = function(_, entities)
|
||||
for _, entity in entities.matter do
|
||||
newWorld:despawn(entity)
|
||||
end
|
||||
Mirror = function(_, a)
|
||||
mcs:delete(a.m)
|
||||
end,
|
||||
|
||||
Jecs = function(_, entities)
|
||||
for _, entity in entities.jecs do
|
||||
ecs:delete(entity)
|
||||
end
|
||||
Jecs = function(_, a)
|
||||
ecs:delete(a.j)
|
||||
end,
|
||||
},
|
||||
}
|
||||
|
|
48
demo/src/ReplicatedStorage/track.luau
Normal file
48
demo/src/ReplicatedStorage/track.luau
Normal file
|
@ -0,0 +1,48 @@
|
|||
local events = {}
|
||||
|
||||
local function trackers_invoke(event, component, entity, ...)
|
||||
local trackers = events[event][component]
|
||||
if not trackers then
|
||||
return
|
||||
end
|
||||
|
||||
for _, tracker in trackers do
|
||||
tracker(entity, data)
|
||||
end
|
||||
end
|
||||
|
||||
local function trackers_init(event, component, fn)
|
||||
local ob = events[event]
|
||||
|
||||
return {
|
||||
connect = function(component, fn)
|
||||
local trackers = ob[component]
|
||||
if not trackers then
|
||||
trackers = {}
|
||||
ob[component] = trackers
|
||||
end
|
||||
|
||||
table.insert(trackers, fn)
|
||||
end,
|
||||
invoke = function(component, ...)
|
||||
trackers_invoke(event, component, ...)
|
||||
end
|
||||
}
|
||||
return function(component, fn)
|
||||
local trackers = ob[component]
|
||||
if not trackers then
|
||||
trackers = {}
|
||||
ob[component] = trackers
|
||||
end
|
||||
|
||||
table.insert(trackers, fn)
|
||||
end
|
||||
end
|
||||
|
||||
local trackers = {
|
||||
emplace = trackers_init("emplace"),
|
||||
add = trackers_init("added"),
|
||||
remove = trackers_init("removed")
|
||||
}
|
||||
|
||||
return trackers
|
104
jecs.luau
104
jecs.luau
|
@ -46,7 +46,7 @@ export type Record = {
|
|||
}
|
||||
|
||||
type IdRecord = {
|
||||
columns: { number },
|
||||
cache: { number },
|
||||
counts: { number },
|
||||
flags: number,
|
||||
size: number,
|
||||
|
@ -480,7 +480,7 @@ local function world_target(world: World, entity: i53, relation: i24, index: num
|
|||
nth = nth + count + 1
|
||||
end
|
||||
|
||||
local tr = idr.columns[archetype_id]
|
||||
local tr = idr.cache[archetype_id]
|
||||
|
||||
nth = archetype.types[nth + tr]
|
||||
|
||||
|
@ -537,7 +537,7 @@ local function id_record_ensure(world: World, id: number): IdRecord
|
|||
|
||||
idr = {
|
||||
size = 0,
|
||||
columns = {},
|
||||
cache = {},
|
||||
counts = {},
|
||||
flags = flags,
|
||||
hooks = {
|
||||
|
@ -562,7 +562,7 @@ local function archetype_append_to_records(
|
|||
local archetype_id = archetype.id
|
||||
local archetype_records = archetype.records
|
||||
local archetype_counts = archetype.counts
|
||||
local idr_columns = idr.columns
|
||||
local idr_columns = idr.cache
|
||||
local idr_counts = idr.counts
|
||||
local tr = idr_columns[archetype_id]
|
||||
if not tr then
|
||||
|
@ -1063,7 +1063,7 @@ local function archetype_destroy(world: World, archetype: Archetype)
|
|||
|
||||
for id in records do
|
||||
local idr = component_index[id]
|
||||
idr.columns[archetype_id] = nil :: any
|
||||
idr.cache[archetype_id] = nil :: any
|
||||
idr.counts[archetype_id] = nil
|
||||
idr.size -= 1
|
||||
records[id] = nil :: any
|
||||
|
@ -1122,7 +1122,7 @@ do
|
|||
if idr then
|
||||
local flags = idr.flags
|
||||
if bit32.band(flags, ECS_ID_DELETE) ~= 0 then
|
||||
for archetype_id in idr.columns do
|
||||
for archetype_id in idr.cache do
|
||||
local idr_archetype = archetypes[archetype_id]
|
||||
|
||||
local entities = idr_archetype.entities
|
||||
|
@ -1134,7 +1134,7 @@ do
|
|||
archetype_destroy(world, idr_archetype)
|
||||
end
|
||||
else
|
||||
for archetype_id in idr.columns do
|
||||
for archetype_id in idr.cache do
|
||||
local idr_archetype = archetypes[archetype_id]
|
||||
local entities = idr_archetype.entities
|
||||
local n = #entities
|
||||
|
@ -1147,55 +1147,66 @@ do
|
|||
end
|
||||
end
|
||||
|
||||
local sparse_array = entity_index.sparse_array
|
||||
local dense_array = entity_index.dense_array
|
||||
|
||||
if idr_t then
|
||||
for archetype_id in idr_t.columns do
|
||||
local children = {}
|
||||
local children
|
||||
local ids
|
||||
local count = 0
|
||||
local archetype_ids = idr_t.cache
|
||||
for archetype_id in archetype_ids do
|
||||
local idr_t_archetype = archetypes[archetype_id]
|
||||
|
||||
local idr_t_types = idr_t_archetype.types
|
||||
|
||||
for _, child in idr_t_archetype.entities do
|
||||
table.insert(children, child)
|
||||
end
|
||||
|
||||
local n = #children
|
||||
local entities = idr_t_archetype.entities
|
||||
local removal_queued = false
|
||||
|
||||
for _, id in idr_t_types do
|
||||
if not ECS_IS_PAIR(id) then
|
||||
continue
|
||||
end
|
||||
local object = ecs_pair_second(world, id)
|
||||
if object == delete then
|
||||
local id_record = component_index[id]
|
||||
local flags = id_record.flags
|
||||
local flags_delete_mask: number = bit32.band(flags, ECS_ID_DELETE)
|
||||
if flags_delete_mask ~= 0 then
|
||||
for i = n, 1, -1 do
|
||||
world_delete(world, children[i])
|
||||
end
|
||||
break
|
||||
else
|
||||
local on_remove = id_record.hooks.on_remove
|
||||
local to = archetype_traverse_remove(world, id, idr_t_archetype)
|
||||
local empty = #to.types == 0
|
||||
for i = n, 1, -1 do
|
||||
local child = children[i]
|
||||
if on_remove then
|
||||
on_remove(child)
|
||||
end
|
||||
local r = sparse_array[ECS_ENTITY_T_LO(child)]
|
||||
if not empty then
|
||||
entity_move(entity_index, child, r, to)
|
||||
end
|
||||
end
|
||||
if object ~= delete then
|
||||
continue
|
||||
end
|
||||
local id_record = component_index[id]
|
||||
local flags = id_record.flags
|
||||
local flags_delete_mask: number = bit32.band(flags, ECS_ID_DELETE)
|
||||
if flags_delete_mask ~= 0 then
|
||||
for i = #entities, 1, -1 do
|
||||
local child = entities[i]
|
||||
world_delete(world, child)
|
||||
end
|
||||
break
|
||||
else
|
||||
if not ids then
|
||||
ids = {}
|
||||
end
|
||||
ids[id] = true
|
||||
removal_queued = true
|
||||
end
|
||||
end
|
||||
|
||||
archetype_destroy(world, idr_t_archetype)
|
||||
if not removal_queued then
|
||||
continue
|
||||
end
|
||||
if not children then
|
||||
children = {}
|
||||
end
|
||||
local n = #entities
|
||||
table.move(entities, 1, n, count + 1, children)
|
||||
count += n
|
||||
end
|
||||
|
||||
if ids then
|
||||
for id in ids do
|
||||
for _, child in children do
|
||||
world_remove(world, child, id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for archetype_id in archetype_ids do
|
||||
archetype_destroy(world, archetypes[archetype_id])
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -2096,7 +2107,7 @@ local function world_query(world: World, ...)
|
|||
return q
|
||||
end
|
||||
|
||||
for archetype_id in idr.columns do
|
||||
for archetype_id in idr.cache do
|
||||
local compatibleArchetype = archetypes[archetype_id]
|
||||
if #compatibleArchetype.entities == 0 then
|
||||
continue
|
||||
|
@ -2130,9 +2141,9 @@ local function world_each(world: World, id): () -> ()
|
|||
return NOOP
|
||||
end
|
||||
|
||||
local idr_columns = idr.columns
|
||||
local idr_cache = idr.cache
|
||||
local archetypes = world.archetypes
|
||||
local archetype_id = next(idr_columns, nil) :: number
|
||||
local archetype_id = next(idr_cache, nil) :: number
|
||||
local archetype = archetypes[archetype_id]
|
||||
if not archetype then
|
||||
return NOOP
|
||||
|
@ -2144,7 +2155,7 @@ local function world_each(world: World, id): () -> ()
|
|||
return function(): any
|
||||
local entity = entities[row]
|
||||
while not entity do
|
||||
archetype_id = next(idr_columns, archetype_id) :: number
|
||||
archetype_id = next(idr_cache, archetype_id) :: number
|
||||
if not archetype_id then
|
||||
return
|
||||
end
|
||||
|
@ -2477,6 +2488,7 @@ export type World = {
|
|||
|
||||
return {
|
||||
World = World :: { new: () -> World },
|
||||
world = World.new :: () -> World,
|
||||
|
||||
OnAdd = EcsOnAdd :: Entity<(entity: Entity) -> ()>,
|
||||
OnRemove = EcsOnRemove :: Entity<(entity: Entity) -> ()>,
|
||||
|
@ -2500,6 +2512,8 @@ return {
|
|||
ECS_GENERATION = ECS_GENERATION,
|
||||
ECS_ID_IS_WILDCARD = ECS_ID_IS_WILDCARD,
|
||||
|
||||
ECS_ID_DELETE = ECS_ID_DELETE,
|
||||
|
||||
IS_PAIR = ECS_IS_PAIR,
|
||||
pair_first = ecs_pair_first,
|
||||
pair_second = ecs_pair_second,
|
||||
|
|
25
test/devtools_test.luau
Normal file
25
test/devtools_test.luau
Normal file
|
@ -0,0 +1,25 @@
|
|||
local jecs = require("@jecs")
|
||||
local pair = jecs.pair
|
||||
local ChildOf = jecs.ChildOf
|
||||
local lifetime_tracker_add = require("@tools/lifetime_tracker")
|
||||
local pe = require("@tools/entity_visualiser").prettify
|
||||
local world = lifetime_tracker_add(jecs.world(), {padding_enabled=false})
|
||||
local FriendsWith = world:component()
|
||||
local _1 = world:print_snapshot()
|
||||
local e1 = world:entity()
|
||||
local e2 = world:entity()
|
||||
world:delete(e2)
|
||||
|
||||
local _2 = world:print_snapshot()
|
||||
local e3 = world:entity()
|
||||
world:add(e3, pair(ChildOf, e1))
|
||||
local e4 = world:entity()
|
||||
world:add(e4, pair(FriendsWith, e3))
|
||||
local _3 = world:print_snapshot()
|
||||
world:delete(e1)
|
||||
world:delete(e3)
|
||||
local _4 = world:print_snapshot()
|
||||
world:print_entity_index()
|
||||
world:entity()
|
||||
world:entity()
|
||||
local _5 = world:print_snapshot()
|
|
@ -26,6 +26,50 @@ local N = 2 ^ 8
|
|||
type World = jecs.World
|
||||
type Entity<T=nil> = jecs.Entity<T>
|
||||
|
||||
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)
|
||||
local gen = ECS_GENERATION(e)
|
||||
return c.green(`e{ECS_ID(e)}`)..c.yellow(`v{gen}`)
|
||||
end
|
||||
|
||||
local function pp(e)
|
||||
local gen = ECS_GENERATION(e)
|
||||
return c.green(`e{ECS_ID(e)}`)..c.yellow(`v{jecs.ECS_ENTITY_T_HI(e)}`)
|
||||
end
|
||||
|
||||
local function debug_world_inspect(world: World)
|
||||
local function record(e): jecs.Record
|
||||
return entity_index_try_get_any(world.entity_index, e) :: any
|
||||
|
@ -67,10 +111,61 @@ local function debug_world_inspect(world: World)
|
|||
}
|
||||
end
|
||||
|
||||
local dwi = debug_world_inspect
|
||||
|
||||
local function name(world, e)
|
||||
return world:get(e, jecs.Name)
|
||||
end
|
||||
|
||||
TEST("#repro", function()
|
||||
local world = world_new()
|
||||
|
||||
local function getTargets(relation)
|
||||
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
|
||||
end
|
||||
return tgts
|
||||
end
|
||||
|
||||
local Attacks = world:component()
|
||||
local Eats = world:component()
|
||||
|
||||
local function setAttacksAndEats(entity1, entity2)
|
||||
world:add(entity1, pair(Attacks, entity2))
|
||||
world:add(entity1, pair(Eats, entity2))
|
||||
end
|
||||
|
||||
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))
|
||||
end)
|
||||
|
||||
TEST("archetype", function()
|
||||
local archetype_traverse_add = jecs.archetype_traverse_add
|
||||
local archetype_traverse_remove = jecs.archetype_traverse_remove
|
||||
|
@ -1199,6 +1294,7 @@ TEST("world:delete", function()
|
|||
world:delete(e)
|
||||
end)
|
||||
|
||||
local d = debug_world_inspect(world)
|
||||
for i, friend in friends do
|
||||
CHECK(not world:has(friend, pair(FriendsWith, e)))
|
||||
CHECK(world:has(friend, Health))
|
||||
|
|
33
tools/ansi.luau
Normal file
33
tools/ansi.luau
Normal 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,
|
||||
}
|
43
tools/entity_visualiser.luau
Normal file
43
tools/entity_visualiser.luau
Normal file
|
@ -0,0 +1,43 @@
|
|||
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
|
||||
|
||||
return {
|
||||
components = components,
|
||||
prettify = pe,
|
||||
}
|
212
tools/lifetime_tracker.luau
Normal file
212
tools/lifetime_tracker.luau
Normal file
|
@ -0,0 +1,212 @@
|
|||
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
|
||||
local ansi = require("@tools/ansi")
|
||||
|
||||
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
|
||||
|
||||
local function lifetime_tracker_add(world: jecs.World, opt)
|
||||
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
|
||||
|
||||
local w = setmetatable({}, { __index = world })
|
||||
|
||||
padding_enabled = opt.padding_enabled
|
||||
|
||||
local world_entity = world.entity
|
||||
w.entity = function(self)
|
||||
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
|
||||
w.print_entity_index = 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 = "|--------|"
|
||||
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 = {}
|
||||
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("-------------------------------------------------------------------")
|
||||
pad()
|
||||
end
|
||||
local world_add = world.add
|
||||
local relations = {}
|
||||
w.add = function(self, 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
|
||||
w.delete = function(self, 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 w
|
||||
end
|
||||
|
||||
return lifetime_tracker_add
|
Loading…
Reference in a new issue