promotional bannermobile promotional banner

LibAceCommQueue

An addon library that adds queuing to the AceComm framework

AceCommQueue-1.0 - Transparent Send-Queue for AceComm-3.0

Are your addon's large AceComm messages arriving garbled with CRC errors? Does your guild communication addon silently drop or corrupt multi-chunk messages under load? AceCommQueue-1.0 is the solution.

AceCommQueue-1.0 is a lightweight, transparent queuing library that sits between your addon and AceComm-3.0, ensuring that only one message per channel is ever in-flight through ChatThrottleLib at a time — eliminating the chunk-interleaving bug that causes CRC failures on receivers.

The Problem

AceComm-3.0 splits large messages into FIRST/NEXT/LAST chunks and hands them all to ChatThrottleLib (CTL) at once. CTL files each submission into a pipe identified by priority and queue name, and AceComm names every queue after the prefix alone. So the same prefix sent at two different priorities lands in two different pipes, in two different priority classes — which CTL drains concurrently, each with its own share of the bandwidth. (CTL does not drain ALERT before NORMAL before BULK; its priorities get an equal share when all are backlogged.)

If a second message is submitted on the same prefix immediately after a large first message, and the two use different CTL priorities, CTL can drain the second message's chunks ahead of remaining chunks from the first message. The receiver's AceComm spool holds one partial message per prefix + distribution + sender. When a new FIRST frame arrives mid-stream, the partial spool is overwritten — the assembled payload is a splice of both messages, and any checksum applied to it fails.

BULK  message: FIRST ─── NEXT ─── NEXT ─── LAST
NORMAL message:            FIRST ─ LAST
                                ↑
                      receiver spool corrupted here

This bug is silent. No error is printed. The message simply never arrives. It hits any addon that sends multiple large messages in rapid succession using different CTL priorities.

The Solution

AceCommQueue-1.0 maintains an app-level queue per (prefix, distribution, target). Only one message is ever active in CTL at a time for that combination. The next queued message is not submitted until CTL's callback confirms the previous message's final chunk was handed off.

Between messages, higher-priority items drain first (ALERT > NORMAL > BULK), so an urgent message pushed onto the queue still goes out before pending lower-priority traffic. That strict ordering is this library's own — serialising sends is what makes priority mean anything between messages.

One thing the library does not give you: confirmation that a receiver got the message. Every callback in this stack reports that the client accepted the bytes, not that anyone received them. End-to-end reliability stays your protocol's job.

Core Features

Fully Transparent Integration

  • Zero Call-Site Changes: Embed once in OnInitialize — all existing self:SendCommMessage(...) calls are automatically queued
  • Wrapper-Safe: Captures whatever SendCommMessage is present at embed time — compatible with your existing wrappers
  • LibStub-Versioned: Standard LibStub upgrade semantics — multiple addons can embed the library without conflicts
  • Queue State Preserved: Internal queues survive LibStub upgrades across repeated /reload cycles

Priority-Aware Draining

  • ALERT drains before NORMAL before BULK between messages — a strict ordering this library provides, since ChatThrottleLib itself shares bandwidth equally across its priorities
  • Urgent messages (ALERT) are never held behind lower-priority backlog
  • Per-channel queues: separate queues per (prefix, distribution, target) so unrelated channels never block each other. Distribution and target are case-folded, so "Bob" and "bob" share one queue

A Truthful Delivery Verdict

  • Every message you hand to the queue ends in exactly one callback: callbackFn(arg, bytesSent, bytesTotal, delivered, reason)
  • delivered covers the whole message, not its last chunk — if the client refused any piece of a multi-part send you are told false, because the receiver would have reassembled a corrupt payload
  • reason names the outcome — "refused" (the client said no), "suppressed" (your own wrapper dropped it on purpose), "rejected" (bad prefix, missing text or unknown priority), "error" (the send raised) or "lost" (its completion never arrived and the retries are used up) — so a delivery counter can tell a deliberate skip from a genuine loss
  • A send with no callback that is refused is reported through the standard error handler, once per channel, so a dropped message can never vanish without a trace
  • A send that raises is still reported to the error handler, exactly as it would be without the queue in the way — the queue keeps running, but nothing is swallowed

