Reduce debuff tracking hot-path overhead

This commit is contained in:
2026-09-03 11:14:02 +02:00
parent 3ce39cd39d
commit 73166ce4af
+93 -24
View File
@@ -17,7 +17,13 @@ if GetLocale() == "ruRU" then
end
local libdebuff = CreateFrame("Frame", "ShaguTweaksDebuffsScanner", UIParent)
local scanner = libtipscan:GetScanner("libdebuff")
local scanner
local function GetScanner()
if not scanner then
scanner = libtipscan:GetScanner("libdebuff")
end
return scanner
end
local _, class = UnitClass("player")
local lastspell
@@ -81,11 +87,12 @@ function libdebuff:AddPending(unit, unitlevel, effect, duration)
if not unit then return end
if not L["debuffs"][effect] then return end
duration = duration or libdebuff:GetDuration(effect)
if duration > 0 and libdebuff.pending[3] ~= effect then
libdebuff.pending[1] = unit
libdebuff.pending[2] = unitlevel or 0
libdebuff.pending[3] = effect
libdebuff.pending[4] = duration or libdebuff:GetDuration(effect)
libdebuff.pending[4] = duration
end
end
@@ -110,7 +117,7 @@ function libdebuff:RevertLastAction()
libdebuff:UpdateUnits()
end
function libdebuff:AddEffect(unit, unitlevel, effect, duration)
function libdebuff:AddEffect(unit, unitlevel, effect, duration, deferUpdate)
if not unit or not effect then return end
unitlevel = unitlevel or 0
if not libdebuff.objects[unit] then libdebuff.objects[unit] = {} end
@@ -125,12 +132,22 @@ function libdebuff:AddEffect(unit, unitlevel, effect, duration)
libdebuff.objects[unit][unitlevel][effect].start = GetTime()
libdebuff.objects[unit][unitlevel][effect].duration = duration or libdebuff:GetDuration(effect)
libdebuff:UpdateUnits()
if not deferUpdate then
libdebuff:UpdateUnits()
end
end
-- scan for debuff application
libdebuff:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE")
libdebuff:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE")
-- ClassicAPI exposes aura identity and timing directly. The old periodic
-- combat-text parser only exists to reconstruct off-target applications on
-- older ClassicAPI builds; current ClassicAPI does not register those noisy
-- events at all.
if not (API and API.aurapositional and API.UnitDebuff) then
libdebuff:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE")
libdebuff:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE")
end
-- These events still support the legacy estimated-duration fallback when an
-- exact expiration time is unavailable.
libdebuff:RegisterEvent("CHAT_MSG_SPELL_FAILED_LOCALPLAYER")
libdebuff:RegisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE")
libdebuff:RegisterEvent("PLAYER_TARGET_CHANGED")
@@ -183,25 +200,40 @@ libdebuff:SetScript("OnEvent", function()
-- Add Missing Buffs by Iteration
elseif ( event == "UNIT_AURA" and arg1 == "target" ) or event == "PLAYER_TARGET_CHANGED" then
local unit = UnitName("target")
if not unit then return end
local unitlevel = UnitLevel("target") or 0
local changed = false
for i=1, 16 do
local effect, rank, texture, stacks, dtype, duration, timeleft = libdebuff:UnitDebuff("target", i)
-- abort when no further debuff was found
if not texture then return end
if not texture then break end
if texture and effect and effect ~= "" then
-- don't overwrite existing timers
local unitlevel = UnitLevel("target") or 0
local unit = UnitName("target")
if effect and effect ~= "" then
-- Don't overwrite existing timers. Defer the expensive target-frame
-- refresh until the complete aura pass is done.
if not libdebuff.objects[unit] or not libdebuff.objects[unit][unitlevel] or not libdebuff.objects[unit][unitlevel][effect] then
libdebuff:AddEffect(unit, unitlevel, effect)
libdebuff:AddEffect(unit, unitlevel, effect, nil, true)
changed = true
end
end
end
if changed then
libdebuff:UpdateUnits()
end
-- Update Pending Spells
elseif event == "CHAT_MSG_SPELL_FAILED_LOCALPLAYER" or event == "CHAT_MSG_SPELL_SELF_DAMAGE" then
-- Remove pending spell
-- Most combat messages cannot affect libdebuff. Avoid running the whole
-- miss/resist/immune pattern set unless there is state to repair.
if not libdebuff.pending[3] and not (lastspell and lastspell.start_old) then
return
end
for _, msg in pairs(libdebuff.rp) do
local effect = cmatch(arg1, msg)
if effect and libdebuff.pending[3] == effect then
@@ -215,28 +247,64 @@ libdebuff:SetScript("OnEvent", function()
end
end
elseif event == "SPELLCAST_STOP" then
QueueFunction(libdebuff.PersistPending)
if libdebuff.pending[3] then
QueueFunction(libdebuff.PersistPending)
end
end
end)
-- Gather Data by User Actions
hooksecurefunc("CastSpell", function(id, bookType)
local effect = GetSpellName(id, bookType)
local _, rank = libspell.GetSpellInfo(id, bookType)
local effect, rank = GetSpellName(id, bookType)
if not effect or not L["debuffs"][effect] then return end
local duration = libdebuff:GetDuration(effect, rank)
libdebuff:AddPending(UnitName("target"), UnitLevel("target"), effect, duration)
end)
hooksecurefunc("CastSpellByName", function(effect, target)
local _, rank = libspell.GetSpellInfo(effect)
local duration = libdebuff:GetDuration(effect, rank)
libdebuff:AddPending(UnitName("target"), UnitLevel("target"), effect, duration)
if not effect then return end
-- Strip an explicit rank before checking the duration database. Reject
-- ordinary damage/heal casts before doing any spell metadata work.
local _, _, baseEffect, explicitRank = string.find(effect, "(.+)%((.+)%)")
local name = baseEffect or effect
if not L["debuffs"][name] then return end
if libdebuff.pending[3] == name then return end
local rank = explicitRank
if not rank then
_, rank = libspell.GetSpellInfo(name)
end
local duration = libdebuff:GetDuration(name, rank)
libdebuff:AddPending(UnitName("target"), UnitLevel("target"), name, duration)
end)
hooksecurefunc("UseAction", function(slot, target, button)
-- Macros are intentionally left to CastSpellByName: conditional macros can
-- select a different spell than their first /cast line.
if API and API.actioninfo and API.GetActionInfo then
local actionType, actionID = API.GetActionInfo(slot)
if actionType == "macro" or actionType == "item" then return end
if actionType == "spell" and actionID then
if not IsCurrentAction(slot) then return end
local effect, rank = API.GetSpellInfo(actionID)
if not effect or not L["debuffs"][effect] then return end
local duration = libdebuff:GetDuration(effect, rank)
libdebuff:AddPending(UnitName("target"), UnitLevel("target"), effect, duration)
return
elseif actionType then
return
end
end
if GetActionText(slot) or not IsCurrentAction(slot) then return end
scanner:SetAction(slot)
local effect, rank = scanner:Line(1)
local tip = GetScanner()
tip:SetAction(slot)
local effect, rank = tip:Line(1)
if not effect or not L["debuffs"][effect] then return end
local duration = libdebuff:GetDuration(effect, rank)
libdebuff:AddPending(UnitName("target"), UnitLevel("target"), effect, duration)
end)
@@ -262,8 +330,9 @@ function libdebuff:UnitDebuff(unit, id)
end
if texture and (not effect or effect == "") then
scanner:SetUnitDebuff(unit, id)
effect = scanner:Line(1) or ""
local tip = GetScanner()
tip:SetUnitDebuff(unit, id)
effect = tip:Line(1) or ""
end
if libdebuff.objects[unitname] and libdebuff.objects[unitname][unitlevel] and libdebuff.objects[unitname][unitlevel][effect] then