|
if Platform.cmdline then return end |
|
|
|
MapVar("g_debug_pointers_list", false) |
|
|
|
if FirstLoad then |
|
g_TrackerTexts = {} |
|
g_TrackerTextsThread = false |
|
end |
|
|
|
function OnMsg.PostDoneMap() |
|
g_TrackerTextsThread = false |
|
end |
|
|
|
function StartUpdateTrackerObjs() |
|
if IsValidThread(g_TrackerTextsThread) then |
|
Wakeup(g_TrackerTextsThread) |
|
return |
|
end |
|
g_TrackerTextsThread = CreateMapRealTimeThread(function() |
|
local clean_up_time = RealTime() |
|
local thread = CurrentThread() |
|
while g_TrackerTextsThread == thread do |
|
PauseInfiniteLoopDetection("UpdateTrackerObjs") |
|
local sleep = UpdateTrackerObjs(clean_up_time) |
|
ResumeInfiniteLoopDetection("UpdateTrackerObjs") |
|
WaitWakeup(sleep) |
|
end |
|
end) |
|
end |
|
|
|
OnMsg.NewMap = StartUpdateTrackerObjs |
|
OnMsg.PersistPostLoad = StartUpdateTrackerObjs |
|
|
|
local function GetDebugPointers() |
|
local ptrs = g_debug_pointers_list or {} |
|
g_debug_pointers_list = ptrs |
|
return ptrs |
|
end |
|
|
|
local function AddTrackerDbgText(t) |
|
table.insert(g_TrackerTexts, t) |
|
StartUpdateTrackerObjs() |
|
end |
|
|
|
DefineClass.PointerWhite = { |
|
__parents = { "Object" }, |
|
entity = "ArrowUnits", |
|
} |
|
|
|
DefineClass.Pointer = { |
|
__parents = { "Object" }, |
|
entity = "ArrowUnits", |
|
flags = { gofRealTimeAnim = true }, |
|
} |
|
|
|
function Pointer:Init() |
|
NetTempObject(self) |
|
self:SetAxis(axis_y) |
|
self:SetAngle(180) |
|
self.thread = CreateMapRealTimeThread(function(self) |
|
local move, delta, sleep = 2500, 120, 15 |
|
local x, y, z = self:GetVisualPosXYZ() |
|
while true do |
|
for i = 0, move, delta do |
|
self:SetPos(x, y, z + i, sleep) |
|
Sleep(sleep) |
|
end |
|
for i = move, 0, -delta do |
|
self:SetPos(x, y, z + i, sleep) |
|
Sleep(sleep) |
|
end |
|
end |
|
end, self) |
|
local dbg_ptrs = GetDebugPointers() |
|
dbg_ptrs[#dbg_ptrs + 1] = self |
|
end |
|
|
|
function Pointer:Done() |
|
DeleteThread(self.thread) |
|
GetDebugPointers():Remove(self) |
|
end |
|
|
|
DefineClass.RealTimePoint = { |
|
__parents = {"Mesh", "InitDone"}, |
|
time_used = -60000, |
|
depth_test = false, |
|
vector = false, |
|
} |
|
|
|
function RealTimePoint:Init() |
|
NetTempObject(self) |
|
local dbg_ptrs = GetDebugPointers() |
|
dbg_ptrs[#dbg_ptrs + 1] = self |
|
self:SetShader(ProceduralMeshShaders.mesh_linelist) |
|
self:SetDepthTest(false) |
|
self:SetMesh(CreateSphereVertices(guim / 2)) |
|
end |
|
|
|
function RealTimePoint:Done() |
|
table.remove_value(GetDebugPointers(), self) |
|
if IsValid(self.vector) then |
|
self.vector:delete() |
|
end |
|
end |
|
|
|
function RealTimePoint:SetUp(pt, ptOrigin, color) |
|
if not pt:IsValidZ() then |
|
pt = pt:SetTerrainZ(10) |
|
color = RGB(0, 255, 0) |
|
end |
|
self:SetMesh(CreateSphereVertices(guim / 2, RGB(255, 255, 255))) |
|
Mesh.SetPos(self, pt) |
|
self.vector = self.vector or Vector:new() |
|
self.vector:Set(ptOrigin, pt, RGB(0, 255, 0)) |
|
end |
|
|
|
|
|
function RealTimePoint:SetPos(pt, ...) |
|
local color |
|
if not pt:IsValidZ() then |
|
pt = pt:SetTerrainZ(10) |
|
color = RGB(0, 255, 0) |
|
else |
|
color = RGB(255, 255, 255) |
|
end |
|
Mesh.SetPos(self, pt, ...) |
|
self:SetMesh(CreateSphereVertices(guim / 2, color)) |
|
|
|
if self.vector then |
|
self.vector:Set(self.vector:GetA(), pt, RGB(0, 255, 0)) |
|
end |
|
end |
|
|
|
function RealTimePoint:DetachFromMap() |
|
Mesh.DetachFromMap(self) |
|
if IsValid(self.vector) then |
|
self.vector:delete() |
|
self.vector = false |
|
end |
|
end |
|
|
|
|
|
DefineClass.RealTimeText = { |
|
__parents = {"Text", "InitDone"}, |
|
time_used = -60000, |
|
} |
|
|
|
function RealTimeText:Init() |
|
NetTempObject(self) |
|
local dbg_ptrs = GetDebugPointers() |
|
dbg_ptrs[#dbg_ptrs + 1] = self |
|
self:SetTextStyle("EditorTextBold") |
|
end |
|
|
|
function RealTimeText:Done() |
|
table.remove_entry(GetDebugPointers(), self) |
|
end |
|
|
|
function AddTrackerText(root, expression) |
|
if not IsValid(root) then |
|
local arrow = string.find(expression, "->") |
|
if arrow then |
|
local f |
|
root = string.sub(expression, 1, arrow-1) |
|
local class_name = string.trim_spaces(root) |
|
if g_Classes[class_name] then |
|
root = function () return MapGet("map", class_name) or empty_table end |
|
else |
|
local r, err = load("return " .. root) |
|
if err then |
|
printf("Error while evaluating %s\n%s", string.trim(root, 24, "..."), err) |
|
return |
|
end |
|
root = r |
|
end |
|
local e = string.sub(expression, arrow+2) |
|
if string.find(e, "return") then |
|
local r, err = load("return function(o) " .. e .. " end" ) |
|
if err then |
|
printf("Error while evaluating %s\n%s", string.trim(e, 24, "..."), err) |
|
return |
|
end |
|
f = r() |
|
else |
|
local r, err = load("return function(o) return " .. e .. " end" ) |
|
if err then |
|
printf("Error while evaluating %s\n%s", string.trim(e, 24, "..."), err) |
|
return |
|
end |
|
f = r() |
|
end |
|
AddTrackerDbgText { id = expression, root = root, to_eval = f } |
|
else |
|
local init = 1 |
|
while true do |
|
local i = string.find(expression, "[:.]", init) |
|
if i then |
|
init = i + 1 |
|
root = string.sub(expression, 1, i-1) |
|
else |
|
printf("Not a valid expression to track") |
|
return |
|
end |
|
|
|
local class_name = string.trim_spaces(root) |
|
if g_Classes[class_name] then |
|
root = function () return MapGet("map", class_name) or empty_table end |
|
break |
|
else |
|
local r, err = load("return " .. root) |
|
if not err then |
|
root = r |
|
break |
|
end |
|
end |
|
end |
|
if not root then |
|
printf("Can't deduce the root object from %s\n", string.trim(expression, 24, "...")) |
|
return |
|
end |
|
local object_expression = string.sub(expression, init-1) |
|
local r, err = load("return function(o) return o" .. object_expression .. " end") |
|
if not err then |
|
AddTrackerDbgText{ id = expression, root = root, to_eval = r() } |
|
return |
|
end |
|
end |
|
else |
|
local r, err = load("return function(o) return " .. expression .. " end") |
|
if r then |
|
AddTrackerDbgText{ id = expression, root = root, to_eval = r() } |
|
return |
|
else |
|
printf("Error while evaluating %s\n%s", string.trim(expression, 24, "..."), err) |
|
return |
|
end |
|
end |
|
end |
|
|
|
function ShowPoint(o, pt, label, time) |
|
AddTrackerDbgText{ |
|
id = pt, |
|
root = o, |
|
to_eval = function() |
|
return pt, label |
|
end, |
|
expire_time = GameTime() + (time or 2000), |
|
} |
|
end |
|
|
|
function UpdateTrackerObjs(clean_up_time) |
|
local texts = g_TrackerTexts |
|
local now = RealTime() |
|
local objs_text = {} |
|
|
|
local live_pts = MapFilter(GetDebugPointers(), "map", "RealTimePoint") |
|
local pointers_alive = false |
|
for i = #texts, 1, -1 do |
|
local t = texts[i] |
|
if t.expire_time and t.expire_time - GameTime() < 0 then |
|
table.remove(texts, i) |
|
end |
|
|
|
local ok, root |
|
if type(t.root) ~= "function" then |
|
root = t.root |
|
else |
|
ok, root = pcall(t.root) |
|
end |
|
if IsValid(root) or type(root) == "table" and IsValid(root[1]) then |
|
if IsValid(root) then |
|
root = {root} |
|
end |
|
for j = 1, #root do |
|
local r = root[j] |
|
local labels = {} |
|
local ok, v, text = pcall(t.to_eval, r) |
|
if ok then |
|
if type(v) == "table" or IsPoint(v) or IsValid(v) then |
|
local function UnpackExaminedObj(v, resolve_tables) |
|
if IsPoint(v) then |
|
return v:IsValid() and {v} or {} |
|
elseif IsValid(v) then |
|
if v:IsValidPos() then |
|
return {v:GetVisualPos()}, RGBA(96, 96, 255, 64) |
|
else |
|
return {} |
|
end |
|
elseif type(v) == "table" and IsValid(v[1]) then |
|
local pts = {} |
|
for _, o in ipairs(v) do |
|
if o:IsValidPos() then |
|
pts[#pts+1] = o:GetVisualPos() |
|
end |
|
end |
|
return pts, RGBA(96, 96, 255, 64) |
|
elseif resolve_tables and type(v) == "table" then |
|
local pts = {} |
|
if #v > 0 then |
|
for i = 1, #v do |
|
local o = v[i] |
|
local elements = UnpackExaminedObj(o) |
|
for j = 1, #elements do |
|
table.insert(pts, elements[j]) |
|
end |
|
end |
|
else |
|
for o, t in pairs(v) do |
|
local elements = UnpackExaminedObj(o) |
|
for j = 1, #elements do |
|
table.insert(pts, elements[j]) |
|
labels[elements[j]] = tostring(t) |
|
end |
|
end |
|
end |
|
return pts, RGBA(255, 96, 96, 64) |
|
end |
|
return {} |
|
end |
|
local pts, color = UnpackExaminedObj(v, true) |
|
|
|
pointers_alive = true |
|
text = text and tostring(text) |
|
|
|
for i = 1, #pts do |
|
local pointer |
|
if #live_pts > 0 then |
|
pointer = table.remove(live_pts) |
|
else |
|
pointer = RealTimePoint:new() |
|
end |
|
pointer:SetUp(pts[i], r:GetVisualPos(), color) |
|
pointer.time_used = now |
|
local text = text or labels[pts[i]] |
|
if text then |
|
objs_text[pointer] = objs_text[pointer] or {} |
|
table.insert(objs_text[pointer], string.trim(tostring(text), 30, "...")) |
|
end |
|
end |
|
else |
|
objs_text[r] = objs_text[r] or {} |
|
table.insert(objs_text[r], string.trim(tostring(v), 30, "...")) |
|
end |
|
end |
|
end |
|
end |
|
end |
|
|
|
local live_texts = MapFilter(GetDebugPointers(), "map", "RealTimeText") |
|
for o, t in pairs(objs_text) do |
|
if IsValid(o) and o:IsValidPos() then |
|
local text |
|
if #live_texts > 0 then |
|
text = table.remove(live_texts) |
|
else |
|
text = RealTimeText:new() |
|
end |
|
text:SetText(table.concat(t, "\n")) |
|
text:SetPos(o:GetVisualPos()) |
|
text.time_used = now |
|
end |
|
end |
|
|
|
if now - clean_up_time > 30000 then |
|
for i = #live_texts, 1, -1 do |
|
if now - live_texts[i].time_used > 30000 then |
|
DoneObject(live_texts[i]) |
|
table.remove(live_texts, i) |
|
end |
|
end |
|
for i = #live_pts, 1, -1 do |
|
if now - live_pts[i].time_used > 30000 then |
|
DoneObject(live_pts[i]) |
|
table.remove(live_pts, i) |
|
end |
|
end |
|
end |
|
if #texts > 0 or pointers_alive then |
|
for i = 1, #live_texts do |
|
live_texts[i]:DetachFromMap() |
|
end |
|
for i = 1, #live_pts do |
|
live_pts[i]:DetachFromMap() |
|
end |
|
return 50 |
|
end |
|
DoneObjects(live_texts) |
|
DoneObjects(live_pts) |
|
end |
|
|
|
function ClearTextTrackers(expression) |
|
if expression then |
|
table.remove_value(g_TrackerTexts, "id", expression) |
|
else |
|
g_TrackerTexts = {} |
|
end |
|
end |
|
|
|
function HasTextTrackers(expression) |
|
return table.find(g_TrackerTexts, "id", expression) and true or false |
|
end |
|
|
|
function ToggleTextTrackers(expression, description) |
|
if HasTextTrackers(expression) then |
|
ClearTextTrackers(expression) |
|
if (description or "") ~= "" then |
|
printf("Show %s: OFF", description) |
|
end |
|
else |
|
AddTrackerText(false, expression) |
|
if (description or "") ~= "" then |
|
printf("Show %s: ON", description) |
|
end |
|
end |
|
end |
|
|