annotate Libs/AceComm-3.0/ChatThrottleLib.lua @ 23:52973d00a183

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