From be6ae253155a41cdedbbda8f7691de5cb86bc56c Mon Sep 17 00:00:00 2001 From: Brues <5278969+brues-code@users.noreply.github.com> Date: Wed, 24 Jun 2026 22:41:36 -0500 Subject: [PATCH] libdebuff/nameplates: drop dead per-aura readers and debuff cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With every external caller of libdebuff:UnitDebuff / :UnitOwnDebuff now on C_UnitAuras, the two public per-aura readers and the nameplate-side cache they were feeding have no consumers. - libs/libdebuff.lua: removes libdebuff:UnitDebuff (~120 lines), libdebuff:UnitOwnDebuff (~75 lines), the _ownDebuffSortFunc helper, and the local cache table. The slotOwnership / ownDebuffs / allAuraCasts / pendingCasts bookkeeping stays — GetBestAuraCast (libpredict) and GetEnhancedDebuffs (CleveRoids) still read it, and the event handlers maintain it. GetSlotCaster / GetDebuffSlotMap stay too; the DEBUFF_ADDED_OTHER handler and the debug printer use them. File goes 2010 → 1870 lines. - modules/nameplates.lua: deletes PlateCacheDebuffs (was already rewritten on C_UnitAuras and unused once the display loop bypassed the cache), PlateUnitDebuff, the cachedVerify scaffolding, and the nameplate.UnitDebuff / nameplate.CacheDebuffs registrations. - api/config.lua + modules/gui.lua: drops the now-defunct "guessdebuffs" knob — its only effect was gating the dead cache. --- api/config.lua | 1 - libs/libdebuff.lua | 221 +---------------------------------------- modules/gui.lua | 1 - modules/nameplates.lua | 71 ------------- 4 files changed, 5 insertions(+), 289 deletions(-) diff --git a/api/config.lua b/api/config.lua index c570593a..b4ba0bec 100644 --- a/api/config.lua +++ b/api/config.lua @@ -818,7 +818,6 @@ function pfUI:LoadConfig() pfUI:UpdateConfig("nameplates", nil, "showdebuffs_hostile", "1") pfUI:UpdateConfig("nameplates", nil, "showdebuffs_friendly", "0") pfUI:UpdateConfig("nameplates", nil, "owndebuffs", "0") - pfUI:UpdateConfig("nameplates", nil, "guessdebuffs", "1") pfUI:UpdateConfig("nameplates", nil, "clickthrough", "0") pfUI:UpdateConfig("nameplates", nil, "rightclick", "1") pfUI:UpdateConfig("nameplates", nil, "clickthreshold", "0.5") diff --git a/libs/libdebuff.lua b/libs/libdebuff.lua index 576b50a0..ba79d932 100644 --- a/libs/libdebuff.lua +++ b/libs/libdebuff.lua @@ -11,9 +11,11 @@ setfenv(1, pfUI:GetEnvironment()) -- This eliminates ~400 lines of error-prone shift logic while maintaining full -- multi-caster tracking support. -- --- libdebuff:UnitDebuff(unit, id) --- Returns debuff informations on the given effect of the specified unit. --- name, rank, texture, stacks, dtype, duration, timeleft, caster +-- The public per-aura readers (UnitDebuff, UnitOwnDebuff) were retired in favor +-- of ClassicAPI's C_UnitAuras (which now provides sourceUnit/sourceGUID and +-- non-player expirationTime). What remains in libdebuff is the cast-event +-- bookkeeping consumed by GetBestAuraCast / GetEnhancedDebuffs and the +-- libdebuff_casts / libdebuff_*_hooks broadcast surface. -- return instantly when another libdebuff is already active if pfUI.api.libdebuff then return end @@ -787,219 +789,6 @@ function libdebuff:AddEffect(unit, unitlevel, effect, duration, caster, rank) lastspell = libdebuff.objects[unit][unitlevel][effect] end --- ============================================================================ --- MAIN API: UnitDebuff (GetUnitField-based) --- ============================================================================ - -local cache = {} - -function libdebuff:UnitDebuff(unit, displaySlot) - local unitname = UnitName(unit) - local unitlevel = UnitLevel(unit) - local duration, timeleft = nil, -1 - local rank = nil - local caster = nil - local effect = nil - local texture = nil - local stacks = 0 - local dtype = nil - - -- Nampower: Use GetUnitField for ALL debuff data (no Blizzard UnitDebuff needed) - if hasNampower and UnitGUID then - local guid = UnitGUID(unit) - if not guid then - -- Safety fallback: no GUID available (should not happen with Nampower) - local aura = C_UnitAuras.GetDebuffDataByIndex(unit, displaySlot) - if aura then - return aura.name, rank, aura.icon, aura.applications, aura.dispelName, duration, timeleft, caster - end - return effect, rank, texture, stacks, dtype, duration, timeleft, caster - end - - -- Get current slot map from GetUnitField (cached 50ms) - local slotMap = GetDebuffSlotMap(guid) - if not slotMap or not slotMap[displaySlot] then - return nil - end - - local slotData = slotMap[displaySlot] - effect = slotData.spellName - texture = slotData.texture - stacks = slotData.stacks - dtype = slotData.dtype - local auraSlot = slotData.auraSlot - - -- Get caster info for this slot - local slotCasterGuid, isOurs = GetSlotCaster(guid, auraSlot, effect) - - if isOurs then - -- OUR debuff - get timer from ownDebuffs - if ownDebuffs[guid] and ownDebuffs[guid][effect] then - local data = ownDebuffs[guid][effect] - local remaining = (data.startTime + data.duration) - GetTime() - if remaining > 0 then - duration = data.duration - timeleft = remaining - caster = "player" - rank = data.rank - elseif remaining > -1 then - -- Grace period - show 0 timeleft - duration = data.duration - timeleft = 0 - caster = "player" - rank = data.rank - end - end - else - -- OTHER player's debuff - get timer from allAuraCasts - if slotCasterGuid and allAuraCasts[guid] and allAuraCasts[guid][effect] then - local data = allAuraCasts[guid][effect][slotCasterGuid] - if data then - local remaining = (data.startTime + data.duration) - GetTime() - if remaining > 0 and data.duration > 0 then - duration = data.duration - timeleft = remaining - caster = "other" - rank = data.rank - end - end - end - - -- Fallback: Search all casters if specific one not found - if not duration and allAuraCasts[guid] and allAuraCasts[guid][effect] then - for anyCasterGuid, data in pairs(allAuraCasts[guid][effect]) do - local remaining = (data.startTime + data.duration) - GetTime() - if remaining > 0 and data.duration > 0 then - duration = data.duration - timeleft = remaining - caster = "other" - rank = data.rank - break - end - end - end - end - - return effect, rank, texture, stacks, dtype, duration, timeleft, caster - end - - -- ============================================================================ - -- FALLBACK: Legacy (non-Nampower) system - -- ============================================================================ - - local aura = C_UnitAuras.GetDebuffDataByIndex(unit, displaySlot) - if aura then - texture = aura.icon - stacks = aura.applications - dtype = aura.dispelName - effect = aura.name - end - - - if effect and libdebuff.objects[unitname] then - for level, effects in pairs(libdebuff.objects[unitname]) do - if effects[effect] and effects[effect].duration then - local timeleft = effects[effect].start and - effects[effect].start + effects[effect].duration - GetTime() - - if timeleft and timeleft > 0 then - return effect, effects[effect].rank, texture, stacks, dtype, - effects[effect].duration, timeleft, effects[effect].caster - end - end - end - end - - return effect, rank, texture, stacks, dtype, duration, timeleft, caster -end - --- ============================================================================ --- API: UnitOwnDebuff (only OUR debuffs) --- ============================================================================ - --- Pre-defined sort function for UnitOwnDebuff (avoids closure creation per call) -local _ownDebuffSortFunc = function(a, b) - if a.data.startTime == b.data.startTime then - return a.spellName < b.spellName - end - return a.data.startTime < b.data.startTime -end - -function libdebuff:UnitOwnDebuff(unit, id) - if hasNampower and UnitGUID then - local guid = UnitGUID(unit) - if guid and ownDebuffs[guid] then - -- Build sorted list of our active debuffs - local sortedDebuffs = {} - local now = GetTime() - - local toRemove = nil - for spellName, data in pairs(ownDebuffs[guid]) do - local timeleft = (data.startTime + data.duration) - now - if timeleft > 0 then - local count = table.getn(sortedDebuffs) + 1 - sortedDebuffs[count] = { - spellName = spellName, - data = data, - timeleft = timeleft - } - elseif data.pending then - if timeleft < -2 then - toRemove = toRemove or {} - toRemove[spellName] = true - end - else - toRemove = toRemove or {} - toRemove[spellName] = true - end - end - if toRemove then - for spellName in pairs(toRemove) do - ownDebuffs[guid][spellName] = nil - end - end - - -- Sort by startTime (oldest first = lowest display slot) - -- If startTime is equal (e.g. after Carnage refresh), use spellName for stable sorting - table.sort(sortedDebuffs, _ownDebuffSortFunc) - - -- Return debuff at position 'id' - if sortedDebuffs[id] then - local entry = sortedDebuffs[id] - local texture = entry.data.texture or "Interface\\Icons\\INV_Misc_QuestionMark" - local displayTimeleft = entry.timeleft > 0 and entry.timeleft or 0 - - -- Get dtype from SpellRec DBC via stored spellId - local entryDtype = nil - if entry.data.spellId and GetSpellRecField then - local dispelId = GetSpellRecField(entry.data.spellId, "dispel") - if dispelId and dispelId > 0 then - entryDtype = dispelTypeMap[dispelId] - end - end - - return entry.spellName, entry.data.rank, texture, 1, entryDtype, entry.data.duration, displayTimeleft, "player" - end - end - return nil - end - - -- Fallback: Iterate through all debuffs and filter - for k in pairs(cache) do cache[k] = nil end - local count = 1 - for i=1,16 do - local effect, rank, texture, stacks, dtype, duration, timeleft, caster = libdebuff:UnitDebuff(unit, i) - if effect and not cache[effect] and caster and caster == "player" then - cache[effect] = true - if count == id then - return effect, rank, texture, stacks, dtype, duration, timeleft, caster - else - count = count + 1 - end - end - end -end - -- ============================================================================ -- API: GetBestAuraCast (for libpredict HoT tracking) -- ============================================================================ diff --git a/modules/gui.lua b/modules/gui.lua index a8021d4d..efff9e70 100644 --- a/modules/gui.lua +++ b/modules/gui.lua @@ -2899,7 +2899,6 @@ pfUI:RegisterModule("gui", function () CreateConfig(U["nameplates"], T["Debuff Position"], C.nameplates.debuffs, "position", "dropdown", pfUI.gui.dropdowns.debuffposition) CreateConfig(U["nameplates"], T["Debuff Icon Offset"], C.nameplates, "debuffoffset") CreateConfig(U["nameplates"], T["Debuff Icon Size"], C.nameplates, "debuffsize") - CreateConfig(U["nameplates"], T["Estimate Debuffs"], C.nameplates, "guessdebuffs", "checkbox") CreateConfig(U["nameplates"], T["Show Debuff Stacks"], C.nameplates.debuffs, "showstacks", "checkbox") CreateConfig(U["nameplates"], T["Enable Debuff Timers"], C.nameplates, "debufftimers", "checkbox") CreateConfig(U["nameplates"], T["Show Timer Text"], C.nameplates, "debufftext", "checkbox") diff --git a/modules/nameplates.lua b/modules/nameplates.lua index d292b4c2..5579e1c2 100644 --- a/modules/nameplates.lua +++ b/modules/nameplates.lua @@ -371,62 +371,6 @@ pfUI:RegisterModule("nameplates", function () end end - local function PlateCacheDebuffs(self, unitstr, verify) - if not self.debuffcache then self.debuffcache = {} end - if not libdebuff then return end - - local now = GetTime() - - -- Clear existing cache slots - for id = 1, 16 do - if self.debuffcache[id] then - self.debuffcache[id].empty = true - end - end - - -- Pull debuffs straight from C_UnitAuras. The HARMFUL filter restricts to - -- the debuff range; PLAYER (added when cfg.owndebuffs is on) further - -- restricts to auras whose cached caster GUID matches the local player. - -- expirationTime comes from ClassicAPI's Aura::Source cache (SMSG_SPELL_GO - -- observation) — best-effort, so auras applied before login won't carry - -- timing and we treat them as durationless. - local filter = cfg.owndebuffs and "HARMFUL|PLAYER" or "HARMFUL" - local auras = unitstr and C_UnitAuras.GetUnitAuras(unitstr, filter) or {} - for id, aura in ipairs(auras) do - if id > 16 then break end - local duration = aura.duration or 0 - local stop = (aura.expirationTime and aura.expirationTime > 0) and aura.expirationTime or nil - local start = stop and (stop - duration) or now - local cache = self.debuffcache[id] or {} - cache.effect = aura.name - cache.texture = aura.icon - cache.stacks = aura.applications - cache.dtype = aura.dispelName - cache.duration = duration - cache.start = start - cache.stop = stop - cache.empty = nil - self.debuffcache[id] = cache - end - - self.verify = verify - end - - local function PlateUnitDebuff(self, id) - -- break on unknown data - if not self.debuffcache then return end - if not self.debuffcache[id] then return end - if not self.debuffcache[id].stop then return end - - -- break on timeout debuffs - if self.debuffcache[id].empty then return end - if self.debuffcache[id].stop < GetTime() then return end - - -- return cached debuff - local c = self.debuffcache[id] - return c.effect, c.rank, c.texture, c.stacks, c.dtype, c.duration, (c.stop - GetTime()) - end - local function CreateDebuffIcon(plate, index) plate.debuffs[index] = CreateFrame("Frame", plate.platename.."Debuff"..index, plate) plate.debuffs[index]:Hide() @@ -734,8 +678,6 @@ end nameplate:EnableMouse(0) nameplate.parent = parent nameplate.cache = {} - nameplate.UnitDebuff = PlateUnitDebuff - nameplate.CacheDebuffs = PlateCacheDebuffs nameplate.original = {} -- create shortcuts for all known elements and disable them @@ -1239,19 +1181,6 @@ end local isFriendly = unittype == "FRIENDLY_PLAYER" or unittype == "FRIENDLY_NPC" local showDebuffsForType = cfg.showdebuffs and (isFriendly and cfg.showdebuffs_friendly or (not isFriendly and cfg.showdebuffs_hostile)) if showDebuffsForType then - -- PERF: Cache verify string - only allocate new string when name/level actually changes - if name ~= plate.cachedVerifyName or level ~= plate.cachedVerifyLevel then - plate.cachedVerifyName = name - plate.cachedVerifyLevel = level - plate.cachedVerify = (name or "") .. ":" .. (level or "") - end - local verify = plate.cachedVerify - - -- update cached debuffs - if C.nameplates["guessdebuffs"] == "1" and unitstr then - plate:CacheDebuffs(unitstr, verify) - end - -- Pull debuffs from C_UnitAuras (HARMFUL range). owndebuffs adds the -- PLAYER filter token so only auras whose caster GUID matches the local -- player come through. debuffDisplayBuf is a module-level reusable