# lifetime_tracker.luau Coverage

## Total Execution Hits: 1

## Function Coverage Overview: 9.09%

## Function Coverage:

Function | Hits
--- | ---
| 1
print_centered_entity:12 | 0
name:26 | 0
pad:30 | 0
lifetime_tracker_add:36 | 0
:48 | 0
:62 | 0
:89 | 0
:135 | 0
:164 | 0
:175 | 0

## Source Code:

Line | Hits | Code
--- | --- | ---
1 | 1 | local jecs = require("@jecs")
2 | 1 | local ECS_GENERATION = jecs.ECS_GENERATION
3 | 1 | local ECS_ID = jecs.ECS_ID
4 | 1 | local __ = jecs.Wildcard
5 | 1 | local pair = jecs.pair
6 | N/A | >
7 | 1 | local prettify = require("@tools/entity_visualiser").prettify
8 | N/A | >
9 | 1 | local pe = prettify
10 | 1 | local ansi = require("@tools/ansi")
11 | N/A | >
12 | 1 | function print_centered_entity(entity, width: number)
13 | 0 | local entity_str = tostring(entity)
14 | 0 | local entity_length = #entity_str
15 | N/A | >
16 | 0 | local padding_total = width - 2 - entity_length
17 | N/A | >
18 | 0 | local padding_left = math.floor(padding_total / 2)
19 | 0 | local padding_right = padding_total - padding_left
20 | N/A | >
21 | 0 | local centered_str = string.rep(" ", padding_left) .. entity_str .. string.rep(" ", padding_right)
22 | N/A | >
23 | 0 | print("|" .. centered_str .. "|")
24 | N/A | end>
25 | N/A | >
26 | 1 | local function name(world, e)
27 | 0 | return world:get(world, e, jecs.Name) or pe(e)
28 | N/A | end>
29 | 1 | local padding_enabled = false
30 | 1 | local function pad()
31 | 0 | if padding_enabled then
32 | 0 | print("")
33 | N/A | end>
34 | N/A | end>
35 | N/A | >
36 | 1 | local function lifetime_tracker_add(world: jecs.World, opt)
37 | 0 | local entity_index = world.entity_index
38 | 0 | local dense_array = entity_index.dense_array
39 | 0 | local component_index = world.component_index
40 | N/A | >
41 | 0 | local ENTITY_RANGE = (jecs.Rest :: any) + 1
42 | N/A | >
43 | 0 | local w = setmetatable({}, { __index = world })
44 | N/A | >
45 | 0 | padding_enabled = opt.padding_enabled
46 | N/A | >
47 | 0 | local world_entity = world.entity
48 | 0 | w.entity = function(self, entity)
49 | 0 | if entity then
50 | 0 | return world_entity(world, entity)
51 | N/A | end>
52 | 0 | local will_recycle = entity_index.max_id ~= entity_index.alive_count
53 | 0 | local e = world_entity(world)
54 | 0 | if will_recycle then
55 | 0 | print(`*recycled {pe(e)}`)
56 | 0 | else
57 | 0 | print(`*created {pe(e)}`)
58 | N/A | end>
59 | 0 | pad()
60 | 0 | return e
61 | N/A | end>
62 | 0 | w.print_entity_index = function(self)
63 | 0 | local max_id = entity_index.max_id
64 | 0 | local alive_count = entity_index.alive_count
65 | 0 | local alive = table.move(dense_array, 1 + jecs.Rest :: any, alive_count, 1, {})
66 | 0 | local dead = table.move(dense_array, alive_count + 1, max_id, 1, {})
67 | N/A | >
68 | 0 | local sep = "|--------|"
69 | 0 | if #alive > 0 then
70 | 0 | print("|-alive--|")
71 | 0 | for i = 1, #alive do
72 | 0 | local e = pe(alive[i])
73 | 0 | print_centered_entity(e, 32)
74 | 0 | print(sep)
75 | N/A | end>
76 | 0 | print("\n")
77 | N/A | end>
78 | N/A | >
79 | 0 | if #dead > 0 then
80 | 0 | print("|--dead--|")
81 | 0 | for i = 1, #dead do
82 | 0 | print_centered_entity(pe(dead[i]), 32)
83 | 0 | print(sep)
84 | N/A | end>
85 | N/A | end>
86 | 0 | pad()
87 | N/A | end>
88 | 0 | local timelines = {}
89 | 0 | w.print_snapshot = function(self)
90 | 0 | local timeline = #timelines + 1
91 | 0 | local entity_column_width = 10
92 | 0 | local status_column_width = 8
93 | N/A | >
94 | 0 | local header = string.format("| %-" .. entity_column_width .. "s |", "Entity")
95 | 0 | for i = 1, timeline do
96 | 0 | header = header .. string.format(" %-" .. status_column_width .. "s |", string.format("T%d", i))
97 | N/A | end>
98 | N/A | >
99 | 0 | local max_id = entity_index.max_id
100 | 0 | local alive_count = entity_index.alive_count
101 | 0 | local alive = table.move(dense_array, 1 + jecs.Rest :: any, alive_count, 1, {})
102 | 0 | local dead = table.move(dense_array, alive_count + 1, max_id, 1, {})
103 | N/A | >
104 | 0 | local data = {}
105 | 0 | print("-------------------------------------------------------------------")
106 | 0 | print(header)
107 | N/A | >
108 | N/A | -- Store the snapshot data for this timeline>
109 | 0 | for i = ENTITY_RANGE, max_id do
110 | 0 | if dense_array[i] then
111 | 0 | local entity = dense_array[i]
112 | 0 | local id = ECS_ID(entity)
113 | 0 | local status = "alive"
114 | 0 | if not world:contains(entity) then
115 | 0 | status = "dead"
116 | N/A | end>
117 | 0 | data[id] = status
118 | N/A | end>
119 | N/A | end>
120 | N/A | >
121 | 0 | table.insert(timelines, data)
122 | N/A | >
123 | N/A | -- Create a table to hold entity data for sorting>
124 | 0 | local entities = {}
125 | 0 | for i = ENTITY_RANGE, max_id do
126 | 0 | if dense_array[i] then
127 | 0 | local entity = dense_array[i]
128 | 0 | local id = ECS_ID(entity)
129 | N/A | -- Push entity and id into the new `entities` table>
130 | 0 | table.insert(entities, { entity = entity, id = id })
131 | N/A | end>
132 | N/A | end>
133 | N/A | >
134 | N/A | -- Sort the entities by ECS_ID>
135 | 0 | table.sort(entities, function(a, b)
136 | 0 | return a.id < b.id
137 | N/A | end)>
138 | N/A | >
139 | N/A | -- Print the sorted rows>
140 | 0 | for _, entity_data in ipairs(entities) do
141 | 0 | local entity = entity_data.entity
142 | 0 | local id = entity_data.id
143 | 0 | local status = "alive"
144 | 0 | if id > alive_count then
145 | 0 | status = "dead"
146 | N/A | end>
147 | 0 | local row = string.format("| %-" .. entity_column_width .. "s |", pe(entity))
148 | 0 | for j = 1, timeline do
149 | 0 | local timeline_data = timelines[j]
150 | 0 | local entity_data = timeline_data[id]
151 | 0 | if entity_data then
152 | 0 | row = row .. string.format(" %-" .. status_column_width .. "s |", entity_data)
153 | 0 | else
154 | 0 | row = row .. string.format(" %-" .. status_column_width .. "s |", "-")
155 | N/A | end>
156 | N/A | end>
157 | 0 | print(row)
158 | N/A | end>
159 | 0 | print("-------------------------------------------------------------------")
160 | 0 | pad()
161 | N/A | end>
162 | 0 | local world_add = world.add
163 | 0 | local relations = {}
164 | 0 | w.add = function(self, entity: any, component: any)
165 | 0 | world_add(world, entity, component)
166 | 0 | if jecs.IS_PAIR(component) then
167 | 0 | local relation = jecs.pair_first(world, component)
168 | 0 | local target = jecs.pair_second(world, component)
169 | 0 | print(`*added ({pe(relation)}, {pe(target)}) to {pe(entity)}`)
170 | 0 | pad()
171 | N/A | end>
172 | N/A | end>
173 | N/A | >
174 | 0 | local world_delete = world.delete
175 | 0 | w.delete = function(self, e)
176 | 0 | world_delete(world, e)
177 | N/A | >
178 | 0 | local idr_t = component_index[pair(__, e)]
179 | 0 | if idr_t then
180 | 0 | for archetype_id in idr_t.cache do
181 | 0 | local archetype = world.archetypes[archetype_id]
182 | 0 | for _, id in archetype.types do
183 | 0 | if not jecs.IS_PAIR(id) then
184 | 0 | continue
185 | N/A | end>
186 | 0 | local object = jecs.pair_second(world, id)
187 | 0 | if object ~= e then
188 | 0 | continue
189 | N/A | end>
190 | 0 | local id_record = component_index[id]
191 | 0 | local flags = id_record.flags
192 | 0 | local flags_delete_mask: number = bit32.band(flags, jecs.ECS_ID_DELETE)
193 | 0 | if flags_delete_mask ~= 0 then
194 | 0 | for _, entity in archetype.entities do
195 | 0 | print(`*deleted dependant {pe(entity)} of {pe(e)}`)
196 | 0 | pad()
197 | N/A | end>
198 | 0 | break
199 | 0 | else
200 | 0 | for _, entity in archetype.entities do
201 | 0 | print(
202 | 0 | `*removed dependency ({pe(jecs.pair_first(world, id))}, {pe(object)}) from {pe(entity)}`
203 | 0 | )
204 | N/A | end>
205 | N/A | end>
206 | N/A | end>
207 | N/A | end>
208 | N/A | end>
209 | N/A | >
210 | 0 | print(`*deleted {pe(e)}`)
211 | 0 | pad()
212 | N/A | end>
213 | 0 | return w
214 | N/A | end>
215 | N/A | >
216 | 1 | return lifetime_tracker_add