Automatic Retry

  • A send the client refuses is retried three times with a doubling delay (1s, 2s, 4s) before being reported as failed
  • The channel keeps its place while it waits, so a retry never disturbs message order
  • Tune or disable it with ACQ:SetRetryPolicy(retries, baseDelay) — SetRetryPolicy(0) restores fail-fast

Stall Detection and Self-Recovery

  • If a send's completion never arrives, that channel would otherwise be blocked for the rest of the session. Once it has gone five minutes without progress, the next message sent on that channel makes the library ask the game's own send queue whether the message is still on its way; if it is genuinely gone, the channel is released and the message re-sent, keeping its place at the front
  • A message the game is still rate-limiting is never reported or touched — ordinary whisper throttling on a busy realm is not a fault
  • The first stall on each channel is reported once, naming the channel, how long it waited, how many messages were behind it, and which outcome actually happened — re-sent, or dropped because its retries were already used up. Later stalls are counted in /acq status rather than repeated
  • Tune it with ACQ:SetStallTimeout(seconds); force recovery right now with ACQ:Unstick() or /acq unstick

Suppression Compatible

  • Works with send-suppression wrappers (e.g. in-raid guards) — suppressing wrappers just call the callback with (arg, 0, 0, nil) to unblock the queue cleanly
  • A wrapper that forgets to do so no longer kills the channel: stall recovery releases it after five minutes, at the cost of that delay per message. Fixing the wrapper is still the right fix

Debug & Diagnostics

  • Optional runtime debug output via AceCommQueue:SetDebug(true)
  • Optional slash command registration: ACQ:RegisterSlashCommand("/acq") — pick any command name you like
  • Slash command lets you toggle debug output, inspect live queue state (in-flight flag, items per priority, refusal and stall counts, idle time) and unstick a channel without a reload

Safe to Share Between Addons

  • SetDebug, SetRetryPolicy and SetStallTimeout are session-wide — one library instance serves every addon that embeds it — so they belong to the top-level addon, not to a library in the middle
  • Every copy loaded resolves to the highest revision present. Anything added after the first release should be feature-detected (if ACQ.SetRetryPolicy then ... end) in case a user's other addons ship an older copy; the callback contract itself degrades safely without guards
  • Embed marks the addon object (__AceCommQueue_embedded) so a library handed someone else's addon can tell whether its sends are queued

How It Works

Integration (Three Steps)

  1. Load the library in your .toc before your own files:
    Libs\LibStub\LibStub.lua
    Libs\AceCommQueue-1.0\AceCommQueue-1.0.lua
  2. Embed after AceComm in OnInitialize:
    AceComm must be embedded first, then any custom wrappers, then AceCommQueue last so it wraps the complete chain.
  3. Done. Use self:SendCommMessage(...) as normal — queueing is fully transparent.

Suppression Wrappers

If your addon has a wrapper that sometimes suppresses sends (e.g. an in-raid guard), install it before calling ACQ:Embed(self), and have it call callbackFn(callbackArg, 0, 0, nil) when suppressing. This satisfies last-chunk detection and unblocks the queue so it never stalls permanently.

Why Use AceCommQueue-1.0?

Correctness

  • Eliminates the silent CRC-corruption bug that affects any addon sending large multi-chunk messages in rapid succession
  • No lost messages, no garbage payloads, no confusing intermittent failures under guild channel load
  • Backlogs of any size drain safely — a burst of tens of thousands of queued messages is delivered in full, in order
  • Verified by an offline test suite that runs the real Ace3 and ChatThrottleLib code and reproduces the corruption end to end, so the fix is demonstrated rather than asserted

Zero Overhead

  • Single Lua file, no external dependencies beyond LibStub and AceComm-3.0
  • No polling, no frame update hooks — driven by the send callbacks, with a timer used only to space out a retry
  • Queue drain happens in the same CTL callback that already fires per chunk

