unitframes/buffwatch/libpredict: route aura reads through C_UnitAuras

ClassicAPI's recent C_UnitAuras additions (sourceUnit / sourceGUID /
non-player expirationTime / the PLAYER filter token) finally cover
everything libdebuff:UnitDebuff and :UnitOwnDebuff were doing — caster
attribution, accurate timing for non-player units, and the own-debuffs
filter. Migrating the remaining callers off the libdebuff readers.

- api/unitframes.lua: collapses the debuff render path's three-branch
  if/else into a single C_UnitAuras.GetAuraDataByIndex with a HARMFUL
  or HARMFUL|PLAYER filter selected by the selfdebuff config; tooltip
  slot-finders match by sourceGUID instead of libdebuff's caster flag;
  custom-debuff indicator scan unifies the same way.
- modules/buffwatch.lua: GetBuffData drops the libdebuff fallback;
  tooltip slot-finder mirrors the unitframes pattern.
- libs/libpredict.lua: drops the orphaned UnitHasBuff slot-loop (no
  callers left).
- api/api.lua: pfUI.api.UnitHasBuff tightens from a HELPFUL iteration
  to a single GetAuraDataBySpellName lookup.

Each site that builds a cooldown ring from expirationTime carries the
talent-extension guard — when expirationTime exceeds the dbc base
duration (e.g. Shadow Affinity → SW:P), clamp start to now and use the
remaining time as the effective duration so CooldownFrame_SetTimer
doesn't get a future start it treats as "not yet begun".
This commit is contained in:
Brues
2026-06-24 22:41:13 -05:00
parent fd21b24a74
commit d6951b386e
4 changed files with 42 additions and 80 deletions
+1 -4
View File
@@ -193,10 +193,7 @@ end
-- name [string] The localized name of the buff.
-- return: [bool] true if unit has buff otherwise "nil"
function pfUI.api.UnitHasBuff(unit, name)
for _, aura in ipairs(C_UnitAuras.GetUnitAuras(unit, "HELPFUL")) do
if aura.name == name then return true end
end
return nil
return C_UnitAuras.GetAuraDataBySpellName(unit, name, "HELPFUL") ~= nil or nil
end
-- [ GetUnbuffedRoster ]
+27 -47
View File
@@ -87,13 +87,16 @@ local function DebuffOnEnter()
if this:GetParent().label ~= "player" then
local parent = this:GetParent()
-- For "only own debuffs" mode: find the REAL slot by matching spell name AND caster
if parent.config and parent.config.selfdebuff == "1" and libdebuff then
local ownDebuffName = libdebuff:UnitOwnDebuff(unitstr, this.id)
if ownDebuffName then
-- selfdebuff filters the displayed list to player-cast harmful auras, but
-- SetUnitAura's index has to be into the engine's full HARMFUL list. Look
-- up the displayed aura via the PLAYER filter, then scan engine slots for
-- one whose name + sourceGUID match.
if parent.config and parent.config.selfdebuff == "1" then
local ownAura = C_UnitAuras.GetAuraDataByIndex(unitstr, this.id, "HARMFUL|PLAYER")
if ownAura then
for gameSlot = 1, 16 do
local gameName, _, _, _, _, _, _, gameCaster = libdebuff:UnitDebuff(unitstr, gameSlot)
if gameName == ownDebuffName and gameCaster == "player" then
local check = C_UnitAuras.GetDebuffDataByIndex(unitstr, gameSlot)
if check and check.name == ownAura.name and check.sourceGUID == ownAura.sourceGUID then
GameTooltip:SetUnitAura(unitstr, gameSlot, "HARMFUL")
return
end
@@ -1971,16 +1974,15 @@ function pfUI.uf:RefreshUnit(unit, component)
invert_h * ((row+buffrow)*(multiply*default_border + unit.config.debuffsize + 1) + (multiply*default_border + 1)))
end
local aura
if unit.label ~= "player" and selfdebuff == "1" then
_, _, texture, stacks, dtype = libdebuff:UnitOwnDebuff(unitstr, i)
-- selfdebuff narrows to player-cast harmful auras via the PLAYER filter.
-- Player-frame debuffs aren't gated on it (it'd hide most party-applied
-- effects on you).
local filter = (unit.label ~= "player" and selfdebuff == "1") and "HARMFUL|PLAYER" or "HARMFUL"
local aura = C_UnitAuras.GetAuraDataByIndex(unitstr, i, filter)
if aura then
texture, stacks, dtype = aura.icon, aura.applications, aura.dispelName
else
aura = C_UnitAuras.GetDebuffDataByIndex(unitstr, i)
if aura then
texture, stacks, dtype = aura.icon, aura.applications, aura.dispelName
else
texture, stacks, dtype = nil, 0, nil
end
texture, stacks, dtype = nil, 0, nil
end
unit.debuffs[i].texture:SetTexture(texture)
@@ -1992,8 +1994,9 @@ function pfUI.uf:RefreshUnit(unit, component)
unit.debuffs[i]:Show()
if aura and aura.expirationTime > 0 then
-- Player path: real engine expirationTime. Cap start to now to keep pfUI's
-- cooldown text out of the 2^32-wraparound branch for talent-extended debuffs.
-- Cap start to now so talent-extended debuffs (expirationTime past
-- the dbc base duration) don't push start into the future and trip
-- CooldownFrame_SetTimer's 2^32-wraparound branch.
local now = GetTime()
local start = aura.expirationTime - aura.duration
local duration = aura.duration
@@ -2003,16 +2006,6 @@ function pfUI.uf:RefreshUnit(unit, component)
if duration > 0 then
CooldownFrame_SetTimer(unit.debuffs[i].cd, start, duration, 1)
end
elseif libdebuff and selfdebuff == "1" then
local _, _, _, _, _, duration, timeleft = libdebuff:UnitOwnDebuff(unitstr, i)
if duration and timeleft then
CooldownFrame_SetTimer(unit.debuffs[i].cd, GetTime() + timeleft - duration, duration, 1)
end
elseif libdebuff then
local _, _, _, _, _, duration, timeleft = libdebuff:UnitDebuff(unitstr, i)
if duration and timeleft then
CooldownFrame_SetTimer(unit.debuffs[i].cd, GetTime() + timeleft - duration, duration, 1)
end
end
if stacks > 1 then
@@ -2198,29 +2191,16 @@ function pfUI.uf:RefreshUnit(unit, component)
end
end
local debuffFilter = unit.config.selfdebuff == "1" and "HARMFUL|PLAYER" or "HARMFUL"
for i=1,16 do -- scan for custom debuffs
local aura = C_UnitAuras.GetDebuffDataByIndex(unitstr, i)
local aura = C_UnitAuras.GetAuraDataByIndex(unitstr, i, debuffFilter)
if aura then
local name = aura.name
local texture = aura.icon
local timeleft = aura.expirationTime > 0 and (aura.expirationTime - GetTime()) or nil
-- libdebuff provides caster correlation + reconstructed timer for non-player units
if libdebuff then
if unit.config.selfdebuff == "1" then
name, _, texture, _, _, _, timeleft = libdebuff:UnitOwnDebuff(unitstr, i)
else
name, _, texture, _, _, _, timeleft = libdebuff:UnitDebuff(unitstr, i)
end
end
if name then
for _, filter in pairs(unit.indicator_custom) do
if filter == string.lower(name) then
pfUI.uf:AddIcon(unit, pos, texture, timeleft, aura.applications)
pos = pos + 1
break
end
for _, filter in pairs(unit.indicator_custom) do
if filter == string.lower(aura.name) then
pfUI.uf:AddIcon(unit, pos, aura.icon, timeleft, aura.applications)
pos = pos + 1
break
end
end
end
-10
View File
@@ -944,16 +944,6 @@ local INSTANT_HOT_COOLDOWN = 1.0 -- 1 Sekunde Cooldown (GCD ist 1.5s)
-- Pending HoTs Queue - wird nach Delay verifiziert
local pendingHots = {}
-- Helper: check if buff is present on unit
local function UnitHasBuff(unit, buffName)
for i = 1, 32 do
local name = UnitBuff(unit, i)
if not name then break end
if name == buffName then return true end
end
return false
end
-- Gather Data by User Actions
hooksecurefunc("CastSpell", function(id, bookType)
if not libpredict.sender.enabled then return end
+14 -19
View File
@@ -74,18 +74,11 @@ pfUI:RegisterModule("buffwatch", function ()
end
local function GetBuffData(unit, id, type, selfdebuff)
if unit == "player" then
local aura = C_UnitAuras.GetAuraDataByIndex("player", id, type)
if not aura then return end
local remaining = aura.expirationTime > 0 and (aura.expirationTime - GetTime()) or 0
return remaining, aura.icon, aura.name, aura.applications
elseif libdebuff and selfdebuff then
local name, _, texture, stacks, _, _, timeleft = libdebuff:UnitOwnDebuff(unit, id)
return timeleft, texture, name, stacks
elseif libdebuff then
local name, _, texture, stacks, _, _, timeleft = libdebuff:UnitDebuff(unit, id)
return timeleft, texture, name, stacks
end
local filter = (selfdebuff and type == "HARMFUL") and "HARMFUL|PLAYER" or type
local aura = C_UnitAuras.GetAuraDataByIndex(unit, id, filter)
if not aura then return end
local remaining = aura.expirationTime > 0 and (aura.expirationTime - GetTime()) or 0
return remaining, aura.icon, aura.name, aura.applications
end
local function StatusBarOnClick()
@@ -117,15 +110,17 @@ pfUI:RegisterModule("buffwatch", function ()
if this.unit == "player" then
GameTooltip:SetUnitAura("player", this.id, this.type)
elseif this.type == "HARMFUL" then
-- For "only own debuffs" mode: find the REAL slot by matching spell name AND caster
-- selfdebuff filters the displayed list to player-cast harmful auras, but
-- SetUnitAura's index has to be into the engine's full HARMFUL list. Look
-- up the displayed aura via the PLAYER filter, then scan engine slots for
-- one whose name + sourceGUID match.
local config = this.parent and this.parent.config
if config and config.selfdebuff == "1" and libdebuff then
local ownDebuffName = libdebuff:UnitOwnDebuff(this.unit, this.id)
if ownDebuffName then
-- Search through all game slots to find OUR debuff with matching name
if config and config.selfdebuff == "1" then
local ownAura = C_UnitAuras.GetAuraDataByIndex(this.unit, this.id, "HARMFUL|PLAYER")
if ownAura then
for gameSlot = 1, 16 do
local gameName, _, _, _, _, _, _, gameCaster = libdebuff:UnitDebuff(this.unit, gameSlot)
if gameName == ownDebuffName and gameCaster == "player" then
local check = C_UnitAuras.GetDebuffDataByIndex(this.unit, gameSlot)
if check and check.name == ownAura.name and check.sourceGUID == ownAura.sourceGUID then
GameTooltip:SetUnitAura(this.unit, gameSlot, "HARMFUL")
break
end