Files
Endfield-Data/LuaScripts/Common/Core/TimerManager.lua
2025-12-02 20:37:18 +07:00

268 lines
6.3 KiB
Lua

local minHeapClass = require_ex("Common/Utils/DataStructure/MinHeap")
TimerManager = HL.Class('TimerManager')
TimerManager.m_heap = HL.Field(HL.Forward("MinHeap"))
TimerManager.m_unscaledHeap = HL.Field(HL.Forward("MinHeap"))
TimerManager.m_frameHeap = HL.Field(HL.Forward("MinHeap"))
TimerManager.m_removedTimerKeys = HL.Field(HL.Table)
TimerManager.m_groupTimerKeys = HL.Field(HL.Table)
TimerManager.m_nextTimerKey = HL.Field(HL.Number) << 1
TimerManager.m_frameCount = HL.Field(HL.Number) << 0
TimerManager.m_requestCache = HL.Field(HL.Forward("Stack"))
TimerManager.m_timerStackTraces = HL.Field(HL.Table)
TimerManager.TimerManager = HL.Constructor() << function(self)
self.m_heap = minHeapClass()
self.m_unscaledHeap = minHeapClass()
self.m_frameHeap = minHeapClass()
self.m_requestCache = require_ex("Common/Utils/DataStructure/Stack")()
self.m_removedTimerKeys = {}
self.m_groupTimerKeys = setmetatable({}, { __mode = "k" })
self.m_timerStackTraces = {}
self.m_nextTimerKey = 1
self.m_frameCount = 0
LuaUpdate:Add("Tick", function(deltaTime)
self:Tick(deltaTime)
end)
end
TimerManager.Tick = HL.Method(HL.Number) << function(self, deltaTime)
self.m_frameCount = self.m_frameCount + 1
self:_UpdateTimers(self.m_heap, Time.time)
self:_UpdateTimers(self.m_unscaledHeap, Time.unscaledTime)
self:_UpdateTimers(self.m_frameHeap, self.m_frameCount)
end
TimerManager._UpdateTimers = HL.Method(HL.Forward("MinHeap"), HL.Number) << function(self, heap, time)
while heap:Size() > 0 do
local request, triggerTime = heap:Min()
if self.m_removedTimerKeys[request.key] then
self.m_removedTimerKeys[request.key] = nil
heap:Pop()
if ENABLE_PROFILER then
self.m_timerStackTraces[request.key] = nil
end
self:_CacheRequest(request)
else
if time >= triggerTime then
heap:Pop()
if ENABLE_PROFILER then
local label = self.m_timerStackTraces[request.key]
if label and unity_sample and unity_sample.begin_unity_sample then
unity_sample.begin_unity_sample(label)
end
end
local succ, log = xpcall(request.action, debug.traceback)
if not succ then
logger.critical("Timer Action Fail\n", log)
end
if ENABLE_PROFILER then
if unity_sample and unity_sample.end_unity_sample then
unity_sample.end_unity_sample()
end
self.m_timerStackTraces[request.key] = nil
end
self:_CacheRequest(request)
else
break
end
end
end
end
TimerManager.StartFrameTimer = HL.Method(HL.Number, HL.Function, HL.Opt(HL.Any)).Return(HL.Number)
<< function(self, frameNum, action, groupKey)
local triggerTime = self.m_frameCount + frameNum
local heap = self.m_frameHeap
return self:_StartTimer(heap, triggerTime, action, groupKey)
end
TimerManager.StartTimer = HL.Method(HL.Number, HL.Function, HL.Opt(HL.Boolean, HL.Any)).Return(HL.Number)
<< function(self, time, action, unscaled, groupKey)
local triggerTime = (unscaled and Time.unscaledTime or Time.time) + time
local heap = unscaled and self.m_unscaledHeap or self.m_heap
return self:_StartTimer(heap, triggerTime, action, groupKey)
end
TimerManager.GetTimerTriggerTime = HL.Method(HL.Number).Return(HL.Number) << function(self, timerId)
for key, _ in self.m_heap:NodeIter() do
local request = key.key
if request.key == timerId then
return request.triggerTime
end
end
for key, _ in self.m_unscaledHeap:NodeIter() do
local request = key.key
if request.key == timerId then
return request.triggerTime
end
end
return -1
end
TimerManager._StartTimer = HL.Method(HL.Forward("MinHeap"), HL.Number, HL.Function, HL.Opt(HL.Any)).Return(HL.Number)
<< function(self, heap, triggerTime, action, groupKey)
local key = self.m_nextTimerKey
self.m_nextTimerKey = self.m_nextTimerKey + 1
local request = self:_GetRequest()
request.key = key
request.action = action
request.triggerTime = triggerTime
request.groupKey = groupKey
if ENABLE_PROFILER then
self.m_timerStackTraces[key] = self:_GetCallerStackLabel()
end
heap:Add(request, triggerTime)
if groupKey ~= nil then
local keys = self.m_groupTimerKeys[groupKey]
if not keys then
keys = {}
self.m_groupTimerKeys[groupKey] = keys
end
table.insert(keys, key)
end
return key
end
TimerManager.ClearTimer = HL.Method(HL.Number) << function(self, key)
if key then
self.m_removedTimerKeys[key] = true
if ENABLE_PROFILER then
self.m_timerStackTraces[key] = nil
end
end
end
TimerManager.ClearAllTimer = HL.Method(HL.Any) << function(self, groupKey)
local keys = self.m_groupTimerKeys[groupKey]
if keys then
for _, k in pairs(keys) do
self:ClearTimer(k)
end
self.m_groupTimerKeys[groupKey] = nil
end
end
TimerManager._GetRequest = HL.Method().Return(HL.Table) << function(self)
if self.m_requestCache:Empty() then
return {}
else
return self.m_requestCache:Pop()
end
end
TimerManager._CacheRequest = HL.Method(HL.Table) << function(self, request)
request.action = nil
request.groupKey = nil
self.m_requestCache:Push(request)
end
TimerManager._GetCallerStackLabel = HL.Method().Return(HL.String) << function(self)
local thisSource
for level = 4, 12 do
local info = debug.getinfo(level, "Sl")
if info and info.what ~= "C" and info.source then
local src = info.source
if thisSource == nil or src ~= thisSource then
local line = info.currentline or 0
if src:sub(1,1) == "@" then src = src:sub(2) end
local last = src:match("[^/\\]+$") or src
return string.format("Timer:%s:%d", last, line)
end
end
end
return "Timer:Unknown"
end
HL.Commit(TimerManager)
return TimerManager