From d4d3d746f76616b44c7cdddd92d7f5eed386c417 Mon Sep 17 00:00:00 2001 From: Dusk-92 Date: Wed, 26 Aug 2026 09:10:01 +0200 Subject: [PATCH] Validate SuperWoW channel fallbacks --- api.lua | 15 ++++++++++++--- mods/nameplate-castbar.lua | 33 +++++++++++++++++++++++++++++---- mods/target-castbar.lua | 34 +++++++++++++++++++++++++++++----- 3 files changed, 70 insertions(+), 12 deletions(-) diff --git a/api.lua b/api.lua index 09cff80..71df716 100644 --- a/api.lua +++ b/api.lua @@ -120,10 +120,19 @@ API.GetChannelInfo = function(unit) local name, displayName, texture, startTime, endTime, isTradeSkill, notInterruptible, spellID = _G.C_Spell.UnitChannelInfo(unit) - if not name or not startTime or not endTime then return end + if not name then return end + + -- ClassicAPI can know that a remote unit is channeling even when the + -- channel began before it was observed, in which case timing is nil. Keep + -- the normalized first value nil, but expose the live spell identity in + -- trailing values so castbar modules can safely try a timed SuperWoW cache. + if not startTime or not endTime then + return nil, nil, nil, nil, nil, nil, nil, + notInterruptible, spellID, true + end return name, "", displayName or "", texture, startTime, endTime, - isTradeSkill, notInterruptible, spellID + isTradeSkill, notInterruptible, spellID, true end API.IsSpellInRange = function(spell, unit) @@ -346,4 +355,4 @@ API.UnitGUID = function(unit) if API.unitguid then return _G.UnitGUID(unit) end -end \ No newline at end of file +end diff --git a/mods/nameplate-castbar.lua b/mods/nameplate-castbar.lua index 1a29157..23c4f35 100644 --- a/mods/nameplate-castbar.lua +++ b/mods/nameplate-castbar.lua @@ -27,6 +27,28 @@ local function QueryLegacy(query) end end +local function QuerySuperWoW(guid, channelSpellID, hasChannel) + if not guid then return end + + local cached = ShaguTweaks.libcast.db[guid] + if not cached then return end + + if cached.channel then + if not hasChannel or cached.spellID ~= channelSpellID then return end + + local channel, nameSubtext, text, texture, startTime, endTime, isTradeSkill = LegacyChannelInfo(guid) + if channel then + return channel, nameSubtext, text, texture, startTime, endTime, isTradeSkill, true + end + return + end + + local cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill = LegacyCastingInfo(guid) + if cast then + return cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill, false + end +end + module.enable = function(self) if ShaguPlates then return end @@ -111,17 +133,20 @@ module.enable = function(self) return cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill, false end - local channel - channel, nameSubtext, text, texture, startTime, endTime, isTradeSkill = API.GetChannelInfo(unit) + local channel, notInterruptible, channelSpellID, hasChannel + channel, nameSubtext, text, texture, startTime, endTime, isTradeSkill, + notInterruptible, channelSpellID, hasChannel = API.GetChannelInfo(unit) if channel then return channel, nameSubtext, text, texture, startTime, endTime, isTradeSkill, true end -- With a real ClassicAPI unit token, "no cast" is authoritative. Never -- fall through to the old name-keyed database, otherwise two mobs with - -- the same name can incorrectly share a castbar. + -- the same name can incorrectly share a castbar. The GUID-keyed + -- SuperWoW fallback remains safe; channels must also match ClassicAPI's + -- live channel spellID before their cached timing is trusted. if ShaguTweaks.superwow_active and plate.guid then - return QueryLegacy(plate.guid) + return QuerySuperWoW(plate.guid, channelSpellID, hasChannel) end return end diff --git a/mods/target-castbar.lua b/mods/target-castbar.lua index 0b89967..d32e795 100644 --- a/mods/target-castbar.lua +++ b/mods/target-castbar.lua @@ -76,6 +76,28 @@ local function QueryLegacy(query) end end +local function QuerySuperWoW(guid, channelSpellID, hasChannel) + if not guid then return end + + local cached = ShaguTweaks.libcast.db[guid] + if not cached then return end + + if cached.channel then + if not hasChannel or cached.spellID ~= channelSpellID then return end + + local channel, nameSubtext, text, texture, startTime, endTime, isTradeSkill = LegacyChannelInfo(guid) + if channel then + return channel, nameSubtext, text, texture, startTime, endTime, isTradeSkill, true + end + return + end + + local cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill = LegacyCastingInfo(guid) + if cast then + return cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill, false + end +end + local function QueryCast(unit) if not unit then return end @@ -89,18 +111,20 @@ local function QueryCast(unit) return cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill, false end - local channel - channel, nameSubtext, text, texture, startTime, endTime, isTradeSkill = API.GetChannelInfo(unit) + local channel, notInterruptible, channelSpellID, hasChannel + channel, nameSubtext, text, texture, startTime, endTime, isTradeSkill, + notInterruptible, channelSpellID, hasChannel = API.GetChannelInfo(unit) if channel then return channel, nameSubtext, text, texture, startTime, endTime, isTradeSkill, true end - -- SuperWoW's GUID-keyed legacy cache is still safe because it cannot - -- collide merely because two creatures share the same display name. + -- SuperWoW remains the GUID-keyed fallback for regular casts. Channels + -- additionally require ClassicAPI's authoritative live state and matching + -- spellID, preventing a stale cached channel from becoming a ghost bar. if ShaguTweaks.superwow_active and not UnitIsUnit(unit, "player") then local guid = API.UnitGUID(unit) if guid then - local a, b, c, d, e, f, g, h = QueryLegacy(guid) + local a, b, c, d, e, f, g, h = QuerySuperWoW(guid, channelSpellID, hasChannel) if a then return a, b, c, d, e, f, g, h end end end