chore(libs): sync Ace3 to coa-ace3 d422ad3

Aligns the unsuffixed Ace bundle with the rest of the Exiles forks
(the -ElvUI namespaced copies are intentionally left alone to keep
ElvUI's config UI isolated from any other addon's LibStub registrations).

Per-library MINOR moves:
  CallbackHandler-1.0   6  →  8
  AceAddon-3.0         12  → 13
  AceTimer-3.0       1017  → 17  (canonical numbering — same C_Timer impl)
  AceHook-3.0           8  →  9
  AceDB-3.0            27  → 33
  AceDBOptions-3.0     15  → 15  (unchanged)
  AceComm-3.0          12  → 14
  AceSerializer-3.0     3  →  5

Other Ace libs (Event, Config family, GUI, Locale, Console, Bucket) were
already at the canonical target.
This commit is contained in:
2026-05-24 17:05:40 +02:00
parent 5021fd1bce
commit 0c130e013b
41 changed files with 1340 additions and 1316 deletions
@@ -30,7 +30,7 @@
-- @name AceAddon-3.0.lua
-- @release $Id$
local MAJOR, MINOR = "AceAddon-3.0", 12
local MAJOR, MINOR = "AceAddon-3.0", 13
local AceAddon, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
if not AceAddon then return end -- No Upgrade needed.
@@ -49,10 +49,6 @@ local select, pairs, next, type, unpack = select, pairs, next, type, unpack
local loadstring, assert, error = loadstring, assert, error
local setmetatable, getmetatable, rawset, rawget = setmetatable, getmetatable, rawset, rawget
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: LibStub, IsLoggedIn, geterrorhandler
--[[
xpcall safecall implementation
]]
@@ -62,43 +58,12 @@ local function errorhandler(err)
return geterrorhandler()(err)
end
local function CreateDispatcher(argCount)
local code = [[
local xpcall, eh = ...
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, ...)
-- we check to see if the func is passed is actually a function here and don't error when it isn't
-- this safecall is used for optional functions like OnInitialize OnEnable etc. When they are not
-- present execution should continue without hinderance
if type(func) == "function" then
return Dispatchers[select('#', ...)](func, ...)
return xpcall(func, errorhandler, ...)
end
end
@@ -632,10 +597,20 @@ function AceAddon:IterateAddonStatus() return pairs(self.statuses) end
function AceAddon:IterateEmbedsOnAddon(addon) return pairs(self.embeds[addon]) end
function AceAddon:IterateModulesOfAddon(addon) return pairs(addon.modules) end
-- Blizzard AddOns which can load very early in the loading process and mess with Ace3 addon loading
local BlizzardEarlyLoadAddons = {
Blizzard_DebugTools = true,
Blizzard_TimeManager = true,
Blizzard_BattlefieldMap = true,
Blizzard_MapCanvas = true,
Blizzard_SharedMapDataProviders = true,
Blizzard_CombatLog = true,
}
-- Event Handling
local function onEvent(this, event, arg1)
-- 2011-08-17 nevcairiel - ignore the load event of Blizzard_DebugTools, so a potential startup error isn't swallowed up
if (event == "ADDON_LOADED" and arg1 ~= "Blizzard_DebugTools") or event == "PLAYER_LOGIN" then
-- 2020-08-28 nevcairiel - ignore the load event of Blizzard addons which occur early in the loading process
if (event == "ADDON_LOADED" and (arg1 == nil or not BlizzardEarlyLoadAddons[arg1])) or event == "PLAYER_LOGIN" then
-- if a addon loads another addon, recursion could happen here, so we need to validate the table on every iteration
while(#AceAddon.initializequeue > 0) do
local addon = tremove(AceAddon.initializequeue, 1)
@@ -34,7 +34,7 @@
-- end
-- @class file
-- @name AceBucket-3.0.lua
-- @release $Id: AceBucket-3.0.lua 1202 2019-05-15 23:11:22Z nevcairiel $
-- @release $Id$
local MAJOR, MINOR = "AceBucket-3.0", 4
local AceBucket, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
@@ -53,16 +53,12 @@ local type, next, pairs, select = type, next, pairs, select
local tonumber, tostring, rawset = tonumber, tostring, rawset
local assert, loadstring, error = assert, loadstring, error
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: LibStub, geterrorhandler
local bucketCache = setmetatable({}, {__mode='k'})
--[[
pcall safecall implementation
xpcall safecall implementation
]]
local pcall = pcall
local xpcall = xpcall
local function errorhandler(err)
return geterrorhandler()(err)
@@ -70,13 +66,7 @@ end
local function safecall(func, ...)
if func then
local ok, err = pcall(func, ...)
if ok then
return true
else
errorhandler(err)
return false
end
return xpcall(func, errorhandler, ...)
end
end
@@ -1,4 +1,4 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
<Script file="AceBucket-3.0.lua"/>
</Ui>
</Ui>
@@ -20,7 +20,7 @@ TODO: Time out old data rotting around from dead senders? Not a HUGE deal since
local CallbackHandler = LibStub("CallbackHandler-1.0")
local CTL = assert(ChatThrottleLib, "AceComm-3.0 requires ChatThrottleLib")
local MAJOR, MINOR = "AceComm-3.0", 12
local MAJOR, MINOR = "AceComm-3.0", 14
local AceComm,oldminor = LibStub:NewLibrary(MAJOR, MINOR)
if not AceComm then return end
@@ -28,12 +28,12 @@ if not AceComm then return end
-- Lua APIs
local type, next, pairs, tostring = type, next, pairs, tostring
local strsub, strfind = string.sub, string.find
local match = string.match
local tinsert, tconcat = table.insert, table.concat
local error, assert = error, assert
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: LibStub, DEFAULT_CHAT_FRAME, geterrorhandler
-- WoW APIs
local Ambiguate = Ambiguate
AceComm.embeds = AceComm.embeds or {}
@@ -41,21 +41,32 @@ AceComm.embeds = AceComm.embeds or {}
local MSG_MULTI_FIRST = "\001"
local MSG_MULTI_NEXT = "\002"
local MSG_MULTI_LAST = "\003"
local MSG_ESCAPE = "\004"
AceComm.multipart_origprefixes = AceComm.multipart_origprefixes or {} -- e.g. "Prefix\001"="Prefix", "Prefix\002"="Prefix"
AceComm.multipart_reassemblers = AceComm.multipart_reassemblers or {} -- e.g. "Prefix\001"="OnReceiveMultipartFirst"
-- remove old structures (pre WoW 4.0)
AceComm.multipart_origprefixes = nil
AceComm.multipart_reassemblers = nil
-- the multipart message spool: indexed by a combination of sender+distribution+
AceComm.multipart_spool = AceComm.multipart_spool or {}
--- Register for Addon Traffic on a specified prefix
-- @param prefix A printable character (\032-\255) classification of the message (typically AddonName or AddonNameEvent)
-- @param prefix A printable character (\032-\255) classification of the message (typically AddonName or AddonNameEvent), max 16 characters
-- @param method Callback to call on message reception: Function reference, or method name (string) to call on self. Defaults to "OnCommReceived"
function AceComm:RegisterComm(prefix, method)
if method == nil then
method = "OnCommReceived"
end
if #prefix > 16 then -- TODO: 15?
error("AceComm:RegisterComm(prefix,method): prefix length is limited to 16 characters")
end
if C_ChatInfo then
C_ChatInfo.RegisterAddonMessagePrefix(prefix)
else
RegisterAddonMessagePrefix(prefix)
end
return AceComm._RegisterComm(self, prefix, method) -- created by CallbackHandler
end
@@ -80,51 +91,49 @@ function AceComm:SendCommMessage(prefix, text, distribution, target, prio, callb
error('Usage: SendCommMessage(addon, "prefix", "text", "distribution"[, "target"[, "prio"[, callbackFn, callbackarg]]])', 2)
end
if strfind(prefix, "[\001-\009]") then
if strfind(prefix, "[\001-\003]") then
error("SendCommMessage: Characters \\001--\\003 in prefix are reserved for AceComm metadata", 2)
elseif not warnedPrefix then
-- I have some ideas about future extensions that require more control characters /mikk, 20090808
geterrorhandler()("SendCommMessage: Heads-up developers: Characters \\004--\\009 in prefix are reserved for AceComm future extension")
warnedPrefix = true
end
end
local textlen = #text
local maxtextlen = 254 - #prefix -- 254 is the max length of prefix + text that can be sent in one message
local queueName = prefix..distribution..(target or "")
local maxtextlen = 255 -- Yes, the max is 255 even if the dev post said 256. I tested. Char 256+ get silently truncated. /Mikk, 20110327
local queueName = prefix
local ctlCallback = nil
if callbackFn then
ctlCallback = function(sent)
return callbackFn(callbackArg, sent, textlen)
ctlCallback = function(sent, sendResult)
return callbackFn(callbackArg, sent, textlen, sendResult)
end
end
if textlen <= maxtextlen then
local forceMultipart
if match(text, "^[\001-\009]") then -- 4.1+: see if the first character is a control character
-- we need to escape the first character with a \004
if textlen+1 > maxtextlen then -- would we go over the size limit?
forceMultipart = true -- just make it multipart, no escape problems then
else
text = "\004" .. text
end
end
if not forceMultipart and textlen <= maxtextlen then
-- fits all in one message
CTL:SendAddonMessage(prio, prefix, text, distribution, target, queueName, ctlCallback, textlen)
else
maxtextlen = maxtextlen - 1 -- 1 extra byte for part indicator in prefix
maxtextlen = maxtextlen - 1 -- 1 extra byte for part indicator in prefix(4.0)/start of message(4.1)
-- first part
local chunk = strsub(text, 1, maxtextlen)
CTL:SendAddonMessage(prio, prefix..MSG_MULTI_FIRST, chunk, distribution, target, queueName, ctlCallback, maxtextlen)
CTL:SendAddonMessage(prio, prefix, MSG_MULTI_FIRST..chunk, distribution, target, queueName, ctlCallback, maxtextlen)
-- continuation
local pos = 1+maxtextlen
local prefix2 = prefix..MSG_MULTI_NEXT
while pos+maxtextlen <= textlen do
chunk = strsub(text, pos, pos+maxtextlen-1)
CTL:SendAddonMessage(prio, prefix2, chunk, distribution, target, queueName, ctlCallback, pos+maxtextlen-1)
CTL:SendAddonMessage(prio, prefix, MSG_MULTI_NEXT..chunk, distribution, target, queueName, ctlCallback, pos+maxtextlen-1)
pos = pos + maxtextlen
end
-- final part
chunk = strsub(text, pos)
CTL:SendAddonMessage(prio, prefix..MSG_MULTI_LAST, chunk, distribution, target, queueName, ctlCallback, textlen)
CTL:SendAddonMessage(prio, prefix, MSG_MULTI_LAST..chunk, distribution, target, queueName, ctlCallback, textlen)
end
end
@@ -221,47 +230,31 @@ end
----------------------------------------
if not AceComm.callbacks then
-- ensure that 'prefix to watch' table is consistent with registered
-- callbacks
AceComm.__prefixes = {}
AceComm.callbacks = CallbackHandler:New(AceComm,
"_RegisterComm",
"UnregisterComm",
"UnregisterAllComm")
end
function AceComm.callbacks:OnUsed(target, prefix)
AceComm.multipart_origprefixes[prefix..MSG_MULTI_FIRST] = prefix
AceComm.multipart_reassemblers[prefix..MSG_MULTI_FIRST] = "OnReceiveMultipartFirst"
AceComm.callbacks.OnUsed = nil
AceComm.callbacks.OnUnused = nil
AceComm.multipart_origprefixes[prefix..MSG_MULTI_NEXT] = prefix
AceComm.multipart_reassemblers[prefix..MSG_MULTI_NEXT] = "OnReceiveMultipartNext"
AceComm.multipart_origprefixes[prefix..MSG_MULTI_LAST] = prefix
AceComm.multipart_reassemblers[prefix..MSG_MULTI_LAST] = "OnReceiveMultipartLast"
end
function AceComm.callbacks:OnUnused(target, prefix)
AceComm.multipart_origprefixes[prefix..MSG_MULTI_FIRST] = nil
AceComm.multipart_reassemblers[prefix..MSG_MULTI_FIRST] = nil
AceComm.multipart_origprefixes[prefix..MSG_MULTI_NEXT] = nil
AceComm.multipart_reassemblers[prefix..MSG_MULTI_NEXT] = nil
AceComm.multipart_origprefixes[prefix..MSG_MULTI_LAST] = nil
AceComm.multipart_reassemblers[prefix..MSG_MULTI_LAST] = nil
end
local function OnEvent(this, event, ...)
local function OnEvent(self, event, prefix, message, distribution, sender)
if event == "CHAT_MSG_ADDON" then
local prefix,message,distribution,sender = ...
local reassemblername = AceComm.multipart_reassemblers[prefix]
if reassemblername then
-- multipart: reassemble
local aceCommReassemblerFunc = AceComm[reassemblername]
local origprefix = AceComm.multipart_origprefixes[prefix]
aceCommReassemblerFunc(AceComm, origprefix, message, distribution, sender)
sender = Ambiguate(sender, "none")
local control, rest = match(message, "^([\001-\009])(.*)")
if control then
if control==MSG_MULTI_FIRST then
AceComm:OnReceiveMultipartFirst(prefix, rest, distribution, sender)
elseif control==MSG_MULTI_NEXT then
AceComm:OnReceiveMultipartNext(prefix, rest, distribution, sender)
elseif control==MSG_MULTI_LAST then
AceComm:OnReceiveMultipartLast(prefix, rest, distribution, sender)
elseif control==MSG_ESCAPE then
AceComm.callbacks:Fire(prefix, rest, distribution, sender)
else
-- unknown control character, ignore SILENTLY (dont warn unnecessarily about future extensions!)
end
else
-- single part: fire it off immediately and let CallbackHandler decide if it's registered or not
AceComm.callbacks:Fire(prefix, message, distribution, sender)
@@ -23,7 +23,7 @@
-- LICENSE: ChatThrottleLib is released into the Public Domain
--
local CTL_VERSION = 24
local CTL_VERSION = 31
local _G = _G
@@ -74,9 +74,7 @@ local math_max = math.max
local next = next
local strlen = string.len
local GetFramerate = GetFramerate
local strlower = string.lower
local unpack,type,pairs,wipe = unpack,type,pairs,wipe
local UnitInRaid,GetNumPartyMembers = UnitInRaid,GetNumPartyMembers
local unpack,type,pairs,wipe = unpack,type,pairs,table.wipe
-----------------------------------------------------------------------
@@ -115,6 +113,23 @@ function Ring:Remove(obj)
end
end
-- Note that this is local because there's no upgrade logic for existing ring
-- metatables, and this isn't present on rings created in versions older than
-- v25.
local function Ring_Link(self, other) -- Move and append all contents of another ring to this ring
if not self.pos then
-- This ring is empty, so just transfer ownership.
self.pos = other.pos
other.pos = nil
elseif other.pos then
-- Our tail should point to their head, and their tail to our head.
self.pos.prev.next, other.pos.prev.next = other.pos, self.pos
-- Our head should point to their tail, and their head to our tail.
self.pos.prev, other.pos.prev = other.pos.prev, self.pos.prev
other.pos = nil
end
end
-----------------------------------------------------------------------
@@ -179,6 +194,13 @@ function ChatThrottleLib:Init()
self.Prio["BULK"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
end
if not self.BlockedQueuesDelay then
-- v25: Add blocked queues to rings to handle new client throttles.
for _, Prio in pairs(self.Prio) do
Prio.Blocked = Ring:New()
end
end
-- v4: total send counters per priority
for _, Prio in pairs(self.Prio) do
Prio.nTotalSent = Prio.nTotalSent or 0
@@ -201,6 +223,7 @@ function ChatThrottleLib:Init()
self.Frame:SetScript("OnEvent", self.OnEvent) -- v11: Monitor P_E_W so we can throttle hard for a few seconds
self.Frame:RegisterEvent("PLAYER_ENTERING_WORLD")
self.OnUpdateDelay = 0
self.BlockedQueuesDelay = 0
self.LastAvailUpdate = GetTime()
self.HardThrottlingBeginTime = GetTime() -- v11: Throttle hard for a few seconds after startup
@@ -209,14 +232,43 @@ function ChatThrottleLib:Init()
-- Use secure hooks as of v16. Old regular hook support yanked out in v21.
self.securelyHooked = true
--SendChatMessage
hooksecurefunc("SendChatMessage", function(...)
return ChatThrottleLib.Hook_SendChatMessage(...)
end)
if _G.C_ChatInfo and _G.C_ChatInfo.SendChatMessage then
hooksecurefunc(_G.C_ChatInfo, "SendChatMessage", function(...)
return ChatThrottleLib.Hook_SendChatMessage(...)
end)
else
hooksecurefunc("SendChatMessage", function(...)
return ChatThrottleLib.Hook_SendChatMessage(...)
end)
end
--SendAddonMessage
hooksecurefunc("SendAddonMessage", function(...)
hooksecurefunc(_G.C_ChatInfo, "SendAddonMessage", function(...)
return ChatThrottleLib.Hook_SendAddonMessage(...)
end)
end
-- v26: Hook SendAddonMessageLogged for traffic logging
if not self.securelyHookedLogged then
self.securelyHookedLogged = true
hooksecurefunc(_G.C_ChatInfo, "SendAddonMessageLogged", function(...)
return ChatThrottleLib.Hook_SendAddonMessageLogged(...)
end)
end
-- v29: Hook BNSendGameData for traffic logging
if not self.securelyHookedBNGameData then
self.securelyHookedBNGameData = true
if _G.C_BattleNet and _G.C_BattleNet.SendGameData then
hooksecurefunc(_G.C_BattleNet, "SendGameData", function(...)
return ChatThrottleLib.Hook_BNSendGameData(...)
end)
else
hooksecurefunc("BNSendGameData", function(...)
return ChatThrottleLib.Hook_BNSendGameData(...)
end)
end
end
self.nBypass = 0
end
@@ -245,6 +297,12 @@ function ChatThrottleLib.Hook_SendAddonMessage(prefix, text, chattype, destinati
self.avail = self.avail - size
self.nBypass = self.nBypass + size -- just a statistic
end
function ChatThrottleLib.Hook_SendAddonMessageLogged(prefix, text, chattype, destination, ...)
ChatThrottleLib.Hook_SendAddonMessage(prefix, text, chattype, destination, ...)
end
function ChatThrottleLib.Hook_BNSendGameData(destination, prefix, text)
ChatThrottleLib.Hook_SendAddonMessage(prefix, text, "WHISPER", destination)
end
@@ -262,7 +320,7 @@ function ChatThrottleLib:UpdateAvail()
-- First 5 seconds after startup/zoning: VERY hard clamping to avoid irritating the server rate limiter, it seems very cranky then
avail = math_min(avail + (newavail*0.1), MAX_CPS*0.5)
self.bChoking = true
elseif GetFramerate() < self.MIN_FPS then -- GetFramerate call takes ~0.002 secs
elseif GetFramerate() < self.MIN_FPS then -- GetFrameRate call takes ~0.002 secs
avail = math_min(MAX_CPS, avail + newavail*0.5)
self.bChoking = true -- just a statistic
else
@@ -286,38 +344,89 @@ end
-- - ... made up of N "Pipe"s (1 for each destination/pipename)
-- - and each pipe contains messages
local SendAddonMessageResult = Enum.SendAddonMessageResult or {
Success = 0,
AddonMessageThrottle = 3,
NotInGroup = 5,
ChannelThrottle = 8,
GeneralError = 9,
}
local function MapToSendResult(ok, ...)
local result
if not ok then
-- The send function itself errored; don't look at anything else.
result = SendAddonMessageResult.GeneralError
else
-- Grab the last return value from the send function and remap
-- it from a boolean to an enum code. If there are no results,
-- assume success (true).
result = select(-1, true, ...)
if result == true then
result = SendAddonMessageResult.Success
elseif result == false then
result = SendAddonMessageResult.GeneralError
end
end
return result
end
local function IsThrottledSendResult(result)
return result == SendAddonMessageResult.AddonMessageThrottle
end
-- A copy of this function exists in FrameXML, but for clarity it's here too.
local function CallErrorHandler(...)
return geterrorhandler()(...)
end
local function PerformSend(sendFunction, ...)
bMyTraffic = true
local sendResult = MapToSendResult(xpcall(sendFunction, CallErrorHandler, ...))
bMyTraffic = false
return sendResult
end
function ChatThrottleLib:Despool(Prio)
local ring = Prio.Ring
while ring.pos and Prio.avail > ring.pos[1].nSize do
local msg = table_remove(ring.pos, 1)
if not ring.pos[1] then -- did we remove last msg in this pipe?
local pipe = Prio.Ring.pos
local pipe = ring.pos
local msg = pipe[1]
local sendResult = PerformSend(msg.f, unpack(msg, 1, msg.n))
if IsThrottledSendResult(sendResult) then
-- Message was throttled; move the pipe into the blocked ring.
Prio.Ring:Remove(pipe)
Prio.ByName[pipe.name] = nil
DelPipe(pipe)
Prio.Blocked:Add(pipe)
else
Prio.Ring.pos = Prio.Ring.pos.next
end
local didSend=false
local lowerDest = strlower(msg[3] or "")
if lowerDest == "raid" and not UnitInRaid("player") then
-- do nothing
elseif lowerDest == "party" and GetNumPartyMembers() == 0 then
-- do nothing
else
Prio.avail = Prio.avail - msg.nSize
bMyTraffic = true
msg.f(unpack(msg, 1, msg.n))
bMyTraffic = false
Prio.nTotalSent = Prio.nTotalSent + msg.nSize
-- Dequeue message after submission.
table_remove(pipe, 1)
DelMsg(msg)
didSend = true
if not pipe[1] then -- did we remove last msg in this pipe?
Prio.Ring:Remove(pipe)
Prio.ByName[pipe.name] = nil
DelPipe(pipe)
else
ring.pos = ring.pos.next
end
-- Update bandwidth counters on successful sends.
local didSend = (sendResult == SendAddonMessageResult.Success)
if didSend then
Prio.avail = Prio.avail - msg.nSize
Prio.nTotalSent = Prio.nTotalSent + msg.nSize
end
-- Notify caller of message submission.
if msg.callbackFn then
securecallfunction(msg.callbackFn, msg.callbackArg, didSend, sendResult)
end
end
-- notify caller of delivery (even if we didn't send it)
if msg.callbackFn then
msg.callbackFn (msg.callbackArg, didSend)
end
-- USER CALLBACK MAY ERROR
end
end
@@ -336,6 +445,7 @@ function ChatThrottleLib.OnUpdate(this,delay)
local self = ChatThrottleLib
self.OnUpdateDelay = self.OnUpdateDelay + delay
self.BlockedQueuesDelay = self.BlockedQueuesDelay + delay
if self.OnUpdateDelay < 0.08 then
return
end
@@ -347,40 +457,60 @@ function ChatThrottleLib.OnUpdate(this,delay)
return -- argh. some bastard is spewing stuff past the lib. just bail early to save cpu.
end
-- See how many of our priorities have queued messages (we only have 3, don't worry about the loop)
local n = 0
for prioname,Prio in pairs(self.Prio) do
if Prio.Ring.pos or Prio.avail < 0 then
n = n + 1
-- Integrate blocked queues back into their rings periodically.
if self.BlockedQueuesDelay >= 0.35 then
for _, Prio in pairs(self.Prio) do
Ring_Link(Prio.Ring, Prio.Blocked)
end
self.BlockedQueuesDelay = 0
end
-- Anything queued still?
if n<1 then
-- Nope. Move spillover bandwidth to global availability gauge and clear self.bQueueing
for prioname, Prio in pairs(self.Prio) do
-- See how many of our priorities have queued messages. This is split
-- into two counters because priorities that consist only of blocked
-- queues must keep our OnUpdate alive, but shouldn't count toward
-- bandwidth distribution.
local nSendablePrios = 0
local nBlockedPrios = 0
for prioname, Prio in pairs(self.Prio) do
if Prio.Ring.pos then
nSendablePrios = nSendablePrios + 1
elseif Prio.Blocked.pos then
nBlockedPrios = nBlockedPrios + 1
end
-- Collect unused bandwidth from priorities with nothing to send.
if not Prio.Ring.pos then
self.avail = self.avail + Prio.avail
Prio.avail = 0
end
self.bQueueing = false
self.Frame:Hide()
end
-- Bandwidth reclamation may take us back over the burst cap.
self.avail = math_min(self.avail, self.BURST)
-- If we can't currently send on any priorities, stop processing early.
if nSendablePrios == 0 then
-- If we're completely out of data to send, disable queue processing.
if nBlockedPrios == 0 then
self.bQueueing = false
self.Frame:Hide()
end
return
end
-- There's stuff queued. Hand out available bandwidth to priorities as needed and despool their queues
local avail = self.avail/n
local avail = self.avail / nSendablePrios
self.avail = 0
for prioname, Prio in pairs(self.Prio) do
if Prio.Ring.pos or Prio.avail < 0 then
if Prio.Ring.pos then
Prio.avail = Prio.avail + avail
if Prio.Ring.pos and Prio.avail > Prio.Ring.pos[1].nSize then
self:Despool(Prio)
-- Note: We might not get here if the user-supplied callback function errors out! Take care!
end
self:Despool(Prio)
end
end
end
@@ -423,21 +553,27 @@ function ChatThrottleLib:SendChatMessage(prio, prefix, text, chattype, languag
-- Check if there's room in the global available bandwidth gauge to send directly
if not self.bQueueing and nSize < self:UpdateAvail() then
self.avail = self.avail - nSize
bMyTraffic = true
_G.SendChatMessage(text, chattype, language, destination)
bMyTraffic = false
self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize
if callbackFn then
callbackFn (callbackArg, true)
local sendResult = PerformSend(_G.C_ChatInfo.SendChatMessage or _G.SendChatMessage, text, chattype, language, destination)
if not IsThrottledSendResult(sendResult) then
local didSend = (sendResult == SendAddonMessageResult.Success)
if didSend then
self.avail = self.avail - nSize
self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize
end
if callbackFn then
securecallfunction(callbackFn, callbackArg, didSend, sendResult)
end
return
end
-- USER CALLBACK MAY ERROR
return
end
-- Message needs to be queued
local msg = NewMsg()
msg.f = _G.SendChatMessage
msg.f = _G.C_ChatInfo.SendChatMessage or _G.SendChatMessage
msg[1] = text
msg[2] = chattype or "SAY"
msg[3] = language
@@ -447,43 +583,36 @@ function ChatThrottleLib:SendChatMessage(prio, prefix, text, chattype, languag
msg.callbackFn = callbackFn
msg.callbackArg = callbackArg
self:Enqueue(prio, queueName or (prefix..(chattype or "SAY")..(destination or "")), msg)
self:Enqueue(prio, queueName or prefix, msg)
end
function ChatThrottleLib:SendAddonMessage(prio, prefix, text, chattype, target, queueName, callbackFn, callbackArg)
if not self or not prio or not prefix or not text or not chattype or not self.Prio[prio] then
error('Usage: ChatThrottleLib:SendAddonMessage("{BULK||NORMAL||ALERT}", "prefix", "text", "chattype"[, "target"])', 2)
end
if callbackFn and type(callbackFn)~="function" then
error('ChatThrottleLib:SendAddonMessage(): callbackFn: expected function, got '..type(callbackFn), 2)
end
local nSize = prefix:len() + 1 + text:len();
if nSize>255 then
error("ChatThrottleLib:SendAddonMessage(): prefix + message length cannot exceed 254 bytes", 2)
end
nSize = nSize + self.MSG_OVERHEAD;
local function SendAddonMessageInternal(self, sendFunction, prio, prefix, text, chattype, target, queueName, callbackFn, callbackArg)
local nSize = #text + self.MSG_OVERHEAD
-- Check if there's room in the global available bandwidth gauge to send directly
if not self.bQueueing and nSize < self:UpdateAvail() then
self.avail = self.avail - nSize
bMyTraffic = true
_G.SendAddonMessage(prefix, text, chattype, target)
bMyTraffic = false
self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize
if callbackFn then
callbackFn (callbackArg, true)
local sendResult = PerformSend(sendFunction, prefix, text, chattype, target)
if not IsThrottledSendResult(sendResult) then
local didSend = (sendResult == SendAddonMessageResult.Success)
if didSend then
self.avail = self.avail - nSize
self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize
end
if callbackFn then
securecallfunction(callbackFn, callbackArg, didSend, sendResult)
end
return
end
-- USER CALLBACK MAY ERROR
return
end
-- Message needs to be queued
local msg = NewMsg()
msg.f = _G.SendAddonMessage
msg.f = sendFunction
msg[1] = prefix
msg[2] = text
msg[3] = chattype
@@ -493,10 +622,65 @@ function ChatThrottleLib:SendAddonMessage(prio, prefix, text, chattype, target,
msg.callbackFn = callbackFn
msg.callbackArg = callbackArg
self:Enqueue(prio, queueName or (prefix..chattype..(target or "")), msg)
self:Enqueue(prio, queueName or prefix, msg)
end
function ChatThrottleLib:SendAddonMessage(prio, prefix, text, chattype, target, queueName, callbackFn, callbackArg)
if not self or not prio or not prefix or not text or not chattype or not self.Prio[prio] then
error('Usage: ChatThrottleLib:SendAddonMessage("{BULK||NORMAL||ALERT}", "prefix", "text", "chattype"[, "target"])', 2)
elseif callbackFn and type(callbackFn)~="function" then
error('ChatThrottleLib:SendAddonMessage(): callbackFn: expected function, got '..type(callbackFn), 2)
elseif #text>255 then
error("ChatThrottleLib:SendAddonMessage(): message length cannot exceed 255 bytes", 2)
end
local sendFunction = _G.C_ChatInfo.SendAddonMessage
SendAddonMessageInternal(self, sendFunction, prio, prefix, text, chattype, target, queueName, callbackFn, callbackArg)
end
function ChatThrottleLib:SendAddonMessageLogged(prio, prefix, text, chattype, target, queueName, callbackFn, callbackArg)
if not self or not prio or not prefix or not text or not chattype or not self.Prio[prio] then
error('Usage: ChatThrottleLib:SendAddonMessageLogged("{BULK||NORMAL||ALERT}", "prefix", "text", "chattype"[, "target"])', 2)
elseif callbackFn and type(callbackFn)~="function" then
error('ChatThrottleLib:SendAddonMessageLogged(): callbackFn: expected function, got '..type(callbackFn), 2)
elseif #text>255 then
error("ChatThrottleLib:SendAddonMessageLogged(): message length cannot exceed 255 bytes", 2)
end
local sendFunction = _G.C_ChatInfo.SendAddonMessageLogged
SendAddonMessageInternal(self, sendFunction, prio, prefix, text, chattype, target, queueName, callbackFn, callbackArg)
end
local function BNSendGameDataReordered(prefix, text, _, gameAccountID)
local bnSendFunc = _G.C_BattleNet and _G.C_BattleNet.SendGameData or _G.BNSendGameData
return bnSendFunc(gameAccountID, prefix, text)
end
function ChatThrottleLib:BNSendGameData(prio, prefix, text, chattype, gameAccountID, queueName, callbackFn, callbackArg)
-- Note that this API is intentionally limited to 255 bytes of data
-- for reasons of traffic fairness, which is less than the 4078 bytes
-- BNSendGameData natively supports. Additionally, a chat type is required
-- but must always be set to 'WHISPER' to match what is exposed by the
-- receipt event.
--
-- If splitting messages, callers must also be aware that message
-- delivery over BNSendGameData is unordered.
if not self or not prio or not prefix or not text or not gameAccountID or not chattype or not self.Prio[prio] then
error('Usage: ChatThrottleLib:BNSendGameData("{BULK||NORMAL||ALERT}", "prefix", "text", "chattype", gameAccountID)', 2)
elseif callbackFn and type(callbackFn)~="function" then
error('ChatThrottleLib:BNSendGameData(): callbackFn: expected function, got '..type(callbackFn), 2)
elseif #text>255 then
error("ChatThrottleLib:BNSendGameData(): message length cannot exceed 255 bytes", 2)
elseif chattype ~= "WHISPER" then
error("ChatThrottleLib:BNSendGameData(): chat type must be 'WHISPER'", 2)
end
local sendFunction = BNSendGameDataReordered
SendAddonMessageInternal(self, sendFunction, prio, prefix, text, chattype, gameAccountID, queueName, callbackFn, callbackArg)
end
-----------------------------------------------------------------------
@@ -29,10 +29,6 @@ local max = math.max
-- WoW APIs
local _G = _G
-- 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, SlashCmdList, hash_SlashCmdList
local tmp={}
local function Print(self,frame,...)
local n=0
@@ -174,7 +170,7 @@ function AceConsole:GetArgs(str, numargs, startpos)
while true do
-- find delimiter or hyperlink
local ch,_
local _
pos,_,ch = strfind(str, delim_or_pipe, pos)
if not pos then break end
+69 -13
View File
@@ -41,7 +41,7 @@
-- @class file
-- @name AceDB-3.0.lua
-- @release $Id$
local ACEDB_MAJOR, ACEDB_MINOR = "AceDB-3.0", 27
local ACEDB_MAJOR, ACEDB_MINOR = "AceDB-3.0", 33
local AceDB = LibStub:NewLibrary(ACEDB_MAJOR, ACEDB_MINOR)
if not AceDB then return end -- No upgrade needed
@@ -53,10 +53,6 @@ local setmetatable, rawset, rawget = setmetatable, rawset, rawget
-- WoW APIs
local _G = _G
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: LibStub
AceDB.db_registry = AceDB.db_registry or {}
AceDB.frame = AceDB.frame or CreateFrame("Frame")
@@ -98,11 +94,11 @@ local function copyDefaults(dest, src)
-- This is a metatable used for table defaults
local mt = {
-- This handles the lookup and creation of new subtables
__index = function(t,k)
if k == nil then return nil end
__index = function(t,k2)
if k2 == nil then return nil end
local tbl = {}
copyDefaults(tbl, v)
rawset(t, k, tbl)
rawset(t, k2, tbl)
return tbl
end,
}
@@ -115,7 +111,7 @@ local function copyDefaults(dest, src)
end
else
-- Values are not tables, so this is just a simple return
local mt = {__index = function(t,k) return k~=nil and v or nil end}
local mt = {__index = function(t,k2) return k2~=nil and v or nil end}
setmetatable(dest, mt)
end
elseif type(v) == "table" then
@@ -261,9 +257,12 @@ local _, classKey = UnitClass("player")
local _, raceKey = UnitRace("player")
local factionKey = UnitFactionGroup("player")
local factionrealmKey = factionKey .. " - " .. realmKey
local factionrealmregionKey = factionrealmKey .. " - " .. string.sub(GetCVar("realmList"), 1, 2):upper()
local localeKey = GetLocale():lower()
local regionTable = { "US", "KR", "EU", "TW", "CN" }
local regionKey = regionTable[GetCurrentRegion()] or GetCurrentRegionName() or "TR"
local factionrealmregionKey = factionrealmKey .. " - " .. regionKey
-- Actual database initialization function
local function initdb(sv, defaults, defaultProfile, olddb, parent)
-- Generate the database keys for each section
@@ -361,7 +360,7 @@ local function logoutHandler(frame, event)
-- cleanup sections that are empty without defaults
local sv = rawget(db, "sv")
for section in pairs(db.keys) do
for section in pairs(rawget(db, "keys")) do
if rawget(sv, section) then
-- global is special, all other sections have sub-entrys
-- also don't delete empty profiles on main dbs, only on namespaces
@@ -378,6 +377,26 @@ local function logoutHandler(frame, event)
end
end
end
-- second pass after everything else is cleaned up to remove empty namespaces
-- can't be run in-loop above since there is no guaranteed order
for db in pairs(AceDB.db_registry) do
local sv = rawget(db, "sv")
local namespaces = rawget(sv, "namespaces")
if namespaces then
for name in pairs(namespaces) do
-- cleanout empty profiles table, if still present
if namespaces[name].profiles and not next(namespaces[name].profiles) then
namespaces[name].profiles = nil
end
-- remove entire namespace, if needed
if not next(namespaces[name]) then
namespaces[name] = nil
end
end
end
end
end
end
@@ -526,6 +545,17 @@ function DBObjectLib:DeleteProfile(name, silent)
end
end
-- remove from unloaded namespaces
if self.sv.namespaces then
for nsname, data in pairs(self.sv.namespaces) do
if self.children and self.children[nsname] then
-- already a mapped namespace
elseif data.profiles then
data.profiles[name] = nil
end
end
end
-- switch all characters that use this profile back to the default
if self.sv.profileKeys then
for key, profile in pairs(self.sv.profileKeys) do
@@ -571,6 +601,20 @@ function DBObjectLib:CopyProfile(name, silent)
end
end
-- copy unloaded namespaces
if self.sv.namespaces then
for nsname, data in pairs(self.sv.namespaces) do
if self.children and self.children[nsname] then
-- already a mapped namespace
elseif data.profiles then
-- reset the current profile
data.profiles[self.keys.profile] = {}
-- copy data
copyTable(data.profiles[name], data.profiles[self.keys.profile])
end
end
end
-- Callback: OnProfileCopied, database, sourceProfileKey
self.callbacks:Fire("OnProfileCopied", self, name)
end
@@ -597,6 +641,18 @@ function DBObjectLib:ResetProfile(noChildren, noCallbacks)
end
end
-- reset unloaded namespaces
if self.sv.namespaces and not noChildren then
for nsname, data in pairs(self.sv.namespaces) do
if self.children and self.children[nsname] then
-- already a mapped namespace
elseif data.profiles then
-- reset the current profile
data.profiles[self.keys.profile] = nil
end
end
end
-- Callback: OnProfileReset, database
if not noCallbacks then
self.callbacks:Fire("OnProfileReset", self)
@@ -607,8 +663,8 @@ end
-- profile.
-- @param defaultProfile The profile name to use as the default
function DBObjectLib:ResetDB(defaultProfile)
if defaultProfile and type(defaultProfile) ~= "string" then
error(("Usage: AceDBObject:ResetDB(defaultProfile): 'defaultProfile' - string or nil expected, got %q."):format(type(defaultProfile)), 2)
if defaultProfile and type(defaultProfile) ~= "string" and defaultProfile ~= true then
error(("Usage: AceDBObject:ResetDB(defaultProfile): 'defaultProfile' - string or true expected, got %q."):format(type(defaultProfile)), 2)
end
local sv = self.sv
@@ -10,7 +10,7 @@
-- @class file
-- @name AceHook-3.0
-- @release $Id$
local ACEHOOK_MAJOR, ACEHOOK_MINOR = "AceHook-3.0", 8
local ACEHOOK_MAJOR, ACEHOOK_MINOR = "AceHook-3.0", 9
local AceHook, oldminor = LibStub:NewLibrary(ACEHOOK_MAJOR, ACEHOOK_MINOR)
if not AceHook then return end -- No upgrade needed
@@ -195,7 +195,6 @@ function hook(self, obj, method, handler, script, secure, raw, forceSecure, usag
registry[self][method] = nil
end
handlers[uid], actives[uid], scripts[uid] = nil, nil, nil
uid = nil
end
local orig
@@ -478,10 +477,10 @@ function AceHook:UnhookAll()
for key, value in pairs(registry[self]) do
if type(key) == "table" then
for method in pairs(value) do
self:Unhook(key, method)
AceHook.Unhook(self, key, method)
end
else
self:Unhook(key)
AceHook.Unhook(self, key)
end
end
end
@@ -2,7 +2,7 @@
-- @class file
-- @name AceLocale-3.0
-- @release $Id$
local MAJOR,MINOR = "AceLocale-3.0-ElvUI", 6
local MAJOR,MINOR = "AceLocale-3.0", 6
local AceLocale, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
@@ -12,10 +12,6 @@ if not AceLocale then return end -- no upgrade needed
local assert, tostring, error = assert, tostring, error
local getmetatable, setmetatable, rawset, rawget = getmetatable, setmetatable, rawset, rawget
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: GAME_LOCALE, geterrorhandler
local gameLocale = GetLocale()
if gameLocale == "enGB" then
gameLocale = "enUS"
@@ -91,6 +87,10 @@ local writedefaultproxy = setmetatable({}, {
-- L["string1"] = "Zeichenkette1"
-- @return Locale Table to add localizations to, or nil if the current locale is not required.
function AceLocale:NewLocale(application, locale, isDefault, silent)
-- GAME_LOCALE allows translators to test translations of addons without having that wow client installed
local activeGameLocale = GAME_LOCALE or gameLocale
local app = AceLocale.apps[application]
if silent and app and getmetatable(app) ~= readmetasilent then
@@ -107,14 +107,11 @@ function AceLocale:NewLocale(application, locale, isDefault, silent)
AceLocale.appnames[app] = application
end
-- ElvUI block
if (not app[locale]) or (app[locale] and type(app[locale]) ~= 'table') then
-- app[locale] = setmetatable({}, silent and readmetasilent or readmeta) -- To find missing keys
app[locale] = setmetatable({}, readmetasilent)
if locale ~= activeGameLocale and not isDefault then
return -- nop, we don't need these translations
end
registering = app[locale] -- remember globally for writeproxy and writedefaultproxy
-- end block
registering = app -- remember globally for writeproxy and writedefaultproxy
if isDefault then
return writedefaultproxy
@@ -128,16 +125,9 @@ end
-- @param application Unique name of addon / module
-- @param silent If true, the locale is optional, silently return nil if it's not found (defaults to false, optional)
-- @return The locale table for the current language.
--- Modified by ElvUI to add `locale` as second arg
function AceLocale:GetLocale(application, locale, silent)
if type(locale) == "boolean" then
silent = locale
locale = gameLocale
end
function AceLocale:GetLocale(application, silent)
if not silent and not AceLocale.apps[application] then
error("Usage: GetLocale(application[,locale[, silent]]): 'application' - No locales registered for '"..tostring(application).."'", 2)
error("Usage: GetLocale(application[, silent]): 'application' - No locales registered for '"..tostring(application).."'", 2)
end
return AceLocale.apps[application][locale] or AceLocale.apps[application][gameLocale] -- Just in case the table doesn't exist it reverts to default
return AceLocale.apps[application]
end
@@ -11,7 +11,7 @@
-- @class file
-- @name AceSerializer-3.0
-- @release $Id$
local MAJOR,MINOR = "AceSerializer-3.0", 3
local MAJOR,MINOR = "AceSerializer-3.0", 5
local AceSerializer, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
if not AceSerializer then return end
@@ -24,9 +24,11 @@ local pairs, select, frexp = pairs, select, math.frexp
local tconcat = table.concat
-- quick copies of string representations of wonky numbers
local serNaN = tostring(0/0)
local serInf = tostring(1/0)
local serNegInf = tostring(-1/0)
local inf = math.huge
local serNaN -- can't do this in 4.3, see ace3 ticket 268
local serInf, serInfMac = "1.#INF", "inf"
local serNegInf, serNegInfMac = "-1.#INF", "-inf"
-- Serialization functions
@@ -60,11 +62,15 @@ local function SerializeValue(v, res, nres)
elseif t=="number" then -- ^N = number (just tostring()ed) or ^F (float components)
local str = tostring(v)
if tonumber(str)==v or str==serNaN or str==serInf or str==serNegInf then
if tonumber(str)==v --[[not in 4.3 or str==serNaN]] then
-- translates just fine, transmit as-is
res[nres+1] = "^N"
res[nres+2] = str
nres=nres+2
elseif v == inf or v == -inf then
res[nres+1] = "^N"
res[nres+2] = v == inf and serInf or serNegInf
nres=nres+2
else
local m,e = frexp(v)
res[nres+1] = "^F"
@@ -77,9 +83,9 @@ local function SerializeValue(v, res, nres)
elseif t=="table" then -- ^T...^t = table (list of key,value pairs)
nres=nres+1
res[nres] = "^T"
for k,v in pairs(v) do
nres = SerializeValue(k, res, nres)
nres = SerializeValue(v, res, nres)
for key,value in pairs(v) do
nres = SerializeValue(key, res, nres)
nres = SerializeValue(value, res, nres)
end
nres=nres+1
res[nres] = "^t"
@@ -143,12 +149,12 @@ local function DeserializeStringHelper(escape)
end
local function DeserializeNumberHelper(number)
if number == serNaN then
--[[ not in 4.3 if number == serNaN then
return 0/0
elseif number == serNegInf then
return -1/0
elseif number == serInf then
return 1/0
else]]if number == serNegInf or number == serNegInfMac then
return -inf
elseif number == serInf or number == serInfMac then
return inf
else
return tonumber(number)
end
@@ -2,7 +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.
-- 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.
@@ -16,67 +17,21 @@
-- @name AceTimer-3.0
-- @release $Id$
local MAJOR, MINOR = "AceTimer-3.0", 1017 -- 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")
AceTimer.activeTimers = AceTimer.activeTimers or {} -- Active timer list
local activeTimers = AceTimer.activeTimers -- Upvalue our private data
-- Lua APIs
local assert, loadstring, rawset, tconcat = assert, loadstring, rawset, table.concat
local type, unpack, next, error, select = type, unpack, next, error, select
-- WoW APIs
local GetTime = GetTime
--[[
xpcall safecall implementation
]]
local xpcall = xpcall
local function errorhandler(err)
return geterrorhandler()(err)
end
local function CreateDispatcher(argCount)
local code = [[
local xpcall, eh = ...
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 GetTime, C_TimerAfter = GetTime, C_Timer.After
local function new(self, loop, func, delay, ...)
if delay < 0.01 then
delay = 0.01 -- Restrict to the lowest time
delay = 0.01 -- Restrict to the lowest time that the C_Timer API allows us
end
local timer = {
@@ -85,19 +40,45 @@ local function new(self, loop, func, delay, ...)
looping = loop,
argsCount = select("#", ...),
delay = delay,
timeleft = delay,
ends = GetTime() + delay,
...
}
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 ndelay = timer.delay - (time - timer.ends)
-- Ensure the delay doesn't go below the threshold
if ndelay < 0.01 then ndelay = 0.01 end
C_TimerAfter(ndelay, timer.callback)
timer.ends = time + ndelay
else
activeTimers[timer.handle or timer] = nil
end
end
end
C_TimerAfter(delay, timer.callback)
return timer
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 func Callback function for the timer pulse (funcref or method name).
-- @param delay Delay for the timer, in seconds.
-- @param ... An optional, unlimited amount of arguments to pass to the callback function.
-- @usage
@@ -126,7 +107,7 @@ 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 func Callback function for the timer pulse (funcref or method name).
-- @param delay Delay for the timer, in seconds.
-- @param ... An optional, unlimited amount of arguments to pass to the callback function.
-- @usage
@@ -230,6 +211,7 @@ if oldminor and oldminor < 10 then
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 = {}
@@ -294,34 +276,3 @@ end
for addon in next, AceTimer.embeds do
AceTimer:Embed(addon)
end
AceTimer.frame:SetScript("OnUpdate", function(self, elapsed)
for _, timer in next, activeTimers do
if not timer.cancelled then
if timer.timeleft > elapsed then
timer.timeleft = timer.timeleft - elapsed
else
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.
safecall(timer.object[timer.func], timer.object, unpack(timer, 1, timer.argsCount))
else
safecall(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
timer.ends = time + delay
timer.timeleft = timer.delay
else
activeTimers[timer.handle or timer] = nil
end
end
end
end
end)
@@ -1,5 +1,5 @@
--[[ $Id: CallbackHandler-1.0.lua 18 2014-10-16 02:52:20Z mikk $ ]]
local MAJOR, MINOR = "CallbackHandler-1.0", 6
--[[ $Id: CallbackHandler-1.0.lua 25 2022-12-12 15:02:36Z nevcairiel $ ]]
local MAJOR, MINOR = "CallbackHandler-1.0", 8
local CallbackHandler = LibStub:NewLibrary(MAJOR, MINOR)
if not CallbackHandler then return end -- No upgrade needed
@@ -7,56 +7,20 @@ if not CallbackHandler then return end -- No upgrade needed
local meta = {__index = function(tbl, key) tbl[key] = {} return tbl[key] end}
-- Lua APIs
local tconcat = table.concat
local assert, error, loadstring = assert, error, loadstring
local setmetatable, rawset, rawget = setmetatable, rawset, rawget
local securecallfunction, error = securecallfunction, error
local setmetatable, rawget = setmetatable, rawget
local next, select, pairs, type, tostring = next, select, pairs, type, tostring
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: geterrorhandler
local xpcall = xpcall
local function errorhandler(err)
return geterrorhandler()(err)
local function Dispatch(handlers, ...)
local index, method = next(handlers)
if not method then return end
repeat
securecallfunction(method, ...)
index, method = next(handlers, index)
until not method
end
local function CreateDispatcher(argCount)
local code = [[
local next, xpcall, eh = ...
local method, ARGS
local function call() method(ARGS) end
local function dispatch(handlers, ...)
local index
index, method = next(handlers)
if not method then return end
local OLD_ARGS = ARGS
ARGS = ...
repeat
xpcall(call, eh)
index, method = next(handlers, index)
until not method
ARGS = OLD_ARGS
end
return dispatch
]]
local ARGS, OLD_ARGS = {}, {}
for i = 1, argCount do ARGS[i], OLD_ARGS[i] = "arg"..i, "old_arg"..i end
code = code:gsub("OLD_ARGS", tconcat(OLD_ARGS, ", ")):gsub("ARGS", tconcat(ARGS, ", "))
return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(next, xpcall, errorhandler)
end
local Dispatchers = setmetatable({}, {__index=function(self, argCount)
local dispatcher = CreateDispatcher(argCount)
rawset(self, argCount, dispatcher)
return dispatcher
end})
--------------------------------------------------------------------------
-- CallbackHandler:New
--
@@ -65,7 +29,7 @@ end})
-- UnregisterName - name of the callback unregistration API, default "UnregisterCallback"
-- UnregisterAllName - name of the API to unregister all callbacks, default "UnregisterAllCallbacks". false == don't publish this API.
function CallbackHandler:New(target, RegisterName, UnregisterName, UnregisterAllName)
function CallbackHandler.New(_self, target, RegisterName, UnregisterName, UnregisterAllName)
RegisterName = RegisterName or "RegisterCallback"
UnregisterName = UnregisterName or "UnregisterCallback"
@@ -87,19 +51,19 @@ function CallbackHandler:New(target, RegisterName, UnregisterName, UnregisterAll
local oldrecurse = registry.recurse
registry.recurse = oldrecurse + 1
Dispatchers[select('#', ...) + 1](events[eventname], eventname, ...)
Dispatch(events[eventname], eventname, ...)
registry.recurse = oldrecurse
if registry.insertQueue and oldrecurse==0 then
-- Something in one of our callbacks wanted to register more callbacks; they got queued
for eventname,callbacks in pairs(registry.insertQueue) do
local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten.
for self,func in pairs(callbacks) do
events[eventname][self] = func
for event,callbacks in pairs(registry.insertQueue) do
local first = not rawget(events, event) or not next(events[event]) -- test for empty before. not test for one member after. that one member may have been overwritten.
for object,func in pairs(callbacks) do
events[event][object] = func
-- fire OnUsed callback?
if first and registry.OnUsed then
registry.OnUsed(registry, target, eventname)
registry.OnUsed(registry, target, event)
first = nil
end
end
@@ -0,0 +1,4 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
<Script file="CallbackHandler-1.0.lua"/>
</Ui>
+4 -25
View File
@@ -1,25 +1,16 @@
-- $Id: LibStub.lua 103 2014-10-16 03:02:50Z mikk $
-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/addons/libstub/ for more info
-- LibStub is hereby placed in the Public Domain
-- Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info
-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
local LibStub = _G[LIBSTUB_MAJOR]
-- Check to see is this version of the stub is obsolete
if not LibStub or LibStub.minor < LIBSTUB_MINOR then
LibStub = LibStub or {libs = {}, minors = {} }
_G[LIBSTUB_MAJOR] = LibStub
LibStub.minor = LIBSTUB_MINOR
-- LibStub:NewLibrary(major, minor)
-- major (string) - the major version of the library
-- minor (string or number ) - the minor version of the library
--
-- returns nil if a newer or same version of the lib is already present
-- returns empty library object or old library object if upgrade is needed
function LibStub:NewLibrary(major, minor)
assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
minor = assert(tonumber(string.match(minor, "%d+")), "Minor version must either be a number or contain a number.")
local oldminor = self.minors[major]
if oldminor and oldminor >= minor then return nil end
@@ -27,12 +18,6 @@ if not LibStub or LibStub.minor < LIBSTUB_MINOR then
return self.libs[major], oldminor
end
-- LibStub:GetLibrary(major, [silent])
-- major (string) - the major version of the library
-- silent (boolean) - if true, library is optional, silently return nil if its not found
--
-- throws an error if the library can not be found (except silent is set)
-- returns the library object if found
function LibStub:GetLibrary(major, silent)
if not self.libs[major] and not silent then
error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
@@ -40,12 +25,6 @@ if not LibStub or LibStub.minor < LIBSTUB_MINOR then
return self.libs[major], self.minors[major]
end
-- LibStub:IterateLibraries()
--
-- Returns an iterator for the currently registered libraries
function LibStub:IterateLibraries()
return pairs(self.libs)
end
function LibStub:IterateLibraries() return pairs(self.libs) end
setmetatable(LibStub, { __call = LibStub.GetLibrary })
end