Delete the write-only buff tracking tables and the overflow fallback

Following the aura data source to ClassicAPI exposed a chain of machinery that
nothing consumed any more.

ValidateAura's non-player overflow fallback is gone. It existed because a buff
can occupy server slots 33-48 with no client slot, so it read presence and
duration out of AllCasterAuraTracking behind a UnitDebuff slot scan that stopped
[buff:Name] matching a debuff. ClassicAPI settles this at the source: its
HELPFUL/HARMFUL filters select on each aura's real polarity flag rather than the
slot range it happens to sit in, so per docs/API.md "a debuff parked in a buff
slot still reads harmful" -- and an overflowed buff still reads helpful. The
ClassicAPI resolution already covers the case, and classifies it more accurately
than the slot-range guard did.

That removed the last caller of FindAllCasterAuraByName (67 lines), and the
player-side allBuffAuras timing lookup went with it: for the player, C_Spell...
C_UnitAuras reads expirationTime out of the engine's own player-buff table, so it
is the more authoritative source rather than a fallback.

With no readers left, three tables turn out to be pure overhead -- populated on
every buff aura event, swept periodically, cleared on removal and death, and
never read for aura state:
  - lib.allBuffAuras   [guid][name][caster] = {startTime, duration, rank}
  - lib.ownBuffCasts   [guid][name]         = {startTime, duration, spellId, ...}
  - lib.pendingBuffCasts [guid][spellId]    = {casterGuid, duration, name, time}
pendingBuffCasts only existed to correlate AURA_CAST_ON_OTHER with
BUFF_ADDED_OTHER so the other two could be filled, so removing them emptied the
BUFF_ADDED_OTHER handler entirely. That event is no longer registered, and pfUI
compat no longer unregisters an event we never ask for.

Fixes a regression from the previous commit in this series: removing the
allBuffAuras/ownBuffCasts blocks from BUFF_REMOVED_SELF and BUFF_REMOVED_OTHER
also deleted their `local spellName = C_Spell.GetSpellName(spellId)`, leaving
later `if spellName` guards reading a nil global. The OverflowBuffsByName and
AllCasterAuraTracking prunes in those branches had silently stopped running.
Both declarations are restored.

AllCasterAuraTracking itself stays: OverflowBuffFrame and the aura-tracking
writers still use it.
This commit is contained in:
Brues
2026-09-10 14:40:16 -05:00
parent 2a562e0550
commit d93d97a469
3 changed files with 24 additions and 329 deletions
-1
View File
@@ -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")
+24 -180
View File
@@ -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
-148
View File
@@ -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