Read auras positionally where we scan

C_UnitAuras.UnitDebuff / UnitBuff return the same fifteen values as
GetAuraDataByIndex without building a table, and the two index readers are
called in a loop over every aura on a unit -- one table per slot per refresh is
exactly the garbage the positional form exists to avoid. Only the first seven
values are wanted (name, icon, count, dispelType, duration, expirationTime,
source), and the range-locked variants keep the HARMFUL/HELPFUL split without a
filter string.

FindPlayerDebuff / FindPlayerBuff stay on GetUnitAuraBySpellID: one lookup
rather than a scan, so the AuraData table costs a single allocation and buys the
PLAYER filter. The -1-means-unknown rule moves into RemainingFrom so both shapes
share it.
This commit is contained in:
Brues
2026-09-12 08:20:12 -05:00
parent 7101ad167a
commit 3e2b3db31f
+26 -23
View File
@@ -2137,56 +2137,59 @@ function lib:AddEffect(guid, unitName, spellID, duration, stacks, caster)
end
end
-- Unpack one AuraData into libdebuff's return shape:
-- name, rank, texture, stacks, dispelType, duration, timeleft, caster
-- timeleft is -1 when the aura's timing is unknown -- ClassicAPI reports
-- expirationTime 0 for an aura whose cast it never saw (one that predates login,
-- or a max-stack refresh whose cache entry has elapsed). -1 is what the manual
-- store returned for an untracked aura, so callers testing `timeleft > 0` are
-- unaffected; it means "no timer", not "expired".
local function UnpackAura(aura)
local timeleft = -1
if aura.expirationTime and aura.expirationTime > 0 then
timeleft = aura.expirationTime - GetTime()
local function RemainingFrom(expirationTime)
if expirationTime and expirationTime > 0 then
return expirationTime - GetTime()
end
return aura.name, nil, aura.icon, aura.applications, aura.dispelName,
aura.duration, timeleft, aura.sourceUnit
return -1
end
-- Query debuff data by index. The index space is C_UnitAuras' dense HARMFUL
-- range, so it is walked to the first nil at any index -- no 16-debuff /
-- 32-buff split and no overflow rule, because isHarmful is the aura's real
-- polarity and a debuff parked in a buff slot still reads harmful.
-- Query debuff data by index, in libdebuff's return shape:
-- name, rank, texture, stacks, dispelType, duration, timeleft, caster
-- The index space is C_UnitAuras' dense HARMFUL range, walked to the first nil
-- at any index -- no 16-debuff / 32-buff split and no overflow rule, because
-- isHarmful is the aura's real polarity and a debuff parked in a buff slot
-- still reads harmful. The positional form allocates nothing per slot.
function lib:UnitDebuff(unit, id, filterCaster)
local aura = C_UnitAuras.GetAuraDataByIndex(unit, id, "HARMFUL")
if not aura then return nil end
if filterCaster and aura.sourceUnit ~= filterCaster then return nil end
return UnpackAura(aura)
local name, icon, count, dispelType, duration, expirationTime, source =
C_UnitAuras.UnitDebuff(unit, id)
if not name then return nil end
if filterCaster and source ~= filterCaster then return nil end
return name, nil, icon, count, dispelType, duration, RemainingFrom(expirationTime), source
end
-- Query buff data by index (helpful range).
function lib:UnitBuff(unit, id, filterCaster)
local aura = C_UnitAuras.GetAuraDataByIndex(unit, id, "HELPFUL")
if not aura then return nil end
if filterCaster and aura.sourceUnit ~= filterCaster then return nil end
return UnpackAura(aura)
local name, icon, count, dispelType, duration, expirationTime, source =
C_UnitAuras.UnitBuff(unit, id)
if not name then return nil end
if filterCaster and source ~= filterCaster then return nil end
return name, nil, icon, count, dispelType, duration, RemainingFrom(expirationTime), source
end
-- Find a player-cast debuff by spell ID. The PLAYER filter matches on
-- ClassicAPI's cached caster GUID, so an aura whose cast was never observed is
-- excluded -- the same answer the store's `caster == "player"` test gave, since
-- an unobserved cast was never recorded there either.
-- an unobserved cast was never recorded there either. One lookup, not a scan,
-- so the AuraData table it returns costs a single allocation.
function lib:FindPlayerDebuff(unit, spellID)
local aura = C_UnitAuras.GetUnitAuraBySpellID(unit, spellID, "HARMFUL|PLAYER")
if not aura then return nil end
return UnpackAura(aura)
return aura.name, nil, aura.icon, aura.applications, aura.dispelName,
aura.duration, RemainingFrom(aura.expirationTime), aura.sourceUnit
end
-- Find a player-cast buff by spell ID.
function lib:FindPlayerBuff(unit, spellID)
local aura = C_UnitAuras.GetUnitAuraBySpellID(unit, spellID, "HELPFUL|PLAYER")
if not aura then return nil end
return UnpackAura(aura)
return aura.name, nil, aura.icon, aura.applications, aura.dispelName,
aura.duration, RemainingFrom(aura.expirationTime), aura.sourceUnit
end
local function SeedUnit(unit)