DeltaSync — Efficient Data Sync Library for WoW Addons
For addon developers. DeltaSync is a LibStub library that handles the hard parts of keeping guild-member data in sync — version comparison, delta compression, peer-to-peer catch-up, and message integrity — so you can focus on your addon's actual features instead of writing another sync engine from scratch.
It's the same sync stack that powers TOGBankClassic and TOGProfessionMaster, extracted into a reusable library you can embed in a few lines.
Why Use This
If your addon shares data across guild members — inventories, crafting recipes, raid rosters, settings — you've probably run into some combination of:
- Full re-sends every time anything changes, hammering the guild channel
- CRC errors under load when several addons compete for chat throttle slots
- New members joining and having no idea who to ask for a data catch-up
- Home-rolled hashing that disagrees on tables because Lua iteration order is unstable
- One slow peer freezing everyone else because there's no timeout on expected data
DeltaSync solves all of these. Drop it in, wire up a handful of callbacks, and you get battle-tested sync behavior that's been running in production on live Classic Era guilds.
What You Get
Bandwidth-efficient sync
Instead of re-sending everything whenever something changes, DeltaSync computes and transmits only the differences between states. In practice this cuts sync traffic by 90-99% for incremental updates. Full sync is always available as a fallback for new members or major changes.
Peer-to-peer catch-up
When a player logs in missing data, DeltaSync broadcasts a summary of what they have and lets any peer with fresher data step up. No single "banker" bottleneck, no manual request dance. If the first peer is busy, it politely declines and the next best peer takes over automatically.
Message integrity you can trust
Every structured message is wrapped with a checksum and a stop-marker. On the receive side, truncation and corruption are detected separately — so if a message got mangled mid-flight, you find out instead of silently applying garbage. Corrupt messages are logged with diagnostic info, not quietly dropped.
Modest prefix usage, and a loud failure if the client refuses one
DeltaSync spends 7 addon-message prefixes per consuming addon, one per channel type, auto-derived from your addon's name so you never pick unique strings yourself.
Blizzard caps how many prefixes a client may register, and AceComm discards the return value of RegisterAddonMessagePrefix — so past that cap, messages simply never arrive on that prefix, with no error and no warning. DeltaSync checks the result itself and names the dead channel in the debug log, in chat, and in host.prefixRegistrationFailed.
(Earlier versions of this page said "WoW caps each addon at 16 communication prefixes". That was wrong: the 16 is AceComm's prefix-string length limit, not a count. The real cap on the number of prefixes is not something we have verified, so we no longer quote a figure. Caught by the TOGBankClassic library audit.)
Multiple addons in one client, with zero cross-talk
New in v4.0.0: DeltaSync:NewHost(config) returns an isolated per-host object that owns its own namespace, prefixes, callbacks, peer state, and P2P sessions. Two — or ten — addons in the same client each call NewHost and run completely independent sync, with no shared state to clobber. (Before v4.0.0 the library was a singleton: whichever addon initialized last silently took over the others' prefixes and callbacks.) You call every DeltaSync method on the handle you hold, so the addons never see each other's traffic. The old DeltaSync:Initialize(config) call still works as backward-compatible sugar for a single consumer.
Guild roster tracking via LibGuildRoster-1.0
DeltaSync uses LibGuildRoster-1.0 — the standalone LibGuildRoster addon — to track who's in your guild and who's online, so it can avoid whispering offline members and default its peer-eligibility check. It's a required dependency that CurseForge installs automatically alongside DeltaSync; DeltaSync also feature-detects each method, so it degrades to reasonable inline defaults if the library is somehow unavailable. (As of v3.0.0 this replaces the previously-bundled GuildCache-1.0, which has been retired — see Recent Updates.)
Cross-guild roster sharing (optional)
New in v3.1.0: RosterSync lets confederated guilds share their member lists. Your addon's /who discovery finds an online member of an allied guild and calls lib:RequestRosterSync(peerName) — DeltaSync whispers that peer, compares membership hashes, pulls the roster only if it changed, and feeds it into LibGuildRoster's sister-roster store, all over the existing whisper channels (no extra prefix, no guild broadcast). Entirely opt-in via lib:InitRosterSync(config); addons that never call it are completely unaffected.
Guild-mode for whisper-broken servers (optional)
New in v3.2.0: some private/emulated cores — notably Whitemane — don't deliver addon messages over whisper, which silently breaks the directed query/response/delta channels. Guild-mode is a user-toggled fallback that reroutes those directed channels onto the guild channel, stamping each message with its intended recipient so every other member drops it on receipt — directed behavior over a broadcast transport, no whisper required. Opt-in via lib:InitGuildMode(config) / lib:SetGuildMode(enabled); the host owns the settings toggle, and addons that never enable it are byte-identical to before.
Dependencies
- Ace3 (required) — provides LibStub, AceComm, AceSerializer
- AceCommQueue-1.0 (required) — serializes outgoing messages so chunked payloads don't interleave and corrupt each other. Your host addon embeds this alongside Ace3.
- LibGuildRoster (required) — the guild-roster engine described above. CurseForge installs it automatically when you install DeltaSync.
All three are declared as hard Dependencies: in the TOC, so WoW loads them before DeltaSync.
Quick Start
Basic setup
-- In your addon's OnInitialize / OnEnable:
local AceAddon = LibStub("AceAddon-3.0")
local AceCommQueue = LibStub("AceCommQueue-1.0")
local DeltaSync = LibStub("DeltaSync-1.0")
-- 1. Make sure your AceAddon instance has AceComm and AceCommQueue
local MyAddon = AceAddon:NewAddon("MyAddon", "AceComm-3.0")
AceCommQueue:Embed(MyAddon)
-- 2. Create your isolated DeltaSync host (v4.0.0+). HOLD the returned handle —
-- call every DeltaSync method on it, never on the shared LibStub handle.
local host = DeltaSync:NewHost({
namespace = "MyAddon",
aceAddon = MyAddon, -- REQUIRED — DeltaSync sends through your addon
onDataReceived = function(sender, data, len)
-- apply received data or delta
end,
onDataRequest = function(sender, baseline)
-- peer asked for data; call host:SendData(sender, myData)
end,
})
Sending and receiving
-- Announce your current state to the guild host:BroadcastVersion(myVersion, myHash) -- Respond to a data request host:SendData(sender, myData, false) -- full sync host:SendData(sender, myDelta, true) -- delta sync
Peer-to-peer catch-up
host:InitP2P({
-- MakeHashEntry carries both hash revisions, so mixed-build guilds agree.
getMyHashes = function()
local out = {}
for key, value in pairs(myData) do
out[key] = host:MakeHashEntry(value, value.updatedAt)
end
return out
end,
hasContent = function(key) return myData[key] ~= nil end,
hasMissingItems = function() return nextMissingItem ~= nil end,
onSyncAccepted = function(key, sender)
host:RequestData(sender, myBaseline)
end,
})
host:BroadcastItemHashes(myItemHashes)
Who Should Use This
- Addons that sync structured data (inventories, rosters, recipes, settings) across guild members
- Addons where incremental updates are common and full re-syncs are wasteful
- Addons that want new members to catch up from any peer, not just a single broadcaster
- Addons that want CRC-level message integrity without writing it themselves
What You Don't Have to Write
- AceComm prefix registration and routing
- A wire format that survives partial delivery and chat-throttle chunk interleaving
- A P2P collect/offer/dispatch pipeline with timeouts and busy fallback
- Stable content hashing for Lua tables (iteration order is not your friend)
- An online-member cache that handles connected-realm name suffixes correctly
Recent Updates
v4.0.3 (Current) — A refused send is no longer invisible. DeltaSync never checked whether its messages actually went out. WoW silently discards addon messages under congestion, and AceComm-3.0 forwards only ChatThrottleLib's didSend boolean — so a refused message simply vanished, and a stalled sync gave no clue why. Every send now passes a delivery callback and records the verdict in host.sendsDelivered / host.sendFailures / host.lastSendFailure, logs it plainly as "the message did NOT arrive", and fires the new optional config.onSendFailed so your addon can re-send, degrade, or tell the user instead of assuming delivery. DebugStatus reports the counters, the last refusal, and whether your addon actually embedded AceCommQueue-1.0 — an unqueued host being exactly the one exposed to the chunk-interleaving corruption that library exists to prevent.
- Handles both callback shapes. AceCommQueue-1.0 delivers one terminal callback carrying the whole-message verdict; raw AceComm delivers one per chunk. A check written for one shape misbehaves under the other, so both are modelled in the test suite — including a 5-chunk send with only its 3rd chunk refused, which a naive check records as failed and delivered. A partial multipart stream reassembles into a corrupt payload, so the only correct reading is that it did not arrive.
- All the verdict states are handled, and the distinction that matters is not the obvious one. Three different situations share a
nilverdict: your own wrapper suppressed the send, the queue rejected a bad argument, or the send raised. The last two mean the message did not arrive and are counted as failures; only a deliberate suppression is not — counting that would give an addon with a raid-guard wrapper a forever-climbing error count for behaving correctly. So the rule is "was it suppressed?", not "was it refused?". Where AceCommQueue-1.0 v1.0.5+ supplies itsreasonargument DeltaSync uses it; against an older copy that cannot say, the send is recorded as not-attempted rather than guessed at. - Purely additive. No API removed, renamed or reordered; sending behaviour itself is unchanged. An addon that changes nothing gains the diagnostics for free. Library revision bumped to MINOR 17; feature-detect with
DS.MINOR >= 17. - Deprecated:
config.hashStrategyis inert and commented out, pending removal. It was stored and then read by nothing, so"deep"and"shallow"always behaved identically. Passing it remains harmless.
v4.0.2 — Timer cancellation actually works now, found by a new offline test suite. Four P2P timers were created with C_Timer.After and stored so they could be cancelled later — but only C_Timer.NewTimer returns a handle carrying :Cancel(), so every one of those cancels was a silent no-op sitting behind an if timer then guard. Most visibly, extending the offer collect window did not extend it: the original timer survived and dispatch fired at the old deadline anyway, discarding offers that arrived during the extension, then fired a second time later with the window already closed. On a busy login — where hash-list broadcasts arrive in bursts and repeatedly re-open the window — that cost real offers. All four timers now use NewTimer. No wire-format or API change; a MINOR 16 host and a MINOR 15 peer interoperate exactly as before.
- Offline test suite added — 509 specs at 99.54% line coverage across all six library files, running in milliseconds with no game client, built on the shared WoWAPITesting harness. It loads the real AceSerializer-3.0 and LibGuildRoster-1.0 rather than stubs, and a loopback AceComm network lets two or three DeltaSync hosts genuinely sync with each other so complete broadcast → offer → handshake → deliver → apply cycles are asserted end to end. The environment models the WoW API faithfully rather than conveniently — that is precisely why the timer bug above showed up instead of passing.
- Packaging — the
.pkgmetaignore list was rewritten to the packager's documented syntax (bare folder names, single-star repo-relative globs, no dotfile entries), andTestswas added so the new offline suite never ships to players. The published zip contains only the six library files, the TOC, README, CHANGELOG and LICENSE. - Interface versions refreshed to the current build per flavor —
11509, 20506, 30405, 38000, 40402, 50504, 120007. Entries superseded by a newer build of the same flavor were dropped; every flavor DeltaSync shipped for is still covered. - Content-hash revision 2 — the old hash rendered a number and its string form identically (
{v = 1}and{v = "1"}hash the same, as dotrueand"true"), so a value stored as text looked unchanged to a peer storing it as a number and the sync was skipped. The newComputeHashV2tags each scalar with its type. It ships alongside revision 1 rather than replacing it —ComputeHashis unchanged and now explicitly frozen, because its output is compared between clients: build hash-list entries with the newMakeHashEntryand each one carries both, with the P2P layer comparing on the highest revision both ends advertise. A peer on an older build sends only the revision-1 value, and that absence is the signal to fall back — so mixed-build guilds agree instead of offering each other data forever. Upgrade one player at a time; the fix switches itself on per pair. Retiring revision 1 later changes one library function and no consumer call sites. - Send slots are now capped correctly — a send that completed normally left its safety timer armed, and when that fired it released a slot belonging to a different, later send from the same peer. Because that is an over-release rather than an underflow, the existing guard never caught it and
maxActiveSendscould be quietly exceeded under sustained load. Each safety timer is now tied to the acquisition that scheduled it. - Silent keying bugs in the delta engine now report themselves. Three ways a diff could go quietly wrong, all of which produced a plausible-looking delta and only surfaced as behaviour hours later on someone else's client:
- Records with no
id/ID/key/nameand nokeyFuncwere keyed by their table address — unstable across sessions and between clients, so every diff reported everything added and everything removed and never converged. keyFieldssilently had to cover every fieldkeyFuncreads, or removals matched nothing on the receiving side and deleted items lingered forever.- Two records sharing a key emitted two entries for one target, with the survivor decided by array order — so two clients whose order differed kept different records and re-synced forever.
options.strictKeysturns them into errors for development builds. - Records with no
- Logging is now a choice, in both directions. If your addon has its own debug system, stop running two: either register your categories into DeltaSync's with
host:RegisterDebugCategory(name, tags)and delete your tab, buffer and registry — or passconfig.loggerand take logging over entirely, in which case DeltaSync stops filtering, stops buffering and never claims a chat tab. The first is recommended unless you already own an output layer with log levels and user-facing messages. - Keep your own persistent log and export, and get DeltaSync's diagnostics into it. DeltaSync's buffer is deliberately session-only, in-memory and capped — retention costs your SavedVariables, so it is your policy to set, not a library's. The new
config.onDebugMessage(message, category, tag)is a tap: it fires alongside DeltaSync's own tab rather than replacing it, hands you the structured category and tag as well as the text, skips anything your own filtering suppressed, and routes a throwing sink togeterrorhandler()instead of swallowing it. So the library's own sync diagnostics finally land in your bug reports. - A prefix the client refuses to register is reported instead of becoming a silently dead channel. AceComm discards that return value, so past the client's registered-prefix limit messages simply never arrive, with no error and no warning. DeltaSync checks it, names the dead channel, and records it for
DebugStatus. - A cyclic record no longer hangs the client — the deep-equality comparison recursed with no cycle detection.
- Library revision bumped to MINOR 16. Everything above is additive: no API was removed, renamed or reordered, and an addon that changes nothing keeps working exactly as it does today. See the adoption checklist in the README for what each new capability costs to pick up.
Thanks to the TOGBankClassic library audit, which independently found three of the defects above before its own migration onto DeltaSync — and caught a documentation error here that had been repeated for several releases (the "16 prefixes per addon" figure is AceComm's prefix-string length limit, not a count).
v4.0.1 — Classic Era interface bump: the TOC's Classic Era interface level moves 11508 → 11509 so the library is no longer flagged out-of-date on the current Classic Era client. TOC-only — no library code changed, the LibStub revision stays at MINOR 15, and every other supported flavor is untouched.
v4.0.0 — Multi-host: DeltaSync is no longer a singleton. DeltaSync:NewHost(config) returns an isolated per-host object that owns its own namespace, prefixes, callbacks, peer state, and P2P / RosterSync / guild-mode instances — so multiple consuming addons in one client coexist without clobbering each other's prefixes and callbacks. (Previously the library was a singleton and whichever addon called Initialize last silently took over the rest.) The wire format is unchanged, so a v4.0.0 host interoperates with any existing peer.
DeltaSync:NewHost(config)— the new entry point;configis identical in shape to the oldInitialize. Hold the returned handle and call every method on it:DS:Method(...)becomeshost:Method(...), andDS.p2p:OnItemCompleted(...)becomeshost.p2p:OnItemCompleted(...). Delta ops, serialization, InitP2P, RosterSync, guild-mode, andDebugStatusare all called on the host.- Backward compatible —
DeltaSync:Initialize(config)still works, now as sugar over an implicit "default host", so a single un-migrated consumer is unaffected. Two consumers both onInitializestill share the one default-host slot and clobber each other, so migrate all but at most one consumer toNewHost. - Library revision bumped to MINOR 15, and
lib.MINORis now actually set on the handle (previously documented for feature-detection but never assigned). Detect the multi-host API withDeltaSync.NewHost and LibStub("DeltaSync-1.0").MINOR >= 15.
v3.2.1 — Send-size telemetry: lib:BroadcastData and lib:BroadcastItemHashes now return the serialized payload size (bytes) as an additive second value alongside the existing ok boolean — the same #message metric the receive path already reports — so a host can log accurate outbound send sizes without re-serializing. Purely additive and backward-compatible: existing single-return callers (local ok = lib:BroadcastData(...)) are unaffected, and the early failure paths still return a single value.
- Library revision bumped to MINOR 14 — feature-detect with
LibStub("DeltaSync-1.0").MINOR >= 14before reading the new second return value.
v3.2.0 — Guild-mode: an opt-in, user-toggled mode for private/emulated servers (e.g. Whitemane) that don't deliver addon whispers. It reroutes the five directed channels (QUERY, RESPONSE, DELTA, the directed OFFER reply, HANDSHAKE) from whisper to guild, stamping each directed send with its intended recipient so every other guild member drops it on receipt — identical end behavior to a whisper, with no new prefix and no wire-format change for anyone with it off. Purely additive: a consumer that never calls lib:InitGuildMode is byte-identical to v3.1.0.
lib:InitGuildMode(config)/lib:SetGuildMode(enabled)/lib:IsGuildMode()— opt-in init (applies a host-persisted toggle, firesonChangedon every flip) plus the runtime toggle and state query. The host owns the settings checkbox and its persistence; the library ships no UI, SavedVariables, or slash commands.- Recipient-stamped directed broadcasts — each directed GUILD send is prefixed with an out-of-band
\029<recipient>\029header (outside the CRC envelope); receivers drop messages addressed to someone else and strip their own before deserializing.\029never begins an AceSerializer payload, so detection is unambiguous, and the stamp survives AceComm chunking. The recipient is realm-qualified at send time so same-named characters on different connected realms don't both match. - Interactions — RosterSync (cross-guild, whisper-only) self-disables while guild-mode is active, and the offline-member send guard applies to directed guild sends too, keeping undeliverable traffic off the shared channel.
- Library revision bumped to MINOR 13 — feature-detect with
LibStub("DeltaSync-1.0").MINOR >= 13orif lib.InitGuildMode then.
v3.1.0 — RosterSync: optional cross-guild "sister roster" sharing for confederated guilds, layered on the existing WHISPER channels — no new prefix, no broadcast. A single call, lib:RequestRosterSync(peerName), whispers an online allied-guild member, compares membership hashes (pulling only when they differ), and feeds the result into LibGuildRoster's sister-roster store. Purely additive: a consumer that never calls lib:InitRosterSync is byte-identical to v3.0.0.
lib:InitRosterSync(config)/lib:RequestRosterSync(peerName)— opt-in init plus the one call a consumer makes. DeltaSync owns the wire, LibGuildRoster owns the store, and the host owns presence (its/whopoll) and persistence. Membership-only, single round trip, provider-authoritative guild key.lib:RegisterLeafType(prefix, handlers)— generic, additive leaf-type router: an optional module claims a leaftypeprefix so its directed QUERY/RESPONSE traffic routes to it before the host's data callbacks. Roster traffic coexists with a host's own leaves (e.g. cooldowns/recipes) without either seeing the other's messages; a no-op when nothing registers.- Cooperative trust + isolation — a provider serves only its own guild's roster, the receiver rejects a roster whose stamped provider isn't in it, and roster traffic is directed-whisper-only (it never enters the guild-wide P2P offer path).
- Library revision bumped to MINOR 12 — feature-detect with
LibStub("DeltaSync-1.0").MINOR >= 12orif lib.InitRosterSync then.
v3.0.0 — The bundled GuildCache-1.0 roster library has been retired in favor of LibGuildRoster-1.0 (the standalone LibGuildRoster addon), now a required dependency that CurseForge installs automatically. This is a breaking change for any addon that resolved LibStub("GuildCache-1.0") from DeltaSync's bundle. No wire-format change; the P2P sync protocol is untouched.
- GuildCache-1.0 removed — DeltaSync no longer registers the
GuildCache-1.0LibStub MAJOR. Roster tracking, name normalization, and online-state come fromLibStub("LibGuildRoster-1.0")instead. - Consumer migration — Addons that used DeltaSync's GuildCache should switch to
LibStub("LibGuildRoster-1.0")and renameIsPlayerOnline→IsOnlineandGetOnlineGuildMembers→GetOnlineMembers.NormalizeName,GetNormalizedPlayer,GetMember, and the roster callbacks keep their names. Note thatIsInGuildis now a strict membership check rather than accept-all on an empty roster. - Graceful degradation preserved — DeltaSync feature-detects each library method, so it still falls back to inline defaults (realm derivation, skipped whisper guard, accept-all peers) when the library is absent or older.
- Library revision bumped to MINOR 11 — Consumers can assert the new layout with
LibStub("DeltaSync-1.0").MINOR >= 11.
v2.0.3 — Hash-mismatch offer condition for content-aware-merge consumers. The P2P offer path used to gate on updatedAt > peer.updatedAt, which suppressed legitimate offers from the actual data owner whenever a relayer's updatedAt happened to be higher (e.g. bumped on every RebuildAll even when content didn't change). v2.0.3 flips the condition to "offer whenever our hash differs from the peer's." The wire format is bit-identical to MINOR 8, so older consumers continue to interoperate.
- Hash-mismatch offer condition —
P2PSession:OnHashListReceivednow offers data whenever its hash differs from the peer's, regardless ofupdatedAt. Required by content-aware-merge consumers (TOGProfessionMaster v0.2.0+) that resolve concurrent edits with merge-on-receive instead of last-writer-wins. - Candidate sort downgraded to heuristic — The descending-
updatedAtinsertion sort inOnOfferis retained but is no longer load-bearing for correctness. With content-aware merge on the receive side, dispatching to any candidate converges to the same answer; the sort now serves only as a "try the most-recently-updated peer first" tiebreak when peer load is equal. - Library revision bumped to MINOR 9 — Consumers that need to assert the new offer semantics can check
LibStub("DeltaSync-1.0").MINOR >= 9. - Compatibility — No API or wire-format changes. Existing consumers (TOGBankClassic, etc.) that don't use the P2P offer path are entirely unaffected. Consumers that do use P2P will simply emit and observe more hash-offers when content actually differs. Adopting the new semantics on the receive side requires implementing content-aware merge in
onDataReceived(max-wins for monotonic fields, union for sets).
v2.0.2 — GuildCache-1.0 gains CallbackHandler-1.0 events and real-time online/offline tracking via CHAT_MSG_SYSTEM, plus a login-race retry that fixes empty-roster results in the brief window after PLAYER_LOGIN. (GuildCache MINOR 2.)
- GuildCache callbacks — Six events fire via
CallbackHandler-1.0:OnRosterReady,OnRosterUpdated,OnMemberOnline(name),OnMemberOffline(name),OnMemberJoined(name),OnMemberLeft(name). Payloads are canonical"Name-Realm"strings. Subscriptions persist across LibStub upgrades. - Real-time
CHAT_MSG_SYSTEMparser — Online/offline/join/leave transitions fire immediately on the system message instead of waiting for the nextGUILD_ROSTER_UPDATE. The roster-rebuild diff still fires the same callbacks as a catch-up safety net. - Login-race retry — When
IsInGuild()is true butGetNumGuildMembers()momentarily returns 0 afterPLAYER_LOGIN, GuildCache now re-issuesRequestGuildRoster()(up toMAX_RETRIES = 5) instead of silently producing an empty roster. - Member entry shape extended additively — Entries now include
name,rankIndex, andrankNamealongside the existing fields. Legacyrankis retained as an alias forrankNameso MINOR=1 consumers are unaffected.rankIndexunlocks rank-based peer filtering that custom rank labels can't reliably support.
v2.0.1 — Ships the missing DeltaSyncChannel.lua. v2.0.0 added the integration scaffolding for an optional CHANNEL transport (SendChatMessage send + CHAT_MSG_CHANNEL receive, for working around WoW Classic's silent drop of CHAT_MSG_ADDON on custom channel numbers), but the implementation file itself never made it into the package — any embedder that set channelModule.enabled = true in their Initialize config hit a defensive error at boot. v2.0.1 fixes that.
- New file
DeltaSyncChannel.lua— optional ~160 LOC module loaded afterDeltaSync.luain your TOC. AddsInitChannelModule()(CHAT_MSG_CHANNEL frame setup with self-filter and prefix dispatch into the existingOnComm_*handlers) andSendViaChannel(prefix, body, target)(rawSendChatMessagetransport, with a 255-byte cap warning since custom channels don't get AceComm's chunking). - Library revision bumped to MINOR 8 — Embedders whose private copies were on MINOR 7 (consumer addons that had been carrying their own
DeltaSyncChannel.luato work around the missing file) will now be overridden by the packaged MINOR 8 lib via LibStub, and can drop their embedded copies. - Compatibility — No API or wire-format changes from v2.0.0. Embedders using only GUILD/PARTY/RAID/WHISPER distribution don't need to load the new file at all and see no behavior change.
v2.0.0 — Major release folding in the matured sync engine from TOGProfessionMaster and splitting the guild-roster cache into its own companion library.
- GuildCache extracted into
GuildCache-1.0— Roster and name-normalization methods moved from the DeltaSync handle to their own LibStub library atLibs/GuildCache-1.0/. Other addons can now embed just GuildCache without the full sync stack. This is a breaking change: calls likeDeltaSync:NormalizeName(x)becomeLibStub("GuildCache-1.0"):NormalizeName(x). aceAddonis now a requiredInitialize()config key — DeltaSync routes sends through your AceAddon'sSendCommMessageinstead of embedding AceComm into itself. This keeps throttling and CRC protection wrapping the correct send path. Existing consumers need a one-line addition: pass yourAceAddon:NewAddon(...)handle asconfig.aceAddon.- AceCommQueue moved from library-side to host-side — Your addon embeds AceCommQueue into its own AceAddon handle now. One line:
LibStub("AceCommQueue-1.0"):Embed(MyAddon). - Cross-realm name normalization fixed — Connected-realm peers whose name arrives with a foreign realm suffix are no longer silently rewritten to the local realm. Their per-realm data stays separate where it should.
- New debug APIs —
lib:DebugStatus()prints a diagnostic block showing namespace, library revision, registered prefixes, and wiring state. Useful when sends are silently dropping and you need to confirm which config key is missing.
v1.0.0 — First stable release. P2P session hardening, integrity checks on every channel, and a whisper-online guard so offline members stop eating send slots.
DELIVERY_TIMEOUTadded so a peer that accepts a sync-request and then goes silent no longer pins an inbound session slot forever- All seven channel types (not just DATA) now use the CRC + stop-marker wire format
- Whisper sends now skip guild members the roster knows are offline
- Several P2P state-machine edge cases fixed where late messages could mutate unrelated sessions
Bug Reports & Feedback
Found a bug or have a suggestion? Reach out on Discord.
Credits
- Pimptasty — Author
Special Thanks:
- The Old Gods guild community for real-world testing via TOGBankClassic and TOGProfessionMaster
- Ace3 library maintainers for the excellent framework
- AceCommQueue-1.0 for solving the chunk-interleave CRC problem
License
DeltaSync is open-source software. See the LICENSE file for details.

