Merge branch 'crash-report-server' of https://github.com/Anuken/Mindustry

This commit is contained in:
Anuken
2018-08-30 09:23:10 -04:00
10 changed files with 141 additions and 101 deletions

View File

@@ -232,6 +232,10 @@ project(":kryonet") {
} }
} }
project(":reporter"){
apply plugin: "java"
}
tasks.eclipse.doLast { tasks.eclipse.doLast {
delete ".project" delete ".project"
} }

View File

@@ -40,6 +40,7 @@ public class Vars{
//discord group URL //discord group URL
public static final String discordURL = "https://discord.gg/BKADYds"; public static final String discordURL = "https://discord.gg/BKADYds";
public static final String releasesURL = "https://api.github.com/repos/Anuken/Mindustry/releases"; public static final String releasesURL = "https://api.github.com/repos/Anuken/Mindustry/releases";
public static final String crashReportURL = "http://mindustry.us.to/report";
public static final int maxTextLength = 150; public static final int maxTextLength = 150;
public static final int maxNameLength = 40; public static final int maxNameLength = 40;
public static final float itemSize = 5f; public static final float itemSize = 5f;

View File

@@ -14,7 +14,6 @@ public class GameState{
public boolean gameOver = false; public boolean gameOver = false;
public GameMode mode = GameMode.waves; public GameMode mode = GameMode.waves;
public Difficulty difficulty = Difficulty.normal; public Difficulty difficulty = Difficulty.normal;
public boolean friendlyFire;
public WaveSpawner spawner = new WaveSpawner(); public WaveSpawner spawner = new WaveSpawner();
public Teams teams = new Teams(); public Teams teams = new Teams();
private State state = State.menu; private State state = State.menu;

View File

@@ -63,55 +63,6 @@ public class Administration{
return editLogs; return editLogs;
} }
/*
public void rollbackWorld(int rollbackTimes) {
for(IntMap.Entry<Array<EditLog>> editLog : editLogs.entries()) {
int coords = editLog.key;
Array<EditLog> logs = editLog.value;
for(int i = 0; i < rollbackTimes; i++) {
EditLog log = logs.get(logs.size - 1);
int x = coords % world.width();
int y = coords / world.width();
Block result = log.block;
int rotation = log.rotation;
//TODO fix this mess, broken with 4.0
if(log.action == EditLog.EditAction.PLACE) {
// Build.breakBlock(x, y, false, false);
Packets.BreakPacket packet = new Packets.BreakPacket();
packet.x = (short) x;
packet.y = (short) y;
packet.playerid = 0;
Net.send(packet, Net.SendMode.tcp);
}
else if(log.action == EditLog.EditAction.BREAK) {
//Build.placeBlock(x, y, result, rotation, false, false);
Packets.PlacePacket packet = new Packets.PlacePacket();
packet.x = (short) x;
packet.y = (short) y;
packet.rotation = (byte) rotation;
packet.playerid = 0;
//packet.block = result.id;
Net.send(packet, Net.SendMode.tcp);
}
logs.removeIndex(logs.size - 1);
if(logs.size == 0) {
editLogs.remove(coords);
break;
}
}
}
}*/
public boolean validateBreak(String id, String ip){ public boolean validateBreak(String id, String ip){
if(!isAntiGrief() || isAdmin(id, ip)) return true; if(!isAntiGrief() || isAdmin(id, ip)) return true;

View File

@@ -309,8 +309,12 @@ public class Net{
} }
public static void http(String url, String method, Consumer<String> listener, Consumer<Throwable> failure){ public static void http(String url, String method, Consumer<String> listener, Consumer<Throwable> failure){
http(url, method, null, listener, failure);
}
public static void http(String url, String method, String body, Consumer<String> listener, Consumer<Throwable> failure){
HttpRequest req = new HttpRequestBuilder().newRequest() HttpRequest req = new HttpRequestBuilder().newRequest()
.method(method).url(url).build(); .method(method).url(url).content(body).build();
Gdx.net.sendHttpRequest(req, new HttpResponseListener(){ Gdx.net.sendHttpRequest(req, new HttpResponseListener(){
@Override @Override

View File

@@ -1,21 +1,26 @@
package io.anuke.mindustry.desktop; package io.anuke.mindustry.desktop;
import com.badlogic.gdx.utils.JsonValue;
import com.badlogic.gdx.utils.JsonValue.ValueType;
import com.badlogic.gdx.utils.JsonWriter.OutputType;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.game.Version; import io.anuke.mindustry.game.Version;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.ucore.core.Settings; import io.anuke.ucore.core.Settings;
import io.anuke.ucore.util.Strings; import io.anuke.ucore.util.Log;
import java.nio.file.Files; import java.io.PrintWriter;
import java.nio.file.Paths; import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
public class CrashHandler{ public class CrashHandler{
public static void handle(Throwable e){ public static void handle(Throwable e){
//TODO send full error report to server via HTTP
e.printStackTrace(); e.printStackTrace();
//don't create crash logs for me (anuke), as it's expected
//also don't create logs for custom builds
if(System.getProperty("user.name").equals("anuke") || Version.build == -1) return;
boolean netActive = false, netServer = false; boolean netActive = false, netServer = false;
//attempt to close connections, if applicable //attempt to close connections, if applicable
@@ -27,45 +32,39 @@ public class CrashHandler{
p.printStackTrace(); p.printStackTrace();
} }
//don't create crash logs for me (anuke), as it's expected JsonValue value = new JsonValue(ValueType.object);
if(System.getProperty("user.name").equals("anuke")) return;
String header = "--CRASH REPORT--\n"; boolean fn = netActive, fs = netServer;
//add all relevant info, ignoring exceptions
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("gamemode", new JsonValue(Vars.state.mode.toString())));
ex(() -> value.addChild("os", new JsonValue(System.getProperty("os.name"))));
ex(() -> value.addChild("multithreading", new JsonValue(Settings.getBool("multithread"))));
ex(() -> value.addChild("trace", new JsonValue(parseException(e))));
Log.info("Sending crash report.");
//post to crash report URL
Net.http(Vars.crashReportURL, "POST", value.toJson(OutputType.json), r -> System.exit(1), t -> System.exit(1));
//sleep forever
try{ Thread.sleep(Long.MAX_VALUE); }catch(InterruptedException ignored){}
}
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{ try{
header += "--GAME INFO--\n"; r.run();
header += "Build: " + Version.build + "\n"; }catch(Throwable t){
header += "Net Active: " + netActive + "\n"; t.printStackTrace();
header += "Net Server: " + netServer + "\n";
header += "OS: " + System.getProperty("os.name") + "\n";
header += "Multithreading: " + Settings.getBool("multithread") + "\n----\n";
}catch(Throwable e4){
header += "--error getting additional info--\n";
e4.printStackTrace();
}
//parse exception
String result = header + Strings.parseFullException(e);
boolean failed = false;
String filename = "";
//try to write it
try{
filename = "crash-report-" + new SimpleDateFormat("dd-MM-yy h.mm.ss").format(new Date()) + ".txt";
Files.write(Paths.get(System.getProperty("user.home"), filename), result.getBytes());
}catch(Throwable i){
i.printStackTrace();
failed = true;
}
try{
javax.swing.JOptionPane.showMessageDialog(null, "An error has occured: \n" + result + "\n\n" +
(!failed ? "A crash report has been written to " + Paths.get(System.getProperty("user.home"), filename).toFile().getAbsolutePath() + ".\nPlease send this file to the developer!"
: "Failed to generate crash report.\nPlease send an image of this crash log to the developer!"));
}catch(Throwable i){
i.printStackTrace();
//what now?
} }
} }
} }

View File

@@ -0,0 +1,70 @@
package io.anuke.mindustry.server;
import com.badlogic.gdx.utils.JsonValue;
import com.badlogic.gdx.utils.JsonValue.ValueType;
import com.badlogic.gdx.utils.JsonWriter.OutputType;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.game.Version;
import io.anuke.mindustry.net.Net;
import io.anuke.ucore.core.Settings;
import io.anuke.ucore.util.Log;
import java.io.PrintWriter;
import java.io.StringWriter;
public class CrashHandler{
public static void handle(Throwable e){
e.printStackTrace();
//don't create crash logs for me (anuke), as it's expected
//also don't create logs for custom builds
if(System.getProperty("user.name").equals("anuke") || Version.build == -1) return;
//if getting the crash report property failed, OR if it set to false... don't send it
try{
if(!Settings.getBool("crashreport")) return;
}catch(Throwable ignored){
return;
}
//attempt to close connections, if applicable
try{
Net.dispose();
}catch(Throwable p){
p.printStackTrace();
}
JsonValue value = new JsonValue(ValueType.object);
//add all relevant info, ignoring exceptions
ex(() -> value.addChild("build", new JsonValue(Version.build)));
ex(() -> value.addChild("mode", new JsonValue(Vars.state.mode.toString())));
ex(() -> value.addChild("difficulty", new JsonValue(Vars.state.difficulty.toString())));
ex(() -> value.addChild("players", new JsonValue(Vars.playerGroup.size())));
ex(() -> value.addChild("os", new JsonValue(System.getProperty("os.name"))));
ex(() -> value.addChild("trace", new JsonValue(parseException(e))));
Log.info("&lcSending crash report.");
//post to crash report URL
Net.http(Vars.crashReportURL, "POST", value.toJson(OutputType.json), r -> System.exit(1), t -> System.exit(1));
//sleep forever
try{ Thread.sleep(Long.MAX_VALUE); }catch(InterruptedException ignored){}
}
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();
}
}
}

View File

@@ -54,6 +54,7 @@ public class ServerControl extends Module{
"admins", "", "admins", "",
"sector_x", 0, "sector_x", 0,
"sector_y", 1, "sector_y", 1,
"crashreport", false,
"port", port "port", port
); );
@@ -338,7 +339,14 @@ public class ServerControl extends Module{
} }
}); });
handler.register("debug", "<on/off>", "Disables or enables debug ode", arg -> { handler.register("crashreport", "<on/off>", "Disables or enables automatic crash reporting", arg -> {
boolean value = arg[0].equalsIgnoreCase("on");
Settings.putBool("crashreport", value);
Settings.save();
info("Crash reporting is now {0}.", value ? "on" : "off");
});
handler.register("debug", "<on/off>", "Disables or enables debug mode", arg -> {
boolean value = arg[0].equalsIgnoreCase("on"); boolean value = arg[0].equalsIgnoreCase("on");
debug = value; debug = value;
info("Debug mode is now {0}.", value ? "on" : "off"); info("Debug mode is now {0}.", value ? "on" : "off");

View File

@@ -52,20 +52,24 @@ public class ServerLauncher extends HeadlessApplication{
} }
public static void main(String[] args){ public static void main(String[] args){
try{
Net.setClientProvider(new KryoClient()); Net.setClientProvider(new KryoClient());
Net.setServerProvider(new KryoServer()); Net.setServerProvider(new KryoServer());
HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration(); HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration();
config.preferencesDirectory = OS.getAppDataDirectoryString("Mindustry"); config.preferencesDirectory = OS.getAppDataDirectoryString("Mindustry");
new ServerLauncher(new MindustryServer(args), config); new ServerLauncher(new MindustryServer(args), config);
}catch(Throwable t){
CrashHandler.handle(t);
}
//find and handle uncaught exceptions in libGDX thread //find and handle uncaught exceptions in libGDX thread
for(Thread thread : Thread.getAllStackTraces().keySet()){ for(Thread thread : Thread.getAllStackTraces().keySet()){
if(thread.getName().equals("HeadlessApplication")){ if(thread.getName().equals("HeadlessApplication")){
thread.setUncaughtExceptionHandler((t, throwable) -> { thread.setUncaughtExceptionHandler((t, throwable) -> {
throwable.printStackTrace(); CrashHandler.handle(throwable);
System.exit(-1); System.exit(-1);
}); });
break; break;

View File

@@ -1,4 +1,4 @@
include 'desktop', 'html', 'core', 'android', 'kryonet', 'server', 'ios', 'annotations', 'packer' include 'desktop', 'html', 'core', 'android', 'kryonet', 'server', 'ios', 'annotations', 'packer', 'reporter'
if(System.properties["release"] == null || System.properties["release"].equals("false")){ if(System.properties["release"] == null || System.properties["release"].equals("false")){
if (new File(settingsDir, '../uCore').exists()) { if (new File(settingsDir, '../uCore').exists()) {