Skip to content

Commit

Permalink
Finish decoupling UnitAura collection from frame updates
Browse files Browse the repository at this point in the history
Unify the logic for Classic and Retail in how we collect unit aura information. Importantly, we now have unit aura collection decoupled from the frame visual updates. This should be a performance win for Classic, albeit much less dramatic than on Retail with the new UNIT_AURA payloads added in 10.0.
  • Loading branch information
brittyazel committed Nov 8, 2023
1 parent 609983f commit b999b89
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 103 deletions.
7 changes: 0 additions & 7 deletions AuraIndicators.lua
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,6 @@ function EnhancedRaidFrames:UpdateIndicators(frame, setAppearance)
self:SetIndicatorAppearance(frame)
end

-- Force a refresh of our unitAuras table (Classic and Classic Era Only)
-- In retail we have a new way of doing it using the UNIT_AURA event and C_UnitAuras API
-- This is an inefficient way of triggering a refresh, but it works
if self.isWoWClassicEra or self.isWoWClassic then
self:UpdateUnitAuras_Legacy(frame.unit)
end

-- Loop over all 9 indicators and process them individually
for i = 1, 9 do
--create local pointer for readability
Expand Down
169 changes: 93 additions & 76 deletions CollectUnitAuras.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,29 @@
local _, addonTable = ... --make use of the default addon namespace
local EnhancedRaidFrames = addonTable.EnhancedRaidFrames

-------------------------------------------------------------------------
-------------------------------------------------------------------------

EnhancedRaidFrames.unitAuras = {} -- Matrix to keep a list of all auras on all units
local unitAuras = EnhancedRaidFrames.unitAuras --local handle for the above table
-------------------------------------------------------------------------
-------------------------------------------------------------------------

------------------------------------------------
---------- Update Auras for all units ----------
------------------------------------------------
--- This function scans all raid frame units and updates the unitAuras table with all auras on each unit
function EnhancedRaidFrames:UpdateAllAuras()
-- Clear out the unitAuras table
table.wipe(unitAuras)

-- Iterate over all raid frame units and force a full update
if not self.isWoWClassicEra and not self.isWoWClassic then
CompactRaidFrameContainer:ApplyToFrames("normal",
function(frame)
self:UpdateUnitAuras("", frame.unit, {isFullUpdate = true})
end)
else
CompactRaidFrameContainer_ApplyToFrames(CompactRaidFrameContainer, "normal",
function(frame)
self:UpdateUnitAuras_Legacy("", frame.unit)
end)
end
end

--- This functions is bound to the UNIT_AURA event and is used to track auras on all raid frame units
--- It uses the C_UnitAuras API that was added in 10.0
Expand Down Expand Up @@ -71,15 +85,51 @@ function EnhancedRaidFrames:UpdateUnitAuras(_, unit, payload)
end
end

--function to add or update an aura to the unitAuras table
function EnhancedRaidFrames.addToAuraTable(unit, auraData)
if not auraData then
return
end

local aura = {}
aura.auraInstanceID = auraData.auraInstanceID
if auraData.isHelpful then
aura.auraType = "buff"
elseif auraData.isHarmful then
aura.auraType = "debuff"
end
aura.auraName = auraData.name:lower()
aura.icon = auraData.icon
aura.count = auraData.applications
aura.duration = auraData.duration
aura.expirationTime = auraData.expirationTime
aura.castBy = auraData.sourceUnit
aura.spellID = auraData.spellId

-- Update the aura elements if it already exists
if unitAuras[unit][aura.auraInstanceID] then
for k,v in pairs(aura) do
unitAuras[unit][aura.auraInstanceID][k] = v
end
else
unitAuras[unit][aura.auraInstanceID] = aura
end
end

--- Prior to WoW 10.0, this function was used to track auras on all raid frame units
--- Unit auras are now tracked using the UNIT_AURA event and APIs in Retail
--- Unit aura information is stored in the unitAuras table
function EnhancedRaidFrames:UpdateUnitAuras_Legacy(unit)
function EnhancedRaidFrames:UpdateUnitAuras_Legacy(_, unit)
-- Only process player, raid, and party units
if not string.find(unit, "player") and not string.find(unit, "raid") and not string.find(unit, "party") then
return
end

-- Create or clear out the tables for the unit
unitAuras[unit] = {}

-- Get all unit buffs
local i = 1
local i = 1 --aura index counter
while (true) do
local auraName, icon, count, duration, expirationTime, castBy, spellID

Expand All @@ -89,30 +139,29 @@ function EnhancedRaidFrames:UpdateUnitAuras_Legacy(unit)
auraName, icon, count, _, duration, expirationTime, castBy, _, _, spellID = self.UnitAuraWrapper(unit, i, "HELPFUL") --for wow classic. This is the LibClassicDurations wrapper
end

if not spellID then --break the loop once we have no more buffs
-- break the loop once we have no more auras
if not spellID then
break
end

