bvg/tiny-debug.lua

130 lines
3.9 KiB
Lua

local tiny = require("lib.tiny")
require("lib.inspect")
---@class TinyDebug
local tinyDebug = {}
-- TODO: Track some debugName field when debugging?
local function worldMetaIndex()
local worldMetaTable = getmetatable(tiny.world())
return worldMetaTable.__index
end
local function getCurrentTimeMilliseconds()
return love.timer.getTime() * 1000
end
--- Only applies to already-created systems!
--- Performs a `world:refresh()` to ensure that all added systems are installed.
---@param world World
function tinyDebug.logSystemUpdateTime(world)
world:refresh()
local systems = world.systems
for i = 1, #systems do
local system = systems[i]
local wrappedUpdate = system.update
system.update = function(...)
local currentMs = getCurrentTimeMilliseconds()
local ret = wrappedUpdate(...)
local endTimeMs = getCurrentTimeMilliseconds()
print(tostring(endTimeMs - currentMs) .. "ms taken to update system '" .. (system.name or i) .. "'")
return ret
end
end
end
function tinyDebug.trackEntityAges()
ENTITY_INIT_MS = { "ENTITY_INIT_MS" }
-- tiny._trackEntityAges = true
tiny._wrapAddEntity(function(_, entity)
entity[ENTITY_INIT_MS] = getCurrentTimeMilliseconds()
end)
end
--- Throws an error if trackEntityAges() has not been called
function tinyDebug.getEntityAgeMs(entity)
if ENTITY_INIT_MS == nil then
error("trackEntityAges() has not been enabled!")
end
return getCurrentTimeMilliseconds() - entity[ENTITY_INIT_MS]
end
function tinyDebug.warnWhenNonDataOnEntities()
local function checkForNonData(e, nested, tableCache)
nested = nested or false
tableCache = tableCache or {}
local valType = type(e)
if valType == "table" then
if tableCache[e] then
return
end
tableCache[e] = true
for k, v in pairs(e) do
local keyWarning = checkForNonData(k, true, tableCache)
if keyWarning then
return keyWarning
end
local valueWarning = checkForNonData(v, true, tableCache)
if valueWarning then
return valueWarning
end
end
elseif valType == "function" or valType == "thread" or valType == "userdata" then
return valType
end
end
tiny._wrapAddEntity(function(_, entity)
local nonDataType = checkForNonData(entity)
if nonDataType then
print("Detected non-data type '" .. nonDataType .. "' on entity")
end
end)
end
function tinyDebug.throwOnUnusedEntities()
---@return boolean
local function entityMatchesSomeSystem(world, entity)
local systems = world.systems
for i = 1, #systems do
local system = systems[i]
local filter = system.filter
if filter and filter(system, entity) then
return true
end
end
return false
end
---@return Entity[]
local function anyUnmatchedEntities(world)
local unmatchedEntities = {}
local el = world.entities
for i = 1, #el do
if not entityMatchesSomeSystem(world, el[i]) then
unmatchedEntities[#unmatchedEntities + 1] = el[i]
end
end
return unmatchedEntities
end
local tinyUpdate = tiny.update
worldMetaIndex().update = function(world, dt, filter)
local ret = tinyUpdate(world, dt, filter)
local ecAll = world:getEntityCount()
local unmatchedEntities = anyUnmatchedEntities(world)
if #unmatchedEntities ~= 0 then
error("UNUSED ENTITY COUNT: " .. #unmatchedEntities .. "/" .. ecAll .. " " .. Inspect(unmatchedEntities, { depth = 2 }))
end
return ret
end
end
return tinyDebug