yellowfive@57: -- yellowfive@57: -- ChatThrottleLib by Mikk yellowfive@57: -- yellowfive@57: -- Manages AddOn chat output to keep player from getting kicked off. yellowfive@57: -- yellowfive@57: -- ChatThrottleLib:SendChatMessage/:SendAddonMessage functions that accept yellowfive@57: -- a Priority ("BULK", "NORMAL", "ALERT") as well as prefix for SendChatMessage. yellowfive@57: -- yellowfive@57: -- Priorities get an equal share of available bandwidth when fully loaded. yellowfive@57: -- Communication channels are separated on extension+chattype+destination and yellowfive@57: -- get round-robinned. (Destination only matters for whispers and channels, yellowfive@57: -- obviously) yellowfive@57: -- yellowfive@57: -- Will install hooks for SendChatMessage and SendAddonMessage to measure yellowfive@57: -- bandwidth bypassing the library and use less bandwidth itself. yellowfive@57: -- yellowfive@57: -- yellowfive@57: -- Fully embeddable library. Just copy this file into your addon directory, yellowfive@57: -- add it to the .toc, and it's done. yellowfive@57: -- yellowfive@57: -- Can run as a standalone addon also, but, really, just embed it! :-) yellowfive@57: -- yellowfive@57: -- LICENSE: ChatThrottleLib is released into the Public Domain yellowfive@57: -- yellowfive@57: yellowfive@124: local CTL_VERSION = 24 yellowfive@57: yellowfive@57: local _G = _G yellowfive@57: yellowfive@57: if _G.ChatThrottleLib then yellowfive@57: if _G.ChatThrottleLib.version >= CTL_VERSION then yellowfive@57: -- There's already a newer (or same) version loaded. Buh-bye. yellowfive@57: return yellowfive@57: elseif not _G.ChatThrottleLib.securelyHooked then yellowfive@57: print("ChatThrottleLib: Warning: There's an ANCIENT ChatThrottleLib.lua (pre-wow 2.0, =v16) in it!") yellowfive@57: -- ATTEMPT to unhook; this'll behave badly if someone else has hooked... yellowfive@57: -- ... and if someone has securehooked, they can kiss that goodbye too... >.< yellowfive@57: _G.SendChatMessage = _G.ChatThrottleLib.ORIG_SendChatMessage yellowfive@57: if _G.ChatThrottleLib.ORIG_SendAddonMessage then yellowfive@57: _G.SendAddonMessage = _G.ChatThrottleLib.ORIG_SendAddonMessage yellowfive@57: end yellowfive@57: end yellowfive@57: _G.ChatThrottleLib.ORIG_SendChatMessage = nil yellowfive@57: _G.ChatThrottleLib.ORIG_SendAddonMessage = nil yellowfive@57: end yellowfive@57: yellowfive@57: if not _G.ChatThrottleLib then yellowfive@57: _G.ChatThrottleLib = {} yellowfive@57: end yellowfive@57: yellowfive@57: ChatThrottleLib = _G.ChatThrottleLib -- in case some addon does "local ChatThrottleLib" above us and we're copypasted (AceComm-2, sigh) yellowfive@57: local ChatThrottleLib = _G.ChatThrottleLib yellowfive@57: yellowfive@57: ChatThrottleLib.version = CTL_VERSION yellowfive@57: yellowfive@57: yellowfive@57: yellowfive@57: ------------------ TWEAKABLES ----------------- yellowfive@57: yellowfive@57: ChatThrottleLib.MAX_CPS = 800 -- 2000 seems to be safe if NOTHING ELSE is happening. let's call it 800. yellowfive@57: ChatThrottleLib.MSG_OVERHEAD = 40 -- Guesstimate overhead for sending a message; source+dest+chattype+protocolstuff yellowfive@57: yellowfive@57: ChatThrottleLib.BURST = 4000 -- WoW's server buffer seems to be about 32KB. 8KB should be safe, but seen disconnects on _some_ servers. Using 4KB now. yellowfive@57: yellowfive@57: ChatThrottleLib.MIN_FPS = 20 -- Reduce output CPS to half (and don't burst) if FPS drops below this value yellowfive@57: yellowfive@57: yellowfive@57: local setmetatable = setmetatable yellowfive@57: local table_remove = table.remove yellowfive@57: local tostring = tostring yellowfive@57: local GetTime = GetTime yellowfive@57: local math_min = math.min yellowfive@57: local math_max = math.max yellowfive@57: local next = next yellowfive@57: local strlen = string.len yellowfive@57: local GetFramerate = GetFramerate yellowfive@57: local strlower = string.lower yellowfive@57: local unpack,type,pairs,wipe = unpack,type,pairs,wipe yellowfive@57: local UnitInRaid,UnitInParty = UnitInRaid,UnitInParty yellowfive@57: yellowfive@57: yellowfive@57: ----------------------------------------------------------------------- yellowfive@57: -- Double-linked ring implementation yellowfive@57: yellowfive@57: local Ring = {} yellowfive@57: local RingMeta = { __index = Ring } yellowfive@57: yellowfive@57: function Ring:New() yellowfive@57: local ret = {} yellowfive@57: setmetatable(ret, RingMeta) yellowfive@57: return ret yellowfive@57: end yellowfive@57: yellowfive@57: function Ring:Add(obj) -- Append at the "far end" of the ring (aka just before the current position) yellowfive@57: if self.pos then yellowfive@57: obj.prev = self.pos.prev yellowfive@57: obj.prev.next = obj yellowfive@57: obj.next = self.pos yellowfive@57: obj.next.prev = obj yellowfive@57: else yellowfive@57: obj.next = obj yellowfive@57: obj.prev = obj yellowfive@57: self.pos = obj yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: function Ring:Remove(obj) yellowfive@57: obj.next.prev = obj.prev yellowfive@57: obj.prev.next = obj.next yellowfive@57: if self.pos == obj then yellowfive@57: self.pos = obj.next yellowfive@57: if self.pos == obj then yellowfive@57: self.pos = nil yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: yellowfive@57: yellowfive@57: ----------------------------------------------------------------------- yellowfive@57: -- Recycling bin for pipes yellowfive@57: -- A pipe is a plain integer-indexed queue of messages yellowfive@57: -- Pipes normally live in Rings of pipes (3 rings total, one per priority) yellowfive@57: yellowfive@57: ChatThrottleLib.PipeBin = nil -- pre-v19, drastically different yellowfive@57: local PipeBin = setmetatable({}, {__mode="k"}) yellowfive@57: yellowfive@57: local function DelPipe(pipe) yellowfive@57: PipeBin[pipe] = true yellowfive@57: end yellowfive@57: yellowfive@57: local function NewPipe() yellowfive@57: local pipe = next(PipeBin) yellowfive@57: if pipe then yellowfive@57: wipe(pipe) yellowfive@57: PipeBin[pipe] = nil yellowfive@57: return pipe yellowfive@57: end yellowfive@57: return {} yellowfive@57: end yellowfive@57: yellowfive@57: yellowfive@57: yellowfive@57: yellowfive@57: ----------------------------------------------------------------------- yellowfive@57: -- Recycling bin for messages yellowfive@57: yellowfive@57: ChatThrottleLib.MsgBin = nil -- pre-v19, drastically different yellowfive@57: local MsgBin = setmetatable({}, {__mode="k"}) yellowfive@57: yellowfive@57: local function DelMsg(msg) yellowfive@57: msg[1] = nil yellowfive@57: -- there's more parameters, but they're very repetetive so the string pool doesn't suffer really, and it's faster to just not delete them. yellowfive@57: MsgBin[msg] = true yellowfive@57: end yellowfive@57: yellowfive@57: local function NewMsg() yellowfive@57: local msg = next(MsgBin) yellowfive@57: if msg then yellowfive@57: MsgBin[msg] = nil yellowfive@57: return msg yellowfive@57: end yellowfive@57: return {} yellowfive@57: end yellowfive@57: yellowfive@57: yellowfive@57: ----------------------------------------------------------------------- yellowfive@57: -- ChatThrottleLib:Init yellowfive@57: -- Initialize queues, set up frame for OnUpdate, etc yellowfive@57: yellowfive@57: yellowfive@57: function ChatThrottleLib:Init() yellowfive@57: yellowfive@57: -- Set up queues yellowfive@57: if not self.Prio then yellowfive@57: self.Prio = {} yellowfive@57: self.Prio["ALERT"] = { ByName = {}, Ring = Ring:New(), avail = 0 } yellowfive@57: self.Prio["NORMAL"] = { ByName = {}, Ring = Ring:New(), avail = 0 } yellowfive@57: self.Prio["BULK"] = { ByName = {}, Ring = Ring:New(), avail = 0 } yellowfive@57: end yellowfive@57: yellowfive@57: -- v4: total send counters per priority yellowfive@57: for _, Prio in pairs(self.Prio) do yellowfive@57: Prio.nTotalSent = Prio.nTotalSent or 0 yellowfive@57: end yellowfive@57: yellowfive@57: if not self.avail then yellowfive@57: self.avail = 0 -- v5 yellowfive@57: end yellowfive@57: if not self.nTotalSent then yellowfive@57: self.nTotalSent = 0 -- v5 yellowfive@57: end yellowfive@57: yellowfive@57: yellowfive@57: -- Set up a frame to get OnUpdate events yellowfive@57: if not self.Frame then yellowfive@57: self.Frame = CreateFrame("Frame") yellowfive@57: self.Frame:Hide() yellowfive@57: end yellowfive@57: self.Frame:SetScript("OnUpdate", self.OnUpdate) yellowfive@57: self.Frame:SetScript("OnEvent", self.OnEvent) -- v11: Monitor P_E_W so we can throttle hard for a few seconds yellowfive@57: self.Frame:RegisterEvent("PLAYER_ENTERING_WORLD") yellowfive@57: self.OnUpdateDelay = 0 yellowfive@57: self.LastAvailUpdate = GetTime() yellowfive@57: self.HardThrottlingBeginTime = GetTime() -- v11: Throttle hard for a few seconds after startup yellowfive@57: yellowfive@57: -- Hook SendChatMessage and SendAddonMessage so we can measure unpiped traffic and avoid overloads (v7) yellowfive@57: if not self.securelyHooked then yellowfive@57: -- Use secure hooks as of v16. Old regular hook support yanked out in v21. yellowfive@57: self.securelyHooked = true yellowfive@57: --SendChatMessage yellowfive@57: hooksecurefunc("SendChatMessage", function(...) yellowfive@57: return ChatThrottleLib.Hook_SendChatMessage(...) yellowfive@57: end) yellowfive@57: --SendAddonMessage yellowfive@124: if _G.C_ChatInfo then yellowfive@124: hooksecurefunc(_G.C_ChatInfo, "SendAddonMessage", function(...) yellowfive@124: return ChatThrottleLib.Hook_SendAddonMessage(...) yellowfive@124: end) yellowfive@124: else yellowfive@124: hooksecurefunc("SendAddonMessage", function(...) yellowfive@124: return ChatThrottleLib.Hook_SendAddonMessage(...) yellowfive@124: end) yellowfive@124: end yellowfive@57: end yellowfive@57: self.nBypass = 0 yellowfive@57: end yellowfive@57: yellowfive@57: yellowfive@57: ----------------------------------------------------------------------- yellowfive@57: -- ChatThrottleLib.Hook_SendChatMessage / .Hook_SendAddonMessage yellowfive@57: yellowfive@57: local bMyTraffic = false yellowfive@57: yellowfive@57: function ChatThrottleLib.Hook_SendChatMessage(text, chattype, language, destination, ...) yellowfive@57: if bMyTraffic then yellowfive@57: return yellowfive@57: end yellowfive@57: local self = ChatThrottleLib yellowfive@57: local size = strlen(tostring(text or "")) + strlen(tostring(destination or "")) + self.MSG_OVERHEAD yellowfive@57: self.avail = self.avail - size yellowfive@57: self.nBypass = self.nBypass + size -- just a statistic yellowfive@57: end yellowfive@57: function ChatThrottleLib.Hook_SendAddonMessage(prefix, text, chattype, destination, ...) yellowfive@57: if bMyTraffic then yellowfive@57: return yellowfive@57: end yellowfive@57: local self = ChatThrottleLib yellowfive@57: local size = tostring(text or ""):len() + tostring(prefix or ""):len(); yellowfive@57: size = size + tostring(destination or ""):len() + self.MSG_OVERHEAD yellowfive@57: self.avail = self.avail - size yellowfive@57: self.nBypass = self.nBypass + size -- just a statistic yellowfive@57: end yellowfive@57: yellowfive@57: yellowfive@57: yellowfive@57: ----------------------------------------------------------------------- yellowfive@57: -- ChatThrottleLib:UpdateAvail yellowfive@57: -- Update self.avail with how much bandwidth is currently available yellowfive@57: yellowfive@57: function ChatThrottleLib:UpdateAvail() yellowfive@57: local now = GetTime() yellowfive@57: local MAX_CPS = self.MAX_CPS; yellowfive@57: local newavail = MAX_CPS * (now - self.LastAvailUpdate) yellowfive@57: local avail = self.avail yellowfive@57: yellowfive@57: if now - self.HardThrottlingBeginTime < 5 then yellowfive@57: -- First 5 seconds after startup/zoning: VERY hard clamping to avoid irritating the server rate limiter, it seems very cranky then yellowfive@57: avail = math_min(avail + (newavail*0.1), MAX_CPS*0.5) yellowfive@57: self.bChoking = true yellowfive@57: elseif GetFramerate() < self.MIN_FPS then -- GetFrameRate call takes ~0.002 secs yellowfive@57: avail = math_min(MAX_CPS, avail + newavail*0.5) yellowfive@57: self.bChoking = true -- just a statistic yellowfive@57: else yellowfive@57: avail = math_min(self.BURST, avail + newavail) yellowfive@57: self.bChoking = false yellowfive@57: end yellowfive@57: yellowfive@57: avail = math_max(avail, 0-(MAX_CPS*2)) -- Can go negative when someone is eating bandwidth past the lib. but we refuse to stay silent for more than 2 seconds; if they can do it, we can. yellowfive@57: yellowfive@57: self.avail = avail yellowfive@57: self.LastAvailUpdate = now yellowfive@57: yellowfive@57: return avail yellowfive@57: end yellowfive@57: yellowfive@57: yellowfive@57: ----------------------------------------------------------------------- yellowfive@57: -- Despooling logic yellowfive@57: -- Reminder: yellowfive@57: -- - We have 3 Priorities, each containing a "Ring" construct ... yellowfive@57: -- - ... made up of N "Pipe"s (1 for each destination/pipename) yellowfive@57: -- - and each pipe contains messages yellowfive@57: yellowfive@57: function ChatThrottleLib:Despool(Prio) yellowfive@57: local ring = Prio.Ring yellowfive@57: while ring.pos and Prio.avail > ring.pos[1].nSize do yellowfive@57: local msg = table_remove(ring.pos, 1) yellowfive@57: if not ring.pos[1] then -- did we remove last msg in this pipe? yellowfive@57: local pipe = Prio.Ring.pos yellowfive@57: Prio.Ring:Remove(pipe) yellowfive@57: Prio.ByName[pipe.name] = nil yellowfive@57: DelPipe(pipe) yellowfive@57: else yellowfive@57: Prio.Ring.pos = Prio.Ring.pos.next yellowfive@57: end yellowfive@57: local didSend=false yellowfive@57: local lowerDest = strlower(msg[3] or "") yellowfive@57: if lowerDest == "raid" and not UnitInRaid("player") then yellowfive@57: -- do nothing yellowfive@57: elseif lowerDest == "party" and not UnitInParty("player") then yellowfive@57: -- do nothing yellowfive@57: else yellowfive@57: Prio.avail = Prio.avail - msg.nSize yellowfive@57: bMyTraffic = true yellowfive@57: msg.f(unpack(msg, 1, msg.n)) yellowfive@57: bMyTraffic = false yellowfive@57: Prio.nTotalSent = Prio.nTotalSent + msg.nSize yellowfive@57: DelMsg(msg) yellowfive@57: didSend = true yellowfive@57: end yellowfive@57: -- notify caller of delivery (even if we didn't send it) yellowfive@57: if msg.callbackFn then yellowfive@57: msg.callbackFn (msg.callbackArg, didSend) yellowfive@57: end yellowfive@57: -- USER CALLBACK MAY ERROR yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: yellowfive@57: function ChatThrottleLib.OnEvent(this,event) yellowfive@57: -- v11: We know that the rate limiter is touchy after login. Assume that it's touchy after zoning, too. yellowfive@57: local self = ChatThrottleLib yellowfive@57: if event == "PLAYER_ENTERING_WORLD" then yellowfive@57: self.HardThrottlingBeginTime = GetTime() -- Throttle hard for a few seconds after zoning yellowfive@57: self.avail = 0 yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: yellowfive@57: function ChatThrottleLib.OnUpdate(this,delay) yellowfive@57: local self = ChatThrottleLib yellowfive@57: yellowfive@57: self.OnUpdateDelay = self.OnUpdateDelay + delay yellowfive@57: if self.OnUpdateDelay < 0.08 then yellowfive@57: return yellowfive@57: end yellowfive@57: self.OnUpdateDelay = 0 yellowfive@57: yellowfive@57: self:UpdateAvail() yellowfive@57: yellowfive@57: if self.avail < 0 then yellowfive@57: return -- argh. some bastard is spewing stuff past the lib. just bail early to save cpu. yellowfive@57: end yellowfive@57: yellowfive@57: -- See how many of our priorities have queued messages (we only have 3, don't worry about the loop) yellowfive@57: local n = 0 yellowfive@57: for prioname,Prio in pairs(self.Prio) do yellowfive@57: if Prio.Ring.pos or Prio.avail < 0 then yellowfive@57: n = n + 1 yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: -- Anything queued still? yellowfive@57: if n<1 then yellowfive@57: -- Nope. Move spillover bandwidth to global availability gauge and clear self.bQueueing yellowfive@57: for prioname, Prio in pairs(self.Prio) do yellowfive@57: self.avail = self.avail + Prio.avail yellowfive@57: Prio.avail = 0 yellowfive@57: end yellowfive@57: self.bQueueing = false yellowfive@57: self.Frame:Hide() yellowfive@57: return yellowfive@57: end yellowfive@57: yellowfive@57: -- There's stuff queued. Hand out available bandwidth to priorities as needed and despool their queues yellowfive@57: local avail = self.avail/n yellowfive@57: self.avail = 0 yellowfive@57: yellowfive@57: for prioname, Prio in pairs(self.Prio) do yellowfive@57: if Prio.Ring.pos or Prio.avail < 0 then yellowfive@57: Prio.avail = Prio.avail + avail yellowfive@57: if Prio.Ring.pos and Prio.avail > Prio.Ring.pos[1].nSize then yellowfive@57: self:Despool(Prio) yellowfive@57: -- Note: We might not get here if the user-supplied callback function errors out! Take care! yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: end yellowfive@57: yellowfive@57: yellowfive@57: yellowfive@57: yellowfive@57: ----------------------------------------------------------------------- yellowfive@57: -- Spooling logic yellowfive@57: yellowfive@57: function ChatThrottleLib:Enqueue(prioname, pipename, msg) yellowfive@57: local Prio = self.Prio[prioname] yellowfive@57: local pipe = Prio.ByName[pipename] yellowfive@57: if not pipe then yellowfive@57: self.Frame:Show() yellowfive@57: pipe = NewPipe() yellowfive@57: pipe.name = pipename yellowfive@57: Prio.ByName[pipename] = pipe yellowfive@57: Prio.Ring:Add(pipe) yellowfive@57: end yellowfive@57: yellowfive@57: pipe[#pipe + 1] = msg yellowfive@57: yellowfive@57: self.bQueueing = true yellowfive@57: end yellowfive@57: yellowfive@57: function ChatThrottleLib:SendChatMessage(prio, prefix, text, chattype, language, destination, queueName, callbackFn, callbackArg) yellowfive@57: if not self or not prio or not prefix or not text or not self.Prio[prio] then yellowfive@57: error('Usage: ChatThrottleLib:SendChatMessage("{BULK||NORMAL||ALERT}", "prefix", "text"[, "chattype"[, "language"[, "destination"]]]', 2) yellowfive@57: end yellowfive@57: if callbackFn and type(callbackFn)~="function" then yellowfive@57: error('ChatThrottleLib:ChatMessage(): callbackFn: expected function, got '..type(callbackFn), 2) yellowfive@57: end yellowfive@57: yellowfive@57: local nSize = text:len() yellowfive@57: yellowfive@57: if nSize>255 then yellowfive@57: error("ChatThrottleLib:SendChatMessage(): message length cannot exceed 255 bytes", 2) yellowfive@57: end yellowfive@57: yellowfive@57: nSize = nSize + self.MSG_OVERHEAD yellowfive@57: yellowfive@57: -- Check if there's room in the global available bandwidth gauge to send directly yellowfive@57: if not self.bQueueing and nSize < self:UpdateAvail() then yellowfive@57: self.avail = self.avail - nSize yellowfive@57: bMyTraffic = true yellowfive@57: _G.SendChatMessage(text, chattype, language, destination) yellowfive@57: bMyTraffic = false yellowfive@57: self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize yellowfive@57: if callbackFn then yellowfive@57: callbackFn (callbackArg, true) yellowfive@57: end yellowfive@57: -- USER CALLBACK MAY ERROR yellowfive@57: return yellowfive@57: end yellowfive@57: yellowfive@57: -- Message needs to be queued yellowfive@57: local msg = NewMsg() yellowfive@57: msg.f = _G.SendChatMessage yellowfive@57: msg[1] = text yellowfive@57: msg[2] = chattype or "SAY" yellowfive@57: msg[3] = language yellowfive@57: msg[4] = destination yellowfive@57: msg.n = 4 yellowfive@57: msg.nSize = nSize yellowfive@57: msg.callbackFn = callbackFn yellowfive@57: msg.callbackArg = callbackArg yellowfive@57: yellowfive@57: self:Enqueue(prio, queueName or (prefix..(chattype or "SAY")..(destination or "")), msg) yellowfive@57: end yellowfive@57: yellowfive@57: yellowfive@57: function ChatThrottleLib:SendAddonMessage(prio, prefix, text, chattype, target, queueName, callbackFn, callbackArg) yellowfive@57: if not self or not prio or not prefix or not text or not chattype or not self.Prio[prio] then yellowfive@57: error('Usage: ChatThrottleLib:SendAddonMessage("{BULK||NORMAL||ALERT}", "prefix", "text", "chattype"[, "target"])', 2) yellowfive@57: end yellowfive@57: if callbackFn and type(callbackFn)~="function" then yellowfive@57: error('ChatThrottleLib:SendAddonMessage(): callbackFn: expected function, got '..type(callbackFn), 2) yellowfive@57: end yellowfive@57: yellowfive@57: local nSize = text:len(); yellowfive@57: yellowfive@124: if C_ChatInfo or RegisterAddonMessagePrefix then yellowfive@57: if nSize>255 then yellowfive@57: error("ChatThrottleLib:SendAddonMessage(): message length cannot exceed 255 bytes", 2) yellowfive@57: end yellowfive@57: else yellowfive@57: nSize = nSize + prefix:len() + 1 yellowfive@57: if nSize>255 then yellowfive@57: error("ChatThrottleLib:SendAddonMessage(): prefix + message length cannot exceed 254 bytes", 2) yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: nSize = nSize + self.MSG_OVERHEAD; yellowfive@57: yellowfive@57: -- Check if there's room in the global available bandwidth gauge to send directly yellowfive@57: if not self.bQueueing and nSize < self:UpdateAvail() then yellowfive@57: self.avail = self.avail - nSize yellowfive@57: bMyTraffic = true yellowfive@124: if _G.C_ChatInfo then yellowfive@124: _G.C_ChatInfo.SendAddonMessage(prefix, text, chattype, target) yellowfive@124: else yellowfive@124: _G.SendAddonMessage(prefix, text, chattype, target) yellowfive@124: end yellowfive@57: bMyTraffic = false yellowfive@57: self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize yellowfive@57: if callbackFn then yellowfive@57: callbackFn (callbackArg, true) yellowfive@57: end yellowfive@57: -- USER CALLBACK MAY ERROR yellowfive@57: return yellowfive@57: end yellowfive@57: yellowfive@57: -- Message needs to be queued yellowfive@57: local msg = NewMsg() yellowfive@124: msg.f = _G.C_ChatInfo and _G.C_ChatInfo.SendAddonMessage or _G.SendAddonMessage yellowfive@57: msg[1] = prefix yellowfive@57: msg[2] = text yellowfive@57: msg[3] = chattype yellowfive@57: msg[4] = target yellowfive@57: msg.n = (target~=nil) and 4 or 3; yellowfive@57: msg.nSize = nSize yellowfive@57: msg.callbackFn = callbackFn yellowfive@57: msg.callbackArg = callbackArg yellowfive@57: yellowfive@57: self:Enqueue(prio, queueName or (prefix..chattype..(target or "")), msg) yellowfive@57: end yellowfive@57: yellowfive@57: yellowfive@57: yellowfive@57: yellowfive@57: ----------------------------------------------------------------------- yellowfive@57: -- Get the ball rolling! yellowfive@57: yellowfive@57: ChatThrottleLib:Init() yellowfive@57: yellowfive@57: --[[ WoWBench debugging snippet yellowfive@57: if(WOWB_VER) then yellowfive@57: local function SayTimer() yellowfive@57: print("SAY: "..GetTime().." "..arg1) yellowfive@57: end yellowfive@57: ChatThrottleLib.Frame:SetScript("OnEvent", SayTimer) yellowfive@57: ChatThrottleLib.Frame:RegisterEvent("CHAT_MSG_SAY") yellowfive@57: end yellowfive@57: ]] yellowfive@57: yellowfive@57: