annotate Libs/AceTimer-3.0/AceTimer-3.0.lua @ 56:7c0f819a85c6 v7.3.5.056

- Framework update.
author Tercio
date Sun, 11 Mar 2018 10:50:12 -0300
parents 1c3534391efb
children 0682d738499b
rev   line source
Tercio@4 1 --- **AceTimer-3.0** provides a central facility for registering timers.
Tercio@4 2 -- AceTimer supports one-shot timers and repeating timers. All timers are stored in an efficient
Tercio@4 3 -- data structure that allows easy dispatching and fast rescheduling. Timers can be registered
Tercio@4 4 -- or canceled at any time, even from within a running timer, without conflict or large overhead.\\
Tercio@4 5 -- AceTimer is currently limited to firing timers at a frequency of 0.01s as this is what the WoW timer API
Tercio@4 6 -- restricts us to.
Tercio@4 7 --
Tercio@4 8 -- All `:Schedule` functions will return a handle to the current timer, which you will need to store if you
Tercio@4 9 -- need to cancel the timer you just registered.
Tercio@4 10 --
Tercio@4 11 -- **AceTimer-3.0** can be embeded into your addon, either explicitly by calling AceTimer:Embed(MyAddon) or by
Tercio@4 12 -- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
Tercio@4 13 -- and can be accessed directly, without having to explicitly call AceTimer itself.\\
Tercio@4 14 -- It is recommended to embed AceTimer, otherwise you'll have to specify a custom `self` on all calls you
Tercio@4 15 -- make into AceTimer.
Tercio@4 16 -- @class file
Tercio@4 17 -- @name AceTimer-3.0
Tercio@4 18 -- @release $Id: AceTimer-3.0.lua 1119 2014-10-14 17:23:29Z nevcairiel $
Tercio@4 19
Tercio@4 20 local MAJOR, MINOR = "AceTimer-3.0", 17 -- Bump minor on changes
Tercio@4 21 local AceTimer, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
Tercio@4 22
Tercio@4 23 if not AceTimer then return end -- No upgrade needed
Tercio@4 24 AceTimer.activeTimers = AceTimer.activeTimers or {} -- Active timer list
Tercio@4 25 local activeTimers = AceTimer.activeTimers -- Upvalue our private data
Tercio@4 26
Tercio@4 27 -- Lua APIs
Tercio@4 28 local type, unpack, next, error, select = type, unpack, next, error, select
Tercio@4 29 -- WoW APIs
Tercio@4 30 local GetTime, C_TimerAfter = GetTime, C_Timer.After
Tercio@4 31
Tercio@4 32 local function new(self, loop, func, delay, ...)
Tercio@4 33 if delay < 0.01 then
Tercio@4 34 delay = 0.01 -- Restrict to the lowest time that the C_Timer API allows us
Tercio@4 35 end
Tercio@4 36
Tercio@4 37 local timer = {...}
Tercio@4 38 timer.object = self
Tercio@4 39 timer.func = func
Tercio@4 40 timer.looping = loop
Tercio@4 41 timer.argsCount = select("#", ...)
Tercio@4 42 timer.delay = delay
Tercio@4 43 timer.ends = GetTime() + delay
Tercio@4 44
Tercio@4 45 activeTimers[timer] = timer
Tercio@4 46
Tercio@4 47 -- Create new timer closure to wrap the "timer" object
Tercio@4 48 timer.callback = function()
Tercio@4 49 if not timer.cancelled then
Tercio@4 50 if type(timer.func) == "string" then
Tercio@4 51 -- We manually set the unpack count to prevent issues with an arg set that contains nil and ends with nil
Tercio@4 52 -- e.g. local t = {1, 2, nil, 3, nil} print(#t) will result in 2, instead of 5. This fixes said issue.
Tercio@4 53 timer.object[timer.func](timer.object, unpack(timer, 1, timer.argsCount))
Tercio@4 54 else
Tercio@4 55 timer.func(unpack(timer, 1, timer.argsCount))
Tercio@4 56 end
Tercio@4 57
Tercio@4 58 if timer.looping and not timer.cancelled then
Tercio@4 59 -- Compensate delay to get a perfect average delay, even if individual times don't match up perfectly
Tercio@4 60 -- due to fps differences
Tercio@4 61 local time = GetTime()
Tercio@4 62 local delay = timer.delay - (time - timer.ends)
Tercio@4 63 -- Ensure the delay doesn't go below the threshold
Tercio@4 64 if delay < 0.01 then delay = 0.01 end
Tercio@4 65 C_TimerAfter(delay, timer.callback)
Tercio@4 66 timer.ends = time + delay
Tercio@4 67 else
Tercio@4 68 activeTimers[timer.handle or timer] = nil
Tercio@4 69 end
Tercio@4 70 end
Tercio@4 71 end
Tercio@4 72
Tercio@4 73 C_TimerAfter(delay, timer.callback)
Tercio@4 74 return timer
Tercio@4 75 end
Tercio@4 76
Tercio@4 77 --- Schedule a new one-shot timer.
Tercio@4 78 -- The timer will fire once in `delay` seconds, unless canceled before.
Tercio@4 79 -- @param callback Callback function for the timer pulse (funcref or method name).
Tercio@4 80 -- @param delay Delay for the timer, in seconds.
Tercio@4 81 -- @param ... An optional, unlimited amount of arguments to pass to the callback function.
Tercio@4 82 -- @usage
Tercio@4 83 -- MyAddOn = LibStub("AceAddon-3.0"):NewAddon("MyAddOn", "AceTimer-3.0")
Tercio@4 84 --
Tercio@4 85 -- function MyAddOn:OnEnable()
Tercio@4 86 -- self:ScheduleTimer("TimerFeedback", 5)
Tercio@4 87 -- end
Tercio@4 88 --
Tercio@4 89 -- function MyAddOn:TimerFeedback()
Tercio@4 90 -- print("5 seconds passed")
Tercio@4 91 -- end
Tercio@4 92 function AceTimer:ScheduleTimer(func, delay, ...)
Tercio@4 93 if not func or not delay then
Tercio@4 94 error(MAJOR..": ScheduleTimer(callback, delay, args...): 'callback' and 'delay' must have set values.", 2)
Tercio@4 95 end
Tercio@4 96 if type(func) == "string" then
Tercio@4 97 if type(self) ~= "table" then
Tercio@4 98 error(MAJOR..": ScheduleTimer(callback, delay, args...): 'self' - must be a table.", 2)
Tercio@4 99 elseif not self[func] then
Tercio@4 100 error(MAJOR..": ScheduleTimer(callback, delay, args...): Tried to register '"..func.."' as the callback, but it doesn't exist in the module.", 2)
Tercio@4 101 end
Tercio@4 102 end
Tercio@4 103 return new(self, nil, func, delay, ...)
Tercio@4 104 end
Tercio@4 105
Tercio@4 106 --- Schedule a repeating timer.
Tercio@4 107 -- The timer will fire every `delay` seconds, until canceled.
Tercio@4 108 -- @param callback Callback function for the timer pulse (funcref or method name).
Tercio@4 109 -- @param delay Delay for the timer, in seconds.
Tercio@4 110 -- @param ... An optional, unlimited amount of arguments to pass to the callback function.
Tercio@4 111 -- @usage
Tercio@4 112 -- MyAddOn = LibStub("AceAddon-3.0"):NewAddon("MyAddOn", "AceTimer-3.0")
Tercio@4 113 --
Tercio@4 114 -- function MyAddOn:OnEnable()
Tercio@4 115 -- self.timerCount = 0
Tercio@4 116 -- self.testTimer = self:ScheduleRepeatingTimer("TimerFeedback", 5)
Tercio@4 117 -- end
Tercio@4 118 --
Tercio@4 119 -- function MyAddOn:TimerFeedback()
Tercio@4 120 -- self.timerCount = self.timerCount + 1
Tercio@4 121 -- print(("%d seconds passed"):format(5 * self.timerCount))
Tercio@4 122 -- -- run 30 seconds in total
Tercio@4 123 -- if self.timerCount == 6 then
Tercio@4 124 -- self:CancelTimer(self.testTimer)
Tercio@4 125 -- end
Tercio@4 126 -- end
Tercio@4 127 function AceTimer:ScheduleRepeatingTimer(func, delay, ...)
Tercio@4 128 if not func or not delay then
Tercio@4 129 error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): 'callback' and 'delay' must have set values.", 2)
Tercio@4 130 end
Tercio@4 131 if type(func) == "string" then
Tercio@4 132 if type(self) ~= "table" then
Tercio@4 133 error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): 'self' - must be a table.", 2)
Tercio@4 134 elseif not self[func] then
Tercio@4 135 error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): Tried to register '"..func.."' as the callback, but it doesn't exist in the module.", 2)
Tercio@4 136 end
Tercio@4 137 end
Tercio@4 138 return new(self, true, func, delay, ...)
Tercio@4 139 end
Tercio@4 140
Tercio@4 141 --- Cancels a timer with the given id, registered by the same addon object as used for `:ScheduleTimer`
Tercio@4 142 -- Both one-shot and repeating timers can be canceled with this function, as long as the `id` is valid
Tercio@4 143 -- and the timer has not fired yet or was canceled before.
Tercio@4 144 -- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
Tercio@4 145 function AceTimer:CancelTimer(id)
Tercio@4 146 local timer = activeTimers[id]
Tercio@4 147
Tercio@4 148 if not timer then
Tercio@4 149 return false
Tercio@4 150 else
Tercio@4 151 timer.cancelled = true
Tercio@4 152 activeTimers[id] = nil
Tercio@4 153 return true
Tercio@4 154 end
Tercio@4 155 end
Tercio@4 156
Tercio@4 157 --- Cancels all timers registered to the current addon object ('self')
Tercio@4 158 function AceTimer:CancelAllTimers()
Tercio@4 159 for k,v in pairs(activeTimers) do
Tercio@4 160 if v.object == self then
Tercio@4 161 AceTimer.CancelTimer(self, k)
Tercio@4 162 end
Tercio@4 163 end
Tercio@4 164 end
Tercio@4 165
Tercio@4 166 --- Returns the time left for a timer with the given id, registered by the current addon object ('self').
Tercio@4 167 -- This function will return 0 when the id is invalid.
Tercio@4 168 -- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
Tercio@4 169 -- @return The time left on the timer.
Tercio@4 170 function AceTimer:TimeLeft(id)
Tercio@4 171 local timer = activeTimers[id]
Tercio@4 172 if not timer then
Tercio@4 173 return 0
Tercio@4 174 else
Tercio@4 175 return timer.ends - GetTime()
Tercio@4 176 end
Tercio@4 177 end
Tercio@4 178
Tercio@4 179
Tercio@4 180 -- ---------------------------------------------------------------------
Tercio@4 181 -- Upgrading
Tercio@4 182
Tercio@4 183 -- Upgrade from old hash-bucket based timers to C_Timer.After timers.
Tercio@4 184 if oldminor and oldminor < 10 then
Tercio@4 185 -- disable old timer logic
Tercio@4 186 AceTimer.frame:SetScript("OnUpdate", nil)
Tercio@4 187 AceTimer.frame:SetScript("OnEvent", nil)
Tercio@4 188 AceTimer.frame:UnregisterAllEvents()
Tercio@4 189 -- convert timers
Tercio@4 190 for object,timers in pairs(AceTimer.selfs) do
Tercio@4 191 for handle,timer in pairs(timers) do
Tercio@4 192 if type(timer) == "table" and timer.callback then
Tercio@4 193 local newTimer
Tercio@4 194 if timer.delay then
Tercio@4 195 newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.callback, timer.delay, timer.arg)
Tercio@4 196 else
Tercio@4 197 newTimer = AceTimer.ScheduleTimer(timer.object, timer.callback, timer.when - GetTime(), timer.arg)
Tercio@4 198 end
Tercio@4 199 -- Use the old handle for old timers
Tercio@4 200 activeTimers[newTimer] = nil
Tercio@4 201 activeTimers[handle] = newTimer
Tercio@4 202 newTimer.handle = handle
Tercio@4 203 end
Tercio@4 204 end
Tercio@4 205 end
Tercio@4 206 AceTimer.selfs = nil
Tercio@4 207 AceTimer.hash = nil
Tercio@4 208 AceTimer.debug = nil
Tercio@4 209 elseif oldminor and oldminor < 17 then
Tercio@4 210 -- Upgrade from old animation based timers to C_Timer.After timers.
Tercio@4 211 AceTimer.inactiveTimers = nil
Tercio@4 212 AceTimer.frame = nil
Tercio@4 213 local oldTimers = AceTimer.activeTimers
Tercio@4 214 -- Clear old timer table and update upvalue
Tercio@4 215 AceTimer.activeTimers = {}
Tercio@4 216 activeTimers = AceTimer.activeTimers
Tercio@4 217 for handle, timer in pairs(oldTimers) do
Tercio@4 218 local newTimer
Tercio@4 219 -- Stop the old timer animation
Tercio@4 220 local duration, elapsed = timer:GetDuration(), timer:GetElapsed()
Tercio@4 221 timer:GetParent():Stop()
Tercio@4 222 if timer.looping then
Tercio@4 223 newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.func, duration, unpack(timer.args, 1, timer.argsCount))
Tercio@4 224 else
Tercio@4 225 newTimer = AceTimer.ScheduleTimer(timer.object, timer.func, duration - elapsed, unpack(timer.args, 1, timer.argsCount))
Tercio@4 226 end
Tercio@4 227 -- Use the old handle for old timers
Tercio@4 228 activeTimers[newTimer] = nil
Tercio@4 229 activeTimers[handle] = newTimer
Tercio@4 230 newTimer.handle = handle
Tercio@4 231 end
Tercio@4 232
Tercio@4 233 -- Migrate transitional handles
Tercio@4 234 if oldminor < 13 and AceTimer.hashCompatTable then
Tercio@4 235 for handle, id in pairs(AceTimer.hashCompatTable) do
Tercio@4 236 local t = activeTimers[id]
Tercio@4 237 if t then
Tercio@4 238 activeTimers[id] = nil
Tercio@4 239 activeTimers[handle] = t
Tercio@4 240 t.handle = handle
Tercio@4 241 end
Tercio@4 242 end
Tercio@4 243 AceTimer.hashCompatTable = nil
Tercio@4 244 end
Tercio@4 245 end
Tercio@4 246
Tercio@4 247 -- ---------------------------------------------------------------------
Tercio@4 248 -- Embed handling
Tercio@4 249
Tercio@4 250 AceTimer.embeds = AceTimer.embeds or {}
Tercio@4 251
Tercio@4 252 local mixins = {
Tercio@4 253 "ScheduleTimer", "ScheduleRepeatingTimer",
Tercio@4 254 "CancelTimer", "CancelAllTimers",
Tercio@4 255 "TimeLeft"
Tercio@4 256 }
Tercio@4 257
Tercio@4 258 function AceTimer:Embed(target)
Tercio@4 259 AceTimer.embeds[target] = true
Tercio@4 260 for _,v in pairs(mixins) do
Tercio@4 261 target[v] = AceTimer[v]
Tercio@4 262 end
Tercio@4 263 return target
Tercio@4 264 end
Tercio@4 265
Tercio@4 266 -- AceTimer:OnEmbedDisable(target)
Tercio@4 267 -- target (object) - target object that AceTimer is embedded in.
Tercio@4 268 --
Tercio@4 269 -- cancel all timers registered for the object
Tercio@4 270 function AceTimer:OnEmbedDisable(target)
Tercio@4 271 target:CancelAllTimers()
Tercio@4 272 end
Tercio@4 273
Tercio@4 274 for addon in pairs(AceTimer.embeds) do
Tercio@4 275 AceTimer:Embed(addon)
Tercio@4 276 end