965e8589c9
I implemented a QuestHelper_GetTime function for the next time Blizzard decides to fiddle with the time functions. It returns debugprofilestop() / 1000, to exactly match the precision of GetTime(). I also re-removed references to Cartographer from the rollback.
403 lignes
14 Kio
Lua
403 lignes
14 Kio
Lua
|
|
local GetTime = QuestHelper_GetTime
|
|
|
|
QuestHelper_File["warning.lua"] = "4.0.1.$svnversion$"
|
|
QuestHelper_Loadtime["warning.lua"] = GetTime()
|
|
|
|
--[[
|
|
Much of this code is ganked wholesale from Swatter, and is Copyright (C) 2006 Norganna. Licensed under LGPL v3.0.
|
|
]]
|
|
|
|
local debug_output = false
|
|
if QuestHelper_File["warning.lua"] == "Development Version" then debug_output = true end
|
|
|
|
QuestHelper_local_version = QuestHelper_File["warning.lua"]
|
|
QuestHelper_toc_version = GetAddOnMetadata("QuestHelper", "Version")
|
|
|
|
local origHandler = getwarninghandler()
|
|
|
|
local QuestHelper_WarningCatcher = { }
|
|
|
|
local startup_warnings = {}
|
|
local completely_started = false
|
|
local yelled_at_user = false
|
|
|
|
local first_warning = nil
|
|
|
|
QuestHelper_Warnings = {}
|
|
|
|
function QuestHelper_WarningCatcher.TextWarning(text)
|
|
DEFAULT_CHAT_FRAME:AddMessage(string.format("|cffff8080QuestHelper Warning Handler: |r%s", text))
|
|
end
|
|
|
|
|
|
-- ganked verbatim from Swatter
|
|
function QuestHelper_WarningCatcher.GetQuests()
|
|
local return_string = ""
|
|
|
|
for q = 1, GetNumQuestLogEntries() do
|
|
local title, _, _, _, header, _, _, _, id = GetQuestLogTitle(q)
|
|
if header then
|
|
if id then
|
|
return_string = return_string .. string.format("%s (%d)\n", title, id)
|
|
else
|
|
return_string = return_string .. string.format("[%s]\n", title)
|
|
end
|
|
else
|
|
return_string = return_string .. string.format("\t%s (%d)\n", title, id)
|
|
end
|
|
end
|
|
|
|
return return_string
|
|
end
|
|
function QuestHelper_WarningCatcher.GetAddOns()
|
|
local addlist = ""
|
|
for i = 1, GetNumAddOns() do
|
|
local name, title, notes, enabled, loadable, reason, security = GetAddOnInfo(i)
|
|
|
|
local loaded = IsAddOnLoaded(i)
|
|
if (loaded) then
|
|
if not name then name = "Anonymous" end
|
|
name = name:gsub("[^a-zA-Z0-9]+", "")
|
|
local version = GetAddOnMetadata(i, "Version")
|
|
local class = getglobal(name)
|
|
if not class or type(class)~='table' then class = getglobal(name:lower()) end
|
|
if not class or type(class)~='table' then class = getglobal(name:sub(1,1):upper()..name:sub(2):lower()) end
|
|
if not class or type(class)~='table' then class = getglobal(name:upper()) end
|
|
if class and type(class)=='table' then
|
|
if (class.version) then
|
|
version = class.version
|
|
elseif (class.Version) then
|
|
version = class.Version
|
|
elseif (class.VERSION) then
|
|
version = class.VERSION
|
|
end
|
|
end
|
|
local const = getglobal(name:upper().."_VERSION")
|
|
if (const) then version = const end
|
|
|
|
if type(version)=='table' then
|
|
local allstr = true
|
|
for k, v in pairs(version) do
|
|
if type(v) ~= "string" then allstr = false end
|
|
end
|
|
|
|
if allstr then
|
|
version = table.concat(version,":")
|
|
end
|
|
elseif type(version) == 'function' then
|
|
local yay, v = pcall(version)
|
|
|
|
if yay then version = v end
|
|
end
|
|
|
|
if (version) then
|
|
addlist = addlist.." "..name..", v"..tostring(version).."\n"
|
|
else
|
|
addlist = addlist.." "..name.."\n"
|
|
end
|
|
end
|
|
end
|
|
return addlist
|
|
end
|
|
|
|
local warning_uniqueness_whitelist = {
|
|
["count"] = true,
|
|
["timestamp"] = true,
|
|
}
|
|
|
|
-- here's the logic
|
|
function QuestHelper_WarningCatcher.CondenseWarnings()
|
|
if completely_started then
|
|
while next(startup_warnings) do
|
|
_, warn = next(startup_warnings)
|
|
table.remove(startup_warnings)
|
|
|
|
if not QuestHelper_Warnings[warn.type] then QuestHelper_Warnings[warn.type] = {} end
|
|
|
|
local found = false
|
|
|
|
for _, item in ipairs(QuestHelper_Warnings[warn.type]) do
|
|
local match = true
|
|
for k, v in pairs(warn.dat) do
|
|
if not warning_uniqueness_whitelist[k] and item[k] ~= v then match = false break end
|
|
end
|
|
if match then for k, v in pairs(item) do
|
|
if not warning_uniqueness_whitelist[k] and warn.dat[k] ~= v then match = false break end
|
|
end end
|
|
if match then
|
|
found = true
|
|
item.count = (item.count or 1) + 1
|
|
break
|
|
end
|
|
end
|
|
|
|
if not found then
|
|
table.insert(QuestHelper_Warnings[warn.type], warn.dat)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function QuestHelper_WarningCatcher_RegisterWarning(typ, dat)
|
|
table.insert(startup_warnings, {type = typ, dat = dat})
|
|
QuestHelper_WarningCatcher.CondenseWarnings()
|
|
end
|
|
|
|
function QuestHelper_WarningPackage(depth)
|
|
return {
|
|
timestamp = date("%Y-%m-%d %H:%M:%S"),
|
|
local_version = QuestHelper_local_version,
|
|
toc_version = QuestHelper_toc_version,
|
|
game_version = GetBuildInfo(),
|
|
locale = GetLocale(),
|
|
mutation_passes_exceeded = QuestHelper and QuestHelper.mutation_passes_exceeded,
|
|
stack = debugstack(depth or 4, 20, 20),
|
|
}
|
|
end
|
|
|
|
StaticPopupDialogs["QH_EXPLODEY"] = {
|
|
text = "QuestHelper has broken. You may have to restart WoW. Type \"/qh warning\" for a detailed warning message.",
|
|
button1 = OKAY,
|
|
OnAccept = function(self)
|
|
end,
|
|
timeout = 0,
|
|
whileDead = 1,
|
|
hideOnEscape = 1
|
|
};
|
|
|
|
function QuestHelper_WarningCatcher_ExplicitWarning(loud, o_msg, o_frame, o_stack, ...)
|
|
local msg = o_msg or ""
|
|
|
|
-- We toss it into StartupWarnings, and then if we're running properly, we'll merge it into the main DB.
|
|
local twarning = QuestHelper_WarningPackage()
|
|
|
|
twarning.message = msg
|
|
twarning.addons = QuestHelper_WarningCatcher.GetAddOns()
|
|
twarning.stack = o_stack or twarning.stack
|
|
twarning.silent = not loud
|
|
twarning.quests = QuestHelper_WarningCatcher.GetQuests()
|
|
|
|
QuestHelper_WarningCatcher_RegisterWarning("crash", twarning)
|
|
|
|
if first_warning and first_warning.silent and not first_warning.next_loud and not twarning.silent then first_warning.next_loud = twarning first_warning.addons = "" end
|
|
if not first_warning or first_warning.generated then first_warning = twarning end
|
|
|
|
QuestHelper_WarningCatcher.CondenseWarnings()
|
|
|
|
if (--[[debug_output or]] loud) and not yelled_at_user then
|
|
--print("qhbroken")
|
|
StaticPopupDialogs["QH_EXPLODEY"] = {
|
|
text = "QuestHelper has broken. You may have to restart WoW. Type \"/qh warning\" for a detailed warning message.",
|
|
button1 = OKAY,
|
|
OnAccept = function(self)
|
|
end,
|
|
timeout = 0,
|
|
whileDead = 1,
|
|
hideOnEscape = 1
|
|
}
|
|
|
|
StaticPopup_Show("QH_EXPLODEY")
|
|
yelled_at_user = true
|
|
end
|
|
end
|
|
|
|
function QuestHelper_WarningCatcher_GenerateReport()
|
|
if first_warning then return end -- don't need to generate one
|
|
|
|
local twarning = QuestHelper_WarningPackage()
|
|
|
|
twarning.message = "(Full report)"
|
|
twarning.addons = QuestHelper_WarningCatcher.GetAddOns()
|
|
twarning.stack = ""
|
|
twarning.silent = "(Full report)"
|
|
twarning.generated = true
|
|
twarning.quests = QuestHelper_WarningCatcher.GetQuests()
|
|
|
|
first_warning = twarning
|
|
end
|
|
|
|
function QuestHelper_WarningCatcher.OnWarning(o_msg, o_frame, o_stack, o_etype, ...)
|
|
local warningize = false
|
|
local loud = false
|
|
if o_msg and string.find(o_msg, "QuestHelper") and not string.find(o_msg, "Cannot find a library with name") then loud = true end
|
|
|
|
for lin in string.gmatch(debugstack(2, 20, 20), "([^\n]*)") do
|
|
if string.find(lin, "QuestHelper") and not string.find(lin, "QuestHelper\\AstrolabeQH\\DongleStub.lua") then warningize = true end
|
|
end
|
|
|
|
if string.find(o_msg, "SavedVariables") then warningize, loud = false, false end
|
|
if string.find(o_msg, "C stack overflow") then
|
|
if loud then warningize = true end
|
|
loud = false
|
|
end
|
|
|
|
if loud then warningize = true end
|
|
|
|
if warningize then QuestHelper_WarningCatcher_ExplicitWarning(loud, o_msg, o_frame, o_stack) end
|
|
|
|
--[[
|
|
if o_msg and
|
|
(
|
|
(
|
|
string.find(o_msg, "QuestHelper") -- Obviously we care about our bugs
|
|
)
|
|
or (
|
|
string.find(debugstack(2, 20, 20), "QuestHelper") -- We're being a little overzealous and catching any bug with "QuestHelper" in the stack. This possibly should be removed, I'm not sure it's ever caught anything interesting.
|
|
)
|
|
)
|
|
and not string.match(o_msg, "WTF\\Account\\.*") -- Sometimes the WTF file gets corrupted. This isn't our fault, since we weren't involved in writing it, and there's also nothing we can do about it - in fact we can't even retrieve the remnants of the old file. We may as well just ignore it. I suppose we could pop up a little dialog saying "clear some space on your hard drive, dufus" but, meh.
|
|
and not (string.find(o_msg, "Cannot find a library with name") and string.find(debugstack(2, 20, 20), "QuestHelper\\AstrolabeQH\\DongleStub.lua")) -- We're catching warnings caused by other people mucking up their dongles. Ughh.
|
|
then
|
|
QuestHelper_WarningCatcher_ExplicitWarning(o_msg, o_frame, o_stack)
|
|
end]]
|
|
|
|
return origHandler(o_msg, o_frame, o_stack, o_etype, unpack(arg or {})) -- pass it on
|
|
end
|
|
|
|
setwarninghandler(QuestHelper_WarningCatcher.OnWarning) -- at this point we can catch warnings
|
|
|
|
function QuestHelper_WarningCatcher.CompletelyStarted()
|
|
completely_started = true
|
|
|
|
-- Our old code generated a horrifying number of redundant items. My bad. I considered going and trying to collate them into one chunk, but I think I'm just going to wipe them - it's easier, faster, and should fix some performance issues.
|
|
if not QuestHelper_Warnings.version or QuestHelper_Warnings.version ~= 1 then
|
|
QuestHelper_Warnings = {version = 1}
|
|
end
|
|
|
|
QuestHelper_WarningCatcher.CondenseWarnings()
|
|
end
|
|
|
|
function QuestHelper_WarningCatcher_CompletelyStarted()
|
|
QuestHelper_WarningCatcher.CompletelyStarted()
|
|
end
|
|
|
|
|
|
|
|
-- and here is the GUI
|
|
|
|
local QHE_Gui = {}
|
|
|
|
function QHE_Gui.WarningUpdate()
|
|
QHE_Gui.WarningTextinate()
|
|
QHE_Gui.Warning.Box:SetText(QHE_Gui.Warning.curWarning)
|
|
QHE_Gui.Warning.Scroll:UpdateScrollChildRect()
|
|
QHE_Gui.Warning.Box:ClearFocus()
|
|
end
|
|
|
|
function TextinateWarning(warn)
|
|
local tswarn = string.format("msg: %s\ntoc: %s\nv: %s\ngame: %s\nlocale: %s\ntimestamp: %s\nmutation: %s\nsilent: %s\n\n%s\naddons:\n%s", warn.message, warn.toc_version, warn.local_version, warn.game_version, warn.locale, warn.timestamp, tostring(warn.mutation_passes_exceeded), tostring(warn.silent), warn.stack, warn.addons)
|
|
if warn.next_loud then
|
|
tswarn = tswarn .. "\n\n---- Following loud warning\n\n" .. TextinateWarning(warn.next_loud)
|
|
end
|
|
return tswarn
|
|
end
|
|
|
|
function QHE_Gui.WarningTextinate()
|
|
if first_warning then
|
|
QHE_Gui.Warning.curWarning = TextinateWarning(first_warning)
|
|
else
|
|
QHE_Gui.Warning.curWarning = "None"
|
|
end
|
|
end
|
|
|
|
function QHE_Gui.WarningClicked()
|
|
if (QHE_Gui.Warning.selected) then return end
|
|
QHE_Gui.Warning.Box:HighlightText()
|
|
QHE_Gui.Warning.selected = true
|
|
end
|
|
|
|
function QHE_Gui.WarningDone()
|
|
QHE_Gui.Warning:Hide()
|
|
end
|
|
|
|
|
|
-- Create our warning message frame. Most of this is also ganked from Swatter.
|
|
QHE_Gui.Warning = CreateFrame("Frame", "QHE_GUIWarningFrame", UIParent)
|
|
QHE_Gui.Warning:Hide()
|
|
QHE_Gui.Warning:SetPoint("CENTER", "UIParent", "CENTER")
|
|
QHE_Gui.Warning:SetFrameStrata("TOOLTIP")
|
|
QHE_Gui.Warning:SetHeight(300)
|
|
QHE_Gui.Warning:SetWidth(600)
|
|
QHE_Gui.Warning:SetBackdrop({
|
|
bgFile = "Interface/Tooltips/ChatBubble-Background",
|
|
edgeFile = "Interface/Tooltips/ChatBubble-BackDrop",
|
|
tile = true, tileSize = 32, edgeSize = 32,
|
|
insets = { left = 32, right = 32, top = 32, bottom = 32 }
|
|
})
|
|
QHE_Gui.Warning:SetBackdropColor(0.2,0,0, 1)
|
|
QHE_Gui.Warning:SetScript("OnShow", QHE_Gui.WarningShow)
|
|
QHE_Gui.Warning:SetMovable(true)
|
|
|
|
QHE_Gui.ProxyFrame = CreateFrame("Frame", "QHE_GuiProxyFrame")
|
|
QHE_Gui.ProxyFrame:SetParent(QHE_Gui.Warning)
|
|
QHE_Gui.ProxyFrame.IsShown = function() return QHE_Gui.Warning:IsShown() end
|
|
QHE_Gui.ProxyFrame.escCount = 0
|
|
QHE_Gui.ProxyFrame.timer = 0
|
|
QHE_Gui.ProxyFrame.Hide = (
|
|
function( self )
|
|
local numEscapes = QHE_Gui.numEscapes or 1
|
|
self.escCount = self.escCount + 1
|
|
if ( self.escCount >= numEscapes ) then
|
|
self:GetParent():Hide()
|
|
self.escCount = 0
|
|
end
|
|
if ( self.escCount == 1 ) then
|
|
self.timer = 0
|
|
end
|
|
end
|
|
)
|
|
QHE_Gui.ProxyFrame:SetScript("OnUpdate",
|
|
function( self, elapsed )
|
|
local timer = self.timer + elapsed
|
|
if ( timer >= 1 ) then
|
|
self.escCount = 0
|
|
end
|
|
self.timer = timer
|
|
end
|
|
)
|
|
table.insert(UISpecialFrames, "QHE_GuiProxyFrame")
|
|
|
|
QHE_Gui.Drag = CreateFrame("Button", nil, QHE_Gui.Warning)
|
|
QHE_Gui.Drag:SetPoint("TOPLEFT", QHE_Gui.Warning, "TOPLEFT", 10,-5)
|
|
QHE_Gui.Drag:SetPoint("TOPRIGHT", QHE_Gui.Warning, "TOPRIGHT", -10,-5)
|
|
QHE_Gui.Drag:SetHeight(8)
|
|
QHE_Gui.Drag:SetHighlightTexture("Interface\\FriendsFrame\\UI-FriendsFrame-HighlightBar")
|
|
|
|
QHE_Gui.Drag:SetScript("OnMouseDown", function() QHE_Gui.Warning:StartMoving() end)
|
|
QHE_Gui.Drag:SetScript("OnMouseUp", function() QHE_Gui.Warning:StopMovingOrSizing() end)
|
|
|
|
QHE_Gui.Warning.Done = CreateFrame("Button", "", QHE_Gui.Warning, "OptionsButtonTemplate")
|
|
QHE_Gui.Warning.Done:SetText("Close")
|
|
QHE_Gui.Warning.Done:SetPoint("BOTTOMRIGHT", QHE_Gui.Warning, "BOTTOMRIGHT", -10, 10)
|
|
QHE_Gui.Warning.Done:SetScript("OnClick", QHE_Gui.WarningDone)
|
|
|
|
QHE_Gui.Warning.Mesg = QHE_Gui.Warning:CreateFontString("", "OVERLAY", "GameFontNormalSmall")
|
|
QHE_Gui.Warning.Mesg:SetJustifyH("LEFT")
|
|
QHE_Gui.Warning.Mesg:SetPoint("TOPRIGHT", QHE_Gui.Warning.Prev, "TOPLEFT", -10, 0)
|
|
QHE_Gui.Warning.Mesg:SetPoint("LEFT", QHE_Gui.Warning, "LEFT", 15, 0)
|
|
QHE_Gui.Warning.Mesg:SetHeight(20)
|
|
QHE_Gui.Warning.Mesg:SetText("Select All and Copy the above warning message to report this bug.")
|
|
|
|
QHE_Gui.Warning.Scroll = CreateFrame("ScrollFrame", "QHE_GUIWarningInputScroll", QHE_Gui.Warning, "UIPanelScrollFrameTemplate")
|
|
QHE_Gui.Warning.Scroll:SetPoint("TOPLEFT", QHE_Gui.Warning, "TOPLEFT", 20, -20)
|
|
QHE_Gui.Warning.Scroll:SetPoint("RIGHT", QHE_Gui.Warning, "RIGHT", -30, 0)
|
|
QHE_Gui.Warning.Scroll:SetPoint("BOTTOM", QHE_Gui.Warning.Done, "TOP", 0, 10)
|
|
|
|
QHE_Gui.Warning.Box = CreateFrame("EditBox", "QHE_GUIWarningEditBox", QHE_Gui.Warning.Scroll)
|
|
QHE_Gui.Warning.Box:SetWidth(500)
|
|
QHE_Gui.Warning.Box:SetHeight(85)
|
|
QHE_Gui.Warning.Box:SetMultiLine(true)
|
|
QHE_Gui.Warning.Box:SetAutoFocus(false)
|
|
QHE_Gui.Warning.Box:SetFontObject(GameFontHighlight)
|
|
QHE_Gui.Warning.Box:SetScript("OnEscapePressed", QHE_Gui.WarningDone)
|
|
QHE_Gui.Warning.Box:SetScript("OnTextChanged", QHE_Gui.WarningUpdate)
|
|
QHE_Gui.Warning.Box:SetScript("OnEditFocusGained", QHE_Gui.WarningClicked)
|
|
|
|
QHE_Gui.Warning.Scroll:SetScrollChild(QHE_Gui.Warning.Box)
|
|
|
|
function QuestHelper_WarningCatcher_ReportWarning()
|
|
QHE_Gui.Warning.selected = false
|
|
QHE_Gui.WarningUpdate()
|
|
QHE_Gui.Warning:Show()
|
|
end
|