-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
Internals, load order, module registration, and conventions.
RGX-Framework exposes a single global table:
_G.RGXFrameworkEvery module and every consumer addon references this same table. There is no LibStub, no version negotiation, and no embedding — one addon, one instance.
WoW loads files in the order declared in RGX-Framework.xml. The framework uses this sequence:
1. core/core.lua — global object, module registry, Mixin, CopyTable, Clamp, Lerp, TableCount, Print/Warn/Error/Debug
2. core/systems/config.lua — framework defaults (debugMode, default font, size, flags)
3. core/systems/database.lua— RGX:DB(name, defaults), RGX:InitDatabase()
3b. core/systems/database_test.lua — RGX:RunDBTests() harness; intentionally shipped, runs only via /rgx dbtest
4. core/systems/events.lua — RegisterEvent, RegisterMessage, CreateEmitter, ADDON_ACTION_BLOCKED monitor
5. core/systems/runtime.lua — After, Every, CancelTimer, Hook, RegisterSlashCommand, combat queue, Safe* helpers
6. core/systems/utils.lua — Trim, Split, TableKeys/Values/Contains/Map/Filter/Find, MergeTable, Round, Format, Clamp, StartsWith, EndsWith
7. modules/dropdowns/dropdowns.lua — CreateNestedDropdown, CopyItem, NormalizeItems, ForceWidth, AddInlineButton
8. modules/fonts/definitions.lua — 36 font definitions, unavailableFonts blocklist
9. modules/fonts/init.lua — Fonts:Init(), RegisterModule("fonts")
10. modules/fonts/registry.lua — Register, RegisterAddonFont, RegisterFontPack
11. modules/fonts/query.lua — GetPath, Get, Exists, IsAvailable, List, ListAvailable, FindByPath
12. modules/fonts/defaults.lua — SetDefault, GetDefault, SetDefaultSize, SetDefaultFlags, SetAutoScale
13. modules/fonts/apply.lua — Apply, Quick, ApplyChildren, CreateString, FromTemplate
14. modules/fonts/normalize.lua — SplitFlags, NormalizeFlags, DescribeFlags, GetFlagPresets, NormalizeFontPath
15. modules/fonts/styles.lua — NormalizeStyle, NormalizeColorValue, CreateStyle, ApplyStyle/ApplyTextStyle
16. modules/fonts/grouping.lua — BuildGroupedFontItems, _groupedFontsCache
17. modules/fonts/dropdowns.lua — CreateFontDropdown, buildItems
18. modules/fonts/controls.lua — CreateFontSettingControl
19. modules/fonts/menuitems.lua — CreateFontMenuItems, CreateFlagMenuItems, CreateSizeMenuItems, CreateStyleMenuItems
20. modules/fonts/selectors.lua — CreateStyleSelector, CreateSimpleFontSelector, AttachStyleSelector, AttachFontSelector
21. modules/fonts/preview.lua — FontPreview:Create, _ApplyPreviewSelection
22. modules/colors/colors.lua — full color API (lookup, math, wrapping, apply, picker)
23. modules/colors/colorpicker.lua — rectangular HSV color picker widget
24. modules/textures/textures.lua — statusbar texture registry, LSM import
25. modules/design/design.lua — Design.Colors static palette, visual building blocks
26. modules/ui/controls.lua — UI control factory (slider, toggle, label, dropdown, etc.)
27. modules/ui/options.lua — CreateOptionsPanel (tabbed settings window)
28. modules/minimap/minimap.lua — circular-drag minimap button
29. modules/sharedmedia/sharedmedia.lua — multi-type media registry, DBM/known-addon/generic scanners
30. modules/combat/combat.lua — combat enter/leave/kill/crit/low-health/encounter callbacks
31. modules/petbattles/petbattles.lua — pet battle level/capture/state callbacks
32. modules/reputation/reputation.lua — reputation and renown tracking callbacks
33. modules/databroker/databroker.lua — NewDataObject, LDB bridge
34. modules/sound/sound.lua — Sound:Register, variant playback, SavedVar integration
35. modules/achievement/achievement.lua — achievement unlock callbacks
36. modules/levelup/levelup.lua — level-up callbacks
37. modules/quest/quest.lua — quest lifecycle and progress callbacks
38. modules/honor/honor.lua — honor level callbacks
39. modules/delves/delves.lua — delve companion/lives callbacks
40. modules/housing/housing.lua — housing progression/decor callbacks
41. modules/tradingpost/tradingpost.lua — Trading Post activity callbacks
42. modules/prey/prey.lua — prey hunt callbacks
43. modules/collectibles/collectibles.lua — mount/pet/toy unlock callbacks
44. modules/loot/loot.lua — loot and currency callbacks
45. core/commands.lua — /rgx slash command handler (modules, fonts, debug)
46. core/initialization.lua — ADDON_LOADED handler, database init, module TryInit, OnReady lifecycle
Load order is authoritatively defined by
RGX-Framework.xml. As of v2.1.0 every in-tree module is loaded — there are no dormant modules.
Consumer addons with RequiredDeps: RGX-Framework are guaranteed to load after step 32 completes.
Every Lua file loaded via WoW's <Script> tag receives the addon name and the addon table through the ... varargs:
local addonName, RGX = ...Critical subtlety: WoW passes the same private table to every file. local _, MyModule = ... does not create a unique module table — MyModule is the same table as _G.RGXFramework. This means:
- Generic field names like
Init,name, ordbcan collide across split-module files - The fonts sub-module files (definitions, registry, query, etc.) all populate the same
Fontstable — they use specific, non-colliding field names - If two files both define
function MyModule:Init(), the second one overwrites the first
Convention: Module files should either use unique field names or be organized so that only one file defines any given method.
Modules register themselves at load time:
RGX:RegisterModule(name, moduleTable, { global = "RGXFoo" })This stores the module in RGX.modules[name] and optionally publishes it to _G["RGXFoo"].
RGX:GetModule(name) resolves in two steps:
- Check
RGX.modules[normalizedName](wherenormalizedName = string.lower(name)) - Fall back to
ResolveModuleAlias— look up_G[self.moduleAliases[normalizedName]]
This means a module that sets its own global (e.g. RGXFonts = Fonts) before the framework processes it will still be found via the alias fallback.
The framework provides typed convenience wrappers:
RGX:GetFonts() -- "fonts" → RGXFonts
RGX:GetColors() -- "colors" → RGXColors
RGX:GetTextures() -- "textures" → RGXTextures
RGX:GetDropdowns() -- "dropdowns" → RGXDropdowns
RGX:GetUI() -- "ui" → RGXUI
RGX:GetColorPicker()-- "colorpicker" → RGXColorPicker
RGX:GetMinimap() -- "minimap" → RGXMinimap
RGX:GetDesign() -- "design" → RGXDesign
RGX:GetDataBroker() -- "databroker" → RGXDataBroker
RGX:GetSound() -- "sound" → RGXSoundAs of v2.1.0, there are no dormant modules. All in-tree modules are loaded by the XML loader.
Previously dormant modules and when they were re-enabled:
| Module | Global | Re-enabled |
|---|---|---|
| SharedMedia | RGXSharedMedia |
v2.0.0 |
| PetBattles | RGXPetBattles |
v2.0.0 |
| Reputation | RGXReputation |
v2.0.0 |
| Combat | RGXCombat |
v2.1.0 |
| Achievement, LevelUp, Quest, Honor, Delves, Housing, TradingPost, Prey | various | v2.1.0 |
When WoW fires ADDON_LOADED for "RGX-Framework":
- Initialize
_G.RGXFrameworkDB(or reuse existing) - Set
RGX.db = _G.RGXFrameworkDB - Call
TryInit("RGXFonts")— runsFonts:Init() - Call
TryInitfor each active module: SharedMedia, Combat, PetBattles, Reputation, Achievement, LevelUp, Quest, Honor, Delves, Housing, TradingPost, Prey, Collectibles, Loot - Set
RGX._ready = true - Fire all queued
OnReadycallbacks - Unregister the ADDON_LOADED handler
RGX:OnReady(fn)If the framework is already initialized, fn runs immediately. Otherwise it is queued and fired during step 6 above.
Consumer addons should use OnReady when they need initialized modules (fonts, colors, etc.). For core-only APIs (events, timers, hooks, slash commands), _G.RGXFramework is available immediately — no OnReady needed.
RGX runs its own tick-based timer driver on a hidden OnUpdate frame. Timers are plain tables:
timer = {
id, label, duration, callback, repeating, elapsed, active,
owner?, name?, declarativeName?
}-
RGX:After(dur, cb)— one-shot, returns timer ref -
RGX:Every(dur, cb)— repeating, cb receivestimeras first arg so it can cancel itself -
RGX:CancelTimer(timer)— markstimer.active = false; removed on next tick
RGXAddon can declare every = { name = { seconds, handler } }. These timers
start after the consumer's matching ADDON_LOADED, carry owner/name metadata,
and use a stable AddonName:every:name label. Definitions are sorted and
registered in reverse because the driver walks newest-to-oldest; timers from one
declaration that become due on the same update therefore dispatch in lexical
name order. A persistent scan cursor resumes budget-deferred work on the next
update so a large due set cannot starve later names. Callback errors retain the
timer label and remain failure-isolated.
Budget: timerBudget = { maxPerFrame = 256, maxSeconds = 0.033, slowSeconds = 0.250, slowByLabel = { ["SharedMedia:QueueScan"] = 0.500 } }. Slow callbacks (>250ms by default) are reported via [RGX:timer-slow]; known-heavy labels get per-label overrides instead of raising the global threshold. The driver pauses OnUpdate when no active timers remain. (The threshold was raised from 50ms in v2.0.0-alpha.1 — media scanning is normal I/O, not a fault.)
Two dispatch channels share the same internal handler registry:
| Channel | API | Scope |
|---|---|---|
| Events |
RegisterEvent, UnregisterEvent, FireEvent
|
WoW C events via OnEvent frame |
| Messages |
RegisterMessage, UnregisterMessage, SendMessage
|
Internal addon-to-addon / module-to-module |
Both support id (for targeted unregistration) and owner (for method-name callbacks). Dispatch is pcall-wrapped with error reporting.
RegisterCallback / UnregisterCallback are aliases for RegisterMessage / UnregisterMessage.
Module-local callback emitters:
local emitter = RGX:CreateEmitter("MyModule")
emitter:RegisterCallback("DATA_CHANGED", fn, id)
emitter:Fire("DATA_CHANGED", data)RGX:QueueForCombat(func, ...)If not in combat lockdown, func runs immediately. Otherwise it is queued and processed when PLAYER_REGEN_ENABLED fires.
The Safe* helpers (SafeShow, SafeHide, SafeSetPoint, SafeSetSize, SafeSetText, and the UIDropDownMenu variants) all use this queue internally.
Framework DB is RGXFrameworkDB (declared in TOC as SavedVariables). Consumer addons use their own SavedVariables managed via RGX:NewDatabase(name, defaults, opts), which returns a profile-aware proxy with metamethod access (shipped in v1.9.0, hardened in v2.0.0).
The framework's config.lua provides defaults:
defaults = {
global = {
debugMode = false,
},
profile = {
fonts = {
default = "Inter-Regular",
defaultSize = 12,
defaultFlags = "",
},
},
}Core (events, runtime, utils, config, database)
├── Dropdowns (no deps beyond core)
├── Fonts (depends on Dropdowns for CreateFontDropdown)
│ └── uses RGXDropdowns.CreateNestedDropdown internally
├── Colors (no deps beyond core)
│ └── ColorPicker (no deps beyond core + Colors)
├── Textures (no deps beyond core)
├── Design (depends on Colors for palette)
├── UI (depends on Fonts, Colors, Textures, Dropdowns for control factories)
├── Minimap (no deps beyond core)
├── DataBroker (no deps beyond core)
└── Sound (no deps beyond core)
-
No C_Timer — all deferred work uses
RGX:After/RGX:Every. Inside the framework itself, four call sites keep a guardedelseif C_Timer.Afterfallback for the edge case where the timer driver is unavailable (options.lua x2, sharedmedia.lua, reputation.lua); consumer-facing code has no such exception. -
No manual event frames — use
RGX:RegisterEvent -
No raw SLASH_X patterns — use
RGX:RegisterSlashCommand -
assert(_G.RGXFramework, ...)— consumer addons fail fast if RGX is missing -
RequiredDeps: RGX-Framework— TOC dependency, not optional embedding -
Module methods are colon-call —
Fonts:GetPath("Inter-Regular"), notFonts.GetPath(Fonts, ...) -
Font paths are absolute —
"Interface\\AddOns\\RGX-Framework\\media\\fonts\\Inter-Regular.otf" -
Unavailable fonts are in-tree but blocked (corrupted assets) —
unavailableFontslist in definitions.lua;IsAvailable()returns false;ListAvailable()excludes them; they cannot be selected in dropdowns