annotate Libs/AceComm-3.0/ChatThrottleLib.lua @ 124:e31b02b24488

Updated for 8.0 pre-patch and BfA.
author yellowfive
date Tue, 17 Jul 2018 09:57:39 -0700
parents 01b63b8ed811
children
rev   line source
yellowfive@57 1 --
yellowfive@57 2 -- ChatThrottleLib by Mikk
yellowfive@57 3 --
yellowfive@57 4 -- Manages AddOn chat output to keep player from getting kicked off.
yellowfive@57 5 --
yellowfive@57 6 -- ChatThrottleLib:SendChatMessage/:SendAddonMessage functions that accept
yellowfive@57 7 -- a Priority ("BULK", "NORMAL", "ALERT") as well as prefix for SendChatMessage.
yellowfive@57 8 --
yellowfive@57 9 -- Priorities get an equal share of available bandwidth when fully loaded.
yellowfive@57 10 -- Communication channels are separated on extension+chattype+destination and
yellowfive@57 11 -- get round-robinned. (Destination only matters for whispers and channels,
yellowfive@57 12 -- obviously)
yellowfive@57 13 --
yellowfive@57 14 -- Will install hooks for SendChatMessage and SendAddonMessage to measure
yellowfive@57 15 -- bandwidth bypassing the library and use less bandwidth itself.
yellowfive@57 16 --
yellowfive@57 17 --
yellowfive@57 18 -- Fully embeddable library. Just copy this file into your addon directory,
yellowfive@57 19 -- add it to the .toc, and it's done.
yellowfive@57 20 --
yellowfive@57 21 -- Can run as a standalone addon also, but, really, just embed it! :-)
yellowfive@57 22 --
yellowfive@57 23 -- LICENSE: ChatThrottleLib is released into the Public Domain
yellowfive@57 24 --
yellowfive@57 25
yellowfive@124 26 local CTL_VERSION = 24
yellowfive@57 27
yellowfive@57 28 local _G = _G
yellowfive@57 29
yellowfive@57 30 if _G.ChatThrottleLib then
yellowfive@57 31 if _G.ChatThrottleLib.version >= CTL_VERSION then
yellowfive@57 32 -- There's already a newer (or same) version loaded. Buh-bye.
yellowfive@57 33 return
yellowfive@57 34 elseif not _G.ChatThrottleLib.securelyHooked then
yellowfive@57 35 print("ChatThrottleLib: Warning: There's an ANCIENT ChatThrottleLib.lua (pre-wow 2.0, <v16) in an addon somewhere. Get the addon updated or copy in a newer ChatThrottleLib.lua (>=v16) in it!")
yellowfive@57 36 -- ATTEMPT to unhook; this'll behave badly if someone else has hooked...
yellowfive@57 37 -- ... and if someone has securehooked, they can kiss that goodbye too... >.<
yellowfive@57 38 _G.SendChatMessage = _G.ChatThrottleLib.ORIG_SendChatMessage
yellowfive@57 39 if _G.ChatThrottleLib.ORIG_SendAddonMessage then
yellowfive@57 40 _G.SendAddonMessage = _G.ChatThrottleLib.ORIG_SendAddonMessage
yellowfive@57 41 end
yellowfive@57 42 end
yellowfive@57 43 _G.ChatThrottleLib.ORIG_SendChatMessage = nil
yellowfive@57 44 _G.ChatThrottleLib.ORIG_SendAddonMessage = nil
yellowfive@57 45 end
yellowfive@57 46
yellowfive@57 47 if not _G.ChatThrottleLib then
yellowfive@57 48 _G.ChatThrottleLib = {}
yellowfive@57 49 end
yellowfive@57 50
yellowfive@57 51 ChatThrottleLib = _G.ChatThrottleLib -- in case some addon does "local ChatThrottleLib" above us and we're copypasted (AceComm-2, sigh)
yellowfive@57 52 local ChatThrottleLib = _G.ChatThrottleLib
yellowfive@57 53
yellowfive@57 54 ChatThrottleLib.version = CTL_VERSION
yellowfive@57 55
yellowfive@57 56
yellowfive@57 57
yellowfive@57 58 ------------------ TWEAKABLES -----------------
yellowfive@57 59
yellowfive@57 60 ChatThrottleLib.MAX_CPS = 800 -- 2000 seems to be safe if NOTHING ELSE is happening. let's call it 800.
yellowfive@57 61 ChatThrottleLib.MSG_OVERHEAD = 40 -- Guesstimate overhead for sending a message; source+dest+chattype+protocolstuff
yellowfive@57 62
yellowfive@57 63 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 64
yellowfive@57 65 ChatThrottleLib.MIN_FPS = 20 -- Reduce output CPS to half (and don't burst) if FPS drops below this value
yellowfive@57 66
yellowfive@57 67
yellowfive@57 68 local setmetatable = setmetatable
yellowfive@57 69 local table_remove = table.remove
yellowfive@57 70 local tostring = tostring
yellowfive@57 71 local GetTime = GetTime
yellowfive@57 72 local math_min = math.min
yellowfive@57 73 local math_max = math.max
yellowfive@57 74 local next = next
yellowfive@57 75 local strlen = string.len
yellowfive@57 76 local GetFramerate = GetFramerate
yellowfive@57 77 local strlower = string.lower
yellowfive@57 78 local unpack,type,pairs,wipe = unpack,type,pairs,wipe
yellowfive@57 79 local UnitInRaid,UnitInParty = UnitInRaid,UnitInParty
yellowfive@57 80
yellowfive@57 81
yellowfive@57 82 -----------------------------------------------------------------------
yellowfive@57 83 -- Double-linked ring implementation
yellowfive@57 84
yellowfive@57 85 local Ring = {}
yellowfive@57 86 local RingMeta = { __index = Ring }
yellowfive@57 87
yellowfive@57 88 function Ring:New()
yellowfive@57 89 local ret = {}
yellowfive@57 90 setmetatable(ret, RingMeta)
yellowfive@57 91 return ret
yellowfive@57 92 end
yellowfive@57 93
yellowfive@57 94 function Ring:Add(obj) -- Append at the "far end" of the ring (aka just before the current position)
yellowfive@57 95 if self.pos then
yellowfive@57 96 obj.prev = self.pos.prev
yellowfive@57 97 obj.prev.next = obj
yellowfive@57 98 obj.next = self.pos
yellowfive@57 99 obj.next.prev = obj
yellowfive@57 100 else
yellowfive@57 101 obj.next = obj
yellowfive@57 102 obj.prev = obj
yellowfive@57 103 self.pos = obj
yellowfive@57 104 end
yellowfive@57 105 end
yellowfive@57 106
yellowfive@57 107 function Ring:Remove(obj)
yellowfive@57 108 obj.next.prev = obj.prev
yellowfive@57 109 obj.prev.next = obj.next
yellowfive@57 110 if self.pos == obj then
yellowfive@57 111 self.pos = obj.next
yellowfive@57 112 if self.pos == obj then
yellowfive@57 113 self.pos = nil
yellowfive@57 114 end
yellowfive@57 115 end
yellowfive@57 116 end
yellowfive@57 117
yellowfive@57 118
yellowfive@57 119
yellowfive@57 120 -----------------------------------------------------------------------
yellowfive@57 121 -- Recycling bin for pipes
yellowfive@57 122 -- A pipe is a plain integer-indexed queue of messages
yellowfive@57 123 -- Pipes normally live in Rings of pipes (3 rings total, one per priority)
yellowfive@57 124
yellowfive@57 125 ChatThrottleLib.PipeBin = nil -- pre-v19, drastically different
yellowfive@57 126 local PipeBin = setmetatable({}, {__mode="k"})
yellowfive@57 127
yellowfive@57 128 local function DelPipe(pipe)
yellowfive@57 129 PipeBin[pipe] = true
yellowfive@57 130 end
yellowfive@57 131
yellowfive@57 132 local function NewPipe()
yellowfive@57 133 local pipe = next(PipeBin)
yellowfive@57 134 if pipe then
yellowfive@57 135 wipe(pipe)
yellowfive@57 136 PipeBin[pipe] = nil
yellowfive@57 137 return pipe
yellowfive@57 138 end
yellowfive@57 139 return {}
yellowfive@57 140 end
yellowfive@57 141
yellowfive@57 142
yellowfive@57 143
yellowfive@57 144
yellowfive@57 145 -----------------------------------------------------------------------
yellowfive@57 146 -- Recycling bin for messages
yellowfive@57 147
yellowfive@57 148 ChatThrottleLib.MsgBin = nil -- pre-v19, drastically different
yellowfive@57 149 local MsgBin = setmetatable({}, {__mode="k"})
yellowfive@57 150
yellowfive@57 151 local function DelMsg(msg)
yellowfive@57 152 msg[1] = nil
yellowfive@57 153 -- 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 154 MsgBin[msg] = true
yellowfive@57 155 end
yellowfive@57 156
yellowfive@57 157 local function NewMsg()
yellowfive@57 158 local msg = next(MsgBin)
yellowfive@57 159 if msg then
yellowfive@57 160 MsgBin[msg] = nil
yellowfive@57 161 return msg
yellowfive@57 162 end
yellowfive@57 163 return {}
yellowfive@57 164 end
yellowfive@57 165
yellowfive@57 166
yellowfive@57 167 -----------------------------------------------------------------------
yellowfive@57 168 -- ChatThrottleLib:Init
yellowfive@57 169 -- Initialize queues, set up frame for OnUpdate, etc
yellowfive@57 170
yellowfive@57 171
yellowfive@57 172 function ChatThrottleLib:Init()
yellowfive@57 173
yellowfive@57 174 -- Set up queues
yellowfive@57 175 if not self.Prio then
yellowfive@57 176 self.Prio = {}
yellowfive@57 177 self.Prio["ALERT"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
yellowfive@57 178 self.Prio["NORMAL"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
yellowfive@57 179 self.Prio["BULK"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
yellowfive@57 180 end
yellowfive@57 181
yellowfive@57 182 -- v4: total send counters per priority
yellowfive@57 183 for _, Prio in pairs(self.Prio) do
yellowfive@57 184 Prio.nTotalSent = Prio.nTotalSent or 0
yellowfive@57 185 end
yellowfive@57 186
yellowfive@57 187 if not self.avail then
yellowfive@57 188 self.avail = 0 -- v5
yellowfive@57 189 end
yellowfive@57 190 if not self.nTotalSent then
yellowfive@57 191 self.nTotalSent = 0 -- v5
yellowfive@57 192 end
yellowfive@57 193
yellowfive@57 194
yellowfive@57 195 -- Set up a frame to get OnUpdate events
yellowfive@57 196 if not self.Frame then
yellowfive@57 197 self.Frame = CreateFrame("Frame")
yellowfive@57 198 self.Frame:Hide()
yellowfive@57 199 end
yellowfive@57 200 self.Frame:SetScript("OnUpdate", self.OnUpdate)
yellowfive@57 201 self.Frame:SetScript("OnEvent", self.OnEvent) -- v11: Monitor P_E_W so we can throttle hard for a few seconds
yellowfive@57 202 self.Frame:RegisterEvent("PLAYER_ENTERING_WORLD")
yellowfive@57 203 self.OnUpdateDelay = 0
yellowfive@57 204 self.LastAvailUpdate = GetTime()
yellowfive@57 205 self.HardThrottlingBeginTime = GetTime() -- v11: Throttle hard for a few seconds after startup
yellowfive@57 206
yellowfive@57 207 -- Hook SendChatMessage and SendAddonMessage so we can measure unpiped traffic and avoid overloads (v7)
yellowfive@57 208 if not self.securelyHooked then
yellowfive@57 209 -- Use secure hooks as of v16. Old regular hook support yanked out in v21.
yellowfive@57 210 self.securelyHooked = true
yellowfive@57 211 --SendChatMessage
yellowfive@57 212 hooksecurefunc("SendChatMessage", function(...)
yellowfive@57 213 return ChatThrottleLib.Hook_SendChatMessage(...)
yellowfive@57 214 end)
yellowfive@57 215 --SendAddonMessage
yellowfive@124 216 if _G.C_ChatInfo then
yellowfive@124 217 hooksecurefunc(_G.C_ChatInfo, "SendAddonMessage", function(...)
yellowfive@124 218 return ChatThrottleLib.Hook_SendAddonMessage(...)
yellowfive@124 219 end)
yellowfive@124 220 else
yellowfive@124 221 hooksecurefunc("SendAddonMessage", function(...)
yellowfive@124 222 return ChatThrottleLib.Hook_SendAddonMessage(...)
yellowfive@124 223 end)
yellowfive@124 224 end
yellowfive@57 225 end
yellowfive@57 226 self.nBypass = 0
yellowfive@57 227 end
yellowfive@57 228
yellowfive@57 229
yellowfive@57 230 -----------------------------------------------------------------------
yellowfive@57 231 -- ChatThrottleLib.Hook_SendChatMessage / .Hook_SendAddonMessage
yellowfive@57 232
yellowfive@57 233 local bMyTraffic = false
yellowfive@57 234
yellowfive@57 235 function ChatThrottleLib.Hook_SendChatMessage(text, chattype, language, destination, ...)
yellowfive@57 236 if bMyTraffic then
yellowfive@57 237 return
yellowfive@57 238 end
yellowfive@57 239 local self = ChatThrottleLib
yellowfive@57 240 local size = strlen(tostring(text or "")) + strlen(tostring(destination or "")) + self.MSG_OVERHEAD
yellowfive@57 241 self.avail = self.avail - size
yellowfive@57 242 self.nBypass = self.nBypass + size -- just a statistic
yellowfive@57 243 end
yellowfive@57 244 function ChatThrottleLib.Hook_SendAddonMessage(prefix, text, chattype, destination, ...)
yellowfive@57 245 if bMyTraffic then
yellowfive@57 246 return
yellowfive@57 247 end
yellowfive@57 248 local self = ChatThrottleLib
yellowfive@57 249 local size = tostring(text or ""):len() + tostring(prefix or ""):len();
yellowfive@57 250 size = size + tostring(destination or ""):len() + self.MSG_OVERHEAD
yellowfive@57 251 self.avail = self.avail - size
yellowfive@57 252 self.nBypass = self.nBypass + size -- just a statistic
yellowfive@57 253 end
yellowfive@57 254
yellowfive@57 255
yellowfive@57 256
yellowfive@57 257 -----------------------------------------------------------------------
yellowfive@57 258 -- ChatThrottleLib:UpdateAvail
yellowfive@57 259 -- Update self.avail with how much bandwidth is currently available
yellowfive@57 260
yellowfive@57 261 function ChatThrottleLib:UpdateAvail()
yellowfive@57 262 local now = GetTime()
yellowfive@57 263 local MAX_CPS = self.MAX_CPS;
yellowfive@57 264 local newavail = MAX_CPS * (now - self.LastAvailUpdate)
yellowfive@57 265 local avail = self.avail
yellowfive@57 266
yellowfive@57 267 if now - self.HardThrottlingBeginTime < 5 then
yellowfive@57 268 -- First 5 seconds after startup/zoning: VERY hard clamping to avoid irritating the server rate limiter, it seems very cranky then
yellowfive@57 269 avail = math_min(avail + (newavail*0.1), MAX_CPS*0.5)
yellowfive@57 270 self.bChoking = true
yellowfive@57 271 elseif GetFramerate() < self.MIN_FPS then -- GetFrameRate call takes ~0.002 secs
yellowfive@57 272 avail = math_min(MAX_CPS, avail + newavail*0.5)
yellowfive@57 273 self.bChoking = true -- just a statistic
yellowfive@57 274 else
yellowfive@57 275 avail = math_min(self.BURST, avail + newavail)
yellowfive@57 276 self.bChoking = false
yellowfive@57 277 end
yellowfive@57 278
yellowfive@57 279 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 280
yellowfive@57 281 self.avail = avail
yellowfive@57 282 self.LastAvailUpdate = now
yellowfive@57 283
yellowfive@57 284 return avail
yellowfive@57 285 end
yellowfive@57 286
yellowfive@57 287
yellowfive@57 288 -----------------------------------------------------------------------
yellowfive@57 289 -- Despooling logic
yellowfive@57 290 -- Reminder:
yellowfive@57 291 -- - We have 3 Priorities, each containing a "Ring" construct ...
yellowfive@57 292 -- - ... made up of N "Pipe"s (1 for each destination/pipename)
yellowfive@57 293 -- - and each pipe contains messages
yellowfive@57 294
yellowfive@57 295 function ChatThrottleLib:Despool(Prio)
yellowfive@57 296 local ring = Prio.Ring
yellowfive@57 297 while ring.pos and Prio.avail > ring.pos[1].nSize do
yellowfive@57 298 local msg = table_remove(ring.pos, 1)
yellowfive@57 299 if not ring.pos[1] then -- did we remove last msg in this pipe?
yellowfive@57 300 local pipe = Prio.Ring.pos
yellowfive@57 301 Prio.Ring:Remove(pipe)
yellowfive@57 302 Prio.ByName[pipe.name] = nil
yellowfive@57 303 DelPipe(pipe)
yellowfive@57 304 else
yellowfive@57 305 Prio.Ring.pos = Prio.Ring.pos.next
yellowfive@57 306 end
yellowfive@57 307 local didSend=false
yellowfive@57 308 local lowerDest = strlower(msg[3] or "")
yellowfive@57 309 if lowerDest == "raid" and not UnitInRaid("player") then
yellowfive@57 310 -- do nothing
yellowfive@57 311 elseif lowerDest == "party" and not UnitInParty("player") then
yellowfive@57 312 -- do nothing
yellowfive@57 313 else
yellowfive@57 314 Prio.avail = Prio.avail - msg.nSize
yellowfive@57 315 bMyTraffic = true
yellowfive@57 316 msg.f(unpack(msg, 1, msg.n))
yellowfive@57 317 bMyTraffic = false
yellowfive@57 318 Prio.nTotalSent = Prio.nTotalSent + msg.nSize
yellowfive@57 319 DelMsg(msg)
yellowfive@57 320 didSend = true
yellowfive@57 321 end
yellowfive@57 322 -- notify caller of delivery (even if we didn't send it)
yellowfive@57 323 if msg.callbackFn then
yellowfive@57 324 msg.callbackFn (msg.callbackArg, didSend)
yellowfive@57 325 end
yellowfive@57 326 -- USER CALLBACK MAY ERROR
yellowfive@57 327 end
yellowfive@57 328 end
yellowfive@57 329
yellowfive@57 330
yellowfive@57 331 function ChatThrottleLib.OnEvent(this,event)
yellowfive@57 332 -- v11: We know that the rate limiter is touchy after login. Assume that it's touchy after zoning, too.
yellowfive@57 333 local self = ChatThrottleLib
yellowfive@57 334 if event == "PLAYER_ENTERING_WORLD" then
yellowfive@57 335 self.HardThrottlingBeginTime = GetTime() -- Throttle hard for a few seconds after zoning
yellowfive@57 336 self.avail = 0
yellowfive@57 337 end
yellowfive@57 338 end
yellowfive@57 339
yellowfive@57 340
yellowfive@57 341 function ChatThrottleLib.OnUpdate(this,delay)
yellowfive@57 342 local self = ChatThrottleLib
yellowfive@57 343
yellowfive@57 344 self.OnUpdateDelay = self.OnUpdateDelay + delay
yellowfive@57 345 if self.OnUpdateDelay < 0.08 then
yellowfive@57 346 return
yellowfive@57 347 end
yellowfive@57 348 self.OnUpdateDelay = 0
yellowfive@57 349
yellowfive@57 350 self:UpdateAvail()
yellowfive@57 351
yellowfive@57 352 if self.avail < 0 then
yellowfive@57 353 return -- argh. some bastard is spewing stuff past the lib. just bail early to save cpu.
yellowfive@57 354 end
yellowfive@57 355
yellowfive@57 356 -- See how many of our priorities have queued messages (we only have 3, don't worry about the loop)
yellowfive@57 357 local n = 0
yellowfive@57 358 for prioname,Prio in pairs(self.Prio) do
yellowfive@57 359 if Prio.Ring.pos or Prio.avail < 0 then
yellowfive@57 360 n = n + 1
yellowfive@57 361 end
yellowfive@57 362 end
yellowfive@57 363
yellowfive@57 364 -- Anything queued still?
yellowfive@57 365 if n<1 then
yellowfive@57 366 -- Nope. Move spillover bandwidth to global availability gauge and clear self.bQueueing
yellowfive@57 367 for prioname, Prio in pairs(self.Prio) do
yellowfive@57 368 self.avail = self.avail + Prio.avail
yellowfive@57 369 Prio.avail = 0
yellowfive@57 370 end
yellowfive@57 371 self.bQueueing = false
yellowfive@57 372 self.Frame:Hide()
yellowfive@57 373 return
yellowfive@57 374 end
yellowfive@57 375
yellowfive@57 376 -- There's stuff queued. Hand out available bandwidth to priorities as needed and despool their queues
yellowfive@57 377 local avail = self.avail/n
yellowfive@57 378 self.avail = 0
yellowfive@57 379
yellowfive@57 380 for prioname, Prio in pairs(self.Prio) do
yellowfive@57 381 if Prio.Ring.pos or Prio.avail < 0 then
yellowfive@57 382 Prio.avail = Prio.avail + avail
yellowfive@57 383 if Prio.Ring.pos and Prio.avail > Prio.Ring.pos[1].nSize then
yellowfive@57 384 self:Despool(Prio)
yellowfive@57 385 -- Note: We might not get here if the user-supplied callback function errors out! Take care!
yellowfive@57 386 end
yellowfive@57 387 end
yellowfive@57 388 end
yellowfive@57 389
yellowfive@57 390 end
yellowfive@57 391
yellowfive@57 392
yellowfive@57 393
yellowfive@57 394
yellowfive@57 395 -----------------------------------------------------------------------
yellowfive@57 396 -- Spooling logic
yellowfive@57 397
yellowfive@57 398 function ChatThrottleLib:Enqueue(prioname, pipename, msg)
yellowfive@57 399 local Prio = self.Prio[prioname]
yellowfive@57 400 local pipe = Prio.ByName[pipename]
yellowfive@57 401 if not pipe then
yellowfive@57 402 self.Frame:Show()
yellowfive@57 403 pipe = NewPipe()
yellowfive@57 404 pipe.name = pipename
yellowfive@57 405 Prio.ByName[pipename] = pipe
yellowfive@57 406 Prio.Ring:Add(pipe)
yellowfive@57 407 end
yellowfive@57 408
yellowfive@57 409 pipe[#pipe + 1] = msg
yellowfive@57 410
yellowfive@57 411 self.bQueueing = true
yellowfive@57 412 end
yellowfive@57 413
yellowfive@57 414 function ChatThrottleLib:SendChatMessage(prio, prefix, text, chattype, language, destination, queueName, callbackFn, callbackArg)
yellowfive@57 415 if not self or not prio or not prefix or not text or not self.Prio[prio] then
yellowfive@57 416 error('Usage: ChatThrottleLib:SendChatMessage("{BULK||NORMAL||ALERT}", "prefix", "text"[, "chattype"[, "language"[, "destination"]]]', 2)
yellowfive@57 417 end
yellowfive@57 418 if callbackFn and type(callbackFn)~="function" then
yellowfive@57 419 error('ChatThrottleLib:ChatMessage(): callbackFn: expected function, got '..type(callbackFn), 2)
yellowfive@57 420 end
yellowfive@57 421
yellowfive@57 422 local nSize = text:len()
yellowfive@57 423
yellowfive@57 424 if nSize>255 then
yellowfive@57 425 error("ChatThrottleLib:SendChatMessage(): message length cannot exceed 255 bytes", 2)
yellowfive@57 426 end
yellowfive@57 427
yellowfive@57 428 nSize = nSize + self.MSG_OVERHEAD
yellowfive@57 429
yellowfive@57 430 -- Check if there's room in the global available bandwidth gauge to send directly
yellowfive@57 431 if not self.bQueueing and nSize < self:UpdateAvail() then
yellowfive@57 432 self.avail = self.avail - nSize
yellowfive@57 433 bMyTraffic = true
yellowfive@57 434 _G.SendChatMessage(text, chattype, language, destination)
yellowfive@57 435 bMyTraffic = false
yellowfive@57 436 self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize
yellowfive@57 437 if callbackFn then
yellowfive@57 438 callbackFn (callbackArg, true)
yellowfive@57 439 end
yellowfive@57 440 -- USER CALLBACK MAY ERROR
yellowfive@57 441 return
yellowfive@57 442 end
yellowfive@57 443
yellowfive@57 444 -- Message needs to be queued
yellowfive@57 445 local msg = NewMsg()
yellowfive@57 446 msg.f = _G.SendChatMessage
yellowfive@57 447 msg[1] = text
yellowfive@57 448 msg[2] = chattype or "SAY"
yellowfive@57 449 msg[3] = language
yellowfive@57 450 msg[4] = destination
yellowfive@57 451 msg.n = 4
yellowfive@57 452 msg.nSize = nSize
yellowfive@57 453 msg.callbackFn = callbackFn
yellowfive@57 454 msg.callbackArg = callbackArg
yellowfive@57 455
yellowfive@57 456 self:Enqueue(prio, queueName or (prefix..(chattype or "SAY")..(destination or "")), msg)
yellowfive@57 457 end
yellowfive@57 458
yellowfive@57 459
yellowfive@57 460 function ChatThrottleLib:SendAddonMessage(prio, prefix, text, chattype, target, queueName, callbackFn, callbackArg)
yellowfive@57 461 if not self or not prio or not prefix or not text or not chattype or not self.Prio[prio] then
yellowfive@57 462 error('Usage: ChatThrottleLib:SendAddonMessage("{BULK||NORMAL||ALERT}", "prefix", "text", "chattype"[, "target"])', 2)
yellowfive@57 463 end
yellowfive@57 464 if callbackFn and type(callbackFn)~="function" then
yellowfive@57 465 error('ChatThrottleLib:SendAddonMessage(): callbackFn: expected function, got '..type(callbackFn), 2)
yellowfive@57 466 end
yellowfive@57 467
yellowfive@57 468 local nSize = text:len();
yellowfive@57 469
yellowfive@124 470 if C_ChatInfo or RegisterAddonMessagePrefix then
yellowfive@57 471 if nSize>255 then
yellowfive@57 472 error("ChatThrottleLib:SendAddonMessage(): message length cannot exceed 255 bytes", 2)
yellowfive@57 473 end
yellowfive@57 474 else
yellowfive@57 475 nSize = nSize + prefix:len() + 1
yellowfive@57 476 if nSize>255 then
yellowfive@57 477 error("ChatThrottleLib:SendAddonMessage(): prefix + message length cannot exceed 254 bytes", 2)
yellowfive@57 478 end
yellowfive@57 479 end
yellowfive@57 480
yellowfive@57 481 nSize = nSize + self.MSG_OVERHEAD;
yellowfive@57 482
yellowfive@57 483 -- Check if there's room in the global available bandwidth gauge to send directly
yellowfive@57 484 if not self.bQueueing and nSize < self:UpdateAvail() then
yellowfive@57 485 self.avail = self.avail - nSize
yellowfive@57 486 bMyTraffic = true
yellowfive@124 487 if _G.C_ChatInfo then
yellowfive@124 488 _G.C_ChatInfo.SendAddonMessage(prefix, text, chattype, target)
yellowfive@124 489 else
yellowfive@124 490 _G.SendAddonMessage(prefix, text, chattype, target)
yellowfive@124 491 end
yellowfive@57 492 bMyTraffic = false
yellowfive@57 493 self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize
yellowfive@57 494 if callbackFn then
yellowfive@57 495 callbackFn (callbackArg, true)
yellowfive@57 496 end
yellowfive@57 497 -- USER CALLBACK MAY ERROR
yellowfive@57 498 return
yellowfive@57 499 end
yellowfive@57 500
yellowfive@57 501 -- Message needs to be queued
yellowfive@57 502 local msg = NewMsg()
yellowfive@124 503 msg.f = _G.C_ChatInfo and _G.C_ChatInfo.SendAddonMessage or _G.SendAddonMessage
yellowfive@57 504 msg[1] = prefix
yellowfive@57 505 msg[2] = text
yellowfive@57 506 msg[3] = chattype
yellowfive@57 507 msg[4] = target
yellowfive@57 508 msg.n = (target~=nil) and 4 or 3;
yellowfive@57 509 msg.nSize = nSize
yellowfive@57 510 msg.callbackFn = callbackFn
yellowfive@57 511 msg.callbackArg = callbackArg
yellowfive@57 512
yellowfive@57 513 self:Enqueue(prio, queueName or (prefix..chattype..(target or "")), msg)
yellowfive@57 514 end
yellowfive@57 515
yellowfive@57 516
yellowfive@57 517
yellowfive@57 518
yellowfive@57 519 -----------------------------------------------------------------------
yellowfive@57 520 -- Get the ball rolling!
yellowfive@57 521
yellowfive@57 522 ChatThrottleLib:Init()
yellowfive@57 523
yellowfive@57 524 --[[ WoWBench debugging snippet
yellowfive@57 525 if(WOWB_VER) then
yellowfive@57 526 local function SayTimer()
yellowfive@57 527 print("SAY: "..GetTime().." "..arg1)
yellowfive@57 528 end
yellowfive@57 529 ChatThrottleLib.Frame:SetScript("OnEvent", SayTimer)
yellowfive@57 530 ChatThrottleLib.Frame:RegisterEvent("CHAT_MSG_SAY")
yellowfive@57 531 end
yellowfive@57 532 ]]
yellowfive@57 533
yellowfive@57 534