diff Libs/AceTimer-3.0/AceTimer-3.0.lua @ 5:c31ee4251181

Libs Update
author tercio
date Tue, 25 Nov 2014 21:15:10 -0200
parents fc346da3afd9
children 9ad7f3c634f1
line wrap: on
line diff
--- a/Libs/AceTimer-3.0/AceTimer-3.0.lua	Wed Oct 15 11:50:40 2014 -0300
+++ b/Libs/AceTimer-3.0/AceTimer-3.0.lua	Tue Nov 25 21:15:10 2014 -0200
@@ -2,8 +2,8 @@
 -- AceTimer supports one-shot timers and repeating timers. All timers are stored in an efficient
 -- data structure that allows easy dispatching and fast rescheduling. Timers can be registered
 -- or canceled at any time, even from within a running timer, without conflict or large overhead.\\
--- AceTimer is currently limited to firing timers at a frequency of 0.01s. This constant may change
--- in the future, but for now it's required as animations with lower frequencies are buggy.
+-- AceTimer is currently limited to firing timers at a frequency of 0.01s as this is what the WoW timer API
+-- restricts us to.
 --
 -- All `:Schedule` functions will return a handle to the current timer, which you will need to store if you
 -- need to cancel the timer you just registered.
@@ -15,79 +15,63 @@
 -- make into AceTimer.
 -- @class file
 -- @name AceTimer-3.0
--- @release $Id: AceTimer-3.0.lua 1079 2013-02-17 19:56:06Z funkydude $
+-- @release $Id: AceTimer-3.0.lua 1119 2014-10-14 17:23:29Z nevcairiel $
 
-local MAJOR, MINOR = "AceTimer-3.0", 16 -- Bump minor on changes
+local MAJOR, MINOR = "AceTimer-3.0", 17 -- Bump minor on changes
 local AceTimer, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
 
 if not AceTimer then return end -- No upgrade needed
-
-AceTimer.frame = AceTimer.frame or CreateFrame("Frame", "AceTimer30Frame") -- Animation parent
-AceTimer.inactiveTimers = AceTimer.inactiveTimers or {}                    -- Timer recycling storage
-AceTimer.activeTimers = AceTimer.activeTimers or {}                        -- Active timer list
+AceTimer.activeTimers = AceTimer.activeTimers or {} -- Active timer list
+local activeTimers = AceTimer.activeTimers -- Upvalue our private data
 
 -- Lua APIs
-local type, unpack, next, error, pairs, tostring, select = type, unpack, next, error, pairs, tostring, select
+local type, unpack, next, error, select = type, unpack, next, error, select
+-- WoW APIs
+local GetTime, C_TimerAfter = GetTime, C_Timer.After
 