Drop-In

  • No refactoring of call sites required
  • Compatible with every current WoW client — Classic Era, Anniversary realms, Burning Crusade, Wrath, Cataclysm, Mists, Retail, and World of Warcraft: Forever — from one TOC, with no per-version code paths
  • Ships as a standalone addon or embedded in your own Libs folder

Slash Commands

Register with LibStub("AceCommQueue-1.0"):RegisterSlashCommand("/acq"). Available commands:

  • /acq on — Enable debug output
  • /acq off — Disable debug output
  • /acq status — Print current queue state for all active channels
  • /acq unstick — Get a blocked channel moving again immediately, without waiting for the automatic recovery. Anything that was stuck is re-sent. Channels the game is still working through are left alone
  • /acq — Toggle debug output

Requirements

  • LibStub
  • AceComm-3.0 (part of Ace3)

Recent Updates

v1.1.1 (Latest Release)

Fixed

  • Retail no longer flags the library as out of date. The current Retail builds were missing from the list of game versions it declares, so on Retail it — and any addon that requires it, such as FastGuildInvite — only loaded if you had allowed out-of-date addons. Nothing about how it behaves has changed

Changed

  • The standalone addon now appears under Library in the in-game AddOns list, named "Lib: AceCommQueue-1.0", alongside the other libraries
  • This page now describes stall recovery and retry timing exactly as the library does them: a blocked channel is checked the next time something is sent on it, and a timer is used only to space out retries

v1.1.0

New

  • Loads on World of Warcraft: Forever. Addons that depend on this library (FastGuildInvite among them) can now load there instead of being held back by a dependency the game refused

Changed

  • Declared up to date for the current build of every other supported version — Anniversary and Burning Crusade, Wrath, Mists, and Retail — so the library is no longer flagged out of date on any of them. Nothing about how it behaves has changed
  • This page and the bundled documentation now describe the library as it is today. In particular, an older claim that the game's own throttling drains urgent messages first has been corrected: the game shares bandwidth equally across priorities, and strict priority ordering is something this library provides on top

Fixed

  • The stall report could say a message was being re-sent when it had in fact just been dropped. The report was written before the decision was made, and on an addon that had turned retries off — a supported setting — every stalled message was reported as safe while being abandoned. The report now says which of the two actually happened, so nobody goes looking for a message that is gone, or worrying about one that is on its way

v1.0.6

  • Fixed: a perfectly healthy message could be reported as a fault in your addon. When the game rate-limits a whisper, it keeps retrying that message several times a second and says nothing at all while it does — which looked exactly like a message that had been lost. The library reported it as a bug in the sending addon and named a cause it had no way to check, sending authors to search codebases that were not at fault. It now asks the game's own send queue first, and stays quiet for as long as the message is genuinely still on its way. Whisper rate-limiting on a busy realm triggered the old check routinely
  • Fixed: that same check assumed every addon sends messages the same way. It looked for the message in one specific place — the place the most common comms library happens to file it. An addon that organises its outgoing messages differently would have had a perfectly healthy message reported as lost and then sent a second time. The check now looks everywhere the message could be and identifies it by its own destination details, so it no longer depends on how the sending addon is built. This library is published for anyone to use, so that assumption was not safe to ship
  • A blocked channel now recovers itself instead of staying blocked. Previously a channel whose message never reported back was stuck for the rest of the session, with everything behind it waiting forever. Now, once the library has confirmed the message really is gone, it clears the channel and re-sends the message — keeping its place at the front of the queue — so a lost notification costs a delay rather than a message
  • Added /acq unstick and a matching Unstick call, for getting a channel moving right now instead of waiting out the timer. It applies the same safety check, so it will never cut in front of a message the game is still sending
  • The wait before a channel is considered blocked is now five minutes rather than one. Reporting late costs nothing; reporting wrongly costs someone an afternoon
  • Repeat problems on the same channel are now counted rather than repeated. The first one is reported normally; the running total appears in /acq status, so a persistent fault stays visible without burying the chat frame
  • Your send callback can now report a further outcome — the message's fate is unknown and its retries are used up — so an addon tracking delivery can tell it apart from one it deliberately chose not to send

