- Joined
- May 9, 2014
- Messages
- 1,819
A background on the issues of Timer Natives can be found here: Issues with timer functions
With lua, it is now possible to overwrite natives with our own copies (somewhat). With that in mind, this script fixes some of the issues addressed in that thread:
With lua, it is now possible to overwrite natives with our own copies (somewhat). With that in mind, this script fixes some of the issues addressed in that thread:
Lua:
-- Replaces the ordinary timer natives with safe counterparts
do
OldNatives = {}
local Timer = {}
local doDebug = nil
OldNatives.createTimer = CreateTimer
function CreateTimer()
local obj = OldNatives.createTimer()
Timer[obj] = {
hasCallback = nil,
callback = nil,
looped = nil,
running = nil,
inCallback = nil,
pauseFlag = 0,
duration = 0.,
elapsed = 0.
}
return obj
end
OldNatives.pauseTimer = PauseTimer
function PauseTimer(t)
if Timer[t].running then
Timer[t].running = nil
Timer[t].pauseFlag = (BlzBitOr(Timer[t].pauseFlag, 1))
end
OldNatives.pauseTimer(t)
end
OldNatives.resumeTimer = ResumeTimer
function ResumeTimer(t)
if not Timer[t].hasCallback then
if doDebug then print("ResumeTimer: Attempted to resume a timer " .. I2S(GetHandleId(t)) .. " with no callback") end
return
elseif Timer[t].inCallback and Timer[t].looped then
return
end
if not Timer[t].inCallback and not Timer[t].running then
Timer[t].running = 0
end
OldNatives.resumeTimer(t)
end
OldNatives.timerStart = TimerStart
function TimerStart(t, dur, looper, func)
if not Timer[t].hasCallback then
-- Make it come true
Timer[t].hasCallback = 0
end
if not Timer[t].inCallback then
-- Usually when a timer is created
Timer[t].duration = math.max(dur, 0.)
Timer[t].looped = looper
Timer[t].callback = func
Timer[t].running = 0
-- Create a new function that will act as the callback
OldNatives.timerStart(t, dur, looper, function()
local tr = GetExpiredTimer()
Timer[tr].inCallback = 0
Timer[tr].running = nil
Timer[tr].callback()
Timer[tr].inCallback = nil
if Timer[tr].destroyFlag then
DestroyTimer(tr)
elseif Timer[tr].tempData then
-- Properties of the timer were overwritten
OldNatives.pauseTimer(tr)
TimerStart(tr, Timer[tr].tempData.dur, Timer[tr].tempData.looper, Timer[tr].tempData.func)
Timer[tr].tempData = nil
Timer[tr].elapsed = 0.
else
if Timer[tr].looped and ((BlzBitAnd(Timer[tr].pauseFlag, 1) ~= 0) or (BlzBitAnd(Timer[tr].pauseFlag, 2) ~= 0)) then
Timer[tr].pauseFlag = BlzBitAnd(Timer[tr].pauseFlag, 0)
OldNatives.pauseTimer(tr)
TimerStart(tr, Timer[tr].duration, Timer[tr].looped, Timer[tr].callback)
elseif Timer[tr].looped then
Timer[tr].running = 0
end
Timer[tr].elapsed = 0.
end
end)
else
if not Timer[t].tempData then
Timer[t].tempData = {__mode='k'}
end
Timer[t].tempData.dur = dur
Timer[t].tempData.looper = looper
Timer[t].tempData.func = func
end
end
OldNatives.destroyTimer = DestroyTimer
function DestroyTimer(t)
if not Timer[t] or Timer[t].inCallback then
-- No need to destroy an already-destroyed timer
if Timer[t].inCallback then
Timer[t].onDestroyFlag = 0
end
return
end
if Timer[t].onDestroyFlag then
Timer[t].onDestroyFlag = nil
end
if Timer[t].running or Timer[t].looped then
Timer[t].running = nil
OldNatives.pauseTimer(t)
end
Timer[t] = nil
OldNatives.destroyTimer(t)
end
OldNatives.getTimeout = TimerGetTimeout
function TimerGetTimeout(t) return Timer[t].duration end
OldNatives.getElapsed = TimerGetElapsed
function TimerGetElapsed(t) return OldNatives.getElapsed(t) + Timer[t].elapsed end
function TimerSetRemaining(t, newR, update)
if Timer[t].inCallback then
if doDebug then print('TimerSetRemaining: The remaining duration of the timer ' .. I2S(GetHandleId(t)) .. ' cannot be altered while the callback function is running.') end
return false
end
if not Timer[t].hasCallback then
if doDebug then print('TimerSetRemaining: The remaining duration of the timer ' .. I2S(GetHandleId(t)) .. ' cannot be altered while the callback function is running.') end
return false
end
-- Ensure that newR is not below 0.
newR = math.max(newR, 0.)
local epsilon = OldNatives.getElapsed(t)
local delta = TimerGetRemaining(t) - newR
local newDur = Timer[t].duration - delta
-- If delta is anything but 0., continue with TimerSetRemaining
if delta ~= 0. then
OldNatives.pauseTimer(t)
TimerStart(t, newR, Timer[t].looped, Timer[t].callback)
Timer[t].elapsed = Timer[t].elapsed + epsilon
if update then
Timer[t].duration = newDur
else
Timer[t].duration = newDur + delta
end
Timer[t].pauseFlag = (BlzBitOr(Timer[t].pauseFlag, 2))
return true
end
return false
end
end