You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
560 lines
19 KiB
560 lines
19 KiB
-- Creating a way to conveniently reload UI since we can't neutralize taint for good.
|
|
|
|
local InCombatLockdown = InCombatLockdown;
|
|
local time = time;
|
|
|
|
local SHOW_POPUP = true;
|
|
|
|
local AlertFrame;
|
|
local ErrorDB;
|
|
|
|
local EventListener = CreateFrame("Frame");
|
|
EventListener:RegisterEvent("PLAYER_ENTERING_WORLD");
|
|
|
|
local IsAddOnLoaded = C_AddOns.IsAddOnLoaded;
|
|
|
|
|
|
--[[
|
|
local INCONSEQUENTIAL_ERROR = {
|
|
["CopyToClipboard()"] = true,
|
|
--Narcissus Dressing Room: Caused by Clicking "Copy to Clipboard" if the dressing room was initialized by player clicking "Dressing Room" on Narcissus minimap flyout menu.
|
|
--No way around this but we already provided an alternative to copy outfit string - showing an editbox where player can press Ctrl+C to copy.
|
|
};
|
|
--]]
|
|
|
|
local HIGH_PRIORITY_ERROR = {
|
|
--Updates: Only shows pop-up when player can't use abilities or items.
|
|
["UseAction()"] = true,
|
|
["UseInventoryItem()"] = true,
|
|
--The "unable to use item in the Backpack" error says Unknown() was blocked
|
|
--/script local a = C_Container.UseContainerItem; C_Container.UseContainerItem = a;
|
|
};
|
|
|
|
local function ClearDatedData()
|
|
if NarciStatisticsDB and NarciStatisticsDB.AddOnActionForbidden then
|
|
ErrorDB = NarciStatisticsDB.AddOnActionForbidden;
|
|
if not ErrorDB.addons then
|
|
return
|
|
end
|
|
local currentTime = time();
|
|
local fromIndex;
|
|
local numRecords;
|
|
local maxTimeDiff = 7 * 86400;
|
|
for addonName, data in pairs(ErrorDB.addons) do
|
|
if data.errorTime then
|
|
fromIndex = nil;
|
|
numRecords = #data.errorTime;
|
|
for i = 1, numRecords do
|
|
if currentTime - data.errorTime[i] < maxTimeDiff then
|
|
fromIndex = i;
|
|
break
|
|
end
|
|
end
|
|
fromIndex = fromIndex or (numRecords + 1);
|
|
if fromIndex > 1 then
|
|
local numNewData = numRecords - fromIndex + 1;
|
|
if numNewData > 0 then
|
|
for i = 1, numNewData do
|
|
data.errorTime[i] = data.errorTime[i + fromIndex - 1];
|
|
end
|
|
|
|
for i = numNewData + 1, numRecords do
|
|
data.errorTime[i] = nil;
|
|
end
|
|
else
|
|
data.errorTime = nil;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function CountErrorTimes(addonName, days)
|
|
local count = 0;
|
|
if ErrorDB and ErrorDB.addons and ErrorDB.addons[addonName] then
|
|
local record = ErrorDB.addons[addonName].errorTime;
|
|
if record then
|
|
local currentTime = time();
|
|
local maxTimeDiff = days * 86400;
|
|
for i, t in ipairs(record) do
|
|
if currentTime - t < maxTimeDiff then
|
|
count = count + 1;
|
|
else
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return count
|
|
end
|
|
|
|
local function SetupAlertFrame(addonName, functionName)
|
|
addonName = addonName or "Unknown AddOn";
|
|
functionName = functionName or "Unknown Function";
|
|
|
|
local currentTime = time();
|
|
|
|
if not ErrorDB then
|
|
if not NarciStatisticsDB then
|
|
NarciStatisticsDB = {};
|
|
end
|
|
if not NarciStatisticsDB.AddOnActionForbidden then
|
|
NarciStatisticsDB.AddOnActionForbidden = {
|
|
addons = {},
|
|
timeLastError = currentTime,
|
|
};
|
|
end
|
|
ErrorDB = NarciStatisticsDB.AddOnActionForbidden;
|
|
end
|
|
|
|
local lastTime = ErrorDB.timeLastError or currentTime;
|
|
ErrorDB.timeLastError = currentTime;
|
|
|
|
if not ErrorDB.addons[addonName] then
|
|
ErrorDB.addons[addonName] = {
|
|
count = 0;
|
|
};
|
|
end
|
|
local addonErrorData = ErrorDB.addons[addonName];
|
|
addonErrorData.count = addonErrorData.count + 1;
|
|
addonErrorData.timeLastError = currentTime;
|
|
|
|
if not addonErrorData.errorTime then
|
|
addonErrorData.errorTime = {};
|
|
end
|
|
table.insert(addonErrorData.errorTime, currentTime);
|
|
|
|
if SHOW_POPUP and HIGH_PRIORITY_ERROR[functionName] then
|
|
if not AlertFrame then
|
|
AlertFrame = CreateFrame("Frame", nil, UIParent, "NarciGenericTaintAlertFrameTemplate");
|
|
end
|
|
local daySinceLastError = math.floor((currentTime - lastTime)/86400);
|
|
AlertFrame:SetupDescription(addonName, functionName);
|
|
AlertFrame:ShowFrame();
|
|
AlertFrame:PlayDaysAnimation(daySinceLastError > 0);
|
|
end
|
|
end
|
|
|
|
local function EventListener_OnEvent(self, event, ...)
|
|
if event == "ADDON_ACTION_FORBIDDEN" then
|
|
SetupAlertFrame(...);
|
|
end
|
|
end
|
|
|
|
EventListener:SetScript("OnEvent", function(self, event, ...)
|
|
self:UnregisterEvent(event); --PLAYER_ENTERING_WORLD
|
|
ClearDatedData();
|
|
self:RegisterEvent("ADDON_ACTION_FORBIDDEN");
|
|
self:SetScript("OnEvent", EventListener_OnEvent);
|
|
|
|
if IsAddOnLoaded("BugSack") then
|
|
SHOW_POPUP = false;
|
|
end
|
|
end);
|
|
|
|
|
|
local BUTTON_TOP_PADDING = 14;
|
|
local PARAGRAPH_PADDING = 8;
|
|
local POPUP_TEXT_PADDING = 16;
|
|
local FRAME_WIDTH = 320;
|
|
local CLIPBOARD_HEIGHT = 160;
|
|
|
|
NarciGenericTaintAlertFrameMixin = {};
|
|
|
|
function NarciGenericTaintAlertFrameMixin:OnLoad()
|
|
self.CloseButton:SetScript("OnClick", function()
|
|
self:HideFrame();
|
|
end);
|
|
self.ShowMoreButton:SetScript("OnClick", function()
|
|
self:ShowAdditionalOptions(not self.ReportButton:IsShown());
|
|
end);
|
|
|
|
self:SetWidth(FRAME_WIDTH);
|
|
self.reloadButtonWidth = FRAME_WIDTH - 26*2 - 32 -12;
|
|
self.ReloadButton:SetButtonWidth(self.reloadButtonWidth);
|
|
self.ReloadButton:SetButtonText(RELOADUI or "Reload UI");
|
|
self.ReloadButton:SetScript("OnClick", C_UI.Reload);
|
|
|
|
self.LeaderboardButton:SetButtonText("Leaderboard");
|
|
self.LeaderboardButton:SetScript("OnClick", function()
|
|
self:TogggleLeaderboard();
|
|
end);
|
|
|
|
self.ReportButton:SetButtonText("Report");
|
|
self.ReportButton:SetScript("OnClick", function()
|
|
self:TogggleReport();
|
|
end);
|
|
|
|
local buttonWidth = (FRAME_WIDTH - 26*2 - 12) * 0.5;
|
|
self.LeaderboardButton:SetButtonWidth(buttonWidth);
|
|
self.ReportButton:SetButtonWidth(buttonWidth);
|
|
|
|
--self:SetupDescription("DoSomething()");
|
|
|
|
if InCombatLockdown() then
|
|
self.ReloadButton:Disable();
|
|
end
|
|
end
|
|
|
|
local function AlertFrame_OnKeyDown(self, key)
|
|
if key == "ESCAPE" then
|
|
self:HideFrame();
|
|
self:SetPropagateKeyboardInput(false);
|
|
else
|
|
self:SetPropagateKeyboardInput(true);
|
|
end
|
|
end
|
|
|
|
function NarciGenericTaintAlertFrameMixin:OnShow()
|
|
self:RegisterEvent("PLAYER_REGEN_DISABLED");
|
|
self:RegisterEvent("PLAYER_REGEN_ENABLED");
|
|
self:SetScript("OnKeyDown", AlertFrame_OnKeyDown);
|
|
if InCombatLockdown() then
|
|
self.ReloadButton:Disable();
|
|
else
|
|
self.ReloadButton:Enable();
|
|
end
|
|
end
|
|
|
|
function NarciGenericTaintAlertFrameMixin:OnHide()
|
|
self:UnregisterEvent("PLAYER_REGEN_DISABLED");
|
|
self:UnregisterEvent("PLAYER_REGEN_ENABLED");
|
|
self:SetScript("OnKeyDown", nil);
|
|
end
|
|
|
|
function NarciGenericTaintAlertFrameMixin:OnEvent(event, ...)
|
|
if event == "PLAYER_REGEN_DISABLED" then
|
|
self.ReloadButton:Disable();
|
|
elseif event == "PLAYER_REGEN_ENABLED" then
|
|
self.ReloadButton:Enable();
|
|
end
|
|
end
|
|
|
|
function NarciGenericTaintAlertFrameMixin:ReleaseFontStrings()
|
|
self.numTexts = 0;
|
|
if self.fontStringPool then
|
|
for i, fs in ipairs(self.fontStringPool) do
|
|
fs:Hide();
|
|
fs:SetText("");
|
|
if i ~= 1 then
|
|
fs:ClearAllPoints();
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function NarciGenericTaintAlertFrameMixin:AcquireAndSetFontString(text)
|
|
if not self.fontStringPool then
|
|
self.fontStringPool = {};
|
|
end
|
|
|
|
local index = self.numTexts + 1;
|
|
self.numTexts = index;
|
|
local fs = self.fontStringPool[index];
|
|
if not fs then
|
|
fs = self:CreateFontString(nil, "OVERLAY", "GameFontHighlight");
|
|
fs:SetTextColor(0.8, 0.8, 0.8);
|
|
fs:SetSpacing(2);
|
|
fs:SetWidth(FRAME_WIDTH - 58);
|
|
fs:SetJustifyH("LEFT");
|
|
fs:SetJustifyV("TOP");
|
|
self.fontStringPool[index] = fs;
|
|
if index == 1 then
|
|
fs:SetPoint("TOP", self.FontStringSinceLastError, "BOTTOM", 0, -PARAGRAPH_PADDING);
|
|
end
|
|
end
|
|
if index > 1 then
|
|
fs:SetPoint("TOP", self.fontStringPool[index - 1], "BOTTOM", 0, -PARAGRAPH_PADDING);
|
|
end
|
|
fs:Show();
|
|
fs:SetText(text);
|
|
end
|
|
|
|
function NarciGenericTaintAlertFrameMixin:SetupDescription(addonName, blockedAction)
|
|
self:ReleaseFontStrings();
|
|
local descText;
|
|
if blockedAction then
|
|
descText = string.format("- Blocked function: |cffffffff%s (%s)|r ", blockedAction, addonName);
|
|
self:AcquireAndSetFontString(descText);
|
|
end
|
|
local numErrorsToday = CountErrorTimes(addonName, 1);
|
|
if numErrorsToday > 4 then
|
|
local countAlert = string.format("- |cffffffff%s|r has been mentioned in the error message |cffffffff%s|r times today. Although it may not be the root of the problem.", addonName, numErrorsToday);
|
|
self:AcquireAndSetFontString(countAlert);
|
|
end
|
|
self.addonName = addonName;
|
|
self.blockedAction = blockedAction;
|
|
self:UpdateLayout();
|
|
end
|
|
|
|
function NarciGenericTaintAlertFrameMixin:UpdateLayout()
|
|
local bottomFontString;
|
|
if self.numTexts > 0 then
|
|
bottomFontString = self.fontStringPool[self.numTexts];
|
|
else
|
|
bottomFontString = self.FontStringSinceLastError;
|
|
end
|
|
|
|
|
|
local top = self:GetTop();
|
|
local descBottom = bottomFontString:GetBottom();
|
|
|
|
self.ReloadButton:ClearAllPoints();
|
|
self.ReloadButton:SetPoint("TOPLEFT", self, "TOPLEFT", 26, descBottom - top -BUTTON_TOP_PADDING);
|
|
local bottom = (self.LeaderboardButton:IsShown() and self.LeaderboardButton:GetBottom()) or self.ReloadButton:GetBottom();
|
|
self:SetHeight(math.floor(top - bottom + 0.5) + 26);
|
|
end
|
|
|
|
function NarciGenericTaintAlertFrameMixin:HideFrame()
|
|
self:Hide();
|
|
end
|
|
|
|
function NarciGenericTaintAlertFrameMixin:ShowFrame()
|
|
self:ClearAllPoints();
|
|
if StaticPopup1:IsShown() then
|
|
self:SetPoint("TOP", StaticPopup1, "BOTTOM", 0, -24);
|
|
else
|
|
self:SetPoint("TOP", UIParent, "TOP", 0, -135);
|
|
end
|
|
self:Show();
|
|
end
|
|
|
|
function NarciGenericTaintAlertFrameMixin:PlayDaysAnimation(state)
|
|
self.DayFrame:StopAnimating();
|
|
if state then
|
|
self.DayFrame.FontStringNumber.FlyDown:Play();
|
|
self.DayFrame.FontStringOldNumber.FlyDown:Play();
|
|
end
|
|
end
|
|
|
|
function NarciGenericTaintAlertFrameMixin:ShowAdditionalOptions(state)
|
|
if state then
|
|
self.LeaderboardButton:Show();
|
|
self.ReportButton:Show();
|
|
self.ShowMoreButton.Icon:SetTexCoord(0, 1, 1, 0);
|
|
else
|
|
self.LeaderboardButton:Hide();
|
|
self.ReportButton:Hide();
|
|
self.ShowMoreButton.Icon:SetTexCoord(0, 1, 0, 1);
|
|
end
|
|
self:UpdateLayout();
|
|
end
|
|
|
|
function NarciGenericTaintAlertFrameMixin:GetPopupFrame()
|
|
if not self.PopupFrame then
|
|
local popup = CreateFrame("Frame", nil, self);
|
|
self.PopupFrame = popup;
|
|
popup:SetSize(96, 96);
|
|
popup:SetPoint("TOP", self, "BOTTOM", 0, -8);
|
|
NarciAPI.NineSliceUtil.SetUp(popup, "classTalentTraitTransparent", "backdrop");
|
|
popup:SetScript("OnShow", function(f)
|
|
f:RegisterEvent("GLOBAL_MOUSE_DOWN");
|
|
end);
|
|
popup:SetScript("OnHide", function(f)
|
|
f:Hide();
|
|
f:RegisterEvent("GLOBAL_MOUSE_DOWN");
|
|
f.TextLeft:SetText("");
|
|
f.TextRight:SetText("");
|
|
f.Header:SetText("");
|
|
end);
|
|
popup:SetScript("OnEvent", function(f, event, ...)
|
|
if not (f:IsMouseOver() or (f.parentButton and f.parentButton:IsMouseOver())) then
|
|
f:Hide();
|
|
end
|
|
end);
|
|
|
|
popup.TextLeft = popup:CreateFontString(nil, "OVERLAY", "GameFontHighlight");
|
|
popup.TextLeft:SetTextColor(1, 1, 1);
|
|
popup.TextLeft:SetPoint("TOPLEFT", popup, "TOPLEFT", POPUP_TEXT_PADDING, -2*POPUP_TEXT_PADDING);
|
|
popup.TextLeft:SetJustifyH("LEFT");
|
|
popup.TextLeft:SetJustifyV("TOP");
|
|
popup.TextLeft:SetSpacing(4);
|
|
|
|
popup.TextRight = popup:CreateFontString(nil, "OVERLAY", "GameFontHighlight");
|
|
popup.TextRight:SetTextColor(1, 1, 1);
|
|
popup.TextRight:SetPoint("TOPRIGHT", popup, "TOPRIGHT", -POPUP_TEXT_PADDING, -2*POPUP_TEXT_PADDING);
|
|
popup.TextRight:SetJustifyH("RIGHT");
|
|
popup.TextRight:SetJustifyV("TOP");
|
|
popup.TextRight:SetSpacing(4);
|
|
|
|
popup.Header = popup:CreateFontString(nil, "OVERLAY", "GameFontBlackSmall");
|
|
popup.Header:SetTextColor(0.5, 0.5, 0.5);
|
|
popup.Header:SetPoint("TOP", popup, "TOP", 0, -POPUP_TEXT_PADDING);
|
|
popup.Header:SetJustifyH("CENTER");
|
|
popup.Header:SetJustifyV("TOP");
|
|
|
|
popup.Clipboard = CreateFrame("Frame", nil, popup, "NarciScrollEditBoxTemplate");
|
|
popup.Clipboard:SetWidth(FRAME_WIDTH - 2*POPUP_TEXT_PADDING);
|
|
popup.Clipboard:SetHeight(CLIPBOARD_HEIGHT);
|
|
popup.Clipboard:ClearAllPoints();
|
|
popup.Clipboard:SetPoint("TOP", popup, "TOP", 0, -2*POPUP_TEXT_PADDING);
|
|
popup.Clipboard:Hide();
|
|
popup.Clipboard:SetFontObject(GameFontHighlight);
|
|
popup.Clipboard:SetFontColor(1, 1, 1);
|
|
|
|
popup:Hide();
|
|
end
|
|
return self.PopupFrame
|
|
end
|
|
|
|
local function ConcatenateTexts(data)
|
|
local leftText, rightText;
|
|
for i = 1, #data do
|
|
if i == 1 then
|
|
leftText = data[i][1];
|
|
rightText = data[i][2];
|
|
else
|
|
if i%2 == 0 then
|
|
leftText = leftText.."\n|cffcccccc"..data[i][1].."|r";
|
|
rightText = rightText.."\n|cffcccccc"..data[i][2].."|r";
|
|
else
|
|
leftText = leftText.."\n"..data[i][1];
|
|
rightText = rightText.."\n"..data[i][2];
|
|
end
|
|
end
|
|
end
|
|
return leftText, rightText;
|
|
end
|
|
|
|
function NarciGenericTaintAlertFrameMixin:TogggleLeaderboard()
|
|
local popup = self:GetPopupFrame();
|
|
popup.parentButton = self.LeaderboardButton;
|
|
if popup:IsShown() then
|
|
popup:Hide();
|
|
return
|
|
else
|
|
popup:Show();
|
|
popup.Clipboard:Hide();
|
|
popup.Header:SetText("NUMBERS OF ERRORS OVER THE LAST WEEK");
|
|
|
|
local stats = {};
|
|
|
|
local numRecords;
|
|
for addonName, data in pairs(ErrorDB.addons) do
|
|
if data.errorTime then
|
|
numRecords = #data.errorTime;
|
|
if numRecords > 0 then
|
|
table.insert(stats, {addonName, numRecords});
|
|
end
|
|
end
|
|
end
|
|
|
|
if #stats == 0 then
|
|
stats = {"None", 0};
|
|
end
|
|
|
|
local function SortMethod(a, b)
|
|
if a[2] == b[2] then
|
|
return a[1] < b[1];
|
|
else
|
|
return a[2] > b[2]
|
|
end
|
|
end
|
|
table.sort(stats, SortMethod);
|
|
local leftText, rightText = ConcatenateTexts(stats);
|
|
popup.TextLeft:SetText(leftText);
|
|
popup.TextRight:SetText(rightText);
|
|
local width = math.floor( math.max(popup.Header:GetWrappedWidth(), popup.TextLeft:GetWrappedWidth() + popup.TextRight:GetWrappedWidth() + 16) + 0.5) + 2*POPUP_TEXT_PADDING;
|
|
local height = math.floor(popup:GetTop() - popup.TextLeft:GetBottom() + POPUP_TEXT_PADDING + 0.5);
|
|
popup:SetSize(width, height);
|
|
end
|
|
end
|
|
|
|
local function GenerateReport()
|
|
local format = string.format;
|
|
local GetAddOnInfo = C_AddOns.GetAddOnInfo;
|
|
local GetAddOnMetadata = C_AddOns.GetAddOnMetadata;
|
|
|
|
local line1 = format("Date: %s", date());
|
|
local osName = (IsWindowsClient() and "Windows") or (IsMacClient() and "Mac") or (IsLinuxClient() and "Linux");
|
|
local line2 = format("OS: %s", osName);
|
|
local line3 = format("Region: %s (%s)", GetCurrentRegion(), GetCurrentRegionName());
|
|
local line4 = format("Locale: %s", GetLocale());
|
|
|
|
local line5 = format("Build Info: %s (%s) %s (TOC Version %s)" , GetBuildInfo());
|
|
if IsPublicBuild() then
|
|
line5 = line5 .. " Public Build";
|
|
end
|
|
if IsTestBuild() then
|
|
line5 = line5 .. " Test Build";
|
|
end
|
|
|
|
local specName, _;
|
|
local primaryTalentTree = GetSpecialization();
|
|
local sex = UnitSex("player");
|
|
local className = UnitClass("player");
|
|
if (primaryTalentTree) then
|
|
_, specName = GetSpecializationInfo(primaryTalentTree, nil, nil, nil, sex);
|
|
end
|
|
local level = UnitLevel("player");
|
|
local raceName, raceFile, raceID = UnitRace("player");
|
|
local line6 = format("Character: Level %s %s (%s) %s %s", level, raceName, raceID, specName or "", className);
|
|
|
|
local mapName;
|
|
local mapID = C_Map.GetBestMapForUnit("player");
|
|
if mapID then
|
|
local mapInfo = C_Map.GetMapInfo(mapID);
|
|
if mapInfo then
|
|
mapName = mapInfo.name .. " ("..mapID..")";
|
|
end
|
|
end
|
|
local zoneName = GetMinimapZoneText();
|
|
if zoneName then
|
|
if mapName and zoneName ~= mapName then
|
|
mapName = mapName .. ", "..zoneName
|
|
else
|
|
mapName = zoneName;
|
|
end
|
|
end
|
|
local line7 = format("Location: %s", mapName);
|
|
|
|
local errorAddOnName = AlertFrame.addonName;
|
|
local addonVersion = GetAddOnMetadata(errorAddOnName, "version");
|
|
if addonVersion then
|
|
errorAddOnName = errorAddOnName.." ("..addonVersion..")";
|
|
end
|
|
local line8 = format("Blocked Action: %s %s", AlertFrame.blockedAction, errorAddOnName);
|
|
|
|
local report = string.join("\n", line8, "", line1, line2, line3, line4, line5, line6, line7, "", "Loaded AddOns:");
|
|
|
|
|
|
local addonList;
|
|
local addonName;
|
|
for i = 1, C_AddOns.GetNumAddOns() do
|
|
if IsAddOnLoaded(i) then
|
|
addonName = GetAddOnInfo(i);
|
|
addonVersion = GetAddOnMetadata(i, "version");
|
|
if addonVersion then
|
|
addonName = addonName.." ("..addonVersion..")";
|
|
end
|
|
if addonList then
|
|
addonList = addonList.."\n"..addonName;
|
|
else
|
|
addonList = addonName;
|
|
end
|
|
end
|
|
end
|
|
if addonList then
|
|
report = report .. "\n" ..addonList
|
|
end
|
|
return report
|
|
end
|
|
|
|
function NarciGenericTaintAlertFrameMixin:TogggleReport()
|
|
local popup = self:GetPopupFrame();
|
|
popup.parentButton = self.ReportButton;
|
|
if popup:IsShown() then
|
|
popup:Hide();
|
|
else
|
|
popup:Show();
|
|
popup.TextLeft:Hide();
|
|
popup.TextRight:Hide();
|
|
popup.Clipboard:Show();
|
|
popup:SetWidth(FRAME_WIDTH);
|
|
popup:SetHeight( math.floor(popup:GetTop() - popup.Clipboard:GetBottom() + POPUP_TEXT_PADDING + 0.5) );
|
|
popup.Header:SetText(Narci.L["Press Copy Yellow"]);
|
|
popup.Clipboard:SetText( GenerateReport() );
|
|
popup.Clipboard:SetFocus();
|
|
end
|
|
end
|