-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
RGX-Framework's architecture is built around the WoW ... varargs pattern, a deterministic XML load order, and a central module registry. This page covers every layer from first file load to runtime.
RGX-Framework.xml loads 44 files in strict sequence. Every file receives the same local addonName, addonTable = ... varargs — they all share the same RGX table.
1. core/core.lua — creates _G.RGXFramework, module registry, getters
2. core/systems/config.lua — default config table
3. core/systems/database.lua — InitDatabase, GetDB, DB(), MigrateDB()
4. core/systems/events.lua — RegisterEvent, RegisterMessage, CreateEmitter
5. core/systems/runtime.lua — After, Every, CancelTimer, Hook, QueueForCombat, Safe*
6. core/systems/utils.lua — Trim, Split, TableKeys/Values/Contains, MergeTable, Print/Warn/Error
7. modules/dropdowns/dropdowns.lua — CreateNestedDropdown, NormalizeItems, AddInlineButton
8. modules/fonts/definitions.lua — font definitions table, unavailableFonts
9. modules/fonts/init.lua — module bootstrap, built-in registration
10. modules/fonts/registry.lua — Register, RegisterAddonFont, RegisterFontPack, RegisterBuiltInFonts
11. modules/fonts/query.lua — GetPath, Get, GetFont, List, ListAvailable, FindByPath, ResolveName
12. modules/fonts/defaults.lua — SetDefault, GetDefault, SetDefaultSize, SetDefaultFlags
13. modules/fonts/apply.lua — Apply, Quick, ApplyChildren, CreateString, FromTemplate
14. modules/fonts/normalize.lua — SplitFlags, NormalizeFlags, DescribeFlags, NormalizeColorValue, NormalizeShadowOffset
15. modules/fonts/styles.lua — NormalizeStyle, CreateStyle, GetStyle, ApplyStyle, ApplyStyleMap
16. modules/fonts/grouping.lua — GetGroupedFonts, BuildGroupedFontItems, GetCategoryLabel
17. modules/fonts/dropdowns.lua — CreateFontDropdown, CreateSimpleFontSelector
18. modules/fonts/controls.lua — CreateFontSettingControl, AttachFontSelector, GetOptionValues
19. modules/fonts/menuitems.lua — CreateFontMenuItems, CreateFlagMenuItems, CreateSizeMenuItems, CreateStyleMenuItems
20. modules/fonts/selectors.lua — CreateStyleSelector, AttachStyleSelector, CreateStyleEditorFrame
21. modules/fonts/preview.lua — CreateTestFrame, ToggleTestFrame, _GetPreviewSample, _ApplyPreviewSelection
22. modules/colors/colors.lua — color registry, Get, GetRGB, GetHex, Wrap, Lerp, OpenPicker, CreateColorPicker
23. modules/colors/colorpicker.lua — rectangular HSV picker widget (v2.0.0)
24. modules/textures/textures.lua — RegisterBar, GetBar, CreateBarDropdown, ImportLibSharedMedia
25. modules/design/design.lua — RGXDesign palette, ApplyBackdrop, CreateFrame, CreateButton, CreateSection
26. modules/ui/controls.lua — CreateSlider, CreateToggle, CreateLabel, CreateColorPicker, CreateDropdown, CreateSection
27. modules/ui/options.lua — CreateOptionsPanel, tab system, scroll container, auto-layout helper
28. modules/minimap/minimap.lua — CreateMinimapButton, angle persistence, drag tracking
29. modules/databroker/databroker.lua — NewDataObject, live proxy, LDB bridge
30. modules/sound/sound.lua — Register, Handle API, variant playback
31. core/commands.lua — /rgx slash commands (modules, fonts, debug)
32. core/initialization.lua — lifecycle: IsReady, OnReady, OnLogin, ADDON_LOADED handler, TryInit
Every XML <Script> file receives:
local addonName, addonTable = ...-
addonName="RGX-Framework" -
addonTable= the same table reference as_G.RGXFramework
This means every file adds methods directly to the shared RGX table. No require(), no LibStub, no embedding — just direct table extension.
For submodules (Fonts, Colors, etc.), definitions.lua creates a local table and assigns it to both addon._fontsModule and a _G global. Subsequent font files read the module back from addon._fontsModule:
-- definitions.lua
local Fonts = {}
addon._fontsModule = Fonts
_G.RGXFonts = Fonts
-- init.lua
local Fonts = addon._fontsModuleRegisters a module into the central registry. Called by each module during its init.
Parameters:
| Parameter | Type | Description |
|---|---|---|
name |
string | Module name (case-insensitive) |
module |
table | Module table |
opts |
table? |
{ global = "RGXFonts" } — global variable name |
Behavior:
- Normalizes name to lowercase
- Stores in
RGX.modules[name]andRGX.loadedModules[name] - Sets
module.nameandmodule.framework - If
opts.globalis provided and_G[global]is nil, assigns_G[global] = module - Returns
trueon success,falseon duplicate or invalid input
RGX.moduleAliases = {
fonts = "RGXFonts",
colors = "RGXColors",
textures = "RGXTextures",
dropdowns = "RGXDropdowns",
ui = "RGXUI",
colorpicker = "RGXColorPicker",
minimap = "RGXMinimap",
petbattles = "RGXPetBattles",
sharedmedia = "RGXSharedMedia",
design = "RGXDesign",
combat = "RGXCombat",
reputation = "RGXReputation",
databroker = "RGXDataBroker",
sound = "RGXSound",
}Looks up by normalized name, then falls back to alias resolution via _G.
Same as GetModule, but calls geterrorhandler() if the module is missing.
RGX:GetFonts() → RGXFonts
RGX:GetColors() → RGXColors
RGX:GetTextures() → RGXTextures
RGX:GetDropdowns() → RGXDropdowns
RGX:GetUI() → RGXUI
RGX:GetColorPicker() → RGXColorPicker
RGX:GetMinimap() → RGXMinimap
RGX:GetPetBattles() → RGXPetBattles -- dormant
RGX:GetSharedMedia() → RGXSharedMedia -- dormant
RGX:GetDesign() → RGXDesign
RGX:GetCombat() → RGXCombat -- dormant
RGX:GetReputation() → RGXReputation -- dormant
RGX:GetDataBroker() → RGXDataBroker
RGX:GetSound() → RGXSoundAll 44 files execute in order. Modules that self-initialize (Dropdowns, Colors, Textures, UI, Minimap, DataBroker, Design) call their own :Init() at the bottom of their file. This registers them with RGX:RegisterModule() and sets their _G global.
initialization.lua registers a handler for ADDON_LOADED with the ID "RGX_Init":
- Initialize
_G.RGXFrameworkDBandRGX.db - Call
TryInit("RGXFonts")— callsFonts:Init()if it hasn't been called yet - Call
TryInit("RGXSharedMedia")— no-op if module is dormant - Call
TryInit("RGXCombat")— no-op if module is dormant - Call
TryInit("RGXReputation")— no-op if module is dormant - Set
RGX._ready = true - Fire all queued
OnReadycallbacks - Unregister the
ADDON_LOADEDhandler
PetBattles self-initializes in its own file and does not use
TryInit.
Modules that need post-load state (SharedMedia scanning, PetBattles level scanning) hook PLAYER_LOGIN internally.
RGX:OnReady(function()
-- runs immediately if framework is already ready
-- otherwise queued until ADDON_LOADED completes
end)Consumer addons should always wrap their setup in OnReady:
local RGX = assert(_G.RGXFramework, "MyAddon: RGX-Framework not loaded")
RGX:OnReady(function()
local Fonts = RGX:GetFonts()
-- safe to use all modules here
end)RGX:RegisterEvent(event, callback, id, owner) uses a single hidden eventFrame with OnEvent script:
- WoW fires
eventFrame:GetScript("OnEvent")with(frame, event, ...) - The handler calls
RGX:FireEvent(event, ...) -
FireEventiteratesRGX.events[event]bucket, calling each handler - Errors are caught via
pcalland sent togeterrorhandler() - Unit events (
RegisterUnitEvent) are filtered by unit token before dispatch
Every registered handler gets a string ID. If you don't provide one, a default is generated from the callback's tostring(). Use explicit IDs for clean unregistration:
RGX:RegisterEvent("UNIT_AURA", myHandler, "myAddon_auraTracker")
-- later:
RGX:UnregisterEvent("UNIT_AURA", "myAddon_auraTracker")Timers are driven by a single OnUpdate frame (RGX.timerFrame). The budget system prevents frame-time spikes:
| Setting | Value | Meaning |
|---|---|---|
maxPerFrame |
256 | Max timer callbacks per frame |
maxSeconds |
0.033 | Max seconds spent on timers per frame |
slowSeconds |
0.050 | Threshold for slow-callback warnings |
When the budget is exceeded, remaining timers are deferred to the next frame and a diagnostic message is printed (rate-limited to once per 2 seconds).
The timer frame's OnUpdate script is set to nil when no timers remain, avoiding per-frame overhead.
RGX:QueueForCombat(func, ...)If not in combat, calls func(...) immediately. If in combat, queues the call and fires it when PLAYER_REGEN_ENABLED fires. This is the foundation for all Safe* wrappers:
| Method | Delegates to |
|---|---|
RGX:SafeShow(frame) |
frame:Show() |
RGX:SafeHide(frame) |
frame:Hide() |
RGX:SafeSetPoint(frame, ...) |
frame:ClearAllPoints() + frame:SetPoint(...)
|
RGX:SafeSetSize(frame, w, h) |
frame:SetSize(w, h) |
RGX:SafeSetText(region, text) |
region:SetText(text) |
RGX:SafeUIDropDownMenu_SetText(dropdown, text) |
UIDropDownMenu_SetText() |
RGX:SafeUIDropDownMenu_Initialize(dropdown, init, mode) |
UIDropDownMenu_Initialize() |
RGX:SafeUIDropDownMenu_Refresh(dropdown) |
UIDropDownMenu_Refresh() |
RGX:SafeUIDropDownMenu_EnableDropDown(dropdown) |
UIDropDownMenu_EnableDropDown() |
RGX:SafeUIDropDownMenu_DisableDropDown(dropdown) |
UIDropDownMenu_DisableDropDown() |
RGX:SafeToggleDropDownMenu(...) |
ToggleDropDownMenu() |
RGX:SafeCloseDropDownMenus(...) |
CloseDropDownMenus() |
Four modules are in-tree but not loaded by RGX-Framework.xml since v1.5.18:
| Module | Global | Why dormant |
|---|---|---|
| PetBattles | RGXPetBattles |
Self-initializes; Get*() returns nil |
| SharedMedia | RGXSharedMedia |
TryInit("RGXSharedMedia") is a no-op |
| Combat | RGXCombat |
TryInit("RGXCombat") is a no-op |
| Reputation | RGXReputation |
TryInit("RGXReputation") is a no-op |
To re-enable: add the <Script> line to RGX-Framework.xml. TryInit in initialization.lua will automatically call Init() when the global becomes available.
core.lua
├─ config.lua
├─ database.lua ─── uses config.defaults
├─ events.lua ──── uses core for Debug
├─ runtime.lua ─── uses core for Debug, After for combat queue
└─ utils.lua ───── uses core for Clamp
dropdowns.lua ─── uses core for Debug, Safe*
fonts/
definitions.lua ── standalone
init.lua ───────── uses core (RegisterModule)
registry.lua ───── uses core (Debug)
query.lua ──────── uses core (Debug, Clamp)
defaults.lua ───── uses core (Debug)
apply.lua ──────── standalone (uses module refs)
normalize.lua ──── uses core (Clamp), Colors module
styles.lua ─────── uses core (Clamp), normalize
grouping.lua ───── uses core (Debug)
dropdowns.lua ──── uses Dropdowns module, core (Debug)
controls.lua ───── uses Dropdowns module, core (Debug, CopyTable)
menuitems.lua ──── uses grouping, normalize
selectors.lua ──── uses Dropdowns module, core (CopyTable)
preview.lua ────── uses core (Debug)
colors.lua ────── uses core (Clamp, Debug)
colorpicker.lua ─ uses core (Debug, RegisterModule)
textures.lua ──── uses core (Debug, RegisterModule), Dropdowns
design.lua ─────── uses core (RegisterModule) — no runtime deps
ui/controls.lua ── uses core, Design, ColorPicker, Textures, Dropdowns
ui/options.lua ─── uses core, Design, UI/controls
minimap.lua ────── uses core (Debug, RegisterModule)
databroker.lua ─── uses core (Debug, RegisterModule)
sound.lua ──────── uses core (Debug, RegisterModule, After)
combat.lua ─────── uses core (Debug, RegisterEvent, After) — dormant
reputation.lua ─── uses core (Debug, RegisterEvent, After) — dormant
petbattles.lua ─── uses core (Debug, RegisterEvent, After) — dormant
sharedmedia.lua ── uses core (Debug, RegisterEvent, After) — dormant
Creates or retrieves a _G[name] SavedVariables table. Deep-merges defaults without overwriting existing keys.
Version-based migration runner. Migrations execute from (storedVersion + 1) through currentVersion. On first install (no stored version), no migrations run.
RGX:MigrateDB(db, "MyAddonDB", 3, {
[1] = function(db) db.newKey = true end,
[2] = function(db) db.oldKey = nil end,
[3] = function(db) db.nested = { a = 1 } end,
})RGX.defaults = {
global = {
debugMode = false,
version = RGX.version,
},
profile = {
fonts = {
default = "Inter-Regular",
defaultSize = 12,
defaultFlags = "",
},
},
}