From 112858b773b0faa7d2375b6b47608347651182c4 Mon Sep 17 00:00:00 2001
From: Andrew6810 <16847730+andrew6180@users.noreply.github.com>
Date: Mon, 16 Jan 2023 15:48:47 -0700
Subject: [PATCH] Bagnon/Bag: Embed aceevent and acetimer for acebucket to work
---
Bagnon/embeds.xml | 1 +
Bagnon/libs/AceTimer-3.0/AceTimer-3.0.lua | 473 ++++++++++++++++++++++
Bagnon/libs/AceTimer-3.0/AceTimer-3.0.xml | 4 +
3 files changed, 478 insertions(+)
create mode 100644 Bagnon/libs/AceTimer-3.0/AceTimer-3.0.lua
create mode 100644 Bagnon/libs/AceTimer-3.0/AceTimer-3.0.xml
diff --git a/Bagnon/embeds.xml b/Bagnon/embeds.xml
index 0eefd15..3f31653 100644
--- a/Bagnon/embeds.xml
+++ b/Bagnon/embeds.xml
@@ -3,6 +3,7 @@
+
diff --git a/Bagnon/libs/AceTimer-3.0/AceTimer-3.0.lua b/Bagnon/libs/AceTimer-3.0/AceTimer-3.0.lua
new file mode 100644
index 0000000..fdb2cff
--- /dev/null
+++ b/Bagnon/libs/AceTimer-3.0/AceTimer-3.0.lua
@@ -0,0 +1,473 @@
+--- **AceTimer-3.0** provides a central facility for registering timers.
+-- 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, rescheduled
+-- 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.1s. This constant may change
+-- in the future, but for now it seemed like a good compromise in efficiency and accuracy.
+--
+-- All `:Schedule` functions will return a handle to the current timer, which you will need to store if you
+-- need to cancel or reschedule the timer you just registered.
+--
+-- **AceTimer-3.0** can be embeded into your addon, either explicitly by calling AceTimer:Embed(MyAddon) or by
+-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
+-- and can be accessed directly, without having to explicitly call AceTimer itself.\\
+-- It is recommended to embed AceTimer, otherwise you'll have to specify a custom `self` on all calls you
+-- make into AceTimer.
+-- @class file
+-- @name AceTimer-3.0
+-- @release $Id: AceTimer-3.0.lua 895 2009-12-06 16:28:55Z nevcairiel $
+
+--[[
+ Basic assumptions:
+ * In a typical system, we do more re-scheduling per second than there are timer pulses per second
+ * Regardless of timer implementation, we cannot guarantee timely delivery due to FPS restriction (may be as low as 10)
+
+ This implementation:
+ CON: The smallest timer interval is constrained by HZ (currently 1/10s).
+ PRO: It will still correctly fire any timer slower than HZ over a length of time, e.g. 0.11s interval -> 90 times over 10 seconds
+ PRO: In lag bursts, the system simly skips missed timer intervals to decrease load
+ CON: Algorithms depending on a timer firing "N times per minute" will fail
+ PRO: (Re-)scheduling is O(1) with a VERY small constant. It's a simple linked list insertion in a hash bucket.
+ CAUTION: The BUCKETS constant constrains how many timers can be efficiently handled. With too many hash collisions, performance will decrease.
+
+ Major assumptions upheld:
+ - ALLOWS scheduling multiple timers with the same funcref/method
+ - ALLOWS scheduling more timers during OnUpdate processing
+ - ALLOWS unscheduling ANY timer (including the current running one) at any time, including during OnUpdate processing
+]]
+
+local MAJOR, MINOR = "AceTimer-3.0", 5
+local AceTimer, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceTimer then return end -- No upgrade needed
+
+AceTimer.hash = AceTimer.hash or {} -- Array of [0..BUCKET-1] = linked list of timers (using .next member)
+ -- Linked list gets around ACE-88 and ACE-90.
+AceTimer.selfs = AceTimer.selfs or {} -- Array of [self]={[handle]=timerobj, [handle2]=timerobj2, ...}
+AceTimer.frame = AceTimer.frame or CreateFrame("Frame", "AceTimer30Frame")
+
+-- Lua APIs
+local assert, error, loadstring = assert, error, loadstring
+local setmetatable, rawset, rawget = setmetatable, rawset, rawget
+local select, pairs, type, next, tostring = select, pairs, type, next, tostring
+local floor, max, min = math.floor, math.max, math.min
+local tconcat = table.concat
+
+-- WoW APIs
+local GetTime = GetTime
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: DEFAULT_CHAT_FRAME, geterrorhandler
+
+-- Simple ONE-SHOT timer cache. Much more efficient than a full compost for our purposes.
+local timerCache = nil
+
+--[[
+ Timers will not be fired more often than HZ-1 times per second.
+ Keep at intended speed PLUS ONE or we get bitten by floating point rounding errors (n.5 + 0.1 can be n.599999)
+ If this is ever LOWERED, all existing timers need to be enforced to have a delay >= 1/HZ on lib upgrade.
+ If this number is ever changed, all entries need to be rehashed on lib upgrade.
+ ]]
+local HZ = 11
+
+--[[
+ Prime for good distribution
+ If this number is ever changed, all entries need to be rehashed on lib upgrade.
+]]
+local BUCKETS = 131
+
+local hash = AceTimer.hash
+for i=1,BUCKETS do
+ hash[i] = hash[i] or false -- make it an integer-indexed array; it's faster than hashes
+end
+
+--[[
+ xpcall safecall implementation
+]]
+local xpcall = xpcall
+
+local function errorhandler(err)
+ return geterrorhandler()(err)
+end
+
+local function CreateDispatcher(argCount)
+ local code = [[
+ local xpcall, eh = ... -- our arguments are received as unnamed values in "..." since we don't have a proper function declaration
+ local method, ARGS
+ local function call() return method(ARGS) end
+
+ local function dispatch(func, ...)
+ method = func
+ if not method then return end
+ ARGS = ...
+ return xpcall(call, eh)
+ end
+
+ return dispatch
+ ]]
+
+ local ARGS = {}
+ for i = 1, argCount do ARGS[i] = "arg"..i end
+ code = code:gsub("ARGS", tconcat(ARGS, ", "))
+ return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler)
+end
+
+local Dispatchers = setmetatable({}, {
+ __index=function(self, argCount)
+ local dispatcher = CreateDispatcher(argCount)
+ rawset(self, argCount, dispatcher)
+ return dispatcher
+ end
+})
+Dispatchers[0] = function(func)
+ return xpcall(func, errorhandler)
+end
+
+local function safecall(func, ...)
+ return Dispatchers[select('#', ...)](func, ...)
+end
+
+local lastint = floor(GetTime() * HZ)
+
+-- --------------------------------------------------------------------
+-- OnUpdate handler
+--
+-- traverse buckets, always chasing "now", and fire timers that have expired
+
+local function OnUpdate()
+ local now = GetTime()
+ local nowint = floor(now * HZ)
+
+ -- Have we passed into a new hash bucket?
+ if nowint == lastint then return end
+
+ local soon = now + 1 -- +1 is safe as long as 1 < HZ < BUCKETS/2
+
+ -- Pass through each bucket at most once
+ -- Happens on e.g. instance loads, but COULD happen on high local load situations also
+ for curint = (max(lastint, nowint - BUCKETS) + 1), nowint do -- loop until we catch up with "now", usually only 1 iteration
+ local curbucket = (curint % BUCKETS)+1
+ -- Yank the list of timers out of the bucket and empty it. This allows reinsertion in the currently-processed bucket from callbacks.
+ local nexttimer = hash[curbucket]
+ hash[curbucket] = false -- false rather than nil to prevent the array from becoming a hash
+
+ while nexttimer do
+ local timer = nexttimer
+ nexttimer = timer.next
+ local when = timer.when
+
+ if when < soon then
+ -- Call the timer func, either as a method on given object, or a straight function ref
+ local callback = timer.callback
+ if type(callback) == "string" then
+ safecall(timer.object[callback], timer.object, timer.arg)
+ elseif callback then
+ safecall(callback, timer.arg)
+ else
+ -- probably nilled out by CancelTimer
+ timer.delay = nil -- don't reschedule it
+ end
+
+ local delay = timer.delay -- NOW make a local copy, can't do it earlier in case the timer cancelled itself in the callback
+
+ if not delay then
+ -- single-shot timer (or cancelled)
+ AceTimer.selfs[timer.object][tostring(timer)] = nil
+ timerCache = timer
+ else
+ -- repeating timer
+ local newtime = when + delay
+ if newtime < now then -- Keep lag from making us firing a timer unnecessarily. (Note that this still won't catch too-short-delay timers though.)
+ newtime = now + delay
+ end
+ timer.when = newtime
+
+ -- add next timer execution to the correct bucket
+ local bucket = (floor(newtime * HZ) % BUCKETS) + 1
+ timer.next = hash[bucket]
+ hash[bucket] = timer
+ end
+ else -- if when>=soon
+ -- reinsert (yeah, somewhat expensive, but shouldn't be happening too often either due to hash distribution)
+ timer.next = hash[curbucket]
+ hash[curbucket] = timer
+ end -- if whenhandle->timer registry
+ local handle = tostring(timer)
+
+ local selftimers = AceTimer.selfs[self]
+ if not selftimers then
+ selftimers = {}
+ AceTimer.selfs[self] = selftimers
+ end
+ selftimers[handle] = timer
+ selftimers.__ops = (selftimers.__ops or 0) + 1
+
+ return handle
+end
+
+--- Schedule a new one-shot timer.
+-- The timer will fire once in `delay` seconds, unless canceled before.
+-- @param callback Callback function for the timer pulse (funcref or method name).
+-- @param delay Delay for the timer, in seconds.
+-- @param arg An optional argument to be passed to the callback function.
+-- @usage
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("TimerTest", "AceTimer-3.0")
+--
+-- function MyAddon:OnEnable()
+-- self:ScheduleTimer("TimerFeedback", 5)
+-- end
+--
+-- function MyAddon:TimerFeedback()
+-- print("5 seconds passed")
+-- end
+function AceTimer:ScheduleTimer(callback, delay, arg)
+ return Reg(self, callback, delay, arg)
+end
+
+--- Schedule a repeating timer.
+-- The timer will fire every `delay` seconds, until canceled.
+-- @param callback Callback function for the timer pulse (funcref or method name).
+-- @param delay Delay for the timer, in seconds.
+-- @param arg An optional argument to be passed to the callback function.
+-- @usage
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("TimerTest", "AceTimer-3.0")
+--
+-- function MyAddon:OnEnable()
+-- self.timerCount = 0
+-- self.testTimer = self:ScheduleRepeatingTimer("TimerFeedback", 5)
+-- end
+--
+-- function MyAddon:TimerFeedback()
+-- self.timerCount = self.timerCount + 1
+-- print(("%d seconds passed"):format(5 * self.timerCount))
+-- -- run 30 seconds in total
+-- if self.timerCount == 6 then
+-- self:CancelTimer(self.testTimer)
+-- end
+-- end
+function AceTimer:ScheduleRepeatingTimer(callback, delay, arg)
+ return Reg(self, callback, delay, arg, true)
+end
+
+--- Cancels a timer with the given handle, registered by the same addon object as used for `:ScheduleTimer`
+-- Both one-shot and repeating timers can be canceled with this function, as long as the `handle` is valid
+-- and the timer has not fired yet or was canceled before.
+-- @param handle The handle of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
+-- @param silent If true, no error is raised if the timer handle is invalid (expired or already canceled)
+-- @return True if the timer was successfully cancelled.
+function AceTimer:CancelTimer(handle, silent)
+ if not handle then return end -- nil handle -> bail out without erroring
+ if type(handle) ~= "string" then
+ error(MAJOR..": CancelTimer(handle): 'handle' - expected a string", 2) -- for now, anyway
+ end
+ local selftimers = AceTimer.selfs[self]
+ local timer = selftimers and selftimers[handle]
+ if silent then
+ if timer then
+ timer.callback = nil -- don't run it again
+ timer.delay = nil -- if this is the currently-executing one: don't even reschedule
+ -- The timer object is removed in the OnUpdate loop
+ end
+ return not not timer -- might return "true" even if we double-cancel. we'll live.
+ else
+ if not timer then
+ geterrorhandler()(MAJOR..": CancelTimer(handle[, silent]): '"..tostring(handle).."' - no such timer registered")
+ return false
+ end
+ if not timer.callback then
+ geterrorhandler()(MAJOR..": CancelTimer(handle[, silent]): '"..tostring(handle).."' - timer already cancelled or expired")
+ return false
+ end
+ timer.callback = nil -- don't run it again
+ timer.delay = nil -- if this is the currently-executing one: don't even reschedule
+ return true
+ end
+end
+
+--- Cancels all timers registered to the current addon object ('self')
+function AceTimer:CancelAllTimers()
+ if not(type(self) == "string" or type(self) == "table") then
+ error(MAJOR..": CancelAllTimers(): 'self' - must be a string or a table",2)
+ end
+ if self == AceTimer then
+ error(MAJOR..": CancelAllTimers(): supply a meaningful 'self'", 2)
+ end
+
+ local selftimers = AceTimer.selfs[self]
+ if selftimers then
+ for handle,v in pairs(selftimers) do
+ if type(v) == "table" then -- avoid __ops, etc
+ AceTimer.CancelTimer(self, handle, true)
+ end
+ end
+ end
+end
+
+--- Returns the time left for a timer with the given handle, registered by the current addon object ('self').
+-- This function will raise a warning when the handle is invalid, but not stop execution.
+-- @param handle The handle of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
+-- @return The time left on the timer, or false if the handle is invalid.
+function AceTimer:TimeLeft(handle)
+ if not handle then return end
+ if type(handle) ~= "string" then
+ error(MAJOR..": TimeLeft(handle): 'handle' - expected a string", 2) -- for now, anyway
+ end
+ local selftimers = AceTimer.selfs[self]
+ local timer = selftimers and selftimers[handle]
+ if not timer then
+ geterrorhandler()(MAJOR..": TimeLeft(handle): '"..tostring(handle).."' - no such timer registered")
+ return false
+ end
+ return timer.when - GetTime()
+end
+
+
+-- ---------------------------------------------------------------------
+-- PLAYER_REGEN_ENABLED: Run through our .selfs[] array step by step
+-- and clean it out - otherwise the table indices can grow indefinitely
+-- if an addon starts and stops a lot of timers. AceBucket does this!
+--
+-- See ACE-94 and tests/AceTimer-3.0-ACE-94.lua
+
+local lastCleaned = nil
+
+local function OnEvent(this, event)
+ if event~="PLAYER_REGEN_ENABLED" then
+ return
+ end
+
+ -- Get the next 'self' to process
+ local selfs = AceTimer.selfs
+ local self = next(selfs, lastCleaned)
+ if not self then
+ self = next(selfs)
+ end
+ lastCleaned = self
+ if not self then -- should only happen if .selfs[] is empty
+ return
+ end
+
+ -- Time to clean it out?
+ local list = selfs[self]
+ if (list.__ops or 0) < 250 then -- 250 slosh indices = ~10KB wasted (max!). For one 'self'.
+ return
+ end
+
+ -- Create a new table and copy all members over
+ local newlist = {}
+ local n=0
+ for k,v in pairs(list) do
+ newlist[k] = v
+ n=n+1
+ end
+ newlist.__ops = 0 -- Reset operation count
+
+ -- And since we now have a count of the number of live timers, check that it's reasonable. Emit a warning if not.
+ if n>BUCKETS then
+ DEFAULT_CHAT_FRAME:AddMessage(MAJOR..": Warning: The addon/module '"..tostring(self).."' has "..n.." live timers. Surely that's not intended?")
+ end
+
+ selfs[self] = newlist
+end
+
+-- ---------------------------------------------------------------------
+-- Embed handling
+
+AceTimer.embeds = AceTimer.embeds or {}
+
+local mixins = {
+ "ScheduleTimer", "ScheduleRepeatingTimer",
+ "CancelTimer", "CancelAllTimers",
+ "TimeLeft"
+}
+
+function AceTimer:Embed(target)
+ AceTimer.embeds[target] = true
+ for _,v in pairs(mixins) do
+ target[v] = AceTimer[v]
+ end
+ return target
+end
+
+-- AceTimer:OnEmbedDisable( target )
+-- target (object) - target object that AceTimer is embedded in.
+--
+-- cancel all timers registered for the object
+function AceTimer:OnEmbedDisable( target )
+ target:CancelAllTimers()
+end
+
+
+for addon in pairs(AceTimer.embeds) do
+ AceTimer:Embed(addon)
+end
+
+-- ---------------------------------------------------------------------
+-- Debug tools (expose copies of internals to test suites)
+AceTimer.debug = AceTimer.debug or {}
+AceTimer.debug.HZ = HZ
+AceTimer.debug.BUCKETS = BUCKETS
+
+-- ---------------------------------------------------------------------
+-- Finishing touchups
+
+AceTimer.frame:SetScript("OnUpdate", OnUpdate)
+AceTimer.frame:SetScript("OnEvent", OnEvent)
+AceTimer.frame:RegisterEvent("PLAYER_REGEN_ENABLED")
+
+-- In theory, we should hide&show the frame based on there being timers or not.
+-- However, this job is fairly expensive, and the chance that there will
+-- actually be zero timers running is diminuitive to say the lest.
diff --git a/Bagnon/libs/AceTimer-3.0/AceTimer-3.0.xml b/Bagnon/libs/AceTimer-3.0/AceTimer-3.0.xml
new file mode 100644
index 0000000..38e9021
--- /dev/null
+++ b/Bagnon/libs/AceTimer-3.0/AceTimer-3.0.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file