--[[ This library is intended to fix the shortfalls of using debugprofilestop() for getting accurate sub-second timing in addons. Specifically, this library aims to prevent any conflicts that may arrise with multiple addons using debugprofilestart and debugprofilestop. While perfect accuracy is not guarenteed due to the potential for an addon to load before this library and use the original debugprofilestart/debugprofilestop functions, this library provides a best-effort means of correcting any issues if this is the case. The best solution is for addons to NOT use debugprofilestart() and to NOT store a local reference to debugprofilestop(), even if they aren't using this library directly. ------------------------------------------------------------------------------- AccurateTime is hereby placed in the Public Domain See the wowace page for usage and documentation. Author: Sapu94 (sapu94@gmail.com) Website: http://www.wowace.com/addons/accuratetime/ --]] local _G = _G local AT_VERSION = 7 -- Check if we're already loaded -- If this is a newer version, remove the old hooks and we'll re-hook if _G.AccurateTime then if _G.AccurateTime.version > AT_VERSION then -- newer (or same) version already loaded - abort return end -- undo hook so we can re-hook debugprofilestart = _G.AccurateTime._debugprofilestart debugprofilestop = _G.AccurateTime._debugprofilestop end -- setup global library reference _G.AccurateTime = {} AccurateTime = _G.AccurateTime AccurateTime.version = AT_VERSION -- Store original functions. -- debugprofilestart should never be called, but we'll store it just in case. AccurateTime._debugprofilestop = debugprofilestop AccurateTime._debugprofilestart = debugprofilestart AccurateTime._currentDebugprofilestop = debugprofilestop -- other internal variables AccurateTime._errorTime = AccurateTime._errorTime or 0 AccurateTime._timers = AccurateTime._timers or {} -- Gets the current time in milliseconds. Will be directly from the original -- debugprofilestop() with any error we've detected added in. This error would -- come solely from an addon calling the unhooked debugprofilestart(). function AccurateTime:GetAbsTime() return AccurateTime._debugprofilestop() + AccurateTime._errorTime end -- It is up to the caller to ensure the key they are using is unique. -- Using table reference or description strings is preferable. -- If no key is specified, a unique key will be created and returned. -- If the timer is already running, restart it. -- Usage: local key = AccurateTime:GetTimer([key]) function AccurateTime:StartTimer(key) key = key or {} AccurateTime._timers[key] = AccurateTime._timers[key] or AccurateTime:GetAbsTime() return key end -- gets the current value of a timer -- Usage: local value = AccurateTime:GetTimer(key[, silent]) function AccurateTime:GetTimer(key, silent) assert(key, "No key specified.") if silent and not AccurateTime._timers[key] then return end assert(AccurateTime._timers[key], "No timer currently running for the given key.") return AccurateTime:GetAbsTime() - AccurateTime._timers[key] end -- Removes a timer and returns its current value. -- Usage: local value = AccurateTime:StopTimer(key) function AccurateTime:StopTimer(key) local value = AccurateTime:GetTimer(key) AccurateTime._timers[key] = nil return value end -- apply hooks debugprofilestart = function() error("You should never use debugprofilestart()!", 2) end debugprofilestop = function() return AccurateTime._currentDebugprofilestop() end -- Create an OnUpdate script to detect and attempt to correct other addons -- which use the original (non-hooked) debugprofilestart(). This should in -- theory never happen, but we'll do a best-effort correction if it does. local function OnUpdate(self) local absTime = AccurateTime:GetAbsTime() if absTime < self.lastUpdateAbsTime then -- debugprofilestart() was called and the back-end timer was reset -- Estimate what the absolute time should be using GetTime() (converted -- to ms) and add it to AccurateTime._errorTime. local realAbsTime = self.lastUpdateAbsTime + (GetTime() - self.lastUpdateTime) * 1000 AccurateTime._errorTime = AccurateTime._errorTime + (realAbsTime - absTime) if AccurateTime._errorTime > 0 then -- update AccurateTime._currentDebugprofilestop() to use our version of the function now that there is some error time AccurateTime._currentDebugprofilestop = function() return AccurateTime:GetAbsTime() end end end self.lastUpdateAbsTime = absTime self.lastUpdateTime = GetTime() end if not AccurateTime._frame then -- create frame just once AccurateTime._frame = CreateFrame("Frame") AccurateTime._frame.lastUpdateTime = GetTime() AccurateTime._frame.lastUpdateAbsTime = 0 end -- upgrade the frame AccurateTime._frame:SetScript("OnUpdate", OnUpdate) --[[ function AccurateTimeTest() local start = debugprofilestop() for i=1, 10000000 do end print("loop", debugprofilestop()-start) start = debugprofilestop() for i=1, 10000000 do debugprofilestop() end print("overriden", debugprofilestop()-start) start = debugprofilestop() for i=1, 10000000 do AccurateTime._debugprofilestop() end print("raw", debugprofilestop()-start) end --]]