1 QuestHelper_File
["error.lua"] = "Development Version"
2 QuestHelper_Loadtime
["error.lua"] = GetTime()
5 Much of this code is ganked wholesale from Swatter, and is Copyright (C) 2006 Norganna. Licensed under LGPL v3.0.
8 local debug_output
= false
9 if QuestHelper_File
["error.lua"] == "Development Version" then debug_output
= true end
11 QuestHelper_local_version
= QuestHelper_File
["error.lua"]
12 QuestHelper_toc_version
= GetAddOnMetadata("QuestHelper", "Version")
14 local origHandler
= geterrorhandler()
16 local QuestHelper_ErrorCatcher
= { }
18 local startup_errors
= {}
19 local completely_started
= false
20 local yelled_at_user
= false
22 local first_error
= nil
24 QuestHelper_Errors
= {}
26 function QuestHelper_ErrorCatcher
.TextError(text
)
27 DEFAULT_CHAT_FRAME
:AddMessage(string.format("|cffff8080QuestHelper Error Handler: |r%s", text
))
31 -- ganked verbatim from Swatter
32 function QuestHelper_ErrorCatcher
.GetAddOns()
34 for i
= 1, GetNumAddOns() do
35 local name
, title
, notes
, enabled
, loadable
, reason
, security
= GetAddOnInfo(i
)
37 local loaded
= IsAddOnLoaded(i
)
39 if not name
then name
= "Anonymous" end
40 name
= name
:gsub("[^a-zA-Z0-9]+", "")
41 local version
= GetAddOnMetadata(i
, "Version")
42 local class
= getglobal(name
)
43 if not class
or type(class
)~='table' then class
= getglobal(name
:lower()) end
44 if not class
or type(class
)~='table' then class
= getglobal(name
:sub(1,1):upper()..name
:sub(2):lower()) end
45 if not class
or type(class
)~='table' then class
= getglobal(name
:upper()) end
46 if class
and type(class
)=='table' then
47 if (class
.version
) then
48 version
= class
.version
49 elseif (class
.Version
) then
50 version
= class
.Version
51 elseif (class
.VERSION
) then
52 version
= class
.VERSION
55 local const
= getglobal(name
:upper().."_VERSION")
56 if (const
) then version
= const
end
58 if type(version
)=='table' then
60 for k
, v
in pairs(version
) do
61 if type(v
) ~= "string" then allstr
= false end
65 version
= table.concat(version
,":")
67 elseif type(version
) == 'function' then
68 local yay
, v
= pcall(version
)
70 if yay
then version
= v
end
74 addlist
= addlist
.." "..name
..", v"..tostring(version
).."\n"
76 addlist
= addlist
.." "..name
.."\n"
83 local error_uniqueness_whitelist
= {
89 function QuestHelper_ErrorCatcher
.CondenseErrors()
90 if completely_started
then
91 while next(startup_errors
) do
92 _
, err
= next(startup_errors
)
93 table.remove(startup_errors
)
95 if not QuestHelper_Errors
[err
.type] then QuestHelper_Errors
[err
.type] = {} end
99 for _
, item
in ipairs(QuestHelper_Errors
[err
.type]) do
101 for k
, v
in pairs(err
.dat
) do
102 if not error_uniqueness_whitelist
[k
] and item
[k
] ~= v
then match
= false break end
104 if match
then for k
, v
in pairs(item
) do
105 if not error_uniqueness_whitelist
[k
] and err
.dat
[k
] ~= v
then match
= false break end
109 item
.count
= (item
.count
or 1) + 1
115 table.insert(QuestHelper_Errors
[err
.type], err
.dat
)
121 function QuestHelper_ErrorCatcher_RegisterError(typ
, dat
)
122 table.insert(startup_errors
, {type = typ
, dat
= dat
})
123 QuestHelper_ErrorCatcher
.CondenseErrors()
126 function QuestHelper_ErrorPackage(depth
)
128 timestamp
= date("%Y-%m-%d %H:%M:%S"),
129 local_version
= QuestHelper_local_version
,
130 toc_version
= QuestHelper_toc_version
,
131 game_version
= GetBuildInfo(),
132 locale
= GetLocale(),
133 mutation_passes_exceeded
= QuestHelper
and QuestHelper
.mutation_passes_exceeded
,
134 stack
= debugstack(depth
or 4, 20, 20),
138 StaticPopupDialogs
["QH_EXPLODEY"] = {
139 text
= "QuestHelper has broken. You may have to restart WoW. Type \"/qh error\" for a detailed error message.",
141 OnAccept
= function(self
)
148 function QuestHelper_ErrorCatcher_ExplicitError(loud
, o_msg
, o_frame
, o_stack
, ...)
149 local msg
= o_msg
or ""
151 -- We toss it into StartupErrors, and then if we're running properly, we'll merge it into the main DB.
152 local terror
= QuestHelper_ErrorPackage()
155 terror
.addons
= QuestHelper_ErrorCatcher
.GetAddOns()
156 terror
.stack
= o_stack
or terror
.stack
157 terror
.silent
= not loud
159 QuestHelper_ErrorCatcher_RegisterError("crash", terror
)
161 if first_error
and first_error
.silent
and not first_error
.next_loud
and not terror
.silent
then first_error
.next_loud
= terror first_error
.addons
= "" end
162 if not first_error
or first_error
.generated
then first_error
= terror
end
164 QuestHelper_ErrorCatcher
.CondenseErrors()
166 if (--[[debug_output or]] loud
) and not yelled_at_user
then
168 StaticPopupDialogs
["QH_EXPLODEY"] = {
169 text
= "QuestHelper has broken. You may have to restart WoW. Type \"/qh error\" for a detailed error message.",
171 OnAccept
= function(self
)
178 StaticPopup_Show("QH_EXPLODEY")
179 yelled_at_user
= true
183 function QuestHelper_ErrorCatcher_GenerateReport()
184 if first_error
then return end -- don't need to generate one
186 local terror
= QuestHelper_ErrorPackage()
188 terror
.message
= "(Full report)"
189 terror
.addons
= QuestHelper_ErrorCatcher
.GetAddOns()
191 terror
.silent
= "(Full report)"
192 terror
.generated
= true
197 function QuestHelper_ErrorCatcher
.OnError(o_msg
, o_frame
, o_stack
, o_etype
, ...)
198 local errorize
= false
200 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
202 for lin
in string.gmatch(debugstack(2, 20, 20), "([^\n]*)") do
203 if string.find(lin
, "QuestHelper") and not string.find(lin
, "QuestHelper\\AstrolabeQH\\DongleStub.lua") then errorize
= true end
206 if string.find(o_msg
, "SavedVariables") then errorize
, loud
= false, false end
207 if string.find(o_msg
, "C stack overflow") then
208 if loud
then errorize
= true end
212 if loud
then errorize
= true end
214 if errorize
then QuestHelper_ErrorCatcher_ExplicitError(loud
, o_msg
, o_frame
, o_stack
) end
220 string.find(o_msg, "QuestHelper") -- Obviously we care about our bugs
223 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.
224 and not string.find(o_msg, "Cartographer_POI") -- Cartographer started throwing ridiculous numbers of errors on startup with QH in the stack, and since we caught stuff with QH in the stack, we decided these errors were ours. Urgh. Disabled.
227 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.
228 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 errors caused by other people mucking up their dongles. Ughh.
230 QuestHelper_ErrorCatcher_ExplicitError(o_msg, o_frame, o_stack)
233 return origHandler(o_msg
, o_frame
, o_stack
, o_etype
, unpack(arg
or {})) -- pass it on
236 seterrorhandler(QuestHelper_ErrorCatcher
.OnError
) -- at this point we can catch errors
238 function QuestHelper_ErrorCatcher
.CompletelyStarted()
239 completely_started
= true
241 -- 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.
242 if not QuestHelper_Errors
.version
or QuestHelper_Errors
.version
~= 1 then
243 QuestHelper_Errors
= {version
= 1}
246 QuestHelper_ErrorCatcher
.CondenseErrors()
249 function QuestHelper_ErrorCatcher_CompletelyStarted()
250 QuestHelper_ErrorCatcher
.CompletelyStarted()
255 -- and here is the GUI
259 function QHE_Gui
.ErrorUpdate()
260 QHE_Gui
.ErrorTextinate()
261 QHE_Gui
.Error
.Box
:SetText(QHE_Gui
.Error
.curError
)
262 QHE_Gui
.Error
.Scroll
:UpdateScrollChildRect()
263 QHE_Gui
.Error
.Box
:ClearFocus()
266 function TextinateError(err
)
267 local tserr
= string.format("msg: %s\ntoc: %s\nv: %s\ngame: %s\nlocale: %s\ntimestamp: %s\nmutation: %s\nsilent: %s\n\n%s\naddons:\n%s", err
.message
, err
.toc_version
, err
.local_version
, err
.game_version
, err
.locale
, err
.timestamp
, tostring(err
.mutation_passes_exceeded
), tostring(err
.silent
), err
.stack
, err
.addons
)
268 if err
.next_loud
then
269 tserr
= tserr
.. "\n\n---- Following loud error\n\n" .. TextinateError(err
.next_loud
)
274 function QHE_Gui
.ErrorTextinate()
276 QHE_Gui
.Error
.curError
= TextinateError(first_error
)
278 QHE_Gui
.Error
.curError
= "None"
282 function QHE_Gui
.ErrorClicked()
283 if (QHE_Gui
.Error
.selected
) then return end
284 QHE_Gui
.Error
.Box
:HighlightText()
285 QHE_Gui
.Error
.selected
= true
288 function QHE_Gui
.ErrorDone()
293 -- Create our error message frame. Most of this is also ganked from Swatter.
294 QHE_Gui
.Error
= CreateFrame("Frame", "QHE_GUIErrorFrame", UIParent
)
296 QHE_Gui
.Error
:SetPoint("CENTER", "UIParent", "CENTER")
297 QHE_Gui
.Error
:SetFrameStrata("TOOLTIP")
298 QHE_Gui
.Error
:SetHeight(300)
299 QHE_Gui
.Error
:SetWidth(600)
300 QHE_Gui
.Error
:SetBackdrop({
301 bgFile
= "Interface/Tooltips/ChatBubble-Background",
302 edgeFile
= "Interface/Tooltips/ChatBubble-BackDrop",
303 tile
= true, tileSize
= 32, edgeSize
= 32,
304 insets
= { left
= 32, right
= 32, top
= 32, bottom
= 32 }
306 QHE_Gui
.Error
:SetBackdropColor(0.2,0,0, 1)
307 QHE_Gui
.Error
:SetScript("OnShow", QHE_Gui
.ErrorShow
)
308 QHE_Gui
.Error
:SetMovable(true)
310 QHE_Gui
.ProxyFrame
= CreateFrame("Frame", "QHE_GuiProxyFrame")
311 QHE_Gui
.ProxyFrame
:SetParent(QHE_Gui
.Error
)
312 QHE_Gui
.ProxyFrame
.IsShown
= function() return QHE_Gui
.Error
:IsShown() end
313 QHE_Gui
.ProxyFrame
.escCount
= 0
314 QHE_Gui
.ProxyFrame
.timer
= 0
315 QHE_Gui
.ProxyFrame
.Hide
= (
317 local numEscapes
= QHE_Gui
.numEscapes
or 1
318 self
.escCount
= self
.escCount
+ 1
319 if ( self
.escCount
>= numEscapes
) then
320 self
:GetParent():Hide()
323 if ( self
.escCount
== 1 ) then
328 QHE_Gui
.ProxyFrame
:SetScript("OnUpdate",
329 function( self
, elapsed
)
330 local timer
= self
.timer
+ elapsed
331 if ( timer
>= 1 ) then
337 table.insert(UISpecialFrames
, "QHE_GuiProxyFrame")
339 QHE_Gui
.Drag
= CreateFrame("Button", nil, QHE_Gui
.Error
)
340 QHE_Gui
.Drag
:SetPoint("TOPLEFT", QHE_Gui
.Error
, "TOPLEFT", 10,-5)
341 QHE_Gui
.Drag
:SetPoint("TOPRIGHT", QHE_Gui
.Error
, "TOPRIGHT", -10,-5)
342 QHE_Gui
.Drag
:SetHeight(8)
343 QHE_Gui
.Drag
:SetHighlightTexture("Interface\\FriendsFrame\\UI-FriendsFrame-HighlightBar")
345 QHE_Gui
.Drag
:SetScript("OnMouseDown", function() QHE_Gui
.Error
:StartMoving() end)
346 QHE_Gui
.Drag
:SetScript("OnMouseUp", function() QHE_Gui
.Error
:StopMovingOrSizing() end)
348 QHE_Gui
.Error
.Done
= CreateFrame("Button", "", QHE_Gui
.Error
, "OptionsButtonTemplate")
349 QHE_Gui
.Error
.Done
:SetText("Close")
350 QHE_Gui
.Error
.Done
:SetPoint("BOTTOMRIGHT", QHE_Gui
.Error
, "BOTTOMRIGHT", -10, 10)
351 QHE_Gui
.Error
.Done
:SetScript("OnClick", QHE_Gui
.ErrorDone
)
353 QHE_Gui
.Error
.Mesg
= QHE_Gui
.Error
:CreateFontString("", "OVERLAY", "GameFontNormalSmall")
354 QHE_Gui
.Error
.Mesg
:SetJustifyH("LEFT")
355 QHE_Gui
.Error
.Mesg
:SetPoint("TOPRIGHT", QHE_Gui
.Error
.Prev
, "TOPLEFT", -10, 0)
356 QHE_Gui
.Error
.Mesg
:SetPoint("LEFT", QHE_Gui
.Error
, "LEFT", 15, 0)
357 QHE_Gui
.Error
.Mesg
:SetHeight(20)
358 QHE_Gui
.Error
.Mesg
:SetText("Select All and Copy the above error message to report this bug.")
360 QHE_Gui
.Error
.Scroll
= CreateFrame("ScrollFrame", "QHE_GUIErrorInputScroll", QHE_Gui
.Error
, "UIPanelScrollFrameTemplate")
361 QHE_Gui
.Error
.Scroll
:SetPoint("TOPLEFT", QHE_Gui
.Error
, "TOPLEFT", 20, -20)
362 QHE_Gui
.Error
.Scroll
:SetPoint("RIGHT", QHE_Gui
.Error
, "RIGHT", -30, 0)
363 QHE_Gui
.Error
.Scroll
:SetPoint("BOTTOM", QHE_Gui
.Error
.Done
, "TOP", 0, 10)
365 QHE_Gui
.Error
.Box
= CreateFrame("EditBox", "QHE_GUIErrorEditBox", QHE_Gui
.Error
.Scroll
)
366 QHE_Gui
.Error
.Box
:SetWidth(500)
367 QHE_Gui
.Error
.Box
:SetHeight(85)
368 QHE_Gui
.Error
.Box
:SetMultiLine(true)
369 QHE_Gui
.Error
.Box
:SetAutoFocus(false)
370 QHE_Gui
.Error
.Box
:SetFontObject(GameFontHighlight
)
371 QHE_Gui
.Error
.Box
:SetScript("OnEscapePressed", QHE_Gui
.ErrorDone
)
372 QHE_Gui
.Error
.Box
:SetScript("OnTextChanged", QHE_Gui
.ErrorUpdate
)
373 QHE_Gui
.Error
.Box
:SetScript("OnEditFocusGained", QHE_Gui
.ErrorClicked
)
375 QHE_Gui
.Error
.Scroll
:SetScrollChild(QHE_Gui
.Error
.Box
)
377 function QuestHelper_ErrorCatcher_ReportError()
378 QHE_Gui
.Error
.selected
= false
379 QHE_Gui
.ErrorUpdate()