fix: v1.4.4 - AceGUI pool fixes, event handling nil checks, QuestieLearner serialization, l10n format fixes
This commit is contained in:
@@ -57,6 +57,7 @@ function _QuestieJourney:CreateObjectiveText(desc)
|
||||
end
|
||||
|
||||
function _QuestieJourney:HandleTabChange(container, group)
|
||||
if not container then return end
|
||||
if not _QuestieJourney.containerCache then
|
||||
_QuestieJourney.containerCache = container
|
||||
end
|
||||
|
||||
@@ -28,12 +28,31 @@ local LOG_DEVELOP = false
|
||||
|
||||
local function DebugLog(tier, msg)
|
||||
if tier == "CRITICAL" and LOG_CRITICAL then
|
||||
Questie:Print("|cFF00FF00[QL-CRITICAL]|r " .. msg)
|
||||
-- print("[QuestieLearnerComms] " .. msg)
|
||||
elseif tier == "DEVELOP" and LOG_DEVELOP then
|
||||
Questie:Debug(Questie.DEBUG_DEVELOP, "|cFF00FFFF[QL-DEV]|r " .. msg)
|
||||
-- print("[QuestieLearnerComms] " .. msg)
|
||||
end
|
||||
end
|
||||
|
||||
local function SanitizeData(data, depth)
|
||||
depth = depth or 0
|
||||
if depth > 10 then return nil end -- Prevent infinite recursion
|
||||
if type(data) ~= "table" then return {} end
|
||||
local sanitized = {}
|
||||
for k, v in pairs(data) do
|
||||
if type(k) ~= "string" and type(k) ~= "number" then
|
||||
-- Skip non-string/number keys
|
||||
elseif type(v) == "function" or type(v) == "userdata" or type(v) == "thread" then
|
||||
-- Skip these types
|
||||
elseif type(v) == "table" then
|
||||
sanitized[k] = SanitizeData(v, depth + 1)
|
||||
else
|
||||
sanitized[k] = v
|
||||
end
|
||||
end
|
||||
return sanitized
|
||||
end
|
||||
|
||||
-- Throttling (Token Bucket)
|
||||
local bucketCapacity = 9
|
||||
local bucketWindow = 60
|
||||
@@ -167,18 +186,29 @@ function _QuestieLearnerComms:ProcessReinforcement()
|
||||
end
|
||||
|
||||
function QuestieLearnerComms:BroadcastLearnedData(op, entityType, entityId, data)
|
||||
-- 1. Create Payload
|
||||
if not data or type(data) ~= "table" then return end
|
||||
|
||||
-- 1. Create Payload (sanitize data to remove functions before serialization)
|
||||
local sanitizedData = SanitizeData(data)
|
||||
if not sanitizedData or next(sanitizedData) == nil then return end
|
||||
|
||||
local payload = {
|
||||
_ver = ProtocolVersion,
|
||||
op = op, -- "NEW", "UPDATE", "CONFIRM"
|
||||
typ = entityType,
|
||||
id = entityId,
|
||||
d = data,
|
||||
d = sanitizedData,
|
||||
ts = time()
|
||||
}
|
||||
|
||||
-- 2. Serialize and Compress
|
||||
local serialized = AceSerializer:Serialize(payload)
|
||||
local serialized
|
||||
local success, err = pcall(AceSerializer.Serialize, AceSerializer, payload)
|
||||
if not success then
|
||||
DebugLog("CRITICAL", "AceSerializer error: " .. tostring(err))
|
||||
return
|
||||
end
|
||||
serialized = err
|
||||
local compressed = LibDeflate:CompressDeflate(serialized, {level = 9})
|
||||
local encoded = LibDeflate:EncodeForPrint(compressed)
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ end
|
||||
-- Polyfill for xpcall variadic arguments (missing in standard Lua 5.0/5.1 WoW clients).
|
||||
-- Modern Ace3 uses xpcall(func, err, ...) which drops arguments on legacy clients,
|
||||
-- leading to 'self' being nil in addon callbacks.
|
||||
-- Fix #14: Do NOT write to bare _G.xpcall — store in QuestieCompat namespace only.
|
||||
-- Writing to _G.xpcall pollutes the global namespace and can cause taint on protected contexts.
|
||||
local _xpcall = xpcall
|
||||
local xpcall_supported = false
|
||||
pcall(function()
|
||||
@@ -24,7 +26,7 @@ pcall(function()
|
||||
end)
|
||||
|
||||
if not xpcall_supported then
|
||||
_G.xpcall = function(func, err, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22, arg23, arg24, arg25)
|
||||
QuestieCompat.xpcall = function(func, err, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22, arg23, arg24, arg25)
|
||||
-- To avoid the GC overhead of building {...} on every event fire, we pre-check argument counts.
|
||||
-- We support up to 25 arguments just like our select() polyfill.
|
||||
if arg25 ~= nil then return _xpcall(function() return func(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22, arg23, arg24, arg25) end, err) end
|
||||
@@ -56,6 +58,33 @@ if not xpcall_supported then
|
||||
-- No extra args provided
|
||||
return _xpcall(func, err)
|
||||
end
|
||||
else
|
||||
-- Native xpcall works fine, expose it
|
||||
QuestieCompat.xpcall = _xpcall
|
||||
end
|
||||
|
||||
------------------------------------------
|
||||
-- GetCurrentRegion polyfill (WotLK/Classic)
|
||||
------------------------------------------
|
||||
|
||||
-- GetCurrentRegion and GetCurrentRegionName are modern API functions that don't exist in WotLK.
|
||||
-- AceDB-3.0 uses these for realm identification. Provide fallbacks based on locale.
|
||||
if not GetCurrentRegion then
|
||||
local regionByLocale = {
|
||||
["enUS"] = 1, ["enGB"] = 1, ["koKR"] = 2, ["frFR"] = 3, ["deDE"] = 3,
|
||||
["zhCN"] = 5, ["zhTW"] = 4, ["esES"] = 3, ["esMX"] = 1, ["ruRU"] = 3,
|
||||
["ptBR"] = 1, ["itIT"] = 3,
|
||||
}
|
||||
GetCurrentRegion = function()
|
||||
return regionByLocale[GetLocale()] or 1
|
||||
end
|
||||
end
|
||||
|
||||
if not GetCurrentRegionName then
|
||||
local regionNames = { "US", "KR", "EU", "TW", "CN" }
|
||||
GetCurrentRegionName = function()
|
||||
return regionNames[GetCurrentRegion()] or "US"
|
||||
end
|
||||
end
|
||||
|
||||
-- addon is running on 3.3.5 WotLK client
|
||||
@@ -91,6 +120,25 @@ if not TooltipBackdropTemplateMixin then
|
||||
TooltipBackdropTemplateMixin = BackdropTemplateMixin
|
||||
end
|
||||
|
||||
-------------------------------------------
|
||||
-- AceComm/AceSerializer compatibility (WotLK)
|
||||
-------------------------------------------
|
||||
|
||||
-- Ambiguate is used to disambiguate realm names but doesn't exist in WotLK.
|
||||
-- On WotLK, realm names are already unique in the format, so we can just return the name.
|
||||
if not Ambiguate then
|
||||
Ambiguate = function(name, kind)
|
||||
return name
|
||||
end
|
||||
end
|
||||
|
||||
-- RegisterAddonMessagePrefix may not exist in all WotLK versions.
|
||||
if not RegisterAddonMessagePrefix then
|
||||
RegisterAddonMessagePrefix = function(prefix)
|
||||
-- No-op on versions that don't support it
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------
|
||||
-- API difference compatibility (Era/Wotlk)
|
||||
-------------------------------------------
|
||||
|
||||
@@ -16,6 +16,12 @@ local QuestieCorrections = QuestieLoader:ImportModule("QuestieCorrections")
|
||||
---@type l10n
|
||||
local l10n = QuestieLoader:ImportModule("l10n")
|
||||
|
||||
---@type QuestieCompat
|
||||
local QuestieCompat = QuestieLoader:ImportModule("QuestieCompat")
|
||||
|
||||
---@type C_Timer
|
||||
local C_Timer = QuestieCompat.C_Timer
|
||||
|
||||
local DebugInformation = {} -- stores text of debug data dump per session
|
||||
local debugIndex = 0 -- current debug index, used so we can still retrieve info from previous offers
|
||||
local openDebugWindows = {} -- determines if existing debug window is already open, prevents duplicates
|
||||
@@ -642,15 +648,18 @@ local LINK_COLOR = CreateColorFromHexString("cff71d5ff");
|
||||
local LINK_LENGTHS = LINK_CODE:len();
|
||||
|
||||
-- handles clicking on link
|
||||
-- FIX: Added InCombatLockdown guard and pcall to prevent tainting secure execution paths.
|
||||
-- SetItemRef can be called during action button clicks (e.g., quest item tooltips) which
|
||||
-- run in a protected execution context. If the hook runs insecure code, it can taint
|
||||
-- the call chain and cause "ADDON_ACTION_BLOCKED: tried to call UseAction()" errors.
|
||||
-- FIX: Added InCombatLockdown guard, deferred execution, and pcall to prevent tainting
|
||||
-- secure execution paths. SetItemRef can be called during action button clicks (e.g., quest
|
||||
-- item tooltips) which run in a protected execution context. If the hook runs insecure code,
|
||||
-- it can taint the call chain and cause "ADDON_ACTION_BLOCKED: tried to call UseAction()" errors.
|
||||
-- Using C_Timer.After to defer execution to after the protected context completes.
|
||||
hooksecurefunc("SetItemRef", function(link)
|
||||
if InCombatLockdown() then return end
|
||||
local linkType = link:sub(1, LINK_LENGTHS);
|
||||
if linkType == LINK_CODE then
|
||||
pcall(QuestieDebugOffer.ShowOffer, link)
|
||||
C_Timer.After(0, function()
|
||||
pcall(QuestieDebugOffer.ShowOffer, link)
|
||||
end)
|
||||
end
|
||||
end);
|
||||
|
||||
|
||||
@@ -288,6 +288,7 @@ end
|
||||
--- Fires when a System Message (yellow text) is output to the main chat window
|
||||
---@param message string The message value from the CHAT_MSG_SYSTEM event
|
||||
function _EventHandler:ChatMsgSystem(message)
|
||||
if not message then return end
|
||||
-- When a new quest is accepted or completed quest is turned in, update the LibDataBroker text with the appropriate message
|
||||
if string.find(message, questCompletedMessage) == 1 or string.find(message, questAcceptedMessage) == 1 then
|
||||
MinimapIcon:UpdateText(message)
|
||||
|
||||
@@ -55,6 +55,7 @@ end
|
||||
--Always compare to the UnitLevel parameter, returning the highest.
|
||||
---@param level Level
|
||||
function QuestiePlayer:SetPlayerLevel(level)
|
||||
if level == nil then return end
|
||||
local localLevel = UnitLevel("player");
|
||||
_QuestiePlayer.playerLevel = math_max(localLevel, level);
|
||||
end
|
||||
|
||||
@@ -190,18 +190,18 @@ function MapIconTooltip:Show()
|
||||
end
|
||||
elseif iconData.Type == "available" or iconData.Type == "complete" then
|
||||
local tip = _MapIconTooltip:GetAvailableOrCompleteTooltip(icon)
|
||||
if not npcAndObjectOrder[tip.title] then
|
||||
npcAndObjectOrder[tip.title] = {npcNames = {}, quests = {}};
|
||||
if not npcAndObjectOrder["default"] then
|
||||
npcAndObjectOrder["default"] = {npcNames = {}, quests = {}};
|
||||
end
|
||||
npcAndObjectOrder[tip.title].npcNames[iconData.Name] = true
|
||||
npcAndObjectOrder[tip.title].quests[tip.title] = tip
|
||||
npcAndObjectOrder["default"].npcNames[iconData.Name] = true
|
||||
npcAndObjectOrder["default"].quests[tip.title] = tip
|
||||
elseif iconData.Type == "monster" or iconData.Type == "killcredit" or iconData.Type == "spell" or iconData.Type == "object" or iconData.Type == "event" or iconData.Type == "item" then
|
||||
local tip = _MapIconTooltip:GetAvailableOrCompleteTooltip(icon)
|
||||
if not npcAndObjectOrder[tip.title] then
|
||||
npcAndObjectOrder[tip.title] = {npcNames = {}, quests = {}};
|
||||
if not npcAndObjectOrder["default"] then
|
||||
npcAndObjectOrder["default"] = {npcNames = {}, quests = {}};
|
||||
end
|
||||
npcAndObjectOrder[tip.title].npcNames[iconData.Name] = true
|
||||
npcAndObjectOrder[tip.title].quests[tip.title] = tip
|
||||
npcAndObjectOrder["default"].npcNames[iconData.Name] = true
|
||||
npcAndObjectOrder["default"].quests[tip.title] = tip
|
||||
elseif iconData.CustomTooltipData then
|
||||
manualOrder[iconData.CustomTooltipData.Title] = { Body = { iconData.CustomTooltipData.Body or "" } }
|
||||
elseif iconData.ManualTooltipData then
|
||||
|
||||
Reference in New Issue
Block a user