diff --git a/HealBot_Controller_Events.lua b/HealBot_Controller_Events.lua index 4550b94..c274a0a 100644 --- a/HealBot_Controller_Events.lua +++ b/HealBot_Controller_Events.lua @@ -3,6 +3,8 @@ HealBot_View_DirtyUnits = {} HealBot_View_DirtyPower = {} +local unitsToRefresh = {} +local powerToRefresh = {} local HealBot_Timer1, HealsIn_Timer = 0, 0; HealBot_LastModState = "" @@ -152,7 +154,7 @@ function HealBot_OnUpdate(this, arg1) end -- Process Dirty Queue for MVC View - local unitsToRefresh = {} + for k in pairs(unitsToRefresh) do unitsToRefresh[k] = nil end for unitID in pairs(HealBot_View_DirtyUnits) do unitsToRefresh[unitID] = true HealBot_View_DirtyUnits[unitID] = nil @@ -162,7 +164,7 @@ function HealBot_OnUpdate(this, arg1) HealBot_Action_Refresh(unitID) end - local powerToRefresh = {} + for k in pairs(powerToRefresh) do powerToRefresh[k] = nil end for unitID in pairs(HealBot_View_DirtyPower) do powerToRefresh[unitID] = true HealBot_View_DirtyPower[unitID] = nil @@ -490,6 +492,14 @@ end function HealBot_OnEvent_PlayerRegenEnabled(this) HealBot_IsFighting = false; HealBot_Delay_RecalcParty = 1; + + if HealBot_IncomingHealers then + for sender, data in pairs(HealBot_IncomingHealers) do + if not HealBot_FindUnitID(sender) then + HealBot_IncomingHealers[sender] = nil + end + end + end end -- HealBot_OnEvent_PlayerTargetChanged: Internal utility: HealBot_OnEvent_PlayerTargetChanged diff --git a/HealBot_Model.lua b/HealBot_Model.lua index f808ce5..2114e4f 100644 --- a/HealBot_Model.lua +++ b/HealBot_Model.lua @@ -1,6 +1,13 @@ -- HealBot_Model.lua -- Centralized Data Store and Observer System for HealBotBlue +local pool_oldGUIDs = {} +local pool_newUnitForGUID = {} +local pool_stateSwaps = {} +local pool_iconSwaps = {} +local pool_missingBuffSwaps = {} +local pool_debuffSwaps = {} + -- Safe local wrappers to prevent native UIDropDownMenu concatenation crashes -- when setting selected values on closed dropdowns during initialization. function HealBot_UIDropDownMenu_SetSelectedID(frame, id, useValue) @@ -180,14 +187,16 @@ end function HealBot_Model:PreserveStateByGUID() if not (HealBot_Integrations_SuperWoW_Active or HealBot_Integrations_ClassicAPI_Active) or not HealBot_GetUnitGUID then return end - local oldGUIDs = {} + for k in pairs(pool_oldGUIDs) do pool_oldGUIDs[k] = nil end + local oldGUIDs = pool_oldGUIDs for unit, guid in pairs(self.unitGUIDs) do if string.find(unit, "^party") or string.find(unit, "^raid") or unit == "player" or unit == "pet" then oldGUIDs[unit] = guid end end - local newUnitForGUID = {} + for k in pairs(pool_newUnitForGUID) do pool_newUnitForGUID[k] = nil end + local newUnitForGUID = pool_newUnitForGUID -- Scan the new roster for _, unit in ipairs(self.partyMembers) do local guid = HealBot_GetUnitGUID(unit) @@ -206,10 +215,14 @@ function HealBot_Model:PreserveStateByGUID() end end - local stateSwaps = {} - local iconSwaps = {} - local missingBuffSwaps = {} - local debuffSwaps = {} + for k in pairs(pool_stateSwaps) do pool_stateSwaps[k] = nil end + local stateSwaps = pool_stateSwaps + for k in pairs(pool_iconSwaps) do pool_iconSwaps[k] = nil end + local iconSwaps = pool_iconSwaps + for k in pairs(pool_missingBuffSwaps) do pool_missingBuffSwaps[k] = nil end + local missingBuffSwaps = pool_missingBuffSwaps + for k in pairs(pool_debuffSwaps) do pool_debuffSwaps[k] = nil end + local debuffSwaps = pool_debuffSwaps for oldUnit, guid in pairs(oldGUIDs) do local newUnit = newUnitForGUID[guid] @@ -231,11 +244,22 @@ function HealBot_Model:PreserveStateByGUID() for targetUnit, stateData in pairs(stateSwaps) do -- Deep copy to prevent memory aliasing - self.units[targetUnit] = {} - for k, v in pairs(stateData) do - self.units[targetUnit][k] = v + if not self.units[targetUnit] then + self.units[targetUnit] = { icons = {} } end - self.units[targetUnit].icons = {} + + local targetIcons = self.units[targetUnit].icons + if not targetIcons then targetIcons = {} end + for k in pairs(targetIcons) do targetIcons[k] = nil end + + for k in pairs(self.units[targetUnit]) do self.units[targetUnit][k] = nil end + + for k, v in pairs(stateData) do + if k ~= "icons" then + self.units[targetUnit][k] = v + end + end + self.units[targetUnit].icons = targetIcons if HealBot_UnitIcons and iconSwaps[targetUnit] then if not HealBot_UnitIcons[targetUnit] then HealBot_UnitIcons[targetUnit] = {} end diff --git a/README.md b/README.md index 3643863..25a9396 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,8 @@ Default installation path: `C:\Program Files\World of Warcraft\Interface\AddOns\ ### Change Log **v1.7.1** +* **Performance Fix - Table Pooling** - Fixed massive Vanilla Lua 5.0 garbage collection memory leaks caused by unbounded table allocations in high-frequency update loops (e.g., `OnUpdate` and `PreserveStateByGUID`). Moved tables to file-local scope and implemented inline clearing. +* **Performance Fix - OOC Cleanup** - Added an out-of-combat garbage collection hook (`PLAYER_REGEN_ENABLED`) to purge disconnected senders from the `HealBot_IncomingHealers` global table, preventing memory bloat during prolonged play sessions. * **Bug Fix - Incoming Heals Comms** - Fixed a regex string parsing bug that caused incoming heals from other HealBot instances to drop if a unit's name contained non-alphabetic characters (e.g. dashes or spaces in pet names). * **Feature - Standard HealComm Sync** - Implemented lightweight parsing of the standard `HealComm` addon channel. HealBot now perfectly syncs incoming heals with modern raid frames like Luna, Grid, and pfUI, while retaining backwards compatibility with older versions of HealBot. * **UI Update - Raid Marks** - Anchored raid target icons to top of unit frames instead of center.