From aa2cb95ec2a1ef6b95c38d0826fce80424942b15 Mon Sep 17 00:00:00 2001 From: Meow <30401521+me0wg4ming@users.noreply.github.com> Date: Tue, 31 Mar 2026 19:51:36 +0200 Subject: [PATCH] Several changes Fix stale debuff icons/tooltips on target frame after target swap Fix debuffs not showing on nameplates for timer-less spells (e.g. Hurricane) Fix buffwatch timer visually speeding up when another debuff fades Remove 50ms aura cache (slotMapCache/auraFC) - caused stale debuff data Remove "Show Only Own Debuffs" from unitframes and nameplates GUI Force selfdebuff=0 for unitframes/nameplates on login to reset old SavedVariables --- api/config.lua | 2 ++ libs/libdebuff.lua | 40 ++++----------------------- modules/buffwatch.lua | 12 ++------ modules/nameplates.lua | 63 ++++++++++++++++++++++++------------------ 4 files changed, 47 insertions(+), 70 deletions(-) diff --git a/api/config.lua b/api/config.lua index c1c81355..49b2775e 100644 --- a/api/config.lua +++ b/api/config.lua @@ -454,6 +454,7 @@ function pfUI:LoadConfig() local ufs = { "player", "target", "focus", "focustarget", "group", "grouptarget", "grouppet", "raid", "ttarget", "pet", "ptarget", "fallback", "tttarget" } for _, unit in pairs(ufs) do + pfUI:UpdateConfig("unitframes", unit, "selfdebuff", "0") pfUI:UpdateConfig("unitframes", unit, "visible", "1") pfUI:UpdateConfig("unitframes", unit, "showPVP", "0") pfUI:UpdateConfig("unitframes", unit, "pvpiconsize", "16" ) @@ -813,6 +814,7 @@ function pfUI:LoadConfig() pfUI:UpdateConfig("nameplates", nil, "targetcastbar", "0") pfUI:UpdateConfig("nameplates", nil, "spellname", "0") pfUI:UpdateConfig("nameplates", nil, "showdebuffs", "1") + pfUI:UpdateConfig("nameplates", nil, "selfdebuff", "0") pfUI:UpdateConfig("nameplates", nil, "showdebuffs_hostile", "1") pfUI:UpdateConfig("nameplates", nil, "showdebuffs_friendly", "0") pfUI:UpdateConfig("nameplates", nil, "guessdebuffs", "1") diff --git a/libs/libdebuff.lua b/libs/libdebuff.lua index 1332bc7a..a1f6d086 100644 --- a/libs/libdebuff.lua +++ b/libs/libdebuff.lua @@ -409,11 +409,6 @@ end -- CORE: GetUnitField-based Slot Mapping (THE KEY INNOVATION!) -- ============================================================================ --- Cache for GetDebuffSlotMap to reduce GetUnitField calls --- [guid] = {map, timestamp} -local slotMapCache = {} -local SLOT_MAP_CACHE_DURATION = 0.05 -- 50ms cache (1-2 frames) - -- Dispel type mapping: SpellRec.dispel index -> Blizzard DebuffTypeColor key local dispelTypeMap = { [1] = "Magic", @@ -428,27 +423,19 @@ local function GetDebuffSlotMap(guid) if not guid or not GetUnitField then return nil end - - -- Check cache first - local now = GetTime() - local cached = slotMapCache[guid] - if cached and cached.map and (now - cached.timestamp) < SLOT_MAP_CACHE_DURATION then - return cached.map - end - + local auras = GetUnitField(guid, "aura") if not auras then return nil end - - -- Fetch stacks array (reusable reference - extract values immediately) + local auraApps = GetUnitField(guid, "auraApplications") - + if debugStats.enabled then debugStats.getunitfield_calls = debugStats.getunitfield_calls + 1 end - + local map = {} local displaySlot = 0 - + -- Debuff aura slots are 33-48 for auraSlot = 33, 48 do local spellId = auras[auraSlot] @@ -456,11 +443,7 @@ local function GetDebuffSlotMap(guid) displaySlot = displaySlot + 1 local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") local texture = libdebuff:GetSpellIcon(spellId) - - -- Get stacks from auraApplications (0-indexed, so +1 for display) local stacks = (auraApps and auraApps[auraSlot] or 0) + 1 - - -- Get debuff type from SpellRec DBC local dtype = nil if GetSpellRecField then local dispelId = GetSpellRecField(spellId, "dispel") @@ -468,7 +451,6 @@ local function GetDebuffSlotMap(guid) dtype = dispelTypeMap[dispelId] end end - map[displaySlot] = { auraSlot = auraSlot, spellId = spellId, @@ -479,14 +461,7 @@ local function GetDebuffSlotMap(guid) } end end - - -- Cache the result (separate from buffMap to avoid cross-invalidation) - if not slotMapCache[guid] then - slotMapCache[guid] = { timestamp = now } - end - slotMapCache[guid].map = map - slotMapCache[guid].timestamp = now - + return map end @@ -1779,7 +1754,6 @@ if hasNampower then local auraSlot = auraSlot_0based and (auraSlot_0based + 1) or nil -- Invalidate slot map cache for this GUID - slotMapCache[guid] = nil local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") if not spellName then return end @@ -1915,7 +1889,6 @@ if hasNampower then local auraSlot = auraSlot_0based and (auraSlot_0based + 1) or nil -- Invalidate slot map cache for this GUID - slotMapCache[guid] = nil local spellName = (GetSpellRecField and GetSpellRecField(spellId, "name")) or "?" @@ -2006,7 +1979,6 @@ if hasNampower then if targetGuid and targetGuid ~= "" then -- Invalidate slot map cache on retarget -- Prevents stale slot mappings after untarget/retarget cycles - slotMapCache[targetGuid] = nil -- Cleanup expired timers for new target CleanupExpiredTimers(targetGuid) end diff --git a/modules/buffwatch.lua b/modules/buffwatch.lua index 8a42c805..15a89803 100644 --- a/modules/buffwatch.lua +++ b/modules/buffwatch.lua @@ -277,14 +277,8 @@ pfUI:RegisterModule("buffwatch", "vanilla:tbc", function () and data[3] and data[3] ~= "" -- buff has a name and data[4] and data[4] ~= "" -- buff has a texture then - -- For player: no slot in uuid (slots shift when other buffs expire) - -- For target: include slot (multiple players can have same debuff, slot identifies who) - local uuid - if frame.unit == "player" or (frame.config and frame.config.selfdebuff == "1") then - uuid = data[4] .. data[3] -- texture + name only (stable in selfdebuff mode) - else - uuid = data[4] .. data[3] .. data[2] -- texture + name + slot - end + -- No slot in uuid: slots shift when debuffs expire causing timer resets + local uuid = data[4] .. data[3] -- texture + name only -- update bar data frame.bars[bar] = frame.bars[bar] or CreateStatusBar(bar, frame) @@ -518,4 +512,4 @@ pfUI:RegisterModule("buffwatch", "vanilla:tbc", function () pfUI.uf.target.debuffbar:SetPoint("BOTTOM", pfUI.uf.target, "TOP", 0, border*2+1) UpdateMovable(pfUI.uf.target.debuffbar) end -end) +end) \ No newline at end of file diff --git a/modules/nameplates.lua b/modules/nameplates.lua index 942bf2c6..e9c0ed8c 100644 --- a/modules/nameplates.lua +++ b/modules/nameplates.lua @@ -327,28 +327,39 @@ pfUI:RegisterModule("nameplates", "vanilla", function () local function PlateCacheDebuffs(self, unitstr, verify) if not self.debuffcache then self.debuffcache = {} end - if not libdebuff then return end -- Safety check + if not libdebuff then return end + local now = GetTime() + + -- Clear existing cache slots for id = 1, 16 do - local effect, _, texture, stacks, _, duration, timeleft - - if unitstr and C.nameplates.selfdebuff == "1" then - effect, _, texture, stacks, _, duration, timeleft = libdebuff:UnitOwnDebuff(unitstr, id) - else - effect, _, texture, stacks, _, duration, timeleft = libdebuff:UnitDebuff(unitstr, id) + if self.debuffcache[id] then + self.debuffcache[id].empty = true end + end - if effect and timeleft and timeleft > 0 then - local start = GetTime() - ( (duration or 0) - ( timeleft or 0) ) - local stop = GetTime() + ( timeleft or 0 ) - self.debuffcache[id] = self.debuffcache[id] or {} - self.debuffcache[id].effect = effect - self.debuffcache[id].texture = texture - self.debuffcache[id].stacks = stacks - self.debuffcache[id].duration = duration or 0 - self.debuffcache[id].start = start - self.debuffcache[id].stop = stop - self.debuffcache[id].empty = nil + -- Get GUID - unitstr may already be a GUID from plate.parent:GetName(1) + local guid = unitstr + if unitstr and not string.find(unitstr, "^0x") and GetUnitGUID then + guid = GetUnitGUID(unitstr) or unitstr + end + + if guid then + for id = 1, 16 do + local effect, _, texture, stacks, dtype, duration, timeleft + effect, _, texture, stacks, dtype, duration, timeleft = libdebuff:UnitDebuff(guid, id) + if effect and texture then + local stop = (timeleft and timeleft > 0) and (now + timeleft) or nil + local start = stop and (stop - (duration or 0)) or now + self.debuffcache[id] = self.debuffcache[id] or {} + self.debuffcache[id].effect = effect + self.debuffcache[id].texture = texture + self.debuffcache[id].stacks = stacks + self.debuffcache[id].duration = duration or 0 + self.debuffcache[id].start = start + self.debuffcache[id].stop = stop + self.debuffcache[id].empty = nil + end end end @@ -359,15 +370,15 @@ pfUI:RegisterModule("nameplates", "vanilla", function () -- 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 + if self.debuffcache[id].stop and 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()) + local timeleft = c.stop and (c.stop - GetTime()) or -1 + return c.effect, c.rank, c.texture, c.stacks, c.dtype, c.duration, timeleft end local function CreateDebuffIcon(plate, index) @@ -994,8 +1005,8 @@ nameplates:RegisterEvent("ZONE_CHANGED_NEW_AREA") -- yet updated. So while being inside this event, we cannot trust the unitstr. if event == "PLAYER_TARGET_CHANGED" then unitstr = nil end - -- remove unitstr on unit name mismatch - if unitstr and UnitName(unitstr) ~= name then unitstr = nil end + -- remove unitstr on unit name mismatch (skip for GUIDs - they're always valid) + if unitstr and not string.find(unitstr, "^0x") and UnitName(unitstr) ~= name then unitstr = nil end -- use mobhealth values if addon is running if (MobHealth3 or MobHealthFrame) and target and name == UnitName('target') and MobHealth_GetTargetCurHP() then @@ -1193,13 +1204,11 @@ nameplates:RegisterEvent("ZONE_CHANGED_NEW_AREA") plate:CacheDebuffs(unitstr, verify) end - -- update all debuff icons + -- update all debuff icons - use direct UnitDebuff when unitstr available for i = 1, 16 do local effect, rank, texture, stacks, dtype, duration, timeleft - if unitstr and C.nameplates.selfdebuff == "1" and libdebuff then - effect, rank, texture, stacks, dtype, duration, timeleft = libdebuff:UnitOwnDebuff(unitstr, i) - elseif unitstr and libdebuff then + if unitstr and libdebuff then effect, rank, texture, stacks, dtype, duration, timeleft = libdebuff:UnitDebuff(unitstr, i) elseif plate.verify == verify then effect, rank, texture, stacks, dtype, duration, timeleft = plate:UnitDebuff(i)