diff --git a/ElvUI_SLE/modules/datatexts/friends.lua b/ElvUI_SLE/modules/datatexts/friends.lua index 32143c39..a182917b 100644 --- a/ElvUI_SLE/modules/datatexts/friends.lua +++ b/ElvUI_SLE/modules/datatexts/friends.lua @@ -380,6 +380,9 @@ local function OnEvent(self, event, message) -- when this is the case, we invalidate our buffered table and update the -- datatext information if event == 'CHAT_MSG_SYSTEM' then + -- Retail 12.0+: CHAT_MSG_SYSTEM message can be a 'secret value' (not safe for string ops on tainted paths). + if _G.issecretvalue and _G.issecretvalue(message) then return end + if type(message) ~= 'string' then return end if not (strfind(message, friendOnline) or strfind(message, friendOffline)) then return end end diff --git a/ElvUI_SLE/modules/nameplates.lua b/ElvUI_SLE/modules/nameplates.lua index 9e681b5f..515be2c7 100644 --- a/ElvUI_SLE/modules/nameplates.lua +++ b/ElvUI_SLE/modules/nameplates.lua @@ -19,6 +19,15 @@ function N:CreateThreatIndicator(nameplate) nameplate.SLE_threatInfo:FontTemplate(E.LSM:Fetch('font', E.db.sle.nameplates.threat.font), E.db.sle.nameplates.threat.size, E.db.sle.nameplates.threat.fontOutline) end +local issecretvalue = _G.issecretvalue + +-- Retail 12.0+: Some API strings can be 'secret values'. Avoid comparing secret values on tainted paths. +local function SafeUnitGUID(unit) + local guid = UnitGUID(unit) + if issecretvalue and issecretvalue(guid) then return nil end + return guid +end + hooksecurefunc(NP, 'ThreatIndicator_PostUpdate', function(threat, unit) if not threat.__owner then return end if threat.__owner.SLE_threatInfo then @@ -27,7 +36,8 @@ hooksecurefunc(NP, 'ThreatIndicator_PostUpdate', function(threat, unit) if E.db.sle.nameplates.threat.enable and threat.__owner.frameType == 'ENEMY_NPC' then if not unit then for i=1, 4 do - if threat.__owner.guid == UnitGUID(format('boss%d', i)) then + local bossGUID = SafeUnitGUID(format('boss%d', i)) + if bossGUID and threat.__owner.guid == bossGUID then unit = format('boss%d', i) break end @@ -35,7 +45,8 @@ hooksecurefunc(NP, 'ThreatIndicator_PostUpdate', function(threat, unit) end if unit and not UnitIsPlayer(unit) and UnitCanAttack('player', unit) then local status, percent = select(2, UnitDetailedThreatSituation('player', unit)) - if (status) then + -- Retail 12.0+: status may be nil/invalid (or secret); guard before calling GetThreatStatusColor. + if status and type(status) == 'number' and not (issecretvalue and issecretvalue(status)) then threat.__owner.SLE_threatInfo:SetFormattedText('%s%.0f%%|r', E:RGBToHex(GetThreatStatusColor(status)), percent or '') end end @@ -76,10 +87,11 @@ function N:UpdateCount(event, unit, force) for _, unitid in pairs(N.GroupMembers) do --For every unit in roster if not UnitIsUnit(unitid, 'player') and plate.unit then target = format('%starget', unitid) --Get group member's target - plate.guid = UnitGUID(plate.unit) --Find unit's guid + plate.guid = SafeUnitGUID(plate.unit) --Find unit's guid if plate.guid and UnitExists(target) then --If target exists and plate actually has unit, then someone actually targets this plate - if UnitGUID(target) == plate.guid then + local tgtGUID = SafeUnitGUID(target) + if tgtGUID and tgtGUID == plate.guid then plate.SLE_TargetedByCounter = plate.SLE_TargetedByCounter + 1 end end @@ -89,10 +101,11 @@ function N:UpdateCount(event, unit, force) --If debug mode is set if N.TestSoloTarget then - plate.guid = UnitGUID(plate.unit) + plate.guid = SafeUnitGUID(plate.unit) if plate.guid and UnitExists('target') then - if UnitGUID('target') == plate.guid then + local tgtGUID = SafeUnitGUID('target') + if tgtGUID and tgtGUID == plate.guid then plate.SLE_TargetedByCounter = plate.SLE_TargetedByCounter + 1 end end diff --git a/ElvUI_SLE/modules/professions/deconstruct.lua b/ElvUI_SLE/modules/professions/deconstruct.lua index 1b84532d..66200202 100644 --- a/ElvUI_SLE/modules/professions/deconstruct.lua +++ b/ElvUI_SLE/modules/professions/deconstruct.lua @@ -1,4 +1,4 @@ -local SLE, T, E, L, V, P, G = unpack(ElvUI_SLE) +local SLE, T, E, L, V, P, G = unpack(ElvUI_SLE) local Pr = SLE.Professions local B = E.Bags local lib = LibStub('LibProcessable') @@ -10,7 +10,11 @@ local format, strfind, strsplit, gsub, type, tostring = format, strfind, strspli local GetTradeTargetItemLink = GetTradeTargetItemLink local InCombatLockdown = InCombatLockdown local LOCKED = LOCKED -local ActionButton_ShowOverlayGlow, ActionButton_HideOverlayGlow, AutoCastShine_AutoCastStart = ActionButton_ShowOverlayGlow, ActionButton_HideOverlayGlow, AutoCastShine_AutoCastStart +local ActionButton_ShowOverlayGlow, ActionButton_HideOverlayGlow, AutoCastShine_AutoCastStart = _G.ActionButton_ShowOverlayGlow, _G.ActionButton_HideOverlayGlow, _G.AutoCastShine_AutoCastStart +-- Retail builds may not expose these helpers as globals in all UI load orders; avoid nil-call errors. +ActionButton_ShowOverlayGlow = ActionButton_ShowOverlayGlow or function() end +ActionButton_HideOverlayGlow = ActionButton_HideOverlayGlow or function() end +AutoCastShine_AutoCastStart = AutoCastShine_AutoCastStart or function() end local C_Container_GetContainerItemLink = C_Container.GetContainerItemLink local C_Container_GetContainerItemInfo = C_Container.GetContainerItemInfo diff --git a/ElvUI_SLE/modules/pvp.lua b/ElvUI_SLE/modules/pvp.lua index 6a32e65e..10a7b61f 100644 --- a/ElvUI_SLE/modules/pvp.lua +++ b/ElvUI_SLE/modules/pvp.lua @@ -1,4 +1,4 @@ -local SLE, T, E, L, V, P, G = unpack(ElvUI_SLE) +local SLE, T, E, L, V, P, G = unpack(ElvUI_SLE) local PvP = SLE.PVP --GLOBALS: hooksecurefunc, CreateFrame @@ -23,6 +23,16 @@ local C_PvP_IsActiveBattlefield = C_PvP.IsActiveBattlefield local BG_Opponents = {} PvP.HonorStrings = {} + +-- Prefer ElvUI event dispatcher to avoid AceEvent taint/protected RegisterEvent path +local function RegisterEventSafe(obj, event, method) + if E and E.RegisterEventForObject then + E:RegisterEventForObject(obj, event, method) + elseif obj and obj.RegisterEvent then + obj:RegisterEvent(event, method) + end +end + function PvP:Release() local resOptions = GetSortedSelfResurrectOptions() if (PvP.db.rebirth and not resOptions[1]) or not PvP.db.rebirth then RepopMe() end @@ -81,15 +91,15 @@ function PvP:Initialize() PvP.db = E.db.sle.pvp --AutoRes event - self:RegisterEvent('PLAYER_DEAD', 'Dead') + RegisterEventSafe(self, 'PLAYER_DEAD', 'Dead') if E.db.movers['PvPMover'] then E.db.movers['TopCenterContainerMover'] = E.db.movers['PvPMover'] E.db.movers['PvPMover'] = nil end - self:RegisterEvent('DUEL_REQUESTED', 'Duels') - self:RegisterEvent('PET_BATTLE_PVP_DUEL_REQUESTED', 'Duels') + RegisterEventSafe(self, 'DUEL_REQUESTED', 'Duels') + RegisterEventSafe(self, 'PET_BATTLE_PVP_DUEL_REQUESTED', 'Duels') function PvP:ForUpdateAll() PvP.db = E.db.sle.pvp @@ -111,8 +121,8 @@ function PvP:Initialize() end end end) - self:RegisterEvent('COMBAT_LOG_EVENT_UNFILTERED', 'LogParse') - self:RegisterEvent('UPDATE_BATTLEFIELD_SCORE', 'OpponentsTable') + RegisterEventSafe(self, 'COMBAT_LOG_EVENT_UNFILTERED', 'LogParse') + RegisterEventSafe(self, 'UPDATE_BATTLEFIELD_SCORE', 'OpponentsTable') end end diff --git a/ElvUI_SLE/skins/blizzard/merchant.lua b/ElvUI_SLE/skins/blizzard/merchant.lua index b26eb58a..706a552b 100644 --- a/ElvUI_SLE/skins/blizzard/merchant.lua +++ b/ElvUI_SLE/skins/blizzard/merchant.lua @@ -1,4 +1,4 @@ -local SLE, T, E, L, V, P, G = unpack(ElvUI_SLE) +local SLE, T, E, L, V, P, G = unpack(ElvUI_SLE) local Sk = SLE.Skins local S = E.Skins @@ -9,7 +9,31 @@ local ItemsPerSubpage, SubpagesPerPage local math_max, math_ceil = math.max, math.ceil local MerchantFrame_UpdateAltCurrency, MoneyFrame_Update = MerchantFrame_UpdateAltCurrency, MoneyFrame_Update local GetMerchantNumItems = GetMerchantNumItems -local GetMerchantItemInfo, GetMerchantItemLink = GetMerchantItemInfo, GetMerchantItemLink +local GetMerchantItemInfo = _G.GetMerchantItemInfo +local GetMerchantItemLink = _G.GetMerchantItemLink + +-- Retail compatibility: GetMerchantItemInfo migrated to C_MerchantFrame.GetItemInfo +if not GetMerchantItemInfo and C_MerchantFrame and C_MerchantFrame.GetItemInfo then + GetMerchantItemInfo = function(index) + local info = C_MerchantFrame.GetItemInfo(index) + if not info then return end + + local texture = info.icon or info.iconFileID or info.iconTexture or info.texture or info.iconID + if not texture then + local itemID = info.itemID or (_G.GetMerchantItemID and _G.GetMerchantItemID(index)) + if itemID and _G.GetItemIcon then texture = _G.GetItemIcon(itemID) end + end + + local name = info.name + local price = info.price + local quantity = info.stackCount or info.quantity or info.stack + local numAvailable = info.numAvailable + local isPurchasable = info.isPurchasable + local isUsable = info.isUsable + local extendedCost = info.extendedCost or info.hasExtendedCost or info.isExtendedCost + return name, texture, price, quantity, numAvailable, isPurchasable, isUsable, extendedCost + end +end local SetItemButtonCount, SetItemButtonStock, SetItemButtonTexture = SetItemButtonCount, SetItemButtonStock, SetItemButtonTexture local SetItemButtonNameFrameVertexColor, SetItemButtonSlotVertexColor, SetItemButtonTextureVertexColor, SetItemButtonNormalTextureVertexColor = SetItemButtonNameFrameVertexColor, SetItemButtonSlotVertexColor, SetItemButtonTextureVertexColor, SetItemButtonNormalTextureVertexColor local C_CurrencyInfo_GetCurrencyInfo = C_CurrencyInfo.GetCurrencyInfo @@ -186,9 +210,15 @@ local function UpdateMerchantInfo() local merchantMoney = _G['MerchantItem'..i..'MoneyFrame'] local merchantAltCurrency = _G['MerchantItem'..i..'AltCurrencyFrame'] if (index <= visibleMerchantItems) then - name, texture, price, quantity, numAvailable, isPurchasable, isUsable, extendedCost = GetMerchantItemInfo(indexes[index]) + local itemIndex = indexes[index] + name, texture, price, quantity, numAvailable, isPurchasable, isUsable, extendedCost = GetMerchantItemInfo(itemIndex) + -- Some vendors use item/currency costs; ensure extendedCost is detected even if GetMerchantItemInfo doesn't flag it + if not extendedCost and _G.GetMerchantItemCostInfo then + local costCount = _G.GetMerchantItemCostInfo(itemIndex) + if costCount and costCount > 0 then extendedCost = true end + end if (name ~= nil) then - local canAfford = CanAffordMerchantItem(index) + local canAfford = CanAffordMerchantItem(itemIndex) _G['MerchantItem'..i..'Name']:SetText(name) SetItemButtonCount(itemButton, quantity) SetItemButtonStock(itemButton, numAvailable) @@ -198,9 +228,9 @@ local function UpdateMerchantInfo() itemButton.price = nil itemButton.extendedCost = true itemButton.name = name - itemButton.link = GetMerchantItemLink(indexes[index]) + itemButton.link = GetMerchantItemLink(itemIndex) itemButton.texture = texture - MerchantFrame_UpdateAltCurrency(index, i, canAfford) + MerchantFrame_UpdateAltCurrency(itemIndex, i, canAfford) merchantAltCurrency:ClearAllPoints() merchantAltCurrency:SetPoint('BOTTOMLEFT', 'MerchantItem'..i..'NameFrame', 'BOTTOMLEFT', 0, 31) merchantMoney:Hide() @@ -209,9 +239,9 @@ local function UpdateMerchantInfo() itemButton.price = price itemButton.extendedCost = true itemButton.name = name - itemButton.link = GetMerchantItemLink(indexes[index]) + itemButton.link = GetMerchantItemLink(itemIndex) itemButton.texture = texture - local altCurrencyWidth = MerchantFrame_UpdateAltCurrency(index, i, canAfford) + local altCurrencyWidth = MerchantFrame_UpdateAltCurrency(itemIndex, i, canAfford) MoneyFrame_SetMaxDisplayWidth(merchantMoney, MAX_MONEY_DISPLAY_WIDTH - altCurrencyWidth) MoneyFrame_Update(merchantMoney:GetName(), price) local color @@ -227,7 +257,7 @@ local function UpdateMerchantInfo() itemButton.price = price itemButton.extendedCost = nil itemButton.name = name - itemButton.link = GetMerchantItemLink(indexes[index]) + itemButton.link = GetMerchantItemLink(itemIndex) itemButton.texture = texture MoneyFrame_SetMaxDisplayWidth(merchantMoney, MAX_MONEY_DISPLAY_WIDTH) MoneyFrame_Update(merchantMoney:GetName(), price) @@ -242,14 +272,14 @@ local function UpdateMerchantInfo() MerchantFrameItem_UpdateQuality(merchantButton, itemButton.link) - local merchantItemID = GetMerchantItemID(index) + local merchantItemID = GetMerchantItemID(itemIndex) local isHeirloom = merchantItemID and C_Heirloom.IsItemHeirloom(merchantItemID) local isKnownHeirloom = isHeirloom and C_Heirloom.PlayerHasHeirloom(merchantItemID) itemButton.showNonrefundablePrompt = isHeirloom itemButton.hasItem = true - itemButton:SetID(indexes[index]) + itemButton:SetID(itemIndex) itemButton:Show() local tintRed = not isPurchasable or (not isUsable and not isHeirloom) diff --git a/ElvUI_SLE/skins/blizzard/merchantList.lua b/ElvUI_SLE/skins/blizzard/merchantList.lua index 1e61488b..a1fc55ff 100644 --- a/ElvUI_SLE/skins/blizzard/merchantList.lua +++ b/ElvUI_SLE/skins/blizzard/merchantList.lua @@ -1,4 +1,4 @@ -local SLE, T, E, L, V, P, G = unpack(ElvUI_SLE) +local SLE, T, E, L, V, P, G = unpack(ElvUI_SLE) local Sk = SLE.Skins local S = E.Skins @@ -12,7 +12,31 @@ local MerchantItemButton_OnClick = MerchantItemButton_OnClick local BuyMerchantItem = BuyMerchantItem local MerchantFrame_ConfirmExtendedItemCost = MerchantFrame_ConfirmExtendedItemCost local FauxScrollFrame_OnVerticalScroll = FauxScrollFrame_OnVerticalScroll -local GetMerchantItemInfo, GetMerchantItemLink = GetMerchantItemInfo, GetMerchantItemLink +local GetMerchantItemInfo = _G.GetMerchantItemInfo +local GetMerchantItemLink = _G.GetMerchantItemLink + +-- Retail compatibility: GetMerchantItemInfo migrated to C_MerchantFrame.GetItemInfo +if not GetMerchantItemInfo and C_MerchantFrame and C_MerchantFrame.GetItemInfo then + GetMerchantItemInfo = function(index) + local info = C_MerchantFrame.GetItemInfo(index) + if not info then return end + + local texture = info.icon or info.iconFileID or info.iconTexture or info.texture or info.iconID + if not texture then + local itemID = info.itemID or (_G.GetMerchantItemID and _G.GetMerchantItemID(index)) + if itemID and _G.GetItemIcon then texture = _G.GetItemIcon(itemID) end + end + + local name = info.name + local price = info.price + local quantity = info.stackCount or info.quantity or info.stack + local numAvailable = info.numAvailable + local isPurchasable = info.isPurchasable + local isUsable = info.isUsable + local extendedCost = info.extendedCost or info.hasExtendedCost or info.isExtendedCost + return name, texture, price, quantity, numAvailable, isPurchasable, isUsable, extendedCost + end +end local GetMoney, GetCoinTextureString = GetMoney, GetCoinTextureString local GetMerchantItemCostInfo, GetMerchantItemCostItem = GetMerchantItemCostInfo, GetMerchantItemCostItem local FauxScrollFrame_GetOffset, FauxScrollFrame_Update = FauxScrollFrame_GetOffset, FauxScrollFrame_Update