diff --git a/android/src/io/anuke/mindustry/AndroidLauncher.java b/android/src/io/anuke/mindustry/AndroidLauncher.java index 2cc928d322..96f99dcd5f 100644 --- a/android/src/io/anuke/mindustry/AndroidLauncher.java +++ b/android/src/io/anuke/mindustry/AndroidLauncher.java @@ -18,6 +18,7 @@ import io.anuke.arc.util.*; import io.anuke.arc.util.serialization.*; import io.anuke.mindustry.game.Saves.*; import io.anuke.mindustry.io.*; +import io.anuke.mindustry.mod.*; import io.anuke.mindustry.ui.dialogs.*; import java.io.*; @@ -134,14 +135,11 @@ public class AndroidLauncher extends AndroidApplication{ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER); } - @Override - public boolean canDonate(){ - return true; - } }, new AndroidApplicationConfiguration(){{ useImmersiveMode = true; depth = 0; hideStatusBar = true; + errorHandler = ModCrashHandler::handle; }}); checkFiles(getIntent()); } diff --git a/core/src/io/anuke/mindustry/core/ContentLoader.java b/core/src/io/anuke/mindustry/core/ContentLoader.java index 77ed6f219f..e69e244539 100644 --- a/core/src/io/anuke/mindustry/core/ContentLoader.java +++ b/core/src/io/anuke/mindustry/core/ContentLoader.java @@ -109,8 +109,11 @@ public class ContentLoader{ for(ContentType type : ContentType.values()){ for(Content content : contentMap[type.ordinal()]){ - //TODO catch error and display it per mod - callable.accept(content); + try{ + callable.accept(content); + }catch(Throwable e){ + mods.handleError(e, content.mod); + } } } diff --git a/core/src/io/anuke/mindustry/core/Platform.java b/core/src/io/anuke/mindustry/core/Platform.java index c2fad7a15f..588e5081f1 100644 --- a/core/src/io/anuke/mindustry/core/Platform.java +++ b/core/src/io/anuke/mindustry/core/Platform.java @@ -72,11 +72,6 @@ public interface Platform{ default void updateRPC(){ } - /** Whether donating is supported. */ - default boolean canDonate(){ - return false; - } - /** Must be a base64 string 8 bytes in length. */ default String getUUID(){ String uuid = Core.settings.getString("uuid", ""); diff --git a/core/src/io/anuke/mindustry/input/PlaceMode.java b/core/src/io/anuke/mindustry/input/PlaceMode.java index 8eb6bb5105..5ac66c5220 100644 --- a/core/src/io/anuke/mindustry/input/PlaceMode.java +++ b/core/src/io/anuke/mindustry/input/PlaceMode.java @@ -1,5 +1,5 @@ package io.anuke.mindustry.input; enum PlaceMode{ - none, breaking, placing + none, breaking, placing, schematicSelect } diff --git a/core/src/io/anuke/mindustry/mod/ModCrashHandler.java b/core/src/io/anuke/mindustry/mod/ModCrashHandler.java new file mode 100644 index 0000000000..3cbf3a656a --- /dev/null +++ b/core/src/io/anuke/mindustry/mod/ModCrashHandler.java @@ -0,0 +1,66 @@ +package io.anuke.mindustry.mod; + +import io.anuke.arc.*; +import io.anuke.arc.collection.*; +import io.anuke.arc.graphics.*; +import io.anuke.arc.graphics.g2d.*; +import io.anuke.arc.math.*; +import io.anuke.arc.scene.ui.layout.*; +import io.anuke.arc.util.*; +import io.anuke.mindustry.graphics.*; +import io.anuke.mindustry.mod.Mods.*; +import io.anuke.mindustry.ui.*; + +public class ModCrashHandler{ + + public static void handle(Throwable t){ + Array list = Strings.getCauses(t); + Throwable modCause = list.find(e -> e instanceof ModLoadException); + if(modCause != null && Fonts.outline != null){ + String text = "[scarlet][[A fatal crash has occured while loading a mod!][]\n\nReason:[accent] " + modCause.getMessage(); + String bottom = "[scarlet]The associated mod has been disabled. Swipe out of the app and launch it again."; + GlyphLayout layout = new GlyphLayout(); + Core.atlas = TextureAtlas.blankAtlas(); + Colors.put("accent", Pal.accent); + + Core.app.addListener(new ApplicationListener(){ + @Override + public void update(){ + Core.graphics.clear(0.1f, 0.1f, 0.1f, 1f); + float rad = Math.min(Core.graphics.getWidth(), Core.graphics.getHeight()) / 2f / 1.3f; + Draw.color(Color.scarlet, Color.black, Mathf.absin(Core.graphics.getFrameId(), 15f, 0.6f)); + Lines.stroke(Scl.scl(40f)); + //Lines.poly2(Core.graphics.getWidth()/2f, Core.graphics.getHeight()/2f, 3, rad, 0f); + float cx = Core.graphics.getWidth()/2f, cy = Core.graphics.getHeight()/2f; + for(int i = 0; i < 3; i++){ + float angle1 = i * 120f + 90f; + float angle2 = (i + 1) * 120f + 90f; + Tmp.v1.trnsExact(angle1, rad - Lines.getStroke()/2f).add(cx, cy); + Tmp.v2.trnsExact(angle2, rad - Lines.getStroke()/2f).add(cx, cy); + Tmp.v3.trnsExact(angle1, rad + Lines.getStroke()/2f).add(cx, cy); + Tmp.v4.trnsExact(angle2, rad + Lines.getStroke()/2f).add(cx, cy); + Fill.quad(Tmp.v1.x, Tmp.v1.y, Tmp.v2.x, Tmp.v2.y, Tmp.v4.x, Tmp.v4.y, Tmp.v3.x, Tmp.v3.y); + } + Lines.lineAngleCenter(Core.graphics.getWidth()/2f, Core.graphics.getHeight()/2f - Scl.scl(5f), 90f, rad/3.1f); + Fill.square(Core.graphics.getWidth()/2f, Core.graphics.getHeight()/2f + rad/2f - Scl.scl(15f), Lines.getStroke()/2f); + Draw.reset(); + + Fonts.outline.getData().markupEnabled = true; + layout.setText(Fonts.outline, text, Color.white, Core.graphics.getWidth(), Align.left, true); + Fonts.outline.draw(text, Core.graphics.getWidth()/2f - layout.width/2f, Core.graphics.getHeight() - Scl.scl(50f), Core.graphics.getWidth(), Align.left, true); + + layout.setText(Fonts.outline, bottom, Color.white, Core.graphics.getWidth(), Align.left, true); + Fonts.outline.draw(bottom, Core.graphics.getWidth()/2f - layout.width/2f, layout.height + Scl.scl(10f), Core.graphics.getWidth(), Align.left, true); + Draw.flush(); + } + + @Override + public void resize(int width, int height){ + Draw.proj().setOrtho(0, 0, width, height); + } + }); + }else{ + throw new RuntimeException(t); + } + } +} diff --git a/core/src/io/anuke/mindustry/mod/Mods.java b/core/src/io/anuke/mindustry/mod/Mods.java index cfc23bf0b1..b280985199 100644 --- a/core/src/io/anuke/mindustry/mod/Mods.java +++ b/core/src/io/anuke/mindustry/mod/Mods.java @@ -322,7 +322,8 @@ public class Mods implements Loadable{ /** Makes a mod enabled or disabled. shifts it.*/ public void setEnabled(LoadedMod mod, boolean enabled){ if(mod.enabled() != enabled){ - Core.settings.putSave(mod.name + "-enabled", enabled); + Core.settings.putSave("mod-" + mod.name + "-enabled", enabled); + Core.settings.save(); requiresReload = true; if(!enabled){ loaded.remove(mod); @@ -369,6 +370,8 @@ public class Mods implements Loadable{ } } + setEnabled(mod, false); + 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); @@ -463,7 +466,7 @@ public class Mods implements Loadable{ } public boolean enabled(){ - return Core.settings.getBool(name + "-enabled", true); + return Core.settings.getBool("mod-" + name + "-enabled", true); } @Override diff --git a/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java b/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java index b4a0a2078b..c78af82afd 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java @@ -98,6 +98,7 @@ public class MenuFragment extends Fragment{ join = new MobileButton(Icon.add, "$joingame", ui.join::show), editor = new MobileButton(Icon.editor, "$editor", ui.maps::show), tools = new MobileButton(Icon.tools, "$settings", ui.settings::show), + mods = new MobileButton(Icon.wiki, "$mods", ui.mods::show), donate = new MobileButton(Icon.link, "$website", () -> Core.net.openURI("https://anuke.itch.io/mindustry")), exit = new MobileButton(Icon.exit, "$quit", () -> Core.app.exit()); @@ -116,7 +117,8 @@ public class MenuFragment extends Fragment{ table.add(editor); table.add(tools); - if(platform.canDonate()) table.add(donate); + table.add(mods); + //if(platform.canDonate()) table.add(donate); if(!ios) table.add(exit); }).colspan(4); }else{ @@ -134,7 +136,8 @@ public class MenuFragment extends Fragment{ container.table(table -> { table.defaults().set(container.defaults()); - if(platform.canDonate()) table.add(donate); + table.add(mods); + //if(platform.canDonate()) table.add(donate); if(!ios) table.add(exit); }).colspan(2); } diff --git a/gradle.properties b/gradle.properties index c07c359326..d71565dae8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=true org.gradle.jvmargs=-Xms256m -Xmx1024m -archash=e82f446a81abba5d6f712b9053c1b84ec9a73156 +archash=079b22fcc73b96d406f600edb608af4c92cdc2b3 diff --git a/ios/src/io/anuke/mindustry/IOSLauncher.java b/ios/src/io/anuke/mindustry/IOSLauncher.java index 0b0cf974f5..65658f483b 100644 --- a/ios/src/io/anuke/mindustry/IOSLauncher.java +++ b/ios/src/io/anuke/mindustry/IOSLauncher.java @@ -10,6 +10,7 @@ import io.anuke.arc.util.io.*; import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.Saves.*; import io.anuke.mindustry.io.*; +import io.anuke.mindustry.mod.*; import io.anuke.mindustry.ui.*; import org.robovm.apple.foundation.*; import org.robovm.apple.uikit.*; @@ -34,7 +35,7 @@ public class IOSLauncher extends IOSApplication.Delegate{ Scl.setAddition(-0.5f); } - IOSApplicationConfiguration config = new IOSApplicationConfiguration(); + //IOSApplicationConfiguration config = new IOSApplicationConfiguration(); return new IOSApplication(new ClientLauncher(){ @Override @@ -118,7 +119,9 @@ public class IOSLauncher extends IOSApplication.Delegate{ forced = false; UINavigationController.attemptRotationToDeviceOrientation(); } - }, config); + }, new IOSApplicationConfiguration(){{ + errorHandler = ModCrashHandler::handle; + }}); } @Override