annotate Libs/AceTimer-3.0/AceTimer-3.0.lua @ 25:ac501d71c890 tip

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