From 94707e1b931c63efd433aecce66235366e72f61f Mon Sep 17 00:00:00 2001 From: Xurkon Date: Fri, 5 Jun 2026 21:46:16 -0500 Subject: [PATCH] fix: make learner db reads self-sustaining --- Database/QuestieDB.lua | 89 ++++++++++++++++++--- Modules/Quest/QuestieQuestPrivates.lua | 34 ++------ Tests/QuestieLearnerDataSourceMode_spec.lua | 79 ++++++++++++++++-- 3 files changed, 161 insertions(+), 41 deletions(-) diff --git a/Database/QuestieDB.lua b/Database/QuestieDB.lua index ad3dd21..fcc0fa2 100644 --- a/Database/QuestieDB.lua +++ b/Database/QuestieDB.lua @@ -214,6 +214,25 @@ function QuestieDB:IsBaseDatabaseMissing() return QuestieDB.baseDatabaseMissing == true end +local function _GetLearnerSettings() + local ld = Questie and Questie.dbLearner and Questie.dbLearner.global + return ld and ld.settings or nil +end + +local function _GetLearnerRecord(storeName, id) + local ld = Questie and Questie.dbLearner and Questie.dbLearner.global + if not ld then + return nil + end + + local store = ld[storeName] + if not store then + return nil + end + + return store[id] or store[tostring(id)] +end + ---@type QuestieQuest local QuestieQuest = QuestieLoader:ImportModule("QuestieQuest") ---@type QuestieQuestPrivate @@ -542,11 +561,22 @@ function QuestieDB:GetObject(objectId) return _QuestieDB.objectCache[objectId]; end - -- Try to get from compiled DB first - local rawdata = QuestieDB.QueryObject(objectId, QuestieDB._objectAdapterQueryOrder) + local settings = _GetLearnerSettings() + local mode = settings and settings.dataSourceMode or "auto" + local learnerRecord = _GetLearnerRecord("objects", objectId) - -- Check for overrides - local override = QuestieDB.objectDataOverrides and (QuestieDB.objectDataOverrides[objectId] or QuestieDB.objectDataOverrides[tostring(objectId)]) + local rawdata + local override + if mode == "learner" or QuestieDB:IsBaseDatabaseMissing() then + rawdata = learnerRecord + override = nil + else + rawdata = QuestieDB.QueryObject(objectId, QuestieDB._objectAdapterQueryOrder) + if not rawdata and learnerRecord then + rawdata = learnerRecord + end + override = QuestieDB.objectDataOverrides and (QuestieDB.objectDataOverrides[objectId] or QuestieDB.objectDataOverrides[tostring(objectId)]) + end if not rawdata and not override then Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieDB:GetObject] data not found for objectID:", objectId) @@ -588,8 +618,21 @@ function QuestieDB:GetItem(itemId) return _QuestieDB.itemCache[itemId]; end - local rawdata = QuestieDB.QueryItem(itemId, QuestieDB._itemAdapterQueryOrder) - local override = QuestieDB.itemDataOverrides and (QuestieDB.itemDataOverrides[itemId] or QuestieDB.itemDataOverrides[tostring(itemId)]) + local settings = _GetLearnerSettings() + local mode = settings and settings.dataSourceMode or "auto" + local learnerRecord = _GetLearnerRecord("items", itemId) + local rawdata + local override + if mode == "learner" or QuestieDB:IsBaseDatabaseMissing() then + rawdata = learnerRecord + override = nil + else + rawdata = QuestieDB.QueryItem(itemId, QuestieDB._itemAdapterQueryOrder) + if not rawdata and learnerRecord then + rawdata = learnerRecord + end + override = QuestieDB.itemDataOverrides and (QuestieDB.itemDataOverrides[itemId] or QuestieDB.itemDataOverrides[tostring(itemId)]) + end if not rawdata and not override then Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieDB:GetItem] data not found for itemID:", itemId) @@ -1457,8 +1500,21 @@ function QuestieDB.GetQuest(questId, ...) -- /dump QuestieDB.GetQuest(867) return _QuestieDB.questCache[questId]; end - local rawdata = QuestieDB.QueryQuest(questId, QuestieDB._questAdapterQueryOrder) - local overrideData = QuestieDB.questDataOverrides and (QuestieDB.questDataOverrides[questId] or QuestieDB.questDataOverrides[tostring(questId)]) + local settings = _GetLearnerSettings() + local mode = settings and settings.dataSourceMode or "auto" + local learnerRecord = _GetLearnerRecord("quests", questId) + local rawdata + local overrideData + if mode == "learner" or QuestieDB:IsBaseDatabaseMissing() then + rawdata = learnerRecord + overrideData = nil + else + rawdata = QuestieDB.QueryQuest(questId, QuestieDB._questAdapterQueryOrder) + if not rawdata and learnerRecord then + rawdata = learnerRecord + end + overrideData = QuestieDB.questDataOverrides and (QuestieDB.questDataOverrides[questId] or QuestieDB.questDataOverrides[tostring(questId)]) + end if (not rawdata) then rawdata = overrideData @@ -2057,8 +2113,21 @@ function QuestieDB:GetNPC(npcId) return _QuestieDB.npcCache[npcId] end - local rawdata = QuestieDB.QueryNPC(npcId, QuestieDB._npcAdapterQueryOrder) - local override = QuestieDB.npcDataOverrides and (QuestieDB.npcDataOverrides[npcId] or QuestieDB.npcDataOverrides[tostring(npcId)]) + local settings = _GetLearnerSettings() + local mode = settings and settings.dataSourceMode or "auto" + local learnerRecord = _GetLearnerRecord("npcs", npcId) + local rawdata + local override + if mode == "learner" or QuestieDB:IsBaseDatabaseMissing() then + rawdata = learnerRecord + override = nil + else + rawdata = QuestieDB.QueryNPC(npcId, QuestieDB._npcAdapterQueryOrder) + if not rawdata and learnerRecord then + rawdata = learnerRecord + end + override = QuestieDB.npcDataOverrides and (QuestieDB.npcDataOverrides[npcId] or QuestieDB.npcDataOverrides[tostring(npcId)]) + end if not rawdata and not override then Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieDB:GetNPC] data not found for npcID:", npcId) diff --git a/Modules/Quest/QuestieQuestPrivates.lua b/Modules/Quest/QuestieQuestPrivates.lua index ef7bd51..366d57b 100644 --- a/Modules/Quest/QuestieQuestPrivates.lua +++ b/Modules/Quest/QuestieQuestPrivates.lua @@ -148,17 +148,8 @@ monster = function(npcId, objective) and Questie.dbLearner.global and Questie.dbLearner.global.settings and Questie.dbLearner.global.settings.dataSourceMode or "auto" - local strictLearnerOnly = dataSourceMode == "learner" - - local name = strictLearnerOnly and nil or QuestieDB.QueryNPCSingle(npcId, "name") - if not name or name == "" then - if strictLearnerOnly then - local learnedNpc = Questie.dbLearner and Questie.dbLearner.global - and Questie.dbLearner.global.npcs - and Questie.dbLearner.global.npcs[npcId] - name = learnedNpc and learnedNpc[1] or nil - end - end + local npcData = QuestieDB:GetNPC(npcId) + local name = npcData and npcData.name or nil if not name or name == "" then -- Last resort: extract NPC name from objective description text. -- This mirrors the name-parsing logic in the killcredit function and @@ -182,7 +173,7 @@ monster = function(npcId, objective) return nil end - local spawns = strictLearnerOnly and {} or QuestieDB.QueryNPCSingle(npcId, "spawns") + local spawns = npcData and npcData.spawns or {} if (not spawns) then Questie:Debug(Questie.DEBUG_CRITICAL, "Spawn data missing for NPC:", npcId) spawns = {} @@ -229,7 +220,7 @@ monster = function(npcId, objective) end end - local rank = QuestieDB.QueryNPCSingle(npcId, "rank") + local rank = npcData and npcData.rank local enableSpawns = not QuestieCorrections.questNPCBlacklist[npcId] local enableWaypoints = enableSpawns and 2 ~= rank -- a rare mob spawn. todo: option for this @@ -239,7 +230,7 @@ monster = function(npcId, objective) Id = npcId, Name = name, Spawns = enableSpawns and spawns or {}, - Waypoints = enableWaypoints and QuestieDB.QueryNPCSingle(npcId, "waypoints") or {}, + Waypoints = enableWaypoints and (npcData and npcData.waypoints or {}) or {}, Hostile = true, Icon = Questie.ICON_TYPE_SLAY, GetIconScale = _GetIconScaleForMonster, @@ -271,23 +262,14 @@ object = function(objectId, objective) and Questie.dbLearner.global and Questie.dbLearner.global.settings and Questie.dbLearner.global.settings.dataSourceMode or "auto" - local strictLearnerOnly = dataSourceMode == "learner" - - local name = strictLearnerOnly and nil or QuestieDB.QueryObjectSingle(objectId, "name") - if not name or name == "" then - if strictLearnerOnly then - local learnedObj = Questie.dbLearner and Questie.dbLearner.global - and Questie.dbLearner.global.objects - and Questie.dbLearner.global.objects[objectId] - name = learnedObj and learnedObj[1] or nil - end - end + local objectData = QuestieDB:GetObject(objectId) + local name = objectData and objectData.name or nil if (not name) then Questie:Debug(Questie.DEBUG_CRITICAL, "Name missing for object:", objectId) return nil end - local spawns = strictLearnerOnly and {} or QuestieDB.QueryObjectSingle(objectId, "spawns") + local spawns = objectData and objectData.spawns or {} if (not spawns) then Questie:Debug(Questie.DEBUG_CRITICAL, "Spawn data missing for object:", objectId) spawns = {} diff --git a/Tests/QuestieLearnerDataSourceMode_spec.lua b/Tests/QuestieLearnerDataSourceMode_spec.lua index f29681a..12ca3bc 100644 --- a/Tests/QuestieLearnerDataSourceMode_spec.lua +++ b/Tests/QuestieLearnerDataSourceMode_spec.lua @@ -49,11 +49,13 @@ describe("QuestieLearner data source mode", function() it("keeps learner-only pin builders off the static DB lookup path", function() local priv = read("Modules/Quest/QuestieQuestPrivates.lua") - assert.is_true(has(priv, "local strictLearnerOnly = dataSourceMode == \"learner\"")) - assert.is_true(has(priv, "local name = strictLearnerOnly and nil or QuestieDB.QueryNPCSingle(npcId, \"name\")")) - assert.is_true(has(priv, "local spawns = strictLearnerOnly and {} or QuestieDB.QueryNPCSingle(npcId, \"spawns\")")) - assert.is_true(has(priv, "local name = strictLearnerOnly and nil or QuestieDB.QueryObjectSingle(objectId, \"name\")")) - assert.is_true(has(priv, "local spawns = strictLearnerOnly and {} or QuestieDB.QueryObjectSingle(objectId, \"spawns\")")) + assert.is_true(has(priv, "local npcData = QuestieDB:GetNPC(npcId)")) + assert.is_true(has(priv, "local name = npcData and npcData.name or nil")) + assert.is_true(has(priv, "local spawns = npcData and npcData.spawns or {}")) + assert.is_true(has(priv, "local rank = npcData and npcData.rank")) + assert.is_true(has(priv, "local objectData = QuestieDB:GetObject(objectId)")) + assert.is_true(has(priv, "local name = objectData and objectData.name or nil")) + assert.is_true(has(priv, "local spawns = objectData and objectData.spawns or {}")) end) it("maps object objectives from learner object captures before refresh", function() @@ -106,3 +108,70 @@ describe("QuestieLearner learner mode activation", function() assert.is_true(QuestieLearner:IsEnabled()) end) end) + +describe("QuestieDB learner source fallback", function() + before_each(function() + dofile("Tests/wow_api_mock.lua") + dofile("Database/QuestieDB.lua") + dofile("Database/npcDB.lua") + dofile("Database/objectDB.lua") + dofile("Database/questDB.lua") + dofile("Database/itemDB.lua") + + Questie.dbLearner.global.settings.enabled = true + Questie.dbLearner.global.settings.dataSourceMode = "learner" + Questie.dbLearner.global.npcs = { + [9001] = { + [1] = "Learner Whelp", + [7] = { + [44] = { + { 12.3, 45.6 }, + }, + }, + [8] = { + [44] = { + { 13.3, 46.6 }, + }, + }, + [9] = 44, + }, + } + Questie.dbLearner.global.objects = { + [9002] = { + [1] = "Learner Cache", + [4] = { + [44] = { + { 11.1, 22.2 }, + }, + }, + [5] = 44, + }, + } + + QuestieDB.QueryNPC = function() return nil end + QuestieDB.QueryObject = function() return nil end + QuestieDB.QueryQuest = function() return nil end + QuestieDB.QueryItem = function() return nil end + QuestieDB.private.npcCache = {} + QuestieDB.private.objectCache = {} + QuestieDB.private.questCache = {} + QuestieDB.private.itemCache = {} + end) + + it("returns learner NPC data when static queries are unavailable", function() + local npc = QuestieDB:GetNPC(9001) + assert.is_table(npc) + assert.equals("Learner Whelp", npc.name) + assert.is_table(npc.spawns) + assert.is_table(npc.waypoints) + assert.equals(44, npc.zoneID) + end) + + it("returns learner object data when static queries are unavailable", function() + local obj = QuestieDB:GetObject(9002) + assert.is_table(obj) + assert.equals("Learner Cache", obj.name) + assert.is_table(obj.spawns) + assert.equals(44, obj.zoneID) + end) +end)