Implement NoCrash

This commit is contained in:
Runemoro 2018-04-25 02:20:10 -04:00
parent 5095518b44
commit 774084be35
5 changed files with 245 additions and 1 deletions

View 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();
}
}
}

View 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);
}
}

View file

@ -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);
}
}
}

View file

@ -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

View file

@ -7,7 +7,8 @@
"mixins": [
"MixinNetHandlerPlayServer"
],
"client": [],
"client": [
"client.MixinMinecraft"],
"server": [],
"injectors": {
"defaultRequire": 1