--it's important to use the 4th argument in string.find to turn of pattern matching, otherwise things with parentheses in them will fail to be found
if auraName and self.allAuras:find(" "..auraName:lower().." ", nil, true) or self.allAuras:find(" "..spellID.." ", nil, true) then -- Only add the spell if we're watching for it
local auraTable = {}
auraTable.auraType = "buff"
auraTable.auraIndex = i
auraTable.auraName = auraName:lower()
auraTable.icon = icon
auraTable.count = count
auraTable.duration = duration
auraTable.expirationTime = expirationTime
auraTable.castBy = castBy
auraTable.spellID = spellID

table.insert(unitAuras[unit], auraTable)
end

local auraTable = {}
auraTable.auraType = "buff"
auraTable.auraIndex = i
auraTable.auraName = auraName:lower()
auraTable.icon = icon
auraTable.count = count
auraTable.duration = duration
auraTable.expirationTime = expirationTime
auraTable.castBy = castBy
auraTable.spellID = spellID

table.insert(unitAuras[unit], auraTable)

i = i + 1
end

-- Get all unit debuffs
i = 1
i = 1 --aura index counter
while (true) do
local auraName, icon, count, duration, expirationTime, castBy, spellID, debuffType

Expand All @@ -122,59 +171,27 @@ function EnhancedRaidFrames:UpdateUnitAuras_Legacy(unit)
auraName, icon, count, debuffType, duration, expirationTime, castBy, _, _, spellID = self.UnitAuraWrapper(unit, i, "HARMFUL") --for wow classic. This is the LibClassicDurations wrapper
end

if not spellID then --break the loop once we have no more buffs
-- break the loop once we have no more auras
if not spellID then
break
end

--it's important to use the 4th argument in string.find to turn off pattern matching, otherwise things with parentheses in them will fail to be found
if auraName and self.allAuras:find(" "..auraName:lower().." ", nil, true) or self.allAuras:find(" "..spellID.." ", nil, true) or (debuffType and self.allAuras:find(" "..debuffType:lower().." ", nil, true)) then -- Only add the spell if we're watching for it
local auraTable = {}
auraTable.auraType = "debuff"
auraTable.auraIndex = i
auraTable.auraName = auraName:lower()
auraTable.icon = icon
auraTable.count = count
if debuffType then
auraTable.debuffType = debuffType:lower()
end
auraTable.duration = duration
auraTable.expirationTime = expirationTime
auraTable.castBy = castBy
auraTable.spellID = spellID

table.insert(unitAuras[unit], auraTable)

local auraTable = {}
auraTable.auraType = "debuff"
auraTable.auraIndex = i
auraTable.auraName = auraName:lower()
auraTable.icon = icon
auraTable.count = count
if debuffType then
auraTable.debuffType = debuffType:lower()
end
i = i + 1
end
end
auraTable.duration = duration
auraTable.expirationTime = expirationTime
auraTable.castBy = castBy
auraTable.spellID = spellID

--function to add or update an aura to the unitAuras table
function EnhancedRaidFrames.addToAuraTable(thisUnit, thisAuraData)
if not thisAuraData then
return
end

local aura = {}
aura.auraInstanceID = thisAuraData.auraInstanceID
if thisAuraData.isHelpful then
aura.auraType = "buff"
elseif thisAuraData.isHarmful then
aura.auraType = "debuff"
end
aura.auraName = thisAuraData.name:lower()
aura.icon = thisAuraData.icon
aura.count = thisAuraData.applications
aura.duration = thisAuraData.duration
aura.expirationTime = thisAuraData.expirationTime
aura.castBy = thisAuraData.sourceUnit
aura.spellID = thisAuraData.spellId

-- Update the aura elements if it already exists
if unitAuras[thisUnit][aura.auraInstanceID] then
for k,v in pairs(aura) do
unitAuras[thisUnit][aura.auraInstanceID][k] = v
end
else
unitAuras[thisUnit][aura.auraInstanceID] = aura
table.insert(unitAuras[unit], auraTable)

i = i + 1
end
end
46 changes: 26 additions & 20 deletions EnhancedRaidFrames.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
local addonName, addonTable = ... --make use of the default addon namespace

---@class EnhancedRaidFrames : AceAddon-3.0 @define The main addon object for the Enhanced Raid Frames add-on
addonTable.EnhancedRaidFrames = LibStub("AceAddon-3.0"):NewAddon("EnhancedRaidFrames", "AceTimer-3.0", "AceHook-3.0", "AceEvent-3.0", "AceBucket-3.0", "AceConsole-3.0", "AceSerializer-3.0")
addonTable.EnhancedRaidFrames = LibStub("AceAddon-3.0"):NewAddon("EnhancedRaidFrames", "AceTimer-3.0", "AceHook-3.0",
"AceEvent-3.0", "AceBucket-3.0", "AceConsole-3.0", "AceSerializer-3.0")
local EnhancedRaidFrames = addonTable.EnhancedRaidFrames

