mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-25 01:20:04 +00:00
Devtools initial commit
This commit is contained in:
parent
18019679d5
commit
d15266b6d5
6 changed files with 308 additions and 23 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -65,3 +65,7 @@ drafts/
|
||||||
|
|
||||||
# Luau tools
|
# Luau tools
|
||||||
profile.*
|
profile.*
|
||||||
|
|
||||||
|
# Patch files
|
||||||
|
|
||||||
|
*.patch
|
||||||
|
|
3
.luaurc
3
.luaurc
|
@ -2,7 +2,8 @@
|
||||||
"aliases": {
|
"aliases": {
|
||||||
"jecs": "jecs",
|
"jecs": "jecs",
|
||||||
"testkit": "test/testkit",
|
"testkit": "test/testkit",
|
||||||
"mirror": "mirror"
|
"mirror": "mirror",
|
||||||
|
"tools": "tools"
|
||||||
},
|
},
|
||||||
"languageMode": "strict"
|
"languageMode": "strict"
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,41 +5,60 @@ local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
local Matter = require(ReplicatedStorage.DevPackages.Matter)
|
local Matter = require(ReplicatedStorage.DevPackages.Matter)
|
||||||
local ecr = require(ReplicatedStorage.DevPackages.ecr)
|
local ecr = require(ReplicatedStorage.DevPackages.ecr)
|
||||||
local jecs = require(ReplicatedStorage.Lib)
|
local jecs = require(ReplicatedStorage.Lib)
|
||||||
|
local pair = jecs.pair
|
||||||
local newWorld = Matter.World.new()
|
local newWorld = Matter.World.new()
|
||||||
local ecs = jecs.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 C1 = ecs:component()
|
||||||
local C, D = ecs:component(), 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 {
|
return {
|
||||||
ParameterGenerator = function()
|
ParameterGenerator = function()
|
||||||
local matter_entities = {}
|
local j = ecs:entity()
|
||||||
local jecs_entities = {}
|
ecs:set(j, C1, true)
|
||||||
local entities = {
|
local m = mcs:entity()
|
||||||
matter = matter_entities,
|
mcs:set(m, E1, true)
|
||||||
jecs = jecs_entities,
|
|
||||||
}
|
|
||||||
for i = 1, 1000 do
|
for i = 1, 1000 do
|
||||||
table.insert(matter_entities, newWorld:spawn(A(), B()))
|
local friend1 = ecs:entity()
|
||||||
local e = ecs:entity()
|
local friend2 = mcs:entity()
|
||||||
ecs:set(e, C, {})
|
|
||||||
ecs:set(e, D, {})
|
ecs:add(friend1, pair(C2, j))
|
||||||
table.insert(jecs_entities, e)
|
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
|
end
|
||||||
return entities
|
return {
|
||||||
|
m = m,
|
||||||
|
j = j,
|
||||||
|
}
|
||||||
end,
|
end,
|
||||||
|
|
||||||
Functions = {
|
Functions = {
|
||||||
Matter = function(_, entities)
|
Mirror = function(_, a)
|
||||||
for _, entity in entities.matter do
|
mcs:delete(a.m)
|
||||||
newWorld:despawn(entity)
|
|
||||||
end
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
Jecs = function(_, entities)
|
Jecs = function(_, a)
|
||||||
for _, entity in entities.jecs do
|
ecs:delete(a.j)
|
||||||
ecs:delete(entity)
|
|
||||||
end
|
|
||||||
end,
|
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
|
20
test/devtools_test.luau
Normal file
20
test/devtools_test.luau
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
local jecs = require("@jecs")
|
||||||
|
local lifetime_tracker_add = require("@tools/lifetime_tracker")
|
||||||
|
local world = lifetime_tracker_add(jecs.world())
|
||||||
|
world:print_snapshot()
|
||||||
|
local e = world:entity()
|
||||||
|
local e1 = world:entity()
|
||||||
|
world:delete(e)
|
||||||
|
|
||||||
|
world:print_snapshot()
|
||||||
|
local e2 = world:entity()
|
||||||
|
local e3 = world:entity()
|
||||||
|
world:print_snapshot()
|
||||||
|
world:delete(e1)
|
||||||
|
world:delete(e2)
|
||||||
|
world:delete(e3)
|
||||||
|
world:print_snapshot()
|
||||||
|
world:print_entities()
|
||||||
|
world:entity()
|
||||||
|
world:entity()
|
||||||
|
world:print_snapshot()
|
193
tools/lifetime_tracker.luau
Normal file
193
tools/lifetime_tracker.luau
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
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
|
Loading…
Reference in a new issue