diff --git a/Compatibility/pfUI.lua b/Compatibility/pfUI.lua index e904533..a48bcd9 100644 --- a/Compatibility/pfUI.lua +++ b/Compatibility/pfUI.lua @@ -552,7 +552,6 @@ function Extension.SetupPfUIEventHooks(lib) ev:UnregisterEvent("AURA_CAST_ON_OTHER") ev:UnregisterEvent("DEBUFF_ADDED_OTHER") ev:UnregisterEvent("DEBUFF_REMOVED_OTHER") - ev:UnregisterEvent("BUFF_ADDED_OTHER") ev:UnregisterEvent("BUFF_REMOVED_SELF") ev:UnregisterEvent("BUFF_REMOVED_OTHER") diff --git a/Conditionals.lua b/Conditionals.lua index ed4814e..0cdc179 100644 --- a/Conditionals.lua +++ b/Conditionals.lua @@ -846,74 +846,6 @@ function CleveRoids.GetAllCasterAuraTimeRemaining(targetGuid, spellId) return nil end --- Helper to find aura by name (or spell ID string) for a target --- Returns player's entry for personal debuffs, any caster for shared auras. --- Reads from pfUI.libdebuff_all_auras when pfUI 7.6+ is active. -function CleveRoids.FindAllCasterAuraByName(targetGuid, searchName) - if not targetGuid or not searchName then return nil, nil end - local targetData, isPfUI = CleveRoids.GetAuraTrackingData(targetGuid) - if not targetData then return nil, nil end - - -- Resolve spell ID to name for direct lookup - local searchID = tonumber(searchName) - if searchID then - local resolvedName = C_Spell.GetSpellName(searchID) - if not resolvedName then return nil, nil end - searchName = resolvedName - end - - local now = GetTime() - - -- Try exact match first (O(1) hash lookup) - local casters = targetData[searchName] - - -- Case-insensitive fallback - if not casters then - local searchLower = string.lower(searchName) - for spellName, c in pairs(targetData) do - local baseName = CleveRoids.StripRank(spellName) - if string.lower(baseName) == searchLower then - casters = c - break - end - end - end - - if not casters then return nil, nil end - - local playerGuid = CleveRoids.GetGUID("player") - - -- Always check player's own entry first - if playerGuid and casters[playerGuid] then - local auraData = casters[playerGuid] - local startTime = AuraStart(auraData, isPfUI) - if startTime and auraData.duration then - local remaining = auraData.duration + startTime - now - if remaining > 0 then return remaining, playerGuid end - end - end - - -- Get a spellId from any entry to check personal vs shared - local anySpellId = nil - for _, aData in pairs(casters) do - anySpellId = aData.spellId - break - end - - -- Personal debuff and player has no active entry → don't use other players' data - if IsPersonalAura(anySpellId, searchName) then return nil, nil end - - -- Shared aura: return any active caster's entry - for cGuid, auraData in pairs(casters) do - local startTime = AuraStart(auraData, isPfUI) - if startTime and auraData.duration then - local remaining = auraData.duration + startTime - now - if remaining > 0 then return remaining, cGuid end - end - end - return nil, nil -end - -- HitInfo bitfield values (from NampowerAPI.lua, duplicated for local access) -- Converted to decimal for Lua 5.0 compatibility (no hex literals) local HITINFO_MISS = 16 -- 0x10 @@ -1129,31 +1061,11 @@ local function OnAuraCastSelf(spellId, casterGuid, targetGuid, effect, effectAur end end - -- NEW: Populate ownBuffCasts and allBuffAuras for all player buffs (not just overflow) - -- Use the isBuffNotDebuff result determined above (avoids redundant slot scanning) - local lib = CleveRoids.libdebuff - if isBuffNotDebuff and spellId and durationMs and durationMs > 0 and lib and not lib.hasPfUIEnhanced then - local spellName = C_Spell.GetSpellName(spellId) - if spellName then - local playerGuid = CleveRoids.GetGUID("player") - if playerGuid then - lib.ownBuffCasts[playerGuid] = lib.ownBuffCasts[playerGuid] or {} - lib.ownBuffCasts[playerGuid][spellName] = { - startTime = now, - duration = durationMs / 1000, - spellId = spellId, - casterGuid = casterGuid, - } - lib.allBuffAuras[playerGuid] = lib.allBuffAuras[playerGuid] or {} - lib.allBuffAuras[playerGuid][spellName] = lib.allBuffAuras[playerGuid][spellName] or {} - lib.allBuffAuras[playerGuid][spellName][casterGuid or "unknown"] = { - startTime = now, - duration = durationMs / 1000, - rank = 0, - } - end - end - end + -- Removed: this populated lib.ownBuffCasts and lib.allBuffAuras for every player + -- buff. Both tables were write-only -- populated here and on BUFF_ADDED_OTHER, + -- swept periodically, cleared on removal and death, and never read for aura state. + -- Player buff timing now comes from C_UnitAuras, which reads expirationTime out of + -- the engine's own player-buff table. end local function OnAuraCastOther(spellId, casterGuid, targetGuid, effect, effectAuraName, @@ -1190,7 +1102,7 @@ local function OnAuraCastOther(spellId, casterGuid, targetGuid, effect, effectAu string.format("|cffff6600[AuraTrack]|r %s Rank %d blocked by Rank %d (%.1fs left) on %s", spellName, newRank, existingRank, timeleft, string.sub(tostring(targetGuid), 1, 16))) - spellName = nil -- skip pendingBuffCasts below too + spellName = nil -- downranked: don't record this cast end end end @@ -1220,24 +1132,6 @@ local function OnAuraCastOther(spellId, casterGuid, targetGuid, effect, effectAu string.sub(tostring(casterGuid), 1, 16), durationMs / 1000)) end - -- Store in pendingBuffCasts for BUFF_ADDED_OTHER to confirm as buff - -- (AURA_CAST_ON_OTHER fires for both buffs and debuffs; BUFF_ADDED_OTHER confirms buff) - local lib = CleveRoids.libdebuff - if lib and not lib.hasPfUIEnhanced then - local spellNameForPending = C_Spell.GetSpellName(spellId) - if spellNameForPending then - local normTargetGuid = CleveRoids.NormalizeGUID(targetGuid) - if normTargetGuid then - lib.pendingBuffCasts[normTargetGuid] = lib.pendingBuffCasts[normTargetGuid] or {} - lib.pendingBuffCasts[normTargetGuid][spellId] = { - casterGuid = CleveRoids.NormalizeGUID(casterGuid), - duration = durationMs / 1000, - spellName = spellNameForPending, - time = now, - } - end - end - end end -- Store cap status for this target GUID (if available) @@ -3773,77 +3667,27 @@ function CleveRoids.ValidateAura(unit, args, isbuff) end end - -- allBuffAuras fallback for player buff timing: when slow path found the buff but - -- returned no remaining time, check lib.allBuffAuras for cached AURA_CAST timing. - if found and remaining == nil and isPlayer and isbuff and searchName then - local lib = type(CleveRoids.libdebuff) == "table" and CleveRoids.libdebuff or nil - if lib and lib.allBuffAuras then - local playerGuid = CleveRoids.GetGUID("player") - if playerGuid and lib.allBuffAuras[playerGuid] then - -- Try exact name match first - local casters = lib.allBuffAuras[playerGuid][args.name] - -- Try lowercase match if exact didn't work - if not casters then - for bName, c in pairs(lib.allBuffAuras[playerGuid]) do - if _string_lower(bName) == searchName then - casters = c - break - end - end - end - if casters then - for _, cData in pairs(casters) do - local elapsed = GetTime() - (cData.startTime or 0) - remaining = cData.duration > 0 and (cData.duration - elapsed) or -1 - break - end - end - end + -- Player timing gap-fill, from ClassicAPI. Replaces the lib.allBuffAuras lookup + -- that cached AURA_CAST start/duration for this: for the player, C_UnitAuras reads + -- expirationTime out of the engine's own player-buff table, so it is the more + -- authoritative source, not a fallback. Only runs when the scans above found the + -- aura but produced no time. + if found and remaining == nil and isPlayer and (searchID or searchName) then + local _, _, capRemaining = ResolveUnitAuraViaClassicAPI(unit, searchID, searchName, isbuff) + if capRemaining ~= nil then + remaining = capRemaining end end - -- Non-player overflow fallback: buff exists in server slots 33-48 (no client slot) - -- AllCasterAuraTracking already has data from AURA_CAST_ON_OTHER for all aura applications. - -- If the normal scan didn't find the buff, check there for presence + duration. - -- Guard: verify the spell isn't a visible debuff on the target (AllCasterAuraTracking - -- stores both buffs and debuffs, so without this check [buff:DebuffName] could false-positive). - if not found and not isPlayer and isbuff and (searchID or searchName) then - local targetGuid = CleveRoids.GetGUID(unit) - if targetGuid then - -- Check if the spell is in a visible debuff slot — if so, it's a debuff, not a buff - local isDebuff = false - local di = 1 - while true do - local dtex, _, _, dspellId = UnitDebuff(unit, di) - if not dtex then break end - if dspellId then - if searchID then - if dspellId == searchID then - isDebuff = true - break - end - elseif searchName then - local lowerName = GetLowercaseSpellName(dspellId) - if lowerName and lowerName == searchName then - isDebuff = true - break - end - end - end - di = di + 1 - end - - if not isDebuff then - local trackRemaining = CleveRoids.FindAllCasterAuraByName(targetGuid, - searchID and tostring(searchID) or args.name) - if trackRemaining then - found = true - stacks = 0 - remaining = trackRemaining - end - end - end - end + -- Removed: the non-player overflow fallback. It existed because a buff can sit in + -- server slots 33-48 with no client slot, so it read presence/duration out of + -- AllCasterAuraTracking, guarded by a UnitDebuff slot scan to stop [buff:Name] + -- matching a debuff (that table stores both). ClassicAPI makes all of it moot: its + -- HELPFUL/HARMFUL filters select on each aura's real polarity flag rather than + -- which slot range it happens to occupy, so "a debuff parked in a buff slot still + -- reads harmful" (docs/API.md) and an overflowed buff still reads helpful. The + -- ClassicAPI resolution above therefore already covers the overflow case, and it + -- classifies more accurately than the slot-range guard did. local ops = CleveRoids.operators local cmp = CleveRoids.comparators diff --git a/Utility.lua b/Utility.lua index fcb64df..369b37c 100644 --- a/Utility.lua +++ b/Utility.lua @@ -773,12 +773,6 @@ lib.iconCache = lib.iconCache or {} -- [spellId] = texture (shared with -- Buff tracking tables (parallel to debuff tables, standalone Nampower mode only) -- Buff tables are NOT linked to pfUI — pfUI has no public buff tracking tables. -- They remain empty when hasPfUIEnhanced=true since all buff event handlers are gated by it. -lib.ownBuffCasts = lib.ownBuffCasts or {} -- [targetGUID][buffName] = {startTime, duration, spellId, casterGuid} - -- Player's own buff casts on self or others (AURA_CAST events) -lib.allBuffAuras = lib.allBuffAuras or {} -- [targetGUID][buffName][casterGuid] = {startTime, duration, rank} - -- All buffs from all casters on any unit (confirmed by BUFF_ADDED events) -lib.pendingBuffCasts = lib.pendingBuffCasts or {} -- [targetGUID][spellId] = {casterGuid, duration, spellName, time} - -- Temp storage from AURA_CAST_ON_OTHER, consumed by BUFF_ADDED_OTHER -- Flag indicating whether enhanced pfUI tracking is available lib.hasPfUIEnhanced = false @@ -1264,57 +1258,6 @@ function lib:CleanupStaleTrackingData() end end - -- Clean ownBuffCasts (player-cast buffs on targets) - if lib.ownBuffCasts then - for guid, buffs in pairs(lib.ownBuffCasts) do - for buffName, data in pairs(buffs) do - local elapsed = now - (data.startTime or 0) - local dur = data.duration or 0 - -- Remove if expired (duration > 0 and past end time) or stale (no duration and old) - if (dur > 0 and elapsed > dur) or (dur <= 0 and elapsed > staleTime) then - buffs[buffName] = nil - end - end - if not next(buffs) then - lib.ownBuffCasts[guid] = nil - end - end - end - - -- Clean allBuffAuras (all-caster buff tracking) - if lib.allBuffAuras then - for guid, buffs in pairs(lib.allBuffAuras) do - for buffName, casters in pairs(buffs) do - for cGuid, data in pairs(casters) do - local elapsed = now - (data.startTime or 0) - local dur = data.duration or 0 - if (dur > 0 and elapsed > dur) or (dur <= 0 and elapsed > staleTime) then - casters[cGuid] = nil - end - end - if not next(casters) then - buffs[buffName] = nil - end - end - if not next(buffs) then - lib.allBuffAuras[guid] = nil - end - end - end - - -- Clean pendingBuffCasts (short-lived correlation data, 2 second TTL) - if lib.pendingBuffCasts then - for guid, spells in pairs(lib.pendingBuffCasts) do - for spellId, data in pairs(spells) do - if (now - (data.time or 0)) > 2 then - spells[spellId] = nil - end - end - if not next(spells) then - lib.pendingBuffCasts[guid] = nil - end - end - end end -- Get the caster GUID for a debuff on a target @@ -4185,7 +4128,6 @@ if CleveRoids.hasNampower then -- BUFF_ADDED/REMOVED events require v2.30+ for auraSlot and state args if npMajor > 2 or (npMajor == 2 and npMinor >= 30) then - ev:RegisterEvent("BUFF_ADDED_OTHER") ev:RegisterEvent("BUFF_REMOVED_SELF") ev:RegisterEvent("BUFF_REMOVED_OTHER") end @@ -5765,49 +5707,6 @@ ev:SetScript("OnEvent", function() end end - -- NAMPOWER v2.30+ BUFF_ADDED_OTHER - Confirm pending AURA_CAST_ON_OTHER data as buff - elseif event == "BUFF_ADDED_OTHER" then - if lib.hasPfUIEnhanced then return end - - local guid = CleveRoids.NormalizeGUID(arg1) - local spellId = arg3 - local state = arg7 -- 0=added, 1=removed, 2=modified - - if not guid or not spellId then return end - - -- Consume pending AURA_CAST data for this buff - local pending = lib.pendingBuffCasts[guid] and lib.pendingBuffCasts[guid][spellId] - if pending then - local spellName = pending.spellName or (C_Spell.GetSpellName(spellId)) - if spellName then - local casterGuid = pending.casterGuid - local duration = pending.duration - local now = GetTime() - - -- allBuffAuras: confirmed buff from any caster - lib.allBuffAuras[guid] = lib.allBuffAuras[guid] or {} - lib.allBuffAuras[guid][spellName] = lib.allBuffAuras[guid][spellName] or {} - lib.allBuffAuras[guid][spellName][casterGuid or "unknown"] = { - startTime = now, - duration = duration or 0, - rank = 0, - } - - -- ownBuffCasts: only if player is the caster - local playerGuid = CleveRoids.GetGUID("player") - if playerGuid and casterGuid == playerGuid then - lib.ownBuffCasts[guid] = lib.ownBuffCasts[guid] or {} - lib.ownBuffCasts[guid][spellName] = { - startTime = now, - duration = duration or 0, - spellId = spellId, - casterGuid = casterGuid, - } - end - end - lib.pendingBuffCasts[guid][spellId] = nil - end - -- NAMPOWER v2.30+ BUFF_REMOVED_SELF - Player's own buffs removed elseif event == "BUFF_REMOVED_SELF" then if lib.hasPfUIEnhanced then return end @@ -5818,22 +5717,6 @@ ev:SetScript("OnEvent", function() if state == 2 then return end -- Stack decrease only, not full removal local spellName = C_Spell.GetSpellName(spellId) - local playerGuid = CleveRoids.GetGUID("player") - - if spellName and playerGuid then - if lib.ownBuffCasts[playerGuid] then - lib.ownBuffCasts[playerGuid][spellName] = nil - if not next(lib.ownBuffCasts[playerGuid]) then - lib.ownBuffCasts[playerGuid] = nil - end - end - if lib.allBuffAuras[playerGuid] then - lib.allBuffAuras[playerGuid][spellName] = nil - if not next(lib.allBuffAuras[playerGuid]) then - lib.allBuffAuras[playerGuid] = nil - end - end - end -- Also prune from OverflowBuffs (existing system) if spellId and CleveRoids.OverflowBuffs and CleveRoids.OverflowBuffs[spellId] then @@ -5859,28 +5742,6 @@ ev:SetScript("OnEvent", function() if state == 2 then return end -- Stack decrease only, not full removal local spellName = C_Spell.GetSpellName(spellId) - if spellName then - if lib.allBuffAuras[guid] then - lib.allBuffAuras[guid][spellName] = nil - if not next(lib.allBuffAuras[guid]) then - lib.allBuffAuras[guid] = nil - end - end - if lib.ownBuffCasts[guid] then - lib.ownBuffCasts[guid][spellName] = nil - if not next(lib.ownBuffCasts[guid]) then - lib.ownBuffCasts[guid] = nil - end - end - end - - -- Clean pending correlation data - if lib.pendingBuffCasts[guid] then - lib.pendingBuffCasts[guid][spellId] = nil - if not next(lib.pendingBuffCasts[guid]) then - lib.pendingBuffCasts[guid] = nil - end - end -- Also clean AllCasterAuraTracking (keyed by spellName) if spellName and CleveRoids.AllCasterAuraTracking[guid] then @@ -5932,15 +5793,6 @@ ev:SetScript("OnEvent", function() end -- Clean up buff tracking tables - if lib.ownBuffCasts[guid] then - lib.ownBuffCasts[guid] = nil - end - if lib.allBuffAuras[guid] then - lib.allBuffAuras[guid] = nil - end - if lib.pendingBuffCasts[guid] then - lib.pendingBuffCasts[guid] = nil - end -- Clean up cast tracking for this unit (they can't be casting if dead) if CleveRoids.castTracking[guid] then