From 69a9a4a7a38f283af87e04b7fb746c06f7b69539 Mon Sep 17 00:00:00 2001 From: Xurkon Date: Tue, 9 Jun 2026 20:41:37 -0500 Subject: [PATCH] fix(tracker): guard VoiceOver questPlayButtons and AceGUI tree tooltip (#15) Two crashes reported in #15 on Elune (1.6.3): - TrackerUtils:IsVoiceOverLoaded only verified the VoiceOver addons were loaded, not that VoiceOver.QuestOverlayUI.questPlayButtons exists. Some VoiceOver builds expose a QuestOverlayUI without that table, so UpdateVoiceOverPlayButtons and SetAllPlayButtonAlpha crashed with 'attempt to index field questPlayButtons (a nil value)'. Now verified in IsVoiceOverLoaded, which all play-button call sites gate on. - AceGUIContainer-TreeGroup crashed indexing a nil AceGUI.tooltip when a conflicting addon registered a broken AceGUI-3.0 core (version 1.#INF) that won LibStub but never created the shared tooltip frame. The tree button handlers now lazily recreate it; widget version bumped 47->48 so the fixed widget wins registration. --- CHANGELOG.md | 2 ++ .../widgets/AceGUIContainer-TreeGroup.lua | 14 ++++++++++++-- Modules/Tracker/TrackerUtils.lua | 9 ++++++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ff0558..8d3456c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,8 @@ ### Bug Fixes +- **[Tracker - VoiceOver questPlayButtons Nil Crash]** (#15) Fixed `attempt to index field 'questPlayButtons' (a nil value)` in `TrackerUtils:UpdateVoiceOverPlayButtons` and `TrackerLinePool.SetAllPlayButtonAlpha`. `TrackerUtils:IsVoiceOverLoaded` only checked that the VoiceOver addons were loaded, but some VoiceOver builds (seen on Elune) expose a `QuestOverlayUI` without a `questPlayButtons` table. It now also verifies `VoiceOver.QuestOverlayUI.questPlayButtons` exists; since every play-button call site gates on this function, the integration is now safely skipped instead of crashing on those builds. +- **[Libs/AceGUI - Tree Tooltip Nil Crash]** (#15) Hardened `AceGUIContainer-TreeGroup` against `attempt to index ... 'tooltip' (a nil value)` when a conflicting addon registers a broken `AceGUI-3.0` core (observed in the wild as version `1.#INF`) that wins LibStub but never creates the shared `AceGUI.tooltip` frame. The tree button enter/leave handlers now lazily (re)create the tooltip frame; the widget version was bumped 47→48 so the fixed widget wins registration over an unpatched same-version copy. - **[Tooltip - Learner Spawn Data Never In Main Tooltip]** Learner spawn/kill data is no longer added inline to the main NPC tooltip under any setting. Previously, with the "Use secondary learner tooltip" option off, `_AddLearnedSpawnTooltipLine` appended the lines directly to `GameTooltip`. The toggle now gates the data entirely: on = shown in the separate secondary frame, off = not shown anywhere. Removed the now-unused `_AddTooltipSeparator` helper and updated the option description. (Complements the earlier System A suppression that kept the duplicate learner lines out of the main tooltip.) - **[Tooltip - Stale Quest Objective Lookup On Turn-In]** Hovering an NPC right after turning in (or abandoning) one of its associated quests spammed a `debugstack` trace in DEVELOP mode: the learner objective-correlation block in `QuestieTooltips:GetTooltip` called `QuestLogCache.GetQuestObjectives` for a quest no longer in `QuestLogCache` (gracefully returns `{}`, but logs a stack). The NPC and object correlation loops now skip quests not in `QuestiePlayer.currentQuestlog`, so live objective progress is only looked up for quests the player is currently on. No functional change for active quests (a completed quest had no progress to show anyway). - **[Tooltip - Secondary Learner Tooltip Leak]** With the "Use secondary learner tooltip" option enabled, learner spawn/kill lines still appeared in the main NPC tooltip instead of going solely to the secondary frame. Two parallel learner-tooltip systems were both rendering on unit hover: QuestieLearner's `OnTooltipSetUnit` hook (toggle-aware, secondary-capable) and `Tooltip.lua`'s `_GetLearnerTooltipLines` (always injected "Learned spawn / Total spawns learned / Total kills recorded" into the `m_` tooltip data). `QuestieTooltips:GetTooltip` now takes a `suppressLearnerLines` flag, and the NPC unit-hover call in `TooltipHandler.lua` passes it so the inline learner lines are omitted there — the `OnTooltipSetUnit` hook owns that display and routes it to the main tooltip (secondary off) or the separate secondary frame (secondary on). Map-pin and object tooltips are unaffected and keep their learner lines. Also removes the latent duplicate learner lines that appeared in the main tooltip even with the secondary frame disabled. diff --git a/Libs/AceGUI-3.0/widgets/AceGUIContainer-TreeGroup.lua b/Libs/AceGUI-3.0/widgets/AceGUIContainer-TreeGroup.lua index 416bc9a..6a767da 100644 --- a/Libs/AceGUI-3.0/widgets/AceGUIContainer-TreeGroup.lua +++ b/Libs/AceGUI-3.0/widgets/AceGUIContainer-TreeGroup.lua @@ -2,7 +2,10 @@ TreeGroup Container Container that uses a tree control to switch between groups. -------------------------------------------------------------------------------]] -local Type, Version = "TreeGroup", 47 +-- Version bumped 47 -> 48 (Questie-X): hardened Button_OnEnter/OnLeave against a nil +-- AceGUI.tooltip caused by a conflicting addon's broken AceGUI-3.0 core. The bump ensures +-- this fixed widget wins registration over an unpatched same-version copy. +local Type, Version = "TreeGroup", 48 local AceGUI = LibStub and LibStub("AceGUI-3.0", true) if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end @@ -202,6 +205,13 @@ local function Button_OnEnter(frame) self:Fire("OnButtonEnter", frame.uniquevalue, frame) if self.enabletooltips then + -- AceGUI.tooltip can be nil when a conflicting addon registered a broken + -- AceGUI-3.0 core (seen in the wild as version "1.#INF") that won LibStub + -- but never created the shared tooltip frame. Lazily (re)create it so tree + -- button tooltips work instead of erroring with "attempt to index ... tooltip". + if not AceGUI.tooltip then + AceGUI.tooltip = CreateFrame("GameTooltip", "AceGUITooltip", UIParent, "GameTooltipTemplate") + end local tooltip = AceGUI.tooltip tooltip:SetOwner(frame, "ANCHOR_NONE") tooltip:ClearAllPoints() @@ -216,7 +226,7 @@ local function Button_OnLeave(frame) local self = frame.obj self:Fire("OnButtonLeave", frame.uniquevalue, frame) - if self.enabletooltips then + if self.enabletooltips and AceGUI.tooltip then AceGUI.tooltip:Hide() end end diff --git a/Modules/Tracker/TrackerUtils.lua b/Modules/Tracker/TrackerUtils.lua index cc612c2..433c944 100644 --- a/Modules/Tracker/TrackerUtils.lua +++ b/Modules/Tracker/TrackerUtils.lua @@ -1232,7 +1232,14 @@ function TrackerUtils:GetSortedQuestIds() end function TrackerUtils:IsVoiceOverLoaded() - if (IsAddOnLoaded("AI_VoiceOver") and IsAddOnLoaded("AI_VoiceOverData_Vanilla")) then + -- Require not just that the VoiceOver addons are loaded, but that the runtime + -- structure we index actually exists. Some VoiceOver builds (e.g. on Elune) expose + -- a different QuestOverlayUI shape where questPlayButtons is absent, which previously + -- crashed UpdateVoiceOverPlayButtons / SetAllPlayButtonAlpha with "attempt to index + -- field 'questPlayButtons' (a nil value)". All play-button call sites gate on this + -- function, so verifying the table here makes them all safe. + if IsAddOnLoaded("AI_VoiceOver") and IsAddOnLoaded("AI_VoiceOverData_Vanilla") + and VoiceOver and VoiceOver.QuestOverlayUI and VoiceOver.QuestOverlayUI.questPlayButtons then return true end