fix: resolve UI rendering crashes, address unit frame aliasing bugs, and implement automatic debuff tracking with lazy-load identity fetches.

This commit is contained in:
Bluewhale1337
2026-08-22 17:18:48 +02:00
parent d0e027ec12
commit b3c2b4a096
7 changed files with 145 additions and 63 deletions
+6
View File
@@ -205,6 +205,12 @@ function HealBot_Action_HealUnit_OnClick(this,button)
end
end
-- Special cases for targeting
if string.lower(pattern) == "target" or string.lower(pattern) == "/target" then
TargetUnit(this.unit)
return
end
-- Priority 1: Inline Scripts (starts with /)
if string.sub(pattern, 1, 1) == "/" then
local oldTarget = nil
+54 -11
View File
@@ -155,10 +155,21 @@ local HealBot_TrackedHoTs = {
["Interface\\Icons\\Spell_Holy_AshesToAshes"] = true,
}
local HealBot_DebuffTypeMap = nil
function HealBot_OnEvent_UnitAura(this, unit)
if not HealBot_DebuffTypeMap then
HealBot_DebuffTypeMap = {
[HEALBOT_DISEASE] = HEALBOT_DISEASE_en,
[HEALBOT_MAGIC] = HEALBOT_MAGIC_en,
[HEALBOT_POISON] = HEALBOT_POISON_en,
[HEALBOT_CURSE] = HEALBOT_CURSE_en
}
end
local DebuffType;
if HealBot_Heals[unit] and unit ~= "target" then
if HealBot_Heals[unit] then
if not HealBot_UnitIcons[unit] then
HealBot_UnitIcons[unit] = {}
end
@@ -168,17 +179,43 @@ function HealBot_OnEvent_UnitAura(this, unit)
local iconCount = 0
local i = 1;
HealBot_UnitDebuff[unit] = nil;
while true do
local debuff, tmp, debuff_type = UnitDebuff(unit, i, 1)
local debuff, tmp, debuff_type = UnitDebuff(unit, i)
if debuff then
local mapped_type = nil
if debuff_type then
mapped_type = HealBot_DebuffTypeMap[debuff_type] or debuff_type
end
local unitClass = HealBot_Model.units[unit] and HealBot_Model.units[unit].class or UnitClass(unit)
local shouldTrack = false
if unit == "target" then
shouldTrack = true
elseif HealBot_CDCInc[unitClass] == 1 then
shouldTrack = true
elseif not unitClass or HealBot_CDCInc[unitClass] == nil then
shouldTrack = true
end
if shouldTrack and mapped_type and HealBot_DebuffWatch[mapped_type] then
if iconCount < 10 then
iconCount = iconCount + 1
HealBot_UnitIcons[unit][iconCount] = debuff
end
if HealBot_CDCInc[UnitClass(unit)] == 1 and debuff_type and HealBot_DebuffWatch[debuff_type] then
HealBot_UnitDebuff[unit] = debuff_type
DebuffType = debuff_type;
if HealBot_DebuffPriority[debuff_type] then
HealBot_UnitDebuff[unit] = mapped_type
DebuffType = mapped_type;
local isPriority = false
if HealBot_DebuffPriority then
for _, pType in ipairs(HealBot_DebuffPriority) do
if pType == mapped_type then
isPriority = true
break
end
end
end
if isPriority then
break
end
end
@@ -213,16 +250,22 @@ function HealBot_OnEvent_UnitAura(this, unit)
if HealBot_UnitDebuff[unit] then
if DebuffType and HealBot_Range_Check(unit, 27) == 1 then
if HealBot_Config.ShowDebuffWarning == 1 then
local color = HealBot_Config.CDCBarColour[DebuffType]
local r, g, b = 1, 0, 0
if color then
r, g, b = color.R, color.G, color.B
end
UIErrorsFrame:AddMessage(UnitName(unit) .. " suffers from " .. DebuffType,
HealBot_Config.CDCBarColour[DebuffType].R,
HealBot_Config.CDCBarColour[DebuffType].G,
HealBot_Config.CDCBarColour[DebuffType].B,
r, g, b,
1, UIERRORS_HOLD_TIME);
end
if HealBot_Config.SoundDebuffWarning == 1 then HealBot_PlaySound(HealBot_Config.SoundDebuffPlay); end
end
end
HealBot_CheckBuffs(unit);
HealBot_RecalcHeals(unit);
-- Check buffs synchronously because tooltip scanning fails in OnUpdate
HealBot_CheckBuffs(unit)
-- Defer UI updates
HealBot_View_DirtyUnits[unit] = true
end
end
+18 -5
View File
@@ -2,6 +2,7 @@
-- Manages WoW Frame events and periodic updates, routing to respective services
HealBot_View_DirtyUnits = {}
HealBot_View_DirtyPower = {}
local HealBot_Timer1, HealsIn_Timer = 0, 0;
HealBot_LastModState = ""
@@ -20,7 +21,7 @@ function HealBot_OnLoad(this)
HealBot_View_DirtyUnits[unitID] = true
end)
HealBot_Model:RegisterObserver("UNIT_POWER_CHANGED", function(unitID)
HealBot_View_DirtyUnits[unitID] = true
HealBot_View_DirtyPower[unitID] = true
end)
HealBot_Model:RegisterObserver("UNIT_AURA_CHANGED", function(unitID)
HealBot_View_DirtyUnits[unitID] = true
@@ -74,11 +75,23 @@ function HealBot_OnUpdate(this, arg1)
end
-- Process Dirty Queue for MVC View
local unitID, _ = next(HealBot_View_DirtyUnits)
while unitID do
HealBot_Action_RefreshButtons(unitID)
local unitsToRefresh = {}
for unitID in pairs(HealBot_View_DirtyUnits) do
unitsToRefresh[unitID] = true
HealBot_View_DirtyUnits[unitID] = nil
unitID, _ = next(HealBot_View_DirtyUnits)
HealBot_View_DirtyPower[unitID] = nil -- No need to do power-only if full refresh is queued
end
for unitID in pairs(unitsToRefresh) do
HealBot_Action_Refresh(unitID)
end
local powerToRefresh = {}
for unitID in pairs(HealBot_View_DirtyPower) do
powerToRefresh[unitID] = true
HealBot_View_DirtyPower[unitID] = nil
end
for unitID in pairs(powerToRefresh) do
HealBot_Action_RefreshPower(unitID)
end
if HealBot_EquipChangeTimer > 0 then
+16 -6
View File
@@ -146,7 +146,7 @@ function HealBot_Model:UpdateUnitIdentity(unit)
if (HealBot_Integrations_SuperWoW_Active or HealBot_Integrations_ClassicAPI_Active) and HealBot_GetUnitGUID then
local guid = HealBot_GetUnitGUID(unit)
if guid then
if guid and guid ~= "0" and guid ~= "0x0000000000000000" then
self.unitGUIDs[unit] = guid
self.guidUnits[guid] = unit
end
@@ -174,14 +174,16 @@ function HealBot_Model:PreserveStateByGUID()
local 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 = {}
-- Scan the new roster
for _, unit in ipairs(self.partyMembers) do
local guid = HealBot_GetUnitGUID(unit)
if guid then
if guid and guid ~= "0" and guid ~= "0x0000000000000000" then
newUnitForGUID[guid] = unit
self.unitGUIDs[unit] = guid
self.guidUnits[guid] = unit
@@ -189,7 +191,7 @@ function HealBot_Model:PreserveStateByGUID()
end
for _, unit in ipairs(self.raidMembers) do
local guid = HealBot_GetUnitGUID(unit)
if guid then
if guid and guid ~= "0" and guid ~= "0x0000000000000000" then
newUnitForGUID[guid] = unit
self.unitGUIDs[unit] = guid
self.guidUnits[guid] = unit
@@ -220,10 +222,18 @@ function HealBot_Model:PreserveStateByGUID()
end
for targetUnit, stateData in pairs(stateSwaps) do
self.units[targetUnit] = stateData
-- Deep copy to prevent memory aliasing
self.units[targetUnit] = {}
for k, v in pairs(stateData) do
self.units[targetUnit][k] = v
end
self.units[targetUnit].icons = {}
if HealBot_UnitIcons then
HealBot_UnitIcons[targetUnit] = iconSwaps[targetUnit]
if HealBot_UnitIcons and iconSwaps[targetUnit] then
if not HealBot_UnitIcons[targetUnit] then HealBot_UnitIcons[targetUnit] = {} end
for j=1, 10 do
HealBot_UnitIcons[targetUnit][j] = iconSwaps[targetUnit][j]
end
end
if HealBot_MissingBuffs then
HealBot_MissingBuffs[targetUnit] = missingBuffSwaps[targetUnit]
+6 -16
View File
@@ -244,22 +244,12 @@ function HealBot_Options_CDC_SetCombo(spell, button, class)
HealBot_Options_KeyCombo_Change()
end
function HealBot_Options_Debuff_Reset()
local classEN=HealBot_UnitClass("player")
if classEN=="PRIEST" or classEN=="DRUID" or classEN=="PALADIN" or classEN=="SHAMAN" then
local spell = HealBot_Config.CDCLeftText[UnitClass("player")];
HealBot_DebuffWatch = {[HEALBOT_DISEASE_en]=false, [HEALBOT_MAGIC_en]=false, [HEALBOT_POISON_en]=false, [HEALBOT_CURSE_en]=false }
if spell ~= "None" then
table.foreach(HealBot_Debuff_Types[spell], function (index,debuff)
HealBot_DebuffWatch[debuff]=true;
end)
end
spell = HealBot_Config.CDCRightText[UnitClass("player")];
if spell ~= "None" then
table.foreach(HealBot_Debuff_Types[spell], function (index,debuff)
HealBot_DebuffWatch[debuff]=true;
end)
end
end
HealBot_DebuffWatch = {
[HEALBOT_DISEASE_en] = true,
[HEALBOT_MAGIC_en] = true,
[HEALBOT_POISON_en] = true,
[HEALBOT_CURSE_en] = true
}
end
function HealBot_Colorpick_OnClick(CDCType)
HealBot_ColourObjWaiting=CDCType;
+25 -4
View File
@@ -12,7 +12,6 @@ HealBot_Action_HealGroup = {
"party4",
};
HealBot_Action_HealGroup = {};
HealBot_Action_HealTarget = {};
HealBot_Action_HealFocus = {};
HealBot_Action_HealButtons = {};
@@ -30,9 +29,11 @@ end
function HealBot_HealthColor(unit, hlth, maxhlth)
if HealBot_UnitDebuff[unit] then
local debuff_type = HealBot_UnitDebuff[unit]
local dr = HealBot_Config.CDCBarColour[debuff_type].R
local dg = HealBot_Config.CDCBarColour[debuff_type].G
local db = HealBot_Config.CDCBarColour[debuff_type].B
local color = HealBot_Config.CDCBarColour[debuff_type]
if color then
local dr = color.R
local dg = color.G
local db = color.B
if HealBot_Config.btexture[HealBot_Config.Current_Skin] == 10 then
dr = dr * 4
dg = dg * 4
@@ -43,6 +44,7 @@ function HealBot_HealthColor(unit, hlth, maxhlth)
end
return dr, dg, db, HealBot_Config.Barcola[HealBot_Config.Current_Skin];
end
end
local text = UnitName(unit);
if not HealBot_HealsIn[text] then
@@ -102,6 +104,10 @@ function HealBot_Action_EnableButton(button)
local state = HealBot_Model.units[unit]
if not state then return end
if not state.englishClass then
HealBot_Model:UpdateUnitIdentity(unit)
end
local hlth = state.health
local maxhlth = state.maxHealth
local name = state.name
@@ -305,6 +311,21 @@ function HealBot_Action_RefreshButtons(unit)
end
end
function HealBot_Action_RefreshPower(unit)
if not unit or not HealBot_Action_UnitButtons[unit] then return end
local state = HealBot_Model.units[unit]
if not state then return end
for index, button in pairs(HealBot_Action_UnitButtons[unit]) do
if not button.bar3 then button.bar3 = getglobal(button:GetName() .. "Bar3") end
local bar3 = button.bar3
if bar3 and bar3:IsVisible() then
bar3:SetMinMaxValues(0, state.maxMana)
bar3:SetValue(state.mana)
end
end
end
function HealBot_Action_PositionButton(button, OsetX, OsetY, bwidth, bheight, checked, header)
local brspace = HealBot_Config.brspace[HealBot_Config.Current_Skin] or 3;
if header then
+7 -8
View File
@@ -49,15 +49,14 @@ Default installation path: `C:\Program Files\World of Warcraft\Interface\AddOns\
### Change Log
**v1.7.0**
* **Integrations UI** - Added a new 'Extras' tab in the Options menu to easily toggle external mod integrations.
* **UnitXP_SP3 Integration** - Added optional support for UnitXP's API to provide ultra-precise 3D range finding and strict Line of Sight (LoS) checks. This completely bypasses the limitations of Vanilla's 2D map coordinate distance checks.
* **Nampower Integration** - Implemented background aura tracking to map explicit HoT expiration timestamps directly from nampower. This allows for accurate real-time heal and buff tracking instead of estimating based on cast times.
* **SuperWoW Integration** - Added robust GUID-based state tracking leveraging SuperWoW's enhanced API. This tracks players by their unique IDs rather than volatile unit strings.
* **ClassicAPI Integration** - Added support for ClassicAPI to natively inject accurate +Healing bonuses, ultra-fast 3D distance checks (`UnitDistanceSquared`), and a dedicated Focus frame for pinning Main Tanks without requiring additional memory-heavy addons.
* **External Addon Integrations** - Added a new 'Extras' tab in Options to optionally enable integrations with `UnitXP_SP3` (for ultra-precise 3D range and Line of Sight checks) and `nampower` (for accurate real-time heal/buff tracking).
* **UI Layout** - Widened the Options UI and neatly centered elements to accommodate the new integrations tab.
* **Bug Fix - Class Colors** - Fixed a bug where class colors failed to apply to new units due to a delayed server response.
* **Bug Fix - Combat Updates** - Fixed an issue where new party members joining mid-combat failed to append to the grid without dropping combat.
* **Bug Fix - Target Swaps** - Implemented optional SuperWoW GUID-based state preservation to prevent UI tracking bugs when group members are rearranged during combat.
* **Bug Fix - Rendering Crash** - Fixed a critical UI issue where unrecognized debuff types would crash the rendering loop, causing unit frames to disappear.
* **Bug Fix - Missing Group Frames** - Fixed a layout initialization bug where the group unit array was accidentally cleared, causing the panel to draw 0 bars and shrink to a floating "Options" button.
* **Bug Fix - Target State Aliasing** - Fixed a bug in `PreserveStateByGUID` where targeting a party member caused their frame to alias the target's state table in memory. This previously caused party frames to adopt enemy mob names and lose their class colors when changing targets.
* **Bug Fix - Group Aliasing** - Fixed a bug where roster updates caused frames to share memory references, causing the UI to display the same player across multiple group slots.
* **Bug Fix - Slow Client Init** - Fixed missing class colors after `/reload` by adding lazy-load identity fetches directly into the render pipeline for when `UnitClass` data is delayed by the server.
* **Bug Fix - Debuff Tracking** - HealBot now automatically tracks all curable debuffs for the player's class out of the box. Previously, users had to manually assign a cure spell to a click binding to enable CDC debuff tracking.
**v1.6.3**
* **Hotfix string splitter** - added falback for string splitter if the the string is empty or nil.