--- Upvalue our private data
-local inactiveTimers = AceTimer.inactiveTimers
-local activeTimers = AceTimer.activeTimers
-
-local function OnFinished(self)
-	local id = self.id
-	if type(self.func) == "string" then
-		-- We manually set the unpack count to prevent issues with an arg set that contains nil and ends with nil
-		-- e.g. local t = {1, 2, nil, 3, nil} print(#t) will result in 2, instead of 5. This fixes said issue.
-		self.object[self.func](self.object, unpack(self.args, 1, self.argsCount))
-	else
-		self.func(unpack(self.args, 1, self.argsCount))
+local function new(self, loop, func, delay, ...)
+	if delay < 0.01 then
+		delay = 0.01 -- Restrict to the lowest time that the C_Timer API allows us
 	end
 
-	-- If the id is different it means that the timer was already cancelled
-	-- and has been used to create a new timer during the OnFinished callback.
-	if not self.looping and id == self.id then
-		activeTimers[self.id] = nil
-		self.args = nil
-		inactiveTimers[self] = true
-	end
-end
-
-local function new(self, loop, func, delay, ...)
-	local timer = next(inactiveTimers)
-	if timer then
-		inactiveTimers[timer] = nil
-	else
-		local anim = AceTimer.frame:CreateAnimationGroup()
-		timer = anim:CreateAnimation()
-		timer:SetScript("OnFinished", OnFinished)
-	end
-
-	-- Very low delays cause the animations to fail randomly.
-	-- A limited resolution of 0.01 seems reasonable.
-	if delay < 0.01 then
-		delay = 0.01
-	end
-
+	local timer = {...}
 	timer.object = self
 	timer.func = func
 	timer.looping = loop
-	timer.args = {...}
 	timer.argsCount = select("#", ...)
+	timer.delay = delay
+	timer.ends = GetTime() + delay
 
-	local anim = timer:GetParent()
-	if loop then
-		anim:SetLooping("REPEAT")
-	else
-		anim:SetLooping("NONE")
+	activeTimers[timer] = timer
+
+	-- Create new timer closure to wrap the "timer" object
+	timer.callback = function() 
+		if not timer.cancelled then
+			if type(timer.func) == "string" then
+				-- We manually set the unpack count to prevent issues with an arg set that contains nil and ends with nil
+				-- e.g. local t = {1, 2, nil, 3, nil} print(#t) will result in 2, instead of 5. This fixes said issue.
+				timer.object[timer.func](timer.object, unpack(timer, 1, timer.argsCount))
+			else
+				timer.func(unpack(timer, 1, timer.argsCount))
+			end
+
+			if timer.looping and not timer.cancelled then
+				-- Compensate delay to get a perfect average delay, even if individual times don't match up perfectly
+				-- due to fps differences
+				local time = GetTime()
+				local delay = timer.delay - (time - timer.ends)
+				-- Ensure the delay doesn't go below the threshold
+				if delay < 0.01 then delay = 0.01 end
+				C_TimerAfter(delay, timer.callback)
+				timer.ends = time + delay
+			else
+				activeTimers[timer.handle or timer] = nil
+			end
+		end
 	end
-	timer:SetDuration(delay)
 
-	local id = tostring(timer.args)
-	timer.id = id
-	activeTimers[id] = timer
-
-	anim:Play()
-	return id
+	C_TimerAfter(delay, timer.callback)
+	return timer
 end
 
 --- Schedule a new one-shot timer.
@@ -160,15 +144,14 @@
 -- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
 function AceTimer:CancelTimer(id)
 	local timer = activeTimers[id]
-	if not timer then return false end
 
-	local anim = timer:GetParent()
-	anim:Stop()
-
-	activeTimers[id] = nil
-	timer.args = nil
-	inactiveTimers[timer] = true
-	return true
+	if not timer then
+		return false
+	else
+		timer.cancelled = true
+		activeTimers[id] = nil
+		return true
+	end
 end
 
 --- Cancels all timers registered to the current addon object ('self')
@@ -186,15 +169,18 @@
 -- @return The time left on the timer.
 function AceTimer:TimeLeft(id)
 	local timer = activeTimers[id]
-	if not timer then return 0 end
-	return timer:GetDuration() - timer:GetElapsed()
+	if not timer then
+		return 0
+	else
+		return timer.ends - GetTime()
+	end
 end
 
 
 -- ---------------------------------------------------------------------
 -- Upgrading
 
--- Upgrade from old hash-bucket based timers to animation timers
+-- Upgrade from old hash-bucket based timers to C_Timer.After timers.
 if oldminor and oldminor < 10 then
 	-- disable old timer logic
 	AceTimer.frame:SetScript("OnUpdate", nil)
@@ -204,42 +190,58 @@
 	for object,timers in pairs(AceTimer.selfs) do
 		for handle,timer in pairs(timers) do
 			if type(timer) == "table" and timer.callback then
-				local id
+				local newTimer
 				if timer.delay then
-					id = AceTimer.ScheduleRepeatingTimer(timer.object, timer.callback, timer.delay, timer.arg)
+					newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.callback, timer.delay, timer.arg)
 				else
-					id = AceTimer.ScheduleTimer(timer.object, timer.callback, timer.when - GetTime(), timer.arg)
+					newTimer = AceTimer.ScheduleTimer(timer.object, timer.callback, timer.when - GetTime(), timer.arg)
 				end
-				-- change id to the old handle
-				local t = activeTimers[id]
-				activeTimers[id] = nil
-				activeTimers[handle] = t
-				t.id = handle
+				-- Use the old handle for old timers
+				activeTimers[newTimer] = nil
+				activeTimers[handle] = newTimer
+				newTimer.handle = handle
 			end
 		end
 	end
 	AceTimer.selfs = nil
 	AceTimer.hash = nil
 	AceTimer.debug = nil
-elseif oldminor and oldminor < 13 then
-	for handle, id in pairs(AceTimer.hashCompatTable) do
-		local t = activeTimers[id]
-		if t then
-			activeTimers[id] = nil
-			activeTimers[handle] = t
-			t.id = handle
+elseif oldminor and oldminor < 17 then
+	-- Upgrade from old animation based timers to C_Timer.After timers.
+	AceTimer.inactiveTimers = nil
+	AceTimer.frame = nil
+	local oldTimers = AceTimer.activeTimers
+	-- Clear old timer table and update upvalue
+	AceTimer.activeTimers = {}
+	activeTimers = AceTimer.activeTimers
+	for handle, timer in pairs(oldTimers) do
+		local newTimer
+		-- Stop the old timer animation
+		local duration, elapsed = timer:GetDuration(), timer:GetElapsed()
+		timer:GetParent():Stop()
+		if timer.looping then
+			newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.func, duration, unpack(timer.args, 1, timer.argsCount))
+		else
+			newTimer = AceTimer.ScheduleTimer(timer.object, timer.func, duration - elapsed, unpack(timer.args, 1, timer.argsCount))
 		end
+		-- Use the old handle for old timers
+		activeTimers[newTimer] = nil
+		activeTimers[handle] = newTimer
+		newTimer.handle = handle
 	end
-	AceTimer.hashCompatTable = nil
-end
 
--- upgrade existing timers to the latest OnFinished
-for timer in pairs(inactiveTimers) do
-	timer:SetScript("OnFinished", OnFinished)
-end
-
-for _,timer in pairs(activeTimers) do
-	timer:SetScript("OnFinished", OnFinished)
+	-- Migrate transitional handles
+	if oldminor < 13 and AceTimer.hashCompatTable then
+		for handle, id in pairs(AceTimer.hashCompatTable) do
+			local t = activeTimers[id]
+			if t then
+				activeTimers[id] = nil
+				activeTimers[handle] = t
+				t.handle = handle
+			end
+		end
+		AceTimer.hashCompatTable = nil
+	end
 end
 
 -- ---------------------------------------------------------------------