-
-
Notifications
You must be signed in to change notification settings - Fork 25
/
core.lua
335 lines (294 loc) · 10.4 KB
/
core.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
local addonName, addon = ...
-----------------------------------------------------------------------
-- Make sure we are prepared
--
local function print(...) _G.print("|cff259054BugSack:|r", ...) end
if not LibStub then
print("BugSack requires LibStub.")
return
end
local L = addon.L
local BugGrabber = BugGrabber
if not BugGrabber then
local msg = L["|cffff4411BugSack requires the |r|cff44ff44!BugGrabber|r|cffff4411 addon, which you can download from the same place you got BugSack. Happy bug hunting!|r"]
local f = CreateFrame("Frame")
f:SetScript("OnEvent", function()
RaidNotice_AddMessage(RaidWarningFrame, msg, {r=1, g=0.3, b=0.1})
print(msg)
f:UnregisterEvent("PLAYER_ENTERING_WORLD")
f:SetScript("OnEvent", nil)
f = nil
end)
f:RegisterEvent("PLAYER_ENTERING_WORLD")
return
end
-- We seem fine, let the world access us.
_G[addonName] = addon
addon.healthCheck = true
-- Sound
local media = LibStub("LibSharedMedia-3.0")
media:Register("sound", "BugSack: Fatality", "Interface\\AddOns\\BugSack\\Media\\error.ogg")
-----------------------------------------------------------------------
-- Utility
--
local onError
do
local lastError = nil
function onError()
if not lastError or GetTime() > (lastError + 2) then
if not addon.db.mute then
local sound = media:Fetch("sound", addon.db.soundMedia)
PlaySoundFile(sound)
end
if addon.db.chatframe then
print(L["There's a bug in your soup!"])
end
lastError = GetTime()
end
-- If the frame is shown, we need to update it.
if (addon.db.auto and not InCombatLockdown()) or (BugSackFrame and BugSackFrame:IsShown()) then
addon:OpenSack()
end
addon:UpdateDisplay()
end
end
-----------------------------------------------------------------------
-- Event handling
--
do
local eventFrame = CreateFrame("Frame", nil, InterfaceOptionsFramePanelContainer)
eventFrame:SetScript("OnEvent", function(self, event, loadedAddon)
if loadedAddon ~= addonName then return end
self:UnregisterEvent("ADDON_LOADED")
local ac = LibStub("AceComm-3.0", true)
if ac then ac:Embed(addon) end
local as = LibStub("AceSerializer-3.0", true)
if as then as:Embed(addon) end
local popup = _G.StaticPopupDialogs
if type(popup) ~= "table" then popup = {} end
if type(popup.BugSackSendBugs) ~= "table" then
popup.BugSackSendBugs = {
text = L["Send all bugs from the currently viewed session (%d) in the sack to the player specified below."],
button1 = L["Send"],
button2 = CLOSE,
timeout = 0,
whileDead = true,
hideOnEscape = true,
hasEditBox = true,
OnAccept = function(self, data)
local recipient = self.editBox:GetText()
addon:SendBugsToUser(recipient, data)
end,
OnShow = function(self)
self.button1:Disable()
end,
EditBoxOnTextChanged = function(self)
local t = self:GetText()
if t:len() > 2 and not t:find("%s") then
self:GetParent().button1:Enable()
else
self:GetParent().button1:Disable()
end
end,
enterClicksFirstButton = true,
--OnCancel = function() show() end, -- Need to wrap it so we don't pass |self| as an error argument to show().
preferredIndex = STATICPOPUP_NUMDIALOGS,
}
end
if type(BugSackDB) ~= "table" then BugSackDB = {} end
local sv = BugSackDB
sv.profileKeys = nil
sv.profiles = nil
if type(sv.mute) ~= "boolean" then sv.mute = false end
if type(sv.auto) ~= "boolean" then sv.auto = false end
if type(sv.chatframe) ~= "boolean" then sv.chatframe = false end
if type(sv.soundMedia) ~= "string" then sv.soundMedia = "BugSack: Fatality" end
if type(sv.fontSize) ~= "string" then sv.fontSize = "GameFontHighlight" end
addon.db = sv
-- Make sure we grab any errors fired before bugsack loaded.
local session = addon:GetErrors(BugGrabber:GetSessionId())
if #session > 0 then onError() end
if addon.RegisterComm then
addon:RegisterComm("BugSack", "OnBugComm")
end
-- Set up our error event handler
BugGrabber.RegisterCallback(addon, "BugGrabber_BugGrabbed", onError)
SlashCmdList.BugSack = function(msg)
msg = msg:lower()
if msg == "show" then
addon:OpenSack()
else
InterfaceOptionsFrame_OpenToCategory(addonName)
InterfaceOptionsFrame_OpenToCategory(addonName)
end
end
SLASH_BugSack1 = "/bugsack"
self:SetScript("OnEvent", nil)
end)
eventFrame:RegisterEvent("ADDON_LOADED")
addon.frame = eventFrame
end
-----------------------------------------------------------------------
-- API
--
function addon:UpdateDisplay()
-- noop, hooked by displays
end
do
local errors = {}
function addon:GetErrors(sessionId)
-- XXX I've never liked this function, maybe a BugGrabber redesign is in order,
-- XXX where we have one subtable in the DB per session ID.
if sessionId then
wipe(errors)
local db = BugGrabber:GetDB()
for i, e in next, db do
if sessionId == e.session then
errors[#errors + 1] = e
end
end
return errors
else
return BugGrabber:GetDB()
end
end
end
do
local function colorStack(ret)
ret = tostring(ret) or "" -- Yes, it gets called with nonstring from somewhere /mikk
ret = ret:gsub("[%.I][%.n][%.t][%.e][%.r]face\\", "")
ret = ret:gsub("%.?%.?%.?\\?AddOns\\", "")
ret = ret:gsub("|([^chHr])", "||%1"):gsub("|$", "||") -- Pipes
ret = ret:gsub("<(.-)>", "|cffffea00<%1>|r") -- Things wrapped in <>
ret = ret:gsub("%[(.-)%]", "|cffffea00[%1]|r") -- Things wrapped in []
ret = ret:gsub("([\"`'])(.-)([\"`'])", "|cff8888ff%1%2%3|r") -- Quotes
ret = ret:gsub(":(%d+)([%S\n])", ":|cff00ff00%1|r%2") -- Line numbers
ret = ret:gsub("([^\\]+%.lua)", "|cffffffff%1|r") -- Lua files
return ret
end
addon.ColorStack = colorStack
local function colorLocals(ret)
ret = tostring(ret) or "" -- Yes, it gets called with nonstring from somewhere /mikk
ret = ret:gsub("[%.I][%.n][%.t][%.e][%.r]face\\", "")
ret = ret:gsub("%.?%.?%.?\\?AddOns\\", "")
ret = ret:gsub("|(%a)", "||%1"):gsub("|$", "||") -- Pipes
ret = ret:gsub("> %@(.-):(%d+)", "> @|cffeda55f%1|r:|cff00ff00%2|r") -- Files/Line Numbers of locals
ret = ret:gsub("(%s-)([%a_%(][%a_%d%*%)]+) = ", "%1|cffffff80%2|r = ") -- Table keys
ret = ret:gsub("= (%-?[%d%p]+)\n", "= |cffff7fff%1|r\n") -- locals: number
ret = ret:gsub("= nil\n", "= |cffff7f7fnil|r\n") -- locals: nil
ret = ret:gsub("= true\n", "= |cffff9100true|r\n") -- locals: true
ret = ret:gsub("= false\n", "= |cffff9100false|r\n") -- locals: false
ret = ret:gsub("= <(.-)>", "= |cffffea00<%1>|r") -- Things wrapped in <>
return ret
end
addon.ColorLocals = colorLocals
local errorFormat = "%dx %s"
local errorFormatLocals = "%dx %s\n\nLocals:\n%s"
function addon:FormatError(err)
if not err.locals then
local s = colorStack(tostring(err.message) .. (err.stack and "\n"..tostring(err.stack) or ""))
local l = colorLocals(tostring(err.locals))
return errorFormat:format(err.counter or -1, s, l)
else
local s = colorStack(tostring(err.message) .. (err.stack and "\n"..tostring(err.stack) or ""))
local l = colorLocals(tostring(err.locals))
return errorFormatLocals:format(err.counter or -1, s, l)
end
end
end
function addon:Reset()
BugGrabber:Reset()
self:UpdateDisplay()
print(L["All stored bugs have been exterminated painfully."])
end
-- Sends the current session errors to another player using AceComm-3.0
function addon:SendBugsToUser(player, session)
if type(player) ~= "string" or player:trim():len() < 2 then
error(L["Player needs to be a valid name."])
end
if not self.Serialize then return end
local errors = self:GetErrors(session)
if not errors or #errors == 0 then return end
local sz = self:Serialize(errors)
self:SendCommMessage("BugSack", sz, "WHISPER", player, "BULK")
print(L["%d bugs have been sent to %s. He must have BugSack to be able to examine them."]:format(#errors, player))
end
function addon:OnBugComm(prefix, message, _, sender)
if prefix ~= "BugSack" or not self.Deserialize then return end
local good, deSz = self:Deserialize(message)
if not good then
print(L["Failure to deserialize incoming data from %s."]:format(sender))
return
end
-- Store recieved errors in the current session database with a source set to the sender
local s = BugGrabber:GetSessionId()
for i, err in next, deSz do
err.source = sender
err.session = s
BugGrabber:StoreError(err)
end
print(L["You've received %d bugs from %s."]:format(#deSz, sender))
wipe(deSz)
deSz = nil
end
--[[
do
local commFormat = "1#%s#%s"
local function transmit(command, target, argument)
SendAddonMessage("BugGrabber", commFormat:format(command, argument), "WHISPER", target)
end
local retrievedErrors = {}
function addon:GetErrorByPlayerAndID(player, id)
if player == playerName then return self:GetErrorByID(id) end
-- This error was linked by someone else, we need to retrieve it from them
-- using the addon communication channel.
if retrievedErrors[id] then return retrievedErrors[id] end
transmit("FETCH", player, id)
print(L.ERROR_INCOMING:format(id, player))
end
local fakeAddon, comm, serializer = nil, nil, nil
local function commBugCatcher(prefix, message, distribution, sender)
local good, deSz = fakeAddon:Deserialize(message)
if not good then
print("damnit")
return
end
retrievedErrors[deSz.originalId] = deSz
end
local function hasTransmitFacilities()
if fakeAddon then return true end
if not serializer then serializer = LibStub("AceSerializer-3.0", true) end
if not comm then comm = LibStub("AceComm-3.0", true) end
if comm and serializer then
fakeAddon = {}
comm:Embed(fakeAddon)
serializer:Embed(fakeAddon)
fakeAddon:RegisterComm("BGBug", commBugCatcher)
return true
end
end
function frame:CHAT_MSG_ADDON(event, prefix, message, distribution, sender)
if prefix ~= "BugGrabber" then return end
local version, command, argument = strsplit("#", message)
if tonumber(version) ~= 1 or not command then return end
if command == "FETCH" then
local errorObject = addon:GetErrorByID(argument)
if errorObject then
if hasTransmitFacilities() then
errorObject.originalId = argument
local sz = fakeAddon:Serialize(errorObject)
fakeAddon:SendCommMessage("BGBug", sz, "WHISPER", sender, "BULK")
else
-- We can only transmit a gimped and sanitized message
transmit("BUG", sender, errorObject.message:sub(1, 240):gsub("#", ""))
end
else
transmit("FAIL", sender, argument)
end
elseif command == "FAIL" then
print(L.ERROR_FAILED_FETCH:format(argument, sender))
elseif command == "BUG" then
print(L.CRIPPLED_ERROR:format(sender, argument))
end
end
end]]