|
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
|