local LibDeflate = LibStub:GetLibrary("LibDeflate")
Expand All @@ -14,7 +15,7 @@ local L = LibStub("AceLocale-3.0"):GetLocale("EnhancedRaidFrames")
EnhancedRaidFrames.allAuras = " "
EnhancedRaidFrames.auraStrings = {{}, {}, {}, {}, {}, {}, {}, {}, {}} -- Matrix to keep all aura strings to watch for

if WOW_PROJECT_ID == WOW_PROJECT_CLASSIC then --boolean check to set a flag if the current session is WoW Classic. Retail == 1, Classic == 2
if WOW_PROJECT_ID == WOW_PROJECT_CLASSIC then
EnhancedRaidFrames.isWoWClassicEra = true
elseif WOW_PROJECT_ID == WOW_PROJECT_WRATH_CLASSIC then
EnhancedRaidFrames.isWoWClassic = true
Expand Down Expand Up @@ -53,32 +54,39 @@ end
--- Register Events, Hook functions, Create Frames, Get information from
--- the game that wasn't available in OnInitialize
function EnhancedRaidFrames:OnEnable()
--start a repeating timer to updated every frame every 0.8sec to make sure the the countdown timer stays accurate
self.updateTimer = self:ScheduleRepeatingTimer("UpdateAllFrames", 0.5) --this is so countdown text is smooth

--hook our UpdateIndicators function onto the default CompactUnitFrame_UpdateAuras function. The payload of the original function carries the identity of the frame needing updating
-- Register for the UNIT_AURA event to track auras on all raid frame units
if not self.isWoWClassicEra and not self.isWoWClassic then
self:RegisterEvent("UNIT_AURA", "UpdateUnitAuras")
else
self:RegisterEvent("UNIT_AURA", "UpdateUnitAuras_Legacy")
end
-- Run a full aura scan immediately after loading to populate our aura list for all units
self:UpdateAllAuras()

-- Hook our UpdateIndicators function onto the default CompactUnitFrame_UpdateAuras function.
-- The payload of the original function carries the identity of the frame needing updating.
-- Without this, things like the default aura icons will pop in and out.
self:SecureHook("CompactUnitFrame_UpdateAuras", function(frame) self:UpdateIndicators(frame) end)

-- Updates Range Alpha
-- Hook our UpdateInRange function to the default CompactUnitFrame_UpdateInRange function.
self:SecureHook("CompactUnitFrame_UpdateInRange", function(frame) self:UpdateInRange(frame) end)

-- Hook raid icon updates
-- Force a full update of all raid frames when a raid icon changes or the raid roster changes
self:RegisterBucketEvent({"RAID_TARGET_UPDATE", "RAID_ROSTER_UPDATE"}, 1, "UpdateAllFrames")

-- Use new UNIT_AURA event in retail that was added in 10.0 for huge performance gains
if not self.isWoWClassicEra and not self.isWoWClassic then
self:RegisterEvent("UNIT_AURA", "UpdateUnitAuras")
end
-- Start a repeating timer to make sure the the countdown timer stays accurate
self.updateTimer = self:ScheduleRepeatingTimer("UpdateAllFrames", 0.5)

-- Make sure any icons already existing are shown
-- Populate our starting config values
self:RefreshConfig()

-- notify of any new major updates, if necessary
self:UpdateNotifier()


-- Register our slash command to open the config panel
self:RegisterChatCommand("erf",function()
Settings.OpenToCategory("Enhanced Raid Frames")
end)

-- Notify to the chat window of any new major updates, if necessary
self:UpdateNotifier()
end

--- **OnDisable**, which is only called when your addon is manually being disabled.
Expand Down Expand Up @@ -148,7 +156,7 @@ function EnhancedRaidFrames:UpdateAllFrames(setAppearance)
end

--this is the heart and soul of the addon. Everything gets called from here.
if CompactRaidFrameContainer.ApplyToFrames then --10.0 refactored CompactRaidFrameContainer with new functionality
if not self.isWoWClassicEra and not self.isWoWClassic then --10.0 refactored CompactRaidFrameContainer with new functionality
CompactRaidFrameContainer:ApplyToFrames("normal",
function(frame)
self:UpdateIndicators(frame, setAppearance)
Expand Down Expand Up @@ -179,7 +187,6 @@ function EnhancedRaidFrames:RefreshConfig()
end

-- reset aura strings
self.allAuras = " "
self.auraStrings = {{}, {}, {}, {}, {}, {}, {}, {}, {}} -- Matrix to keep all aura strings to watch for

for i = 1, 9 do
Expand All @@ -189,7 +196,6 @@ function EnhancedRaidFrames:RefreshConfig()
auraName = auraName:lower() --force lowercase
auraName = auraName:gsub("^%s*(.-)%s*$", "%1") --strip any leading or trailing whitespace
auraName = auraName:gsub("\"", "") --strip any quotation marks if there are any
self.allAuras = EnhancedRaidFrames.allAuras.." "..auraName.." " -- Add each watched aura to a string so we later can quickly determine if we need to look for one
self.auraStrings[i][j] = auraName
j = j + 1
end
Expand Down

0 comments on commit b999b89

Please sign in to comment.