dobrograd-13-06-2022/garrysmod/addons/util-fprofiler/lua/fprofiler/ui/model.lua
Jonny_Bro (Nikita) e4d5311906 first commit
2023-11-16 15:01:19 +05:00

178 lines
5.7 KiB
Lua

--[[-------------------------------------------------------------------------
The model describes the data that the drives the UI
Loosely based on the Elm architecture
---------------------------------------------------------------------------]]
local model =
{
realm = "client", -- "client" or "server"
serverAccess = false, -- Whether the player has access to profile the server
frameVisible = false, -- Whether the frame is visible
client = {
status = "Stopped", -- Started or Stopped
shouldReset = true, -- Whether profiling should start anew
recordTime = 0, -- Total time spent on the last full profiling session
sessionStart = nil, -- When the last profiling session was started
sessionStartSysTime = nil, -- When the last profiling session was started, measured in SysTime
bottlenecks = {}, -- The list of bottleneck functions
topLagSpikes = {}, -- Top of lagging functions
currentSelected = nil, -- Currently selected function
focusObj = nil, -- The current function being focussed upon in profiling
focusStr = "", -- The current function name being entered
toConsole = nil, -- Any functions that should be printed to console
sourceText = "", -- The text of the source function (if available)
},
server = {
status = "Stopped", -- Started or Stopped
shouldReset = true, -- Whether profiling should start anew
bottlenecks = {}, -- The list of bottleneck functions
recordTime = 0, -- Total time spent on the last full profiling session
sessionStart = nil, -- When the last profiling session was started
topLagSpikes = {}, -- Top of lagging functions
currentSelected = nil, -- Currently selected function
focusObj = nil, -- The current function being focussed upon in profiling
focusStr = "", -- The current function name
toConsole = nil, -- Any functions that should be printed to console
sourceText = "", -- The text of the source function (if available)
fromServer = false, -- Whether a change of the model came from the server.
},
}
local updaters = {}
--[[-------------------------------------------------------------------------
Update the model.
Automatically calls the registered update hook functions
e.g. updating the realm would be:
FProfiler.UI.updateModel("realm", "server")
---------------------------------------------------------------------------]]
function FProfiler.UI.updateModel(path, value)
path = istable(path) and path or {path}
local updTbl = updaters
local mdlTbl = model
local key = path[#path]
for i = 1, #path - 1 do
mdlTbl = mdlTbl[path[i]]
updTbl = updTbl and updTbl[path[i]]
end
local oldValue = mdlTbl[key]
mdlTbl[key] = value
for _, updFunc in ipairs(updTbl and updTbl[key] or {}) do
updFunc(value, oldValue)
end
end
--[[-------------------------------------------------------------------------
Update the model of the current realm
---------------------------------------------------------------------------]]
function FProfiler.UI.updateCurrentRealm(path, value)
path = istable(path) and path or {path}
table.insert(path, 1, model.realm)
FProfiler.UI.updateModel(path, value)
end
--[[-------------------------------------------------------------------------
Retrieve a value of the model
---------------------------------------------------------------------------]]
function FProfiler.UI.getModelValue(path)
path = istable(path) and path or {path}
local mdlTbl = model
local key = path[#path]
for i = 1, #path - 1 do
mdlTbl = mdlTbl[path[i]]
end
return mdlTbl[key]
end
--[[-------------------------------------------------------------------------
Retrieve a value of the model regardless of realm
---------------------------------------------------------------------------]]
function FProfiler.UI.getCurrentRealmValue(path)
path = istable(path) and path or {path}
table.insert(path, 1, model.realm)
return FProfiler.UI.getModelValue(path)
end
--[[-------------------------------------------------------------------------
Registers a hook that gets triggered when a certain part of the model is updated
e.g. FProfiler.UI.onModelUpdate("realm", print) prints when the realm is changed
---------------------------------------------------------------------------]]
function FProfiler.UI.onModelUpdate(path, func)
path = istable(path) and path or {path}
local updTbl = updaters
local mdlTbl = model
local key = path[#path]
for i = 1, #path - 1 do
mdlTbl = mdlTbl[path[i]]
updTbl[path[i]] = updTbl[path[i]] or {}
updTbl = updTbl[path[i]]
end
updTbl[key] = updTbl[key] or {}
table.insert(updTbl[key], func)
-- Call update with the initial value
if mdlTbl[key] ~= nil then
func(mdlTbl[key], mdlTbl[key])
end
end
--[[-------------------------------------------------------------------------
Registers a hook to both realms
---------------------------------------------------------------------------]]
function FProfiler.UI.onCurrentRealmUpdate(path, func)
path = istable(path) and path or {path}
table.insert(path, 1, "client")
FProfiler.UI.onModelUpdate(path, function(...)
if FProfiler.UI.getModelValue("realm") == "server" then return end
func(...)
end)
path[1] = "server"
FProfiler.UI.onModelUpdate(path, function(...)
if FProfiler.UI.getModelValue("realm") == "client" then return end
func(...)
end)
end
--[[-------------------------------------------------------------------------
When the realm is changed, all update functions of the new realm are to be called
---------------------------------------------------------------------------]]
FProfiler.UI.onModelUpdate("realm", function(new, old)
if not updaters[new] then return end
for k, funcTbl in pairs(updaters[new]) do
for _, func in ipairs(funcTbl) do
func(model[new][k], model[new][k])
end
end
end)