v1.0.5

  • Fixed: a message could be reported to your addon as delivered when the client had actually refused part of it. If one piece of a large multi-part message was rejected, the remaining pieces still went out and your addon was told the send succeeded — while the receiving end reassembled a corrupted payload. The delivery result now covers the whole message, so a refused piece means you are told it failed
  • Added automatic retry: a send the client refuses is retried three times with an increasing delay before being reported as failed. The channel keeps its place in the queue while it waits, so message order is never disturbed by a retry. Tune or disable it with SetRetryPolicy
  • A refused message sent without a callback is now reported through the standard error handler, so it can no longer disappear without a trace
  • Your send callback now receives a fifth value naming the outcome — delivered, refused, suppressed by your own code, rejected, or errored — so an addon counting failures can tell a message it deliberately chose not to send from one that was genuinely lost. Existing callbacks are unaffected
  • Added stall detection: if a send's completion callback never arrives, that channel used to block silently for the rest of the session. After a minute of no progress it is now reported, naming the channel and how many messages are waiting behind it. Nothing is dropped or re-sent — the queue is left exactly as it was, and recovers on its own if the callback turns up
  • New documentation section spelling out the delivery contract: what the callback's fourth argument means, and the two traps that catch otherwise-careful code — the result describes the whole message rather than the last piece, and it is a true/false value rather than one of the game's send-result codes

v1.0.4

  • Fixed a crash when sending to a numbered channel — the send died on the way into the queue instead of going out
  • Fixed silent message loss when a very large backlog drained all at once: past roughly twenty thousand queued messages, a few were dropped with no error at all. Backlogs of any size now drain in full, in order
  • A message the queue refuses (bad prefix, missing text, unknown priority) now reports back to your callback instead of ending in silence, so a send chained on the previous message's callback is never stranded
  • A send that fails is now reported to the error handler as well, rather than only appearing with debug output turned on
  • Added an offline test suite that runs the real Ace3 and ChatThrottleLib code outside the game and reproduces the message corruption this library prevents — the three fixes above were found by it. Development-only; it is not part of the download

v1.0.3

  • Updated for the current Classic Era client so the library is no longer flagged out of date; every other supported version (Anniversary realms, Burning Crusade, Wrath, Cataclysm, Mists, and Retail) is unchanged
  • Fixed the packaging rules so the documentation folder and the development-only scripts are correctly left out of the released download — the published zip is now just the library file, the TOC, and the license
  • Internal library revision bumped so this copy takes precedence over an older embedded copy when several addons ship the library side by side
  • No behavioural change — queueing, priority ordering, and the embed contract are identical to v1.0.2

v1.0.2

  • Ace3 is now declared as a proper required dependency for the standalone addon, so it is resolved automatically on install instead of a duplicate copy of LibStub being bundled into the download
  • No change for authors who embed the library — you continue to supply your own LibStub and AceComm-3.0

v1.0.1

  • Added a full changelog, published with each release
  • Expanded the documentation with the background on why chunk interleaving corrupts messages, a step-by-step integration walkthrough, the suppression-wrapper contract, and the slash command reference

v1.0.0

  • Initial public release — transparent per-(prefix, distribution, target) send queue on top of AceComm-3.0; prevents ChatThrottleLib priority-bucket reordering from interleaving chunks across messages; queue drains via CTL callback on last chunk; priority ordering preserved (ALERT > NORMAL > BULK); fully transparent to existing call sites; includes optional /acq slash commands for debug output and live queue inspection

Bug Reports & Feature Requests

Found a bug or have a suggestion?

  • Leave a comment on this CurseForge page

If you have any questions, comments or anything else, please feel free to reach out on Discord!

Credits

Developed by:

  • Pimptasty - Author

Special Thanks:

  • The Old Gods guild community for real-world testing
  • Ace3 library maintainers for the excellent framework
  • WoW Classic community for feedback and support

License

AceCommQueue-1.0 is open-source software. See LICENSE file for details.

LibAceCommQueue Team

Rising Builder tier frameprofile avatar
  • 2
    Followers
  • 32
    Projects
  • 857.0K
    Downloads
Donate

More from PmptastyView all