Better mod crash logs

This commit is contained in:
Anuken
2019-10-11 16:08:14 -04:00
parent b927c2df1a
commit 4e0d1b2746
7 changed files with 87 additions and 21 deletions

View File

@@ -1,5 +1,6 @@
package io.anuke.mindustry.game; package io.anuke.mindustry.game;
import io.anuke.arc.files.*;
import io.anuke.arc.util.ArcAnnotate.*; import io.anuke.arc.util.ArcAnnotate.*;
import io.anuke.mindustry.*; import io.anuke.mindustry.*;
import io.anuke.mindustry.mod.Mods.*; import io.anuke.mindustry.mod.Mods.*;
@@ -11,6 +12,8 @@ public abstract class Content implements Comparable<Content>{
public final short id; public final short id;
/** The mod that loaded this piece of content. */ /** The mod that loaded this piece of content. */
public @Nullable LoadedMod mod; public @Nullable LoadedMod mod;
/** File that this content was loaded from. */
public @Nullable FileHandle sourceFile;
public Content(){ public Content(){
this.id = (short)Vars.content.getBy(getContentType()).size; this.id = (short)Vars.content.getBy(getContentType()).size;

View File

@@ -4,6 +4,7 @@ import io.anuke.arc.*;
import io.anuke.arc.audio.*; import io.anuke.arc.audio.*;
import io.anuke.arc.collection.Array; import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.*; import io.anuke.arc.collection.*;
import io.anuke.arc.files.*;
import io.anuke.arc.function.*; import io.anuke.arc.function.*;
import io.anuke.arc.graphics.*; import io.anuke.arc.graphics.*;
import io.anuke.arc.util.ArcAnnotate.*; import io.anuke.arc.util.ArcAnnotate.*;
@@ -229,9 +230,11 @@ public class ContentParser{
/** Call to read a content's extra info later.*/ /** Call to read a content's extra info later.*/
private void read(Runnable run){ private void read(Runnable run){
Content cont = currentContent;
LoadedMod mod = currentMod; LoadedMod mod = currentMod;
reads.add(() -> { reads.add(() -> {
this.currentMod = mod; this.currentMod = mod;
this.currentContent = cont;
run.run(); run.run();
}); });
} }
@@ -255,7 +258,7 @@ public class ContentParser{
try{ try{
reads.each(Runnable::run); reads.each(Runnable::run);
}catch(Exception e){ }catch(Exception e){
throw new RuntimeException("Error occurred parsing content: " + currentContent, e); Vars.mods.handleError(new ModLoadException("Error occurred parsing content: " + currentContent, currentContent, e), currentMod);
} }
reads.clear(); reads.clear();
} }
@@ -265,9 +268,10 @@ public class ContentParser{
* @param name the name of the file without its extension * @param name the name of the file without its extension
* @param json the json to parse * @param json the json to parse
* @param type the type of content this is * @param type the type of content this is
* @param file file that this content is being parsed from
* @return the content that was parsed * @return the content that was parsed
*/ */
public Content parse(LoadedMod mod, String name, String json, ContentType type) throws Exception{ public Content parse(LoadedMod mod, String name, String json, FileHandle file, ContentType type) throws Exception{
if(contentTypes.isEmpty()){ if(contentTypes.isEmpty()){
init(); init();
} }
@@ -279,6 +283,7 @@ public class ContentParser{
currentMod = mod; currentMod = mod;
Content c = parsers.get(type).parse(mod.name, name, value); Content c = parsers.get(type).parse(mod.name, name, value);
c.sourceFile = file;
c.mod = mod; c.mod = mod;
checkNulls(c); checkNulls(c);
return c; return c;

View File

@@ -268,25 +268,28 @@ public class Mods implements Loadable{
/** Creates all the content found in mod files. */ /** Creates all the content found in mod files. */
public void loadContent(){ public void loadContent(){
for(LoadedMod mod : loaded){ for(LoadedMod mod : loaded){
if(mod.root.child("content").exists()){ safeRun(mod, () -> {
FileHandle contentRoot = mod.root.child("content"); if(mod.root.child("content").exists()){
for(ContentType type : ContentType.all){ FileHandle contentRoot = mod.root.child("content");
FileHandle folder = contentRoot.child(type.name().toLowerCase() + "s"); for(ContentType type : ContentType.all){
if(folder.exists()){ FileHandle folder = contentRoot.child(type.name().toLowerCase() + "s");
for(FileHandle file : folder.list()){ if(folder.exists()){
if(file.extension().equals("json")){ for(FileHandle file : folder.list()){
try{ if(file.extension().equals("json")){
//this binds the content but does not load it entirely try{
Content loaded = parser.parse(mod, file.nameWithoutExtension(), file.readString("UTF-8"), type); //this binds the content but does not load it entirely
Log.info("[{0}] Loaded '{1}'.", mod.meta.name, (loaded instanceof UnlockableContent ? ((UnlockableContent)loaded).localizedName : loaded)); Content loaded = parser.parse(mod, file.nameWithoutExtension(), file.readString("UTF-8"), file, type);
}catch(Exception e){ Log.info("[{0}] Loaded '{1}'.", mod.meta.name,
throw new RuntimeException("Failed to parse content file '" + file + "' for mod '" + mod.meta.name + "'.", e); (loaded instanceof UnlockableContent ? ((UnlockableContent)loaded).localizedName : loaded));
}catch(Exception e){
throw new RuntimeException("Failed to parse content file '" + file + "' for mod '" + mod.meta.name + "'.", e);
}
} }
} }
} }
} }
} }
} });
} }
//this finishes parsing content fields //this finishes parsing content fields
@@ -345,7 +348,40 @@ public class Mods implements Loadable{
/** Iterates through each mod with a main class.*/ /** Iterates through each mod with a main class.*/
public void each(Consumer<Mod> cons){ public void each(Consumer<Mod> cons){
loaded.each(p -> p.mod != null, p -> cons.accept(p.mod)); loaded.each(p -> p.mod != null, p -> safeRun(p, () -> cons.accept(p.mod)));
}
public void handleError(Throwable t, LoadedMod mod){
Array<Throwable> causes = Strings.getCauses(t);
Content content = null;
for(Throwable e : causes){
if(e instanceof ModLoadException && ((ModLoadException) e).content != null){
content = ((ModLoadException) e).content;
}
}
String realCause = "<???>";
for(int i = causes.size -1 ; i >= 0; i--){
if(causes.get(i).getMessage() != null){
realCause = causes.get(i).getMessage();
break;
}
}
if(content != null){
throw new ModLoadException(Strings.format("Error loading '{0}' from mod '{1}' ({2}):\n{3}",
content, mod.meta.name, content.sourceFile.name(), realCause), content, t);
}else{
throw new ModLoadException("Error loading mod " + mod.meta.name, t);
}
}
public void safeRun(LoadedMod mod, Runnable run){
try{
run.run();
}catch(Throwable t){
handleError(t, mod);
}
} }
/** Loads a mod file+meta, but does not add it to the list. /** Loads a mod file+meta, but does not add it to the list.
@@ -439,4 +475,22 @@ public class Mods implements Loadable{
/** Hidden mods are only server-side or client-side, and do not support adding new content. */ /** Hidden mods are only server-side or client-side, and do not support adding new content. */
public boolean hidden; public boolean hidden;
} }
/** Thrown when an error occurs while loading a mod.*/
public static class ModLoadException extends RuntimeException{
public Content content;
public LoadedMod mod;
public ModLoadException(String message, Throwable cause){
super(message, cause);
}
public ModLoadException(String message, @Nullable Content content, Throwable cause){
super(message, cause);
this.content = content;
if(content != null){
this.mod = content.mod;
}
}
}
} }

View File

@@ -26,7 +26,7 @@ public class CrashSender{
exception.printStackTrace(); exception.printStackTrace();
//don't create crash logs for custom builds, as it's expected //don't create crash logs for custom builds, as it's expected
if(Version.build == -1 || (System.getProperty("user.name").equals("anuke") && "release".equals(Version.modifier))) return; //if(Version.build == -1 || (System.getProperty("user.name").equals("anuke") && "release".equals(Version.modifier))) return;
//attempt to load version regardless //attempt to load version regardless
if(Version.number == 0){ if(Version.number == 0){

View File

@@ -70,7 +70,7 @@ public class HostDialog extends FloatingDialog{
player.isAdmin = true; player.isAdmin = true;
if(steam){ if(steam){
Core.app.post(() -> Core.settings.getBoolOnce("steampublic", () -> { Core.app.post(() -> Core.settings.getBoolOnce("steampublic2", () -> {
ui.showCustomConfirm("$setting.publichost.name", "$public.confirm", "$yes", "$no", () -> { ui.showCustomConfirm("$setting.publichost.name", "$public.confirm", "$yes", "$no", () -> {
Core.settings.putSave("publichost", true); Core.settings.putSave("publichost", true);
platform.updateLobby(); platform.updateLobby();

View File

@@ -23,6 +23,7 @@ import io.anuke.mindustry.desktop.steam.*;
import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.Version; import io.anuke.mindustry.game.Version;
import io.anuke.mindustry.maps.Map; import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.mod.Mods.*;
import io.anuke.mindustry.net.*; import io.anuke.mindustry.net.*;
import io.anuke.mindustry.net.Net.*; import io.anuke.mindustry.net.Net.*;
import io.anuke.mindustry.ui.*; import io.anuke.mindustry.ui.*;
@@ -229,7 +230,10 @@ public class DesktopLauncher extends ClientLauncher{
boolean fbgp = badGPU; boolean fbgp = badGPU;
CrashSender.send(e, file -> { CrashSender.send(e, file -> {
Throwable cause = Strings.getFinalCause(e); Array<Throwable> causes = Strings.getCauses(e);
Throwable fc = causes.find(t -> t instanceof ModLoadException);
if(fc == null) fc = Strings.getFinalCause(e);
Throwable cause = fc;
if(!fbgp){ if(!fbgp){
dialog.accept(() -> message("A crash has occured. It has been saved in:\n" + file.getAbsolutePath() + "\n" + cause.getClass().getSimpleName().replace("Exception", "") + (cause.getMessage() == null ? "" : ":\n" + cause.getMessage()))); dialog.accept(() -> message("A crash has occured. It has been saved in:\n" + file.getAbsolutePath() + "\n" + cause.getClass().getSimpleName().replace("Exception", "") + (cause.getMessage() == null ? "" : ":\n" + cause.getMessage())));
} }

View File

@@ -1,3 +1,3 @@
org.gradle.daemon=true org.gradle.daemon=true
org.gradle.jvmargs=-Xms256m -Xmx1024m org.gradle.jvmargs=-Xms256m -Xmx1024m
archash=0db0c0d660952fb57cda0f83d78daa9acbcd5439 archash=9e37a345d59b09e8677adbec29cb3981d26fe86b