diff --git a/Modules/Quest/QuestEventHandler.lua b/Modules/Quest/QuestEventHandler.lua index dc3b5a6..d5b02d4 100644 --- a/Modules/Quest/QuestEventHandler.lua +++ b/Modules/Quest/QuestEventHandler.lua @@ -553,6 +553,10 @@ function _QuestEventHandler:QuestLogUpdate() doFullQuestLogScan = false -- Function call updates doFullQuestLogScan. Order matters. _QuestEventHandler:UpdateAllQuests() + -- Also on this path: UpdateAllQuests only looks at quests still in the log, so a removal + -- that fired no event of its own would sit there unnoticed for as long as full scans keep + -- being asked for. + _QuestEventHandler:CleanupRemovedQuestsFallback() else _QuestEventHandler:CleanupRemovedQuestsFallback() QuestieCombatQueue:Queue(function() @@ -666,7 +670,9 @@ function _QuestEventHandler:CleanupRemovedQuestsFallback() if QuestiePlayer and QuestiePlayer.currentQuestlog then local removedQuestIds = {} for questId in pairs(QuestiePlayer.currentQuestlog) do - if questId and questId > 0 and (not gameQuestIds[questId]) then + -- Typed check: a stray string key (saved variables have produced them) would other- + -- wise error on the comparison and take the whole pass down with it. + if type(questId) == "number" and questId > 0 and (not gameQuestIds[questId]) then removedQuestIds[#removedQuestIds + 1] = questId end end @@ -679,7 +685,12 @@ function _QuestEventHandler:CleanupRemovedQuestsFallback() local wasTurnedIn = questLog[questId] and questLog[questId].state == QUEST_LOG_STATES.QUEST_TURNED_IN local wasAlreadyComplete = Questie.db.char.complete and Questie.db.char.complete[questId] local completeAtRemoval = QuestieDB.IsComplete(questId) - local shouldComplete = wasTurnedIn or wasAlreadyComplete or completeAtRemoval == 1 + -- The server's own record, and the only one that knows anything about a quest the + -- database has never heard of: QuestieDB.IsComplete cannot answer for those, so an + -- Ascension quest the server finished by itself would otherwise be filed as abandoned. + local serverFlaggedComplete = IsQuestFlaggedCompleted and IsQuestFlaggedCompleted(questId) + local shouldComplete = wasTurnedIn or wasAlreadyComplete or completeAtRemoval == 1 or + serverFlaggedComplete QuestLogCache.RemoveQuest(questId) QuestieQuest:SetObjectivesDirty(questId) diff --git a/Modules/Tracker/TrackerUtils.lua b/Modules/Tracker/TrackerUtils.lua index 38c2a09..06ba909 100644 --- a/Modules/Tracker/TrackerUtils.lua +++ b/Modules/Tracker/TrackerUtils.lua @@ -681,8 +681,13 @@ end -- the tracker asks per quest and redraws often -- walking the log once per quest is quadratic. local questLogHeaders = {} +-- Every questId the log currently holds, headers aside. The tracker draws from currentQuestlog, +-- and this is what says whether the player still has a given quest. +local questLogQuestIds = {} + local function BuildQuestLogHeaders() local headers = {} + local questIds = {} local header for i = 1, (GetNumQuestLogEntries and GetNumQuestLogEntries() or 0) do @@ -691,17 +696,21 @@ local function BuildQuestLogHeaders() if title and title ~= "" then header = title end - elseif logId and header then - headers[logId] = header + elseif logId then + questIds[logId] = true + if header then + headers[logId] = header + end end end questLogHeaders = headers + questLogQuestIds = questIds end -- Returns the header title string, or nil if the quest is not in the log under one. local function GetQuestLogZoneName(questId) - if not questLogHeaders[questId] then + if not questLogQuestIds[questId] then -- Asked about a quest the last pass did not see, so the log has moved on since. BuildQuestLogHeaders() end @@ -837,8 +846,16 @@ function TrackerUtils:GetSortedQuestIds() local questDetails = {} local sortObj = Questie.db.profile.trackerSortObjectives - -- One walk of the quest log for the whole draw, so the per-quest zone lookups below are reads. + -- One walk of the quest log for the whole draw, so the per-quest lookups below are reads. BuildQuestLogHeaders() + + -- currentQuestlog is only as good as the removal events that maintain it, and a quest the + -- server finishes on its own -- Ascension's auto-complete quests -- can leave the log without + -- any of them landing, which strands the quest in the tracker for the rest of the session. The + -- log is the authority on what the player still has, so anything missing from it is skipped. + -- Skipped rather than pruned: a redraw that catches the log mid-refresh would otherwise throw + -- away state Questie is about to want back. + local questLogIsReadable = next(questLogQuestIds) ~= nil -- Update quest objectives for questId, quest in pairs(QuestiePlayer.currentQuestlog) do @@ -911,7 +928,14 @@ function TrackerUtils:GetSortedQuestIds() end end - if type(quest) == "table" and quest.IsComplete and quest.Objectives then + local isInQuestLog = (not questLogIsReadable) or (questLogQuestIds[qid] == true) + if not isInQuestLog then + -- Left over from a removal nothing told the tracker about, so the object built for it + -- goes too -- otherwise it would still be here to serve the next draw. + TrackerUtils._fallbackQuests[qid] = nil + end + + if isInQuestLog and type(quest) == "table" and quest.IsComplete and quest.Objectives then -- Insert Quest Ids into sortedQuestIds table tinsert(sortedQuestIds, qid)