diff options
author | Linnea Gräf <nea@nea.moe> | 2024-01-10 20:05:21 +0100 |
---|---|---|
committer | Linnea Gräf <nea@nea.moe> | 2024-01-10 20:26:02 +0100 |
commit | df4f308005528111f8417c03efe95f1c2cee36bd (patch) | |
tree | 875398ea290099505e299f234b405b126460864c | |
parent | 5eb0881e1b470d33a8e02f7d877f9b225516789b (diff) | |
download | NotEnoughUpdates-df4f308005528111f8417c03efe95f1c2cee36bd.tar.gz NotEnoughUpdates-df4f308005528111f8417c03efe95f1c2cee36bd.tar.bz2 NotEnoughUpdates-df4f308005528111f8417c03efe95f1c2cee36bd.zip |
Add crash recovery page to PV
3 files changed, 227 insertions, 33 deletions
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/CrashRecoveryPage.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/CrashRecoveryPage.java new file mode 100644 index 00000000..d68f4a26 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/CrashRecoveryPage.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2024 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.profileviewer; + +import io.github.moulberry.moulconfig.internal.ClipboardUtils; +import io.github.moulberry.notenoughupdates.core.util.render.RenderUtils; +import io.github.moulberry.notenoughupdates.util.Rectangle; +import io.github.moulberry.notenoughupdates.util.Utils; +import lombok.val; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.crash.CrashReport; +import net.minecraft.init.Bootstrap; + +import java.io.IOException; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; + +public class CrashRecoveryPage extends GuiProfileViewerPage { + private final Exception exception; + private final String timestamp; + private final GuiProfileViewer.ProfileViewerPage lastViewedPage; + private int offset = 0; + private final CrashReport crashReport; + + public CrashRecoveryPage( + GuiProfileViewer instance, + Exception exception, + GuiProfileViewer.ProfileViewerPage lastViewedPage + ) { + super(instance); + this.lastViewedPage = lastViewedPage; + this.timestamp = DateTimeFormatter.ISO_ZONED_DATE_TIME.format(OffsetDateTime.ofInstant( + Instant.now(), + ZoneId.systemDefault() + )); + this.exception = exception; + val profile = GuiProfileViewer.getProfile(); + crashReport = new CrashReport("NEU Profile Viewer crashed", exception); + val parameters = crashReport.makeCategory("Profile Viewer Parameters"); + + parameters.addCrashSection("Viewed Player", (profile == null ? "null" : profile.getUuid())); + parameters.addCrashSection("Viewed Profile", GuiProfileViewer.getProfileName()); + parameters.addCrashSection("Timestamp", timestamp); + parameters.addCrashSection("Last Viewed Page", lastViewedPage); + Bootstrap.printToSYSOUT(crashReport.getCompleteReport()); + } + + @Override + public void drawPage(int mouseX, int mouseY, float partialTicks) { + GlStateManager.pushMatrix(); + GlStateManager.translate( + GuiProfileViewer.getGuiLeft() + getInstance().sizeX / 2f, + GuiProfileViewer.getGuiTop() + 20, + 0 + ); + offset = 20; + + drawTitle(); + + drawString("§cLooked like your profile viewer crashed."); + drawString("§cPlease immediately send a screenshot of this screen into #neu-support."); + drawString("§cJoin our support server at §adiscord.gg/moulberry§c."); + + val profile = GuiProfileViewer.getProfile(); + drawString("Viewed Player: " + (profile == null ? "null" : profile.getUuid())); + drawString("Viewed Profile: " + GuiProfileViewer.getProfileName()); + drawString("Timestamp: " + timestamp); + + drawString(""); + drawString(exception.toString()); + for (StackTraceElement stackTraceElement : exception.getStackTrace()) { + if (offset >= getInstance().sizeY - 50) break; + drawString(stackTraceElement.toString()); + } + + GlStateManager.popMatrix(); + + val buttonCoords = getButtonCoordinates(); + RenderUtils.drawFloatingRectWithAlpha( + buttonCoords.getX(), buttonCoords.getY(), + buttonCoords.getWidth(), buttonCoords.getHeight(), + 100, true + ); + Utils.drawStringCenteredScaledMaxWidth( + "Copy Report", + buttonCoords.getCenterX(), + buttonCoords.getCenterY(), + false, + buttonCoords.getWidth(), + -1 + ); + + } + + @Override + public boolean mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException { + if (getButtonCoordinates().contains(mouseX, mouseY) && mouseButton == 0) { + ClipboardUtils.copyToClipboard(crashReport.getCompleteReport()); + } + return super.mouseClicked(mouseX, mouseY, mouseButton); + } + + private Rectangle getButtonCoordinates() { + return new Rectangle( + GuiProfileViewer.getGuiLeft() + getInstance().sizeX / 2 - 40, + GuiProfileViewer.getGuiTop() + getInstance().sizeY - 30, + 80, 12 + ); + } + + private void drawString(String text) { + Utils.drawStringCenteredScaledMaxWidth(text, 0, 0, false, getInstance().sizeX - 20, -1); + val spacing = Minecraft.getMinecraft().fontRendererObj.FONT_HEIGHT + 2; + GlStateManager.translate(0, spacing, 0); + offset += spacing; + } + + private void drawTitle() { + GlStateManager.pushMatrix(); + GlStateManager.scale(2, 2, 2); + Utils.drawStringCenteredScaledMaxWidth("§cKA-BOOM!", 0, 0, false, getInstance().sizeX / 2, -1); + GlStateManager.popMatrix(); + val spacing = Minecraft.getMinecraft().fontRendererObj.FONT_HEIGHT * 2 + 6; + GlStateManager.translate(0, spacing, 0); + offset += spacing; + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/GuiProfileViewer.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/GuiProfileViewer.java index 86db77ca..faff8c5f 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/GuiProfileViewer.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/GuiProfileViewer.java @@ -195,23 +195,28 @@ public class GuiProfileViewer extends GuiScreen { playerNameTextField = new GuiElementTextField(getDisplayName(), GuiElementTextField.SCALE_TEXT); playerNameTextField.setSize(100, 20); - if (currentPage == ProfileViewerPage.LOADING) { + if (currentPage.isTransient()) { currentPage = ProfileViewerPage.BASIC; } - pages.put(ProfileViewerPage.BASIC, new BasicPage(this)); - pages.put(ProfileViewerPage.DUNGEON, new DungeonPage(this)); - pages.put(ProfileViewerPage.EXTRA, new ExtraPage(this)); - pages.put(ProfileViewerPage.INVENTORIES, new InventoriesPage(this)); - pages.put(ProfileViewerPage.COLLECTIONS, new CollectionsPage(this)); - pages.put(ProfileViewerPage.PETS, new PetsPage(this)); - pages.put(ProfileViewerPage.MINING, new MiningPage(this)); - pages.put(ProfileViewerPage.BINGO, new BingoPage(this)); - pages.put(ProfileViewerPage.TROPHY_FISH, new TrophyFishPage(this)); - pages.put(ProfileViewerPage.BESTIARY, new BestiaryPage(this)); - pages.put(ProfileViewerPage.CRIMSON_ISLE, new CrimsonIslePage(this)); - pages.put(ProfileViewerPage.MUSEUM, new MuseumPage(this)); - pages.put(ProfileViewerPage.RIFT, new RiftPage(this)); + try { + pages.put(ProfileViewerPage.BASIC, new BasicPage(this)); + pages.put(ProfileViewerPage.DUNGEON, new DungeonPage(this)); + pages.put(ProfileViewerPage.EXTRA, new ExtraPage(this)); + pages.put(ProfileViewerPage.INVENTORIES, new InventoriesPage(this)); + pages.put(ProfileViewerPage.COLLECTIONS, new CollectionsPage(this)); + pages.put(ProfileViewerPage.PETS, new PetsPage(this)); + pages.put(ProfileViewerPage.MINING, new MiningPage(this)); + pages.put(ProfileViewerPage.BINGO, new BingoPage(this)); + pages.put(ProfileViewerPage.TROPHY_FISH, new TrophyFishPage(this)); + pages.put(ProfileViewerPage.BESTIARY, new BestiaryPage(this)); + pages.put(ProfileViewerPage.CRIMSON_ISLE, new CrimsonIslePage(this)); + pages.put(ProfileViewerPage.MUSEUM, new MuseumPage(this)); + pages.put(ProfileViewerPage.RIFT, new RiftPage(this)); + } catch (Exception ex) { + pages.put(ProfileViewerPage.CRASH_RECOVERY, new CrashRecoveryPage(this, ex, currentPage)); + currentPage = ProfileViewerPage.CRASH_RECOVERY; + } } public static int getGuiLeft() { @@ -240,15 +245,17 @@ public class GuiProfileViewer extends GuiScreen { if (startTime == 0) startTime = currentTime; ProfileViewerPage page = currentPage; - if (profile == null) { - page = ProfileViewerPage.INVALID_NAME; - } else if (profile.getOrLoadSkyblockProfiles(null) == null) { - page = ProfileViewerPage.LOADING; - } + if (page == ProfileViewerPage.CRASH_RECOVERY) { + if (profile == null) { + page = ProfileViewerPage.INVALID_NAME; + } else if (profile.getOrLoadSkyblockProfiles(null) == null) { + page = ProfileViewerPage.LOADING; + } - if (profile != null && profile.getLatestProfileName() == null && - !profile.getUpdatingSkyblockProfilesState().get()) { - page = ProfileViewerPage.NO_SKYBLOCK; + if (profile != null && profile.getLatestProfileName() == null && + !profile.getUpdatingSkyblockProfilesState().get()) { + page = ProfileViewerPage.NO_SKYBLOCK; + } } if (profile != null) { @@ -267,7 +274,8 @@ public class GuiProfileViewer extends GuiScreen { if (NotEnoughUpdates.INSTANCE.config.profileViewer.alwaysShowBingoTab) { showBingoPage = true; } else { - showBingoPage = selectedProfile != null && selectedProfile.getGamemode() != null && selectedProfile.getGamemode().equals("bingo"); + showBingoPage = selectedProfile != null && selectedProfile.getGamemode() != null && + selectedProfile.getGamemode().equals("bingo"); } if (!showBingoPage && currentPage == ProfileViewerPage.BINGO) { @@ -322,7 +330,10 @@ public class GuiProfileViewer extends GuiScreen { if (selectedProfile != null && selectedProfile.getGamemode() != null) { GlStateManager.color(1, 1, 1, 1); - ResourceLocation gamemodeIcon = gamemodeToIcon.getOrDefault(selectedProfile.getGamemode(), gamemodeIconUnknown); + ResourceLocation gamemodeIcon = gamemodeToIcon.getOrDefault( + selectedProfile.getGamemode(), + gamemodeIconUnknown + ); Minecraft.getMinecraft().getTextureManager().bindTexture(gamemodeIcon); Utils.drawTexturedRect(guiLeft - 16 - 5, guiTop + sizeY + 5, 16, 16, GL11.GL_NEAREST); } @@ -394,7 +405,10 @@ public class GuiProfileViewer extends GuiScreen { if (selectedProfile != null && selectedProfile.getGamemode() != null) { GlStateManager.color(1, 1, 1, 1); - ResourceLocation gamemodeIcon = gamemodeToIcon.getOrDefault(selectedProfile.getGamemode(), gamemodeIconUnknown); + ResourceLocation gamemodeIcon = gamemodeToIcon.getOrDefault( + selectedProfile.getGamemode(), + gamemodeIconUnknown + ); Minecraft.getMinecraft().getTextureManager().bindTexture(gamemodeIcon); Utils.drawTexturedRect( guiLeft - 16 - 5, @@ -412,7 +426,14 @@ public class GuiProfileViewer extends GuiScreen { GlStateManager.color(1, 1, 1, 1); if (pages.containsKey(page)) { - pages.get(page).drawPage(mouseX, mouseY, partialTicks); + try { + pages.get(page).drawPage(mouseX, mouseY, partialTicks); + } catch (Exception ex) { + if (page == ProfileViewerPage.CRASH_RECOVERY) + throw ex; // Rethrow if the exception handler crashes. + pages.put(ProfileViewerPage.CRASH_RECOVERY, new CrashRecoveryPage(this, ex, currentPage)); + currentPage = ProfileViewerPage.CRASH_RECOVERY; + } } else { switch (page) { case LOADING: @@ -462,7 +483,8 @@ public class GuiProfileViewer extends GuiScreen { guiLeft + sizeX / 2f, guiTop + 151, true, 0 ); Utils.drawStringCentered( - EnumChatFormatting.YELLOW + String.valueOf(EnumChatFormatting.BOLD) + "What are you doing with your life?", + EnumChatFormatting.YELLOW + String.valueOf(EnumChatFormatting.BOLD) + + "What are you doing with your life?", guiLeft + sizeX / 2f, guiTop + 161, true, 0 ); if (timeDiff > 600000) { @@ -691,7 +713,7 @@ public class GuiProfileViewer extends GuiScreen { } if (selected) { - uMin = 196 /256f; + uMin = 196 / 256f; uMax = 226 / 256f; renderBlurredBackground(width, height, x - 2, y + 2, 30 - 2, 28 - 4); @@ -746,8 +768,14 @@ public class GuiProfileViewer extends GuiScreen { } if (pages.containsKey(currentPage)) { - if (pages.get(currentPage).mouseClicked(mouseX, mouseY, mouseButton)) { - return; + try { + if (pages.get(currentPage).mouseClicked(mouseX, mouseY, mouseButton)) { + return; + } + } catch (Exception ex) { + if (currentPage == ProfileViewerPage.CRASH_RECOVERY) throw ex; + pages.put(ProfileViewerPage.CRASH_RECOVERY, new CrashRecoveryPage(this, ex, currentPage)); + currentPage = ProfileViewerPage.CRASH_RECOVERY; } } @@ -865,7 +893,13 @@ public class GuiProfileViewer extends GuiScreen { super.keyTyped(typedChar, keyCode); if (pages.containsKey(currentPage)) { - pages.get(currentPage).keyTyped(typedChar, keyCode); + try { + pages.get(currentPage).keyTyped(typedChar, keyCode); + } catch (Exception ex) { + if (currentPage == ProfileViewerPage.CRASH_RECOVERY) throw ex; + pages.put(ProfileViewerPage.CRASH_RECOVERY, new CrashRecoveryPage(this, ex, currentPage)); + currentPage = ProfileViewerPage.CRASH_RECOVERY; + } } if (playerNameTextField.getFocus()) { @@ -913,7 +947,9 @@ public class GuiProfileViewer extends GuiScreen { if (levelObj.maxed) { renderGoldBar(x, y + 6, xSize); } else { - if ((skillName.contains("Catacombs") || Weight.DUNGEON_CLASS_NAMES.stream().anyMatch(e -> skillName.toLowerCase().contains(e))) && levelObj.level >= 50) { + if ((skillName.contains("Catacombs") || Weight.DUNGEON_CLASS_NAMES.stream().anyMatch(e -> skillName + .toLowerCase() + .contains(e))) && levelObj.level >= 50) { renderGoldBar(x, y + 6, xSize); } else { renderBar(x, y + 6, xSize, level % 1); @@ -1198,6 +1234,7 @@ public class GuiProfileViewer extends GuiScreen { LOADING(), INVALID_NAME(), NO_SKYBLOCK(), + CRASH_RECOVERY(), BASIC(0, Items.paper, "§9Skills"), DUNGEON(1, Item.getItemFromBlock(Blocks.deadbush), "§eDungeoneering"), EXTRA(2, Items.book, "§7Profile Stats"), @@ -1242,6 +1279,10 @@ public class GuiProfileViewer extends GuiScreen { return null; } + public boolean isTransient() { + return this == LOADING || this == NO_SKYBLOCK || this == INVALID_NAME || this == CRASH_RECOVERY; + } + public Optional<ItemStack> getItem() { return Optional.ofNullable(stack); } diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/Rectangle.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/Rectangle.kt index d44b7721..1882dac4 100644 --- a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/Rectangle.kt +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/Rectangle.kt @@ -31,6 +31,12 @@ data class Rectangle( val x: Int, val y: Int, val width: Int, val height: Int, ) { + val centerX: Int + get() = x + width / 2 + val centerY: Int + get() = y + height / 2 + + /** * The left edge of this rectangle (Low X) */ @@ -69,7 +75,7 @@ data class Rectangle( /** * Check if this rectangle contains the given coordinate */ - fun contains(x1: Int, y1: Int) :Boolean{ + fun contains(x1: Int, y1: Int): Boolean { return left <= x1 && x1 < left + width && top <= y1 && y1 < top + height } } |