annotate Libs/AceTimer-3.0/AceTimer-3.0.lua @ 63:3552946c0b9a tip

Added tag v8.2.0.062 for changeset d6704922ef5d
author Tercioo
date Fri, 28 Jun 2019 20:06:18 -0300
parents 0682d738499b
children
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@58 18 -- @release $Id: AceTimer-3.0.lua 1170 2018-03-29 17:38:58Z funkydude $
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@58 37 local timer = {
Tercio@58 38 object = self,
Tercio@58 39 func = func,
Tercio@58 40 looping = loop,
Tercio@58 41 argsCount = select("#", ...),
Tercio@58 42 delay = delay,
Tercio@58 43 ends = GetTime() + delay,
Tercio@58 44 ...
Tercio@58 45 }
Tercio@4 46
Tercio@4 47 activeTimers[timer] = timer
Tercio@4 48
Tercio@4 49 -- Create new timer closure to wrap the "timer" object
Tercio@4 50 timer.callback = function()
Tercio@4 51 if not timer.cancelled then
Tercio@4 52 if type(timer.func) == "string" then
Tercio@4 53 -- We manually set the unpack count to prevent issues with an arg set that contains nil and ends with nil
Tercio@4 54 -- e.g. local t = {1, 2, nil, 3, nil} print(#t) will result in 2, instead of 5. This fixes said issue.
Tercio@4 55 timer.object[timer.func](timer.object, unpack(timer, 1, timer.argsCount))
Tercio@4 56 else
Tercio@4 57 timer.func(unpack(timer, 1, timer.argsCount))
Tercio@4 58 end
Tercio@4 59
Tercio@4 60 if timer.looping and not timer.cancelled then
Tercio@4 61 -- Compensate delay to get a perfect average delay, even if individual times don't match up perfectly
Tercio@4 62 -- due to fps differences
Tercio@4 63 local time = GetTime()
Tercio@4 64 local delay = timer.delay - (time - timer.ends)
Tercio@4 65 -- Ensure the delay doesn't go below the threshold
Tercio@4 66 if delay < 0.01 then delay = 0.01 end
Tercio@4 67 C_TimerAfter(delay, timer.callback)
Tercio@4 68 timer.ends = time + delay
Tercio@4 69 else
Tercio@4 70 activeTimers[timer.handle or timer] = nil
Tercio@4 71 end
Tercio@4 72 end
Tercio@4 73 end
Tercio@4 74
Tercio@4 75 C_TimerAfter(delay, timer.callback)
Tercio@4 76 return timer
Tercio@4 77 end
Tercio@4 78
Tercio@4 79 --- Schedule a new one-shot timer.
Tercio@4 80 -- The timer will fire once in `delay` seconds, unless canceled before.
Tercio@4 81 -- @param callback Callback function for the timer pulse (funcref or method name).
Tercio@4 82 -- @param delay Delay for the timer, in seconds.
Tercio@4 83 -- @param ... An optional, unlimited amount of arguments to pass to the callback function.
Tercio@4 84 -- @usage
Tercio@4 85 -- MyAddOn = LibStub("AceAddon-3.0"):NewAddon("MyAddOn", "AceTimer-3.0")
Tercio@4 86 --
Tercio@4 87 -- function MyAddOn:OnEnable()
Tercio@4 88 -- self:ScheduleTimer("TimerFeedback", 5)
Tercio@4 89 -- end
Tercio@4 90 --
Tercio@4 91 -- function MyAddOn:TimerFeedback()
Tercio@4 92 -- print("5 seconds passed")
Tercio@4 93 -- end
Tercio@4 94 function AceTimer:ScheduleTimer(func, delay, ...)
Tercio@4 95 if not func or not delay then
Tercio@4 96 error(MAJOR..": ScheduleTimer(callback, delay, args...): 'callback' and 'delay' must have set values.", 2)
Tercio@4 97 end
Tercio@4 98 if type(func) == "string" then
Tercio@4 99 if type(self) ~= "table" then
Tercio@4 100 error(MAJOR..": ScheduleTimer(callback, delay, args...): 'self' - must be a table.", 2)
Tercio@4 101 elseif not self[func] then
Tercio@4 102 error(MAJOR..": ScheduleTimer(callback, delay, args...): Tried to register '"..func.."' as the callback, but it doesn't exist in the module.", 2)
Tercio@4 103 end
Tercio@4 104 end
Tercio@4 105 return new(self, nil, func, delay, ...)
Tercio@4 106 end
Tercio@4 107
Tercio@4 108 --- Schedule a repeating timer.
Tercio@4 109 -- The timer will fire every `delay` seconds, until canceled.
Tercio@4 110 -- @param callback Callback function for the timer pulse (funcref or method name).
Tercio@4 111 -- @param delay Delay for the timer, in seconds.
Tercio@4 112 -- @param ... An optional, unlimited amount of arguments to pass to the callback function.
Tercio@4 113 -- @usage
Tercio@4 114 -- MyAddOn = LibStub("AceAddon-3.0"):NewAddon("MyAddOn", "AceTimer-3.0")
Tercio@4 115 --
Tercio@4 116 -- function MyAddOn:OnEnable()
Tercio@4 117 -- self.timerCount = 0
Tercio@4 118 -- self.testTimer = self:ScheduleRepeatingTimer("TimerFeedback", 5)
Tercio@4 119 -- end
Tercio@4 120 --
Tercio@4 121 -- function MyAddOn:TimerFeedback()
Tercio@4 122 -- self.timerCount = self.timerCount + 1
Tercio@4 123 -- print(("%d seconds passed"):format(5 * self.timerCount))
Tercio@4 124 -- -- run 30 seconds in total
Tercio@4 125 -- if self.timerCount == 6 then
Tercio@4 126 -- self:CancelTimer(self.testTimer)
Tercio@4 127 -- end
Tercio@4 128 -- end
Tercio@4 129 function AceTimer:ScheduleRepeatingTimer(func, delay, ...)
Tercio@4 130 if not func or not delay then
Tercio@4 131 error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): 'callback' and 'delay' must have set values.", 2)
Tercio@4 132 end
Tercio@4 133 if type(func) == "string" then
Tercio@4 134 if type(self) ~= "table" then
Tercio@4 135 error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): 'self' - must be a table.", 2)
Tercio@4 136 elseif not self[func] then
Tercio@4 137 error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): Tried to register '"..func.."' as the callback, but it doesn't exist in the module.", 2)
Tercio@4 138 end
Tercio@4 139 end
Tercio@4 140 return new(self, true, func, delay, ...)
Tercio@4 141 end
Tercio@4 142
Tercio@4 143 --- Cancels a timer with the given id, registered by the same addon object as used for `:ScheduleTimer`
Tercio@4 144 -- Both one-shot and repeating timers can be canceled with this function, as long as the `id` is valid
Tercio@4 145 -- and the timer has not fired yet or was canceled before.
Tercio@4 146 -- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
Tercio@4 147 function AceTimer:CancelTimer(id)
Tercio@4 148 local timer = activeTimers[id]
Tercio@4 149
Tercio@4 150 if not timer then
Tercio@4 151 return false
Tercio@4 152 else
Tercio@4 153 timer.cancelled = true
Tercio@4 154 activeTimers[id] = nil
Tercio@4 155 return true
Tercio@4 156 end
Tercio@4 157 end
Tercio@4 158
Tercio@4 159 --- Cancels all timers registered to the current addon object ('self')
Tercio@4 160 function AceTimer:CancelAllTimers()
Tercio@58 161 for k,v in next, activeTimers do
Tercio@4 162 if v.object == self then
Tercio@4 163 AceTimer.CancelTimer(self, k)
Tercio@4 164 end
Tercio@4 165 end
Tercio@4 166 end
Tercio@4 167
Tercio@4 168 --- Returns the time left for a timer with the given id, registered by the current addon object ('self').
Tercio@4 169 -- This function will return 0 when the id is invalid.
Tercio@4 170 -- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
Tercio@4 171 -- @return The time left on the timer.
Tercio@4 172 function AceTimer:TimeLeft(id)
Tercio@4 173 local timer = activeTimers[id]
Tercio@4 174 if not timer then
Tercio@4 175 return 0
Tercio@4 176 else
Tercio@4 177 return timer.ends - GetTime()
Tercio@4 178 end
Tercio@4 179 end
Tercio@4 180
Tercio@4 181
Tercio@4 182 -- ---------------------------------------------------------------------
Tercio@4 183 -- Upgrading
Tercio@4 184
Tercio@4 185 -- Upgrade from old hash-bucket based timers to C_Timer.After timers.
Tercio@4 186 if oldminor and oldminor < 10 then
Tercio@4 187 -- disable old timer logic
Tercio@4 188 AceTimer.frame:SetScript("OnUpdate", nil)
Tercio@4 189 AceTimer.frame:SetScript("OnEvent", nil)
Tercio@4 190 AceTimer.frame:UnregisterAllEvents()
Tercio@4 191 -- convert timers
Tercio@58 192 for object,timers in next, AceTimer.selfs do
Tercio@58 193 for handle,timer in next, timers do
Tercio@4 194 if type(timer) == "table" and timer.callback then
Tercio@4 195 local newTimer
Tercio@4 196 if timer.delay then
Tercio@4 197 newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.callback, timer.delay, timer.arg)
Tercio@4 198 else
Tercio@4 199 newTimer = AceTimer.ScheduleTimer(timer.object, timer.callback, timer.when - GetTime(), timer.arg)
Tercio@4 200 end
Tercio@4 201 -- Use the old handle for old timers
Tercio@4 202 activeTimers[newTimer] = nil
Tercio@4 203 activeTimers[handle] = newTimer
Tercio@4 204 newTimer.handle = handle
Tercio@4 205 end
Tercio@4 206 end
Tercio@4 207 end
Tercio@4 208 AceTimer.selfs = nil
Tercio@4 209 AceTimer.hash = nil
Tercio@4 210 AceTimer.debug = nil
Tercio@4 211 elseif oldminor and oldminor < 17 then
Tercio@4 212 -- Upgrade from old animation based timers to C_Timer.After timers.
Tercio@4 213 AceTimer.inactiveTimers = nil
Tercio@4 214 AceTimer.frame = nil
Tercio@4 215 local oldTimers = AceTimer.activeTimers
Tercio@4 216 -- Clear old timer table and update upvalue
Tercio@4 217 AceTimer.activeTimers = {}
Tercio@4 218 activeTimers = AceTimer.activeTimers
Tercio@58 219 for handle, timer in next, oldTimers do
Tercio@4 220 local newTimer
Tercio@4 221 -- Stop the old timer animation
Tercio@4 222 local duration, elapsed = timer:GetDuration(), timer:GetElapsed()
Tercio@4 223 timer:GetParent():Stop()
Tercio@4 224 if timer.looping then
Tercio@4 225 newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.func, duration, unpack(timer.args, 1, timer.argsCount))
Tercio@4 226 else
Tercio@4 227 newTimer = AceTimer.ScheduleTimer(timer.object, timer.func, duration - elapsed, unpack(timer.args, 1, timer.argsCount))
Tercio@4 228 end
Tercio@4 229 -- Use the old handle for old timers
Tercio@4 230 activeTimers[newTimer] = nil
Tercio@4 231 activeTimers[handle] = newTimer
Tercio@4 232 newTimer.handle = handle
Tercio@4 233 end
Tercio@4 234
Tercio@4 235 -- Migrate transitional handles
Tercio@4 236 if oldminor < 13 and AceTimer.hashCompatTable then
Tercio@58 237 for handle, id in next, AceTimer.hashCompatTable do
Tercio@4 238 local t = activeTimers[id]
Tercio@4 239 if t then
Tercio@4 240 activeTimers[id] = nil
Tercio@4 241 activeTimers[handle] = t
Tercio@4 242 t.handle = handle
Tercio@4 243 end
Tercio@4 244 end
Tercio@4 245 AceTimer.hashCompatTable = nil
Tercio@4 246 end
Tercio@4 247 end
Tercio@4 248
Tercio@4 249 -- ---------------------------------------------------------------------
Tercio@4 250 -- Embed handling
Tercio@4 251
Tercio@4 252 AceTimer.embeds = AceTimer.embeds or {}
Tercio@4 253
Tercio@4 254 local mixins = {
Tercio@4 255 "ScheduleTimer", "ScheduleRepeatingTimer",
Tercio@4 256 "CancelTimer", "CancelAllTimers",
Tercio@4 257 "TimeLeft"
Tercio@4 258 }
Tercio@4 259
Tercio@4 260 function AceTimer:Embed(target)
Tercio@4 261 AceTimer.embeds[target] = true
Tercio@58 262 for _,v in next, mixins do
Tercio@4 263 target[v] = AceTimer[v]
Tercio@4 264 end
Tercio@4 265 return target
Tercio@4 266 end
Tercio@4 267
Tercio@4 268 -- AceTimer:OnEmbedDisable(target)
Tercio@4 269 -- target (object) - target object that AceTimer is embedded in.
Tercio@4 270 --
Tercio@4 271 -- cancel all timers registered for the object
Tercio@4 272 function AceTimer:OnEmbedDisable(target)
Tercio@4 273 target:CancelAllTimers()
Tercio@4 274 end
Tercio@4 275
Tercio@58 276 for addon in next, AceTimer.embeds do
Tercio@4 277 AceTimer:Embed(addon)
Tercio@4 278 end