Better shared crash handling
This commit is contained in:
@@ -1,143 +0,0 @@
|
||||
package io.anuke.mindustry.desktop;
|
||||
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.collection.ObjectMap;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.io.PropertiesUtils;
|
||||
import io.anuke.arc.util.serialization.JsonValue;
|
||||
import io.anuke.arc.util.serialization.JsonValue.ValueType;
|
||||
import io.anuke.arc.util.serialization.JsonWriter.OutputType;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.game.Version;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import org.lwjgl.util.tinyfd.TinyFileDialogs;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
public class CrashHandler{
|
||||
|
||||
public static void handle(Throwable e){
|
||||
e.printStackTrace();
|
||||
if(Version.number == 0){
|
||||
try{
|
||||
ObjectMap<String, String> map = new ObjectMap<>();
|
||||
PropertiesUtils.load(map, new InputStreamReader(CrashHandler.class.getResourceAsStream("/version.properties")));
|
||||
|
||||
Version.type = map.get("type");
|
||||
Version.number = Integer.parseInt(map.get("number"));
|
||||
Version.modifier = map.get("modifier");
|
||||
if(map.get("build").contains(".")){
|
||||
String[] split = map.get("build").split("\\.");
|
||||
Version.build = Integer.parseInt(split[0]);
|
||||
Version.revision = Integer.parseInt(split[1]);
|
||||
}else{
|
||||
Version.build = Strings.canParseInt(map.get("build")) ? Integer.parseInt(map.get("build")) : -1;
|
||||
}
|
||||
}catch(Throwable ignored){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
try{
|
||||
//check crash report setting
|
||||
if(!Core.settings.getBool("crashreport", true)){
|
||||
return;
|
||||
}
|
||||
}catch(Throwable ignored){
|
||||
//if there's no settings init we don't know what the user wants but chances are it's an important crash, so send it anyway
|
||||
}
|
||||
|
||||
boolean badGPU = false;
|
||||
|
||||
if(e.getMessage() != null && (e.getMessage().contains("Couldn't create window") || e.getMessage().contains("OpenGL 2.0 or higher"))){
|
||||
|
||||
dialog(() -> TinyFileDialogs.tinyfd_messageBox("oh no",
|
||||
e.getMessage().contains("Couldn't create window") ? "A graphics initialization error has occured! Try to update your graphics drivers.\nReport this to the developer." :
|
||||
"Your graphics card does not support OpenGL 2.0!\n" +
|
||||
"Try to update your graphics drivers.\n\n" +
|
||||
"(If that doesn't work, your computer just doesn't support Mindustry.)", "ok", "error", true));
|
||||
badGPU = true;
|
||||
}
|
||||
|
||||
//don't create crash logs for me (anuke) or custom builds, as it's expected
|
||||
if(System.getProperty("user.name").equals("anuke") || Version.build == -1) return;
|
||||
|
||||
boolean netActive = false, netServer = false;
|
||||
|
||||
//attempt to close connections, if applicable
|
||||
try{
|
||||
netActive = Net.active();
|
||||
netServer = Net.server();
|
||||
Net.dispose();
|
||||
}catch(Throwable p){
|
||||
p.printStackTrace();
|
||||
}
|
||||
|
||||
JsonValue value = new JsonValue(ValueType.object);
|
||||
|
||||
boolean fn = netActive, fs = netServer;
|
||||
|
||||
//add all relevant info, ignoring exceptions
|
||||
ex(() -> value.addChild("versionType", new JsonValue(Version.type)));
|
||||
ex(() -> value.addChild("versionNumber", new JsonValue(Version.number)));
|
||||
ex(() -> value.addChild("versionModifier", new JsonValue(Version.modifier)));
|
||||
ex(() -> value.addChild("build", new JsonValue(Version.build)));
|
||||
ex(() -> value.addChild("net", new JsonValue(fn)));
|
||||
ex(() -> value.addChild("server", new JsonValue(fs)));
|
||||
ex(() -> value.addChild("state", new JsonValue(Vars.state.getState().name())));
|
||||
ex(() -> value.addChild("os", new JsonValue(System.getProperty("os.name"))));
|
||||
ex(() -> value.addChild("trace", new JsonValue(parseException(e))));
|
||||
|
||||
try{
|
||||
Path path = Paths.get(OS.getAppDataDirectoryString(Vars.appName), "crashes",
|
||||
"crash-report-" + DateTimeFormatter.ofPattern("MM_dd_yyyy_HH_mm_ss").format(LocalDateTime.now()) + ".txt");
|
||||
Files.createDirectories(Paths.get(OS.getAppDataDirectoryString(Vars.appName), "crashes"));
|
||||
Files.write(path, parseException(e).getBytes());
|
||||
|
||||
if(!badGPU){
|
||||
dialog(() -> TinyFileDialogs.tinyfd_messageBox("oh no", "A crash has occured. It has been saved in:\n" + path.toAbsolutePath().toString(), "ok", "error", true));
|
||||
}
|
||||
}catch(Throwable t){
|
||||
Log.err("Failed to save local crash report.");
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
||||
Log.info("Sending crash report.");
|
||||
//post to crash report URL
|
||||
Net.http(Vars.crashReportURL, "POST", value.toJson(OutputType.json), r -> {
|
||||
Log.info("Crash sent successfully.");
|
||||
System.exit(1);
|
||||
}, t -> {
|
||||
t.printStackTrace();
|
||||
System.exit(1);
|
||||
});
|
||||
|
||||
//sleep for 10 seconds or until crash report is sent
|
||||
try{
|
||||
Thread.sleep(10000);
|
||||
}catch(InterruptedException ignored){
|
||||
}
|
||||
}
|
||||
|
||||
private static void dialog(Runnable r){
|
||||
new Thread(r).start();
|
||||
}
|
||||
|
||||
private static String parseException(Throwable e){
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
private static void ex(Runnable r){
|
||||
try{
|
||||
r.run();
|
||||
}catch(Throwable t){
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ public class DesktopLauncher extends Lwjgl3Application{
|
||||
Net.setServerProvider(new ArcNetServer());
|
||||
new DesktopLauncher(new Mindustry(), config);
|
||||
}catch(Throwable e){
|
||||
CrashHandler.handle(e);
|
||||
DesktopPlatform.handleCrash(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
package io.anuke.mindustry.desktop;
|
||||
|
||||
import club.minnced.discord.rpc.*;
|
||||
import club.minnced.discord.rpc.DiscordEventHandlers;
|
||||
import club.minnced.discord.rpc.DiscordRPC;
|
||||
import club.minnced.discord.rpc.DiscordRichPresence;
|
||||
import io.anuke.arc.collection.Array;
|
||||
import io.anuke.arc.files.FileHandle;
|
||||
import io.anuke.arc.function.Consumer;
|
||||
import io.anuke.arc.util.Log;
|
||||
import io.anuke.arc.util.OS;
|
||||
import io.anuke.arc.util.Strings;
|
||||
import io.anuke.arc.util.serialization.Base64Coder;
|
||||
import io.anuke.mindustry.core.GameState.State;
|
||||
import io.anuke.mindustry.core.Platform;
|
||||
import io.anuke.mindustry.net.CrashSender;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.ui.dialogs.FileChooser;
|
||||
import org.lwjgl.util.tinyfd.TinyFileDialogs;
|
||||
|
||||
import java.net.NetworkInterface;
|
||||
import java.util.Enumeration;
|
||||
@@ -18,7 +23,7 @@ import java.util.Enumeration;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class DesktopPlatform extends Platform{
|
||||
final static boolean useDiscord = OS.is64Bit;
|
||||
static boolean useDiscord = OS.is64Bit;
|
||||
final static String applicationId = "398246104468291591";
|
||||
String[] args;
|
||||
|
||||
@@ -28,13 +33,41 @@ public class DesktopPlatform extends Platform{
|
||||
testMobile = Array.with(args).contains("-testMobile");
|
||||
|
||||
if(useDiscord){
|
||||
DiscordEventHandlers handlers = new DiscordEventHandlers();
|
||||
DiscordRPC.INSTANCE.Discord_Initialize(applicationId, handlers, true, "");
|
||||
try{
|
||||
DiscordEventHandlers handlers = new DiscordEventHandlers();
|
||||
DiscordRPC.INSTANCE.Discord_Initialize(applicationId, handlers, true, "");
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(DiscordRPC.INSTANCE::Discord_Shutdown));
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(DiscordRPC.INSTANCE::Discord_Shutdown));
|
||||
}catch(Throwable t){
|
||||
useDiscord = false;
|
||||
Log.err("Failed to initialize discord.", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void handleCrash(Throwable e){
|
||||
Consumer<Runnable> dialog = r -> new Thread(r).start();
|
||||
boolean badGPU = false;
|
||||
|
||||
if(e.getMessage() != null && (e.getMessage().contains("Couldn't create window") || e.getMessage().contains("OpenGL 2.0 or higher"))){
|
||||
|
||||
dialog.accept(() -> TinyFileDialogs.tinyfd_messageBox("oh no",
|
||||
e.getMessage().contains("Couldn't create window") ? "A graphics initialization error has occured! Try to update your graphics drivers.\nReport this to the developer." :
|
||||
"Your graphics card does not support OpenGL 2.0!\n" +
|
||||
"Try to update your graphics drivers.\n\n" +
|
||||
"(If that doesn't work, your computer just doesn't support Mindustry.)", "ok", "error", true));
|
||||
badGPU = true;
|
||||
}
|
||||
|
||||
boolean fbgp = badGPU;
|
||||
|
||||
CrashSender.send(e, file -> {
|
||||
if(!fbgp){
|
||||
dialog.accept(() -> TinyFileDialogs.tinyfd_messageBox("oh no", "A crash has occured. It has been saved in:\n" + file.getAbsolutePath(), "ok", "error", true));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showFileChooser(String text, String content, Consumer<FileHandle> cons, boolean open, String filter){
|
||||
new FileChooser(text, file -> file.extension().equalsIgnoreCase(filter), open, cons).show();
|
||||
|
||||
Reference in New Issue
Block a user