Implement NoCrash
This commit is contained in:
parent
5095518b44
commit
774084be35
5 changed files with 245 additions and 1 deletions
40
src/main/java/org/dimdev/ddutils/HasteUpload.java
Normal file
40
src/main/java/org/dimdev/ddutils/HasteUpload.java
Normal file
|
@ -0,0 +1,40 @@
|
|||
package org.dimdev.ddutils;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public final class HasteUpload {
|
||||
public static String uploadToHaste(String baseUrl, String extension, String str) throws IOException {
|
||||
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
URL uploadURL = new URL(baseUrl + "/documents");
|
||||
HttpURLConnection connection = (HttpURLConnection) uploadURL.openConnection();
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Content-Type", "text/plain; charset=UTF-8");
|
||||
connection.setFixedLengthStreamingMode(bytes.length);
|
||||
connection.setDoInput(true);
|
||||
connection.setDoOutput(true);
|
||||
connection.connect();
|
||||
|
||||
try {
|
||||
try (OutputStream os = connection.getOutputStream()) {
|
||||
os.write(bytes);
|
||||
}
|
||||
|
||||
try (InputStream is = connection.getInputStream()) {
|
||||
JsonObject json = new Gson().fromJson(new InputStreamReader(is), JsonObject.class);
|
||||
return baseUrl + "/" + json.get("key").getAsString() + (extension == null || extension.equals("") ? "" : "." + extension);
|
||||
}
|
||||
} finally {
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
72
src/main/java/org/dimdev/vanillafix/GuiCrashScreen.java
Normal file
72
src/main/java/org/dimdev/vanillafix/GuiCrashScreen.java
Normal file
|
@ -0,0 +1,72 @@
|
|||
package org.dimdev.vanillafix;
|
||||
|
||||
import net.minecraft.client.gui.*;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.crash.CrashReport;
|
||||
import net.minecraftforge.fml.relauncher.ReflectionHelper;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dimdev.ddutils.HasteUpload;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class GuiCrashScreen extends GuiScreen {
|
||||
private static final String HASTE_BASE_URL = "https://paste.dimdev.org";
|
||||
private static final Logger log = LogManager.getLogger();
|
||||
|
||||
private File reportFile;
|
||||
private final CrashReport report;
|
||||
private String hasteLink = null;
|
||||
|
||||
public GuiCrashScreen(File reportFile, CrashReport report) {
|
||||
this.reportFile = reportFile;
|
||||
this.report = report;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initGui() {
|
||||
buttonList.clear();
|
||||
buttonList.add(new GuiOptionButton(0, width / 2 - 155, height / 4 + 120 + 12, I18n.format("gui.toTitle")));
|
||||
buttonList.add(new GuiOptionButton(1, width / 2 - 155 + 160, height / 4 + 120 + 12, I18n.format("vanillafix.gui.getLink")));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(GuiButton button) {
|
||||
try {
|
||||
if (button.id == 0) {
|
||||
mc.displayGuiScreen(new GuiMainMenu());
|
||||
} else if (button.id == 1) {
|
||||
if (hasteLink == null) {
|
||||
hasteLink = HasteUpload.uploadToHaste(HASTE_BASE_URL, "txt", report.getCompleteReport());
|
||||
}
|
||||
ReflectionHelper.findField(GuiScreen.class, "clickedLinkURI", "field_175286_t").set(this, new URI(hasteLink));
|
||||
mc.displayGuiScreen(new GuiConfirmOpenLink(this, hasteLink, 31102009, false));
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
log.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void keyTyped(char typedChar, int keyCode) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
|
||||
drawDefaultBackground();
|
||||
drawCenteredString(fontRenderer, "Minecraft crashed!", width / 2, height / 4 - 60 + 20, 0xFFFFFF);
|
||||
drawString(fontRenderer, "Minecraft ran into a problem and crashed.", width / 2 - 160, height / 4 - 60 + 60, 0xA0A0A0);
|
||||
drawString(fontRenderer, "This is probably caused by a bug in one of your mods or vanilla", width / 2 - 160, height / 4 - 60 + 60 + 18, 0xA0A0A0);
|
||||
drawString(fontRenderer, "Minecraft. Since you have VanillaFix installed, you can return to", width / 2 - 160, height / 4 - 60 + 60 + 27, 0xA0A0A0);
|
||||
drawString(fontRenderer, "the main menu and keep playing despite the crash.", width / 2 - 160, height / 4 - 60 + 60 + 36, 0xA0A0A0);
|
||||
drawString(fontRenderer, "A crash report has been generated, and can be found here (click):", width / 2 - 160, height / 4 - 60 + 60 + 54, 0xA0A0A0);
|
||||
drawCenteredString(fontRenderer, reportFile != null ? "\u00A7n" + reportFile.getName() : "(report failed to save, see the log instead)", width / 2, height / 4 - 60 + 60 + 65, 0x00FF00);
|
||||
drawString(fontRenderer, "You are encouraged to send it to the mod's author to fix this issue", width / 2 - 160, height / 4 - 60 + 60 + 78, 0xA0A0A0);
|
||||
drawString(fontRenderer, "Click the \"Get Link\" button to upload the crash report.", width / 2 - 160, height / 4 - 60 + 60 + 87, 0xA0A0A0);
|
||||
super.drawScreen(mouseX, mouseY, partialTicks);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
package org.dimdev.vanillafix.mixins.client;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiMemoryErrorScreen;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.crash.CrashReport;
|
||||
import net.minecraft.init.Bootstrap;
|
||||
import net.minecraft.profiler.ISnooperInfo;
|
||||
import net.minecraft.util.IThreadListener;
|
||||
import net.minecraft.util.MinecraftError;
|
||||
import net.minecraft.util.ReportedException;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dimdev.vanillafix.GuiCrashScreen;
|
||||
import org.lwjgl.LWJGLException;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
@SuppressWarnings({"unused", "NonConstantFieldWithUpperCaseName", "RedundantThrows"}) // Shadow
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Mixin(Minecraft.class)
|
||||
public abstract class MixinMinecraft implements IThreadListener, ISnooperInfo {
|
||||
|
||||
@Shadow volatile boolean running;
|
||||
@Shadow private boolean hasCrashed;
|
||||
@Shadow private CrashReport crashReporter;
|
||||
|
||||
@Shadow private void init() throws LWJGLException, IOException {}
|
||||
|
||||
@Shadow private void runGameLoop() throws IOException {}
|
||||
|
||||
@Shadow public void freeMemory() {}
|
||||
|
||||
@Shadow public void displayGuiScreen(@Nullable GuiScreen guiScreenIn) {}
|
||||
|
||||
@Shadow public CrashReport addGraphicsAndWorldToCrashReport(CrashReport theCrash) { return null; }
|
||||
|
||||
@Shadow @Final private static Logger LOGGER;
|
||||
|
||||
@Shadow public void shutdownMinecraftApplet() {}
|
||||
|
||||
@Shadow public void displayCrashReport(CrashReport crashReportIn) {}
|
||||
|
||||
@SuppressWarnings("CallToSystemGC")
|
||||
@Overwrite
|
||||
public void run() {
|
||||
running = true;
|
||||
|
||||
try {
|
||||
init();
|
||||
} catch (Throwable throwable) {
|
||||
CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Initializing game");
|
||||
crashreport.makeCategory("Initialization");
|
||||
displayCrashReport(addGraphicsAndWorldToCrashReport(crashreport)); // TODO: GUI for this too
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
while (running) {
|
||||
if (!hasCrashed || crashReporter == null) {
|
||||
try {
|
||||
runGameLoop();
|
||||
} catch (OutOfMemoryError e) {
|
||||
freeMemory();
|
||||
displayGuiScreen(new GuiMemoryErrorScreen());
|
||||
System.gc();
|
||||
} catch (ReportedException e) {
|
||||
addGraphicsAndWorldToCrashReport(e.getCrashReport());
|
||||
freeMemory();
|
||||
LOGGER.fatal("Reported exception thrown!", e);
|
||||
displayCrashScreen(e.getCrashReport());
|
||||
} catch (Throwable e) {
|
||||
CrashReport report = addGraphicsAndWorldToCrashReport(new CrashReport("Unexpected error", e));
|
||||
freeMemory();
|
||||
LOGGER.fatal("Unreported exception thrown!", e);
|
||||
displayCrashScreen(report);
|
||||
}
|
||||
} else {
|
||||
displayCrashReport(crashReporter);
|
||||
}
|
||||
}
|
||||
} catch (MinecraftError ignored) {
|
||||
} finally {
|
||||
shutdownMinecraftApplet();
|
||||
}
|
||||
}
|
||||
|
||||
public void displayCrashScreen(CrashReport report) {
|
||||
try {
|
||||
File crashReportsDir = new File(Minecraft.getMinecraft().mcDataDir, "crash-reports");
|
||||
File crashReportSaveFile = new File(crashReportsDir, "crash-" + new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss").format(new Date()) + "-client.txt");
|
||||
|
||||
// Print the report in bootstrap
|
||||
Bootstrap.printToSYSOUT(report.getCompleteReport());
|
||||
|
||||
// Save the report and print file in bootstrap
|
||||
File reportFile = null;
|
||||
if (report.getFile() != null) {
|
||||
reportFile = report.getFile();
|
||||
} else if (report.saveToFile(crashReportSaveFile)) {
|
||||
reportFile = crashReportSaveFile;
|
||||
}
|
||||
|
||||
if (reportFile != null) {
|
||||
Bootstrap.printToSYSOUT("Recoverable game crash! Crash report saved to: " + reportFile);
|
||||
} else {
|
||||
Bootstrap.printToSYSOUT("Recoverable game crash! Crash report could not be saved.");
|
||||
}
|
||||
|
||||
// Display the crash screen
|
||||
displayGuiScreen(new GuiCrashScreen(reportFile, report));
|
||||
|
||||
// Keep running
|
||||
hasCrashed = false;
|
||||
} catch (Throwable e) {
|
||||
LOGGER.error("The crash screen threw an error, reverting to default crash report", e);
|
||||
displayCrashReport(report);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -201,3 +201,5 @@ dimdoors.graphics.riftSize=Rift Size
|
|||
dimdoors.graphics.riftSize.tooltip=Multiplier affecting how large rifts should be rendered, 1 being the default size.
|
||||
dimdoors.graphics.riftJitter=Rift Jitter
|
||||
dimdoors.graphics.riftJitter.tooltip=Multiplier affecting how much rifts should jitter, 1 being the default size.
|
||||
|
||||
vanillafix.gui.getLink=Get Link
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
"mixins": [
|
||||
"MixinNetHandlerPlayServer"
|
||||
],
|
||||
"client": [],
|
||||
"client": [
|
||||
"client.MixinMinecraft"],
|
||||
"server": [],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
|
Loading…
Reference in a new issue