Better mod crash logs
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package io.anuke.mindustry.game;
|
||||
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.util.ArcAnnotate.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.mod.Mods.*;
|
||||
@@ -11,6 +12,8 @@ public abstract class Content implements Comparable<Content>{
|
||||
public final short id;
|
||||
/** The mod that loaded this piece of content. */
|
||||
public @Nullable LoadedMod mod;
|
||||
/** File that this content was loaded from. */
|
||||
public @Nullable FileHandle sourceFile;
|
||||
|
||||
public Content(){
|
||||
this.id = (short)Vars.content.getBy(getContentType()).size;
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.anuke.arc.*;
|
||||
import io.anuke.arc.audio.*;
|
||||
import io.anuke.arc.collection.Array;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.util.ArcAnnotate.*;
|
||||
@@ -229,9 +230,11 @@ public class ContentParser{
|
||||
|
||||
/** Call to read a content's extra info later.*/
|
||||
private void read(Runnable run){
|
||||
Content cont = currentContent;
|
||||
LoadedMod mod = currentMod;
|
||||
reads.add(() -> {
|
||||
this.currentMod = mod;
|
||||
this.currentContent = cont;
|
||||
run.run();
|
||||
});
|
||||
}
|
||||
@@ -255,7 +258,7 @@ public class ContentParser{
|
||||
try{
|
||||
reads.each(Runnable::run);
|
||||
}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();
|
||||
}
|
||||
@@ -265,9 +268,10 @@ public class ContentParser{
|
||||
* @param name the name of the file without its extension
|
||||
* @param json the json to parse
|
||||
* @param type the type of content this is
|
||||
* @param file file that this content is being parsed from
|
||||
* @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()){
|
||||
init();
|
||||
}
|
||||
@@ -279,6 +283,7 @@ public class ContentParser{
|
||||
|
||||
currentMod = mod;
|
||||
Content c = parsers.get(type).parse(mod.name, name, value);
|
||||
c.sourceFile = file;
|
||||
c.mod = mod;
|
||||
checkNulls(c);
|
||||
return c;
|
||||
|
||||
@@ -268,25 +268,28 @@ public class Mods implements Loadable{
|
||||
/** Creates all the content found in mod files. */
|
||||
public void loadContent(){
|
||||
for(LoadedMod mod : loaded){
|
||||
if(mod.root.child("content").exists()){
|
||||
FileHandle contentRoot = mod.root.child("content");
|
||||
for(ContentType type : ContentType.all){
|
||||
FileHandle folder = contentRoot.child(type.name().toLowerCase() + "s");
|
||||
if(folder.exists()){
|
||||
for(FileHandle file : folder.list()){
|
||||
if(file.extension().equals("json")){
|
||||
try{
|
||||
//this binds the content but does not load it entirely
|
||||
Content loaded = parser.parse(mod, file.nameWithoutExtension(), file.readString("UTF-8"), type);
|
||||
Log.info("[{0}] Loaded '{1}'.", mod.meta.name, (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);
|
||||
safeRun(mod, () -> {
|
||||
if(mod.root.child("content").exists()){
|
||||
FileHandle contentRoot = mod.root.child("content");
|
||||
for(ContentType type : ContentType.all){
|
||||
FileHandle folder = contentRoot.child(type.name().toLowerCase() + "s");
|
||||
if(folder.exists()){
|
||||
for(FileHandle file : folder.list()){
|
||||
if(file.extension().equals("json")){
|
||||
try{
|
||||
//this binds the content but does not load it entirely
|
||||
Content loaded = parser.parse(mod, file.nameWithoutExtension(), file.readString("UTF-8"), file, type);
|
||||
Log.info("[{0}] Loaded '{1}'.", mod.meta.name,
|
||||
(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
|
||||
@@ -345,7 +348,40 @@ public class Mods implements Loadable{
|
||||
|
||||
/** Iterates through each mod with a main class.*/
|
||||
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.
|
||||
@@ -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. */
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public class CrashSender{
|
||||
exception.printStackTrace();
|
||||
|
||||
//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
|
||||
if(Version.number == 0){
|
||||
|
||||
@@ -70,7 +70,7 @@ public class HostDialog extends FloatingDialog{
|
||||
player.isAdmin = true;
|
||||
|
||||
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", () -> {
|
||||
Core.settings.putSave("publichost", true);
|
||||
platform.updateLobby();
|
||||
|
||||
Reference in New Issue
Block a user