ChatThrottleLib

ChatThrottleLib is a small, embeddable library by Mikk that keeps outbound chat and addon communication from exceeding the output rate limit in WoW that otherwise causes players to disconnect from the server. It also has a number of features that will help you make your addon communication run smoother!
Benefits of using ChatThrottleLib:
* You keep players from getting disconnected when your addon sends too much data
* You can easily prioritize your traffic in three priorities
* Communication to different peers is handled as individual flows. A long stream of data to "Alice" doesn't interrupt short bursts of traffic to "Bob".
* All AddOns using ChatThrottleLib count toward the same maximum, so multiple addons can't cause an overload. Priorities ensure that real-time traffic is still real-time. And addons are given a fair share of bandwidth, so one addon can't lock all others out.
ChatThrottleLib does:
* Round-robin traffic shaping of different communication paths
* Prioritization of messages according to three predefined priorities: "BULK", "NORMAL" and "ALERT"
* NO queueing of traffic needlessly. No lag is introduced until it needs to be.
* Adaptive throttling based on chat traffic bypassing the library
* Adaptive throttling based on framerate
== Communications libraries already using ChatThrottleLib ==
* AceComm (Ace's communications library)
* Telepathy (Sky's replacement as of 1.12)
If you are communicating through these libraries, you do not need to worry about throttling at all. And if you are depending on them / including them, ChatThrottleLib is already there and does not need to be included again; you can use it directly for regular chat.
== ChatThrottleLib APIs ==
=== ChatThrottleLib:SendChatMessage ===
 ChatThrottleLib:SendChatMessage("prio", "prefix", "text", "chattype", "language", "destination"[, "queueName"[, callbackFn, callbackArg]]);
;prio:String - "BULK", "NORMAL" or "ALERT".
;prefix:String - a unique identifier for your addon (or class of traffic). This is only for traffic shaping purposes; prefixes are used in creating unique flows that are processed round-robin inside each priority.
The following parameters are the same as the SendChatMessage() API:
;text:String - the text to be sent
;chattype:String - "SAY", "WHISPER", "CHANNEL", etc...
;language:String or nil
;destination:String or nil - Used for WHISPER, CHANNEL..
The following parameters are optional:
;queueName:String or nil - specific queue to place message in rather than prefix+chattype+destination
;callbackFn:Function - function to call when this message goes out the wire. May be called immediately (recursively).
;callbackArg:Any - an argument to pass to callbackFn
=== ChatThrottleLib:SendAddonMessage ===
 ChatThrottleLib:SendAddonMessage("prio", "prefix", "text", "chattype"[, "target"[, "queueName"[, callbackFn, callbackArg]]]);
;prio:String - "BULK", "NORMAL", or "ALERT".
The following parameters are the same as the SendAddonMessage() API:
;prefix:String - a unique identifier for your addon (or class of traffic)
;text:String - the text to be sent
;chattype:String - "PARTY", "RAID", "GUILD", "BATTLEGROUND", "WHISPER"
;target:String - Only used for "WHISPER" chats (added in WoW 2.1) -- which player to whisper to.
The following parameters are optional:
;queueName:String or nil - specific queue to place message in rather than prefix+chattype+destination
;callbackFn:Function - function to call when this message goes out the wire. May be called immediately (recursively).
;callbackArg:Any - an argument to pass to callbackFn
== Notes on outbound rate ==
The World of Warcraft servers will disconnect you if you generate too much output. This is not limited to chat output; any type of output can cause it. Some of you may have noticed that holding down your right mouse button and wiggling it wildly for an extended time can kick you off.
In my own testing, I've found that a data rate of 3000 CPS will cause a disconnect if sustained for more than a few seconds. There seems to be an input buffer of something along the lines of 32KBytes serverside. Filling it would be what causes the kick.
A total output of 2000 CPS seems safe.
ChatThrottleLib will allow bursts of up to 4000 bytes and after that throttle its output to 800 CPS, to allow room for actual game events. This used to be 8000/1000 but some servers seem more sensitive than others. See this cosmosui.org forum thread.
ChatThrottleLib will also assume that there's a 40-byte overhead associated with sending messages, so if you're sending "hi", it will count as 42 bytes for traffic shaping purposes. It's fairly unscientific, I'm assuming it's fair estimate of source + destination + chattype + some envisioned protocol overhead.
40 bytes seems a bit on the high side for sending data, possibly because the sender does not need to be included (obviously), but it seems about right on the receiving end from measuring actual network traffic (excluding ethernet+IP+TCP headers).
As of v11, ChatThrottleLib also assumes that the server-side rate limiter is somewhat cranky shortly after logon and zoning, and only allows a tenth of the normal rate for the first 5 seconds. This is based on observations from PartyQuests, which used to send the entire quest log (plus texts) to party members - immediately after login if you are logging in to a player still in a party. It has been seen to cause disconnects on at least one server.
== Throttling algorithms ==
There are two types of traffic shaping going on in ChatThrottleLib.
=== Per-stream shaping ===
Each prefix + chattype (+ destination) tuple is treated as a separate stream. Streams output is simply round-robined, i.e. they get the same amount of messages per second. Not necessarily the same CPS though. Doing full-out data rate shaping seemed like unnecessary overhead for very little gain.
=== Prioritization ===
Traffic in the three priorities will be fully data rate shaped so that the net output stays at 800 CPS. Higher priorities do not force lower priority traffic out of the way utterly. In a fully loaded system (with full output in all priorities), each priority gets 1/3rd of the available bandwidth.
It is fully balanced. Examples of attempted transmit rates and actual output results:
* BULK 500 CPS. NORMAL 500 CPS. ALERT 200 CPS
** Bulk gets 300
** Normal gets 300
** Alert gets 200
* BULK 1500 CPS. NORMAL 200 CPS. ALERT 500 CPS
** Bulk gets 300
** Normal gets 200
** Alert gets 300
* BULK 1200 CPS. NORMAL 0 CPS. ALERT 100 CPS
** Bulk gets 700
** Normal gets 0
** Alert gets 100
* BULK 0 CPS. NORMAL 400 CPS. ALERT 300 CPS
** Bulk gets 0
** Normal gets 400
** Alert gets 300
:The total load here is less than the CPS limit, and everyone gets what they ask for.
See this example graph for an illustration of how output in different priorities will be shaped over time.
If you would like to see rate changes in action during development, load ChatThrottleStats.lua in your addon. It displays a small string in the top left corner of your screen showing current rate through the library, burst availability, and rate of traffic bypassing the library. FPS based throttling being in effect causes the string to go red.
== Change Log ==
=== v7 ===
* Adaptive throttling according to FPS
* Adaptive throttling according to traffic bypassing the lib
* Allow prefix to be nil in :SendChatMessage()
=== v9 ===
* Fix SendAddonMessage() hook ending up feeding messages to SendChatMessage(). OOPS! Thank god for test realms.
=== v10 ===
* Will no longer "hook" SendAddonMessage if it doesn't exist. Might fool some addon into thinking that an 1.11 client is 1.12.
=== v11 ===
Aug 25 2006
* Some servers (sometimes?) seem to be cranky about lots of data being sent right after logon, such as e.g. PartyQuests will do if you log in while a member of a party. ChatThrottleLib v11 clamps the maximum output rate to a tenth for the 5 first seconds after logon (and zoning, just to be safe) in an attempt to work around it. I have not been able to reproduce this bug myself though, so it's somewhat of a stab in the dark.
=== v12 ===
Sep 14 2006
* Decrease max CPS to 800. Decrease max burst to 4000 bytes. See this cosmosui.org forum thread.
=== v14 ===
Apr 4 2007
* Bump .toc up to 2.0. Some minor internal code shuffling. (v12 worked just fine in wow 2.0 too, for what it's worth)
=== v15 ===
Apr 8 2007
* Add new "target" parameter to SendAddonMessage. Gets introduced in WoW 2.1 to support the new "WHISPER" addon channel.
* Move constants (max cps, burst size, etc) from locals to member variables to allow tweaking from outside sources.
=== v16 ===
Apr 14 2007
* Use secure hooks of SendAddonMessage/SendChatMessage. Should not affect anything.
=== v17 ===
May 27 2007
* Add a check that :SendAddonMessage text+prefix isn't longer than 254 bytes, as this causes a disconnect, which then may be misinterpreted as a speed problem.
* Localize a stray "msg" variable that ended up in the global namespace.
=== v18 ===
Aug 21 2007
* Fix a repeating (~10 times per second) error that would occur if you talked to more than 25 destinations simultaneously (as in "had queued data for more than 25").
* Add a check that :SendChatMessage text isn't longer than 255, which causes a disconnect.
* Fix cases where ChatThrottleLib is embedded inside another Lua file where "ChatThrottleLib" is a local variable, as is the case inside "AceComm-2.0.lua". This would often cause two separate copies of CTL to be loaded, making it NOT obey speed limits (each copy has its own max). I blame ckknight :-P
=== v19 ===
Nov 11 2007
* Add optional queueName argument at the end of both Send functions. Useful for libs/addons that require in-sequence delivery of messages with different prefixes.
=== v20 ===
Oct 15 2008
* prefix is no longer optional for SendChatMessage, in anticipation of the global this disappearing.
* The standalone ChatThrottleLib addon is no longer LoD due to the 3.0.2 LoD bug.
=== v20-30200 ===
Aug 5 2009
* The standalone ChatThrottleLib addon is again LoD
=== v21 ===
Aug 7 2009
* Added optional callback(+arg) parameters to the :Send()s. This callback will be called (with the given arg) when the message is pushed out the wire. This _MAY_ happen immediately (recursively)!
* Yanked upgrade path support for pre-v16 ChatThrottleLibs (pre-WoW 2.0). There is minimal arse-saving code + alerts should this be detected.
=== v22 ===
Mar 27 2011
* Will now autodetect 4.1+ wow and allow SendAddonMessage text size up to 255 characters. No, the limit is not 256. Anything above 255 is silently stripped by the client.
== How to use ChatThrottleLib ==
* Download ChatThrottleLib from wowinterface.com.
: (Alternatively, SVN extern it from svn://svn.wowace.com/wow/chatthrottlelib/mainline/trunk)
Method 1:
* Copy ChatThrottleLib.lua into your addon directory
* Add "ChatThrottleLib.lua" to your .toc file
Method 2:
* Copy the entire ChatThrottleLib folder into your addon directory, as a sub folder
* Add "ChatThrottleLib/ChatThrottleLib.xml" to your .toc file
You're done, and can now use ChatThrottleLib:SendChatMessage and ChatThrottleLib:SendAddonMessage to send your messages!
The library has built-in checks for if it has already been loaded, and avoids loading again if so.
If your addon has a newer version of the library than one that has already been loaded, it will replace the old version.
Note that you should never ever take local copies of ChatThrottleLib:SendChatMessage et al, since that prevents proper upgrading, and is likely to cause severe breakage:
 local SendChatMsg = ChatThrottleLib.SendChatMessage -- never ever do this!!!!
Category:Function Libraries