From ee3f60a1bbde9d4e3370861d80326208f79486c2 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Fri, 20 Mar 2026 13:07:45 -0700 Subject: [PATCH] feat: add GetNumAddOns and GetAddOnInfo for addon introspection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - GetNumAddOns() — returns count of loaded addons - GetAddOnInfo(indexOrName) — returns name, title, notes, loadable Addon info is stored in the Lua registry from the .toc directives and populated before addon files execute. Useful for addon managers and compatibility checks between addons. Total WoW API: 33 functions. --- include/addons/lua_engine.hpp | 5 +++ src/addons/addon_manager.cpp | 1 + src/addons/lua_engine.cpp | 71 +++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+) diff --git a/include/addons/lua_engine.hpp b/include/addons/lua_engine.hpp index 02f9ce54..4a0027bb 100644 --- a/include/addons/lua_engine.hpp +++ b/include/addons/lua_engine.hpp @@ -9,6 +9,8 @@ namespace wowee::game { class GameHandler; } namespace wowee::addons { +struct TocFile; // forward declaration + class LuaEngine { public: LuaEngine(); @@ -39,6 +41,9 @@ public: bool loadSavedVariables(const std::string& path); bool saveSavedVariables(const std::string& path, const std::vector& varNames); + // Store addon info in registry for GetAddOnInfo/GetNumAddOns + void setAddonList(const std::vector& addons); + lua_State* getState() { return L_; } bool isInitialized() const { return L_ != nullptr; } diff --git a/src/addons/addon_manager.cpp b/src/addons/addon_manager.cpp index ec54f03a..60593792 100644 --- a/src/addons/addon_manager.cpp +++ b/src/addons/addon_manager.cpp @@ -52,6 +52,7 @@ void AddonManager::scanAddons(const std::string& addonsPath) { } void AddonManager::loadAllAddons() { + luaEngine_.setAddonList(addons_); int loaded = 0, failed = 0; for (const auto& addon : addons_) { if (loadAddon(addon)) loaded++; diff --git a/src/addons/lua_engine.cpp b/src/addons/lua_engine.cpp index 77b03bc0..d16ec26c 100644 --- a/src/addons/lua_engine.cpp +++ b/src/addons/lua_engine.cpp @@ -1,4 +1,5 @@ #include "addons/lua_engine.hpp" +#include "addons/toc_parser.hpp" #include "game/game_handler.hpp" #include "game/entity.hpp" #include "core/logger.hpp" @@ -282,6 +283,54 @@ static int lua_InCombatLockdown(lua_State* L) { return 1; } +// --- Addon Info API --- +// These need the AddonManager pointer stored in registry + +static int lua_GetNumAddOns(lua_State* L) { + lua_getfield(L, LUA_REGISTRYINDEX, "wowee_addon_count"); + return 1; +} + +static int lua_GetAddOnInfo(lua_State* L) { + // Accept index (1-based) or addon name + lua_getfield(L, LUA_REGISTRYINDEX, "wowee_addon_info"); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + lua_pushnil(L); return 1; + } + + int idx = 0; + if (lua_isnumber(L, 1)) { + idx = static_cast(lua_tonumber(L, 1)); + } else if (lua_isstring(L, 1)) { + // Search by name + const char* name = lua_tostring(L, 1); + int count = static_cast(lua_objlen(L, -1)); + for (int i = 1; i <= count; i++) { + lua_rawgeti(L, -1, i); + lua_getfield(L, -1, "name"); + const char* aName = lua_tostring(L, -1); + lua_pop(L, 1); + if (aName && strcmp(aName, name) == 0) { idx = i; lua_pop(L, 1); break; } + lua_pop(L, 1); + } + } + + if (idx < 1) { lua_pop(L, 1); lua_pushnil(L); return 1; } + + lua_rawgeti(L, -1, idx); + if (!lua_istable(L, -1)) { lua_pop(L, 2); lua_pushnil(L); return 1; } + + lua_getfield(L, -1, "name"); + lua_getfield(L, -2, "title"); + lua_getfield(L, -3, "notes"); + lua_pushboolean(L, 1); // loadable (always true for now) + lua_pushstring(L, "INSECURE"); // security + lua_pop(L, 1); // pop addon info entry (keep others) + // Return: name, title, notes, loadable, reason, security + return 5; +} + // UnitBuff(unitId, index) / UnitDebuff(unitId, index) // Returns: name, rank, icon, count, debuffType, duration, expirationTime, caster, isStealable, shouldConsolidate, spellId static int lua_UnitAura(lua_State* L, bool wantBuff) { @@ -761,6 +810,8 @@ void LuaEngine::registerCoreAPI() { {"InCombatLockdown", lua_InCombatLockdown}, {"UnitBuff", lua_UnitBuff}, {"UnitDebuff", lua_UnitDebuff}, + {"GetNumAddOns", lua_GetNumAddOns}, + {"GetAddOnInfo", lua_GetAddOnInfo}, // Utilities {"strsplit", lua_strsplit}, {"strtrim", lua_strtrim}, @@ -1163,6 +1214,26 @@ static void serializeLuaValue(lua_State* L, int idx, std::string& out, int inden } } +void LuaEngine::setAddonList(const std::vector& addons) { + if (!L_) return; + lua_pushnumber(L_, static_cast(addons.size())); + lua_setfield(L_, LUA_REGISTRYINDEX, "wowee_addon_count"); + + lua_newtable(L_); + for (size_t i = 0; i < addons.size(); i++) { + lua_newtable(L_); + lua_pushstring(L_, addons[i].addonName.c_str()); + lua_setfield(L_, -2, "name"); + lua_pushstring(L_, addons[i].getTitle().c_str()); + lua_setfield(L_, -2, "title"); + auto notesIt = addons[i].directives.find("Notes"); + lua_pushstring(L_, notesIt != addons[i].directives.end() ? notesIt->second.c_str() : ""); + lua_setfield(L_, -2, "notes"); + lua_rawseti(L_, -2, static_cast(i + 1)); + } + lua_setfield(L_, LUA_REGISTRYINDEX, "wowee_addon_info"); +} + bool LuaEngine::loadSavedVariables(const std::string& path) { if (!L_) return false; std::ifstream f(path);