Better JSON null validation / Disable mods on startup crash

This commit is contained in:
Anuken 2021-02-19 13:28:31 -05:00
parent b46a5c0bda
commit fa7697fc40
12 changed files with 93 additions and 16 deletions

View file

@ -113,7 +113,7 @@ committingchanges = Committing Changes
done = Done
feature.unsupported = Your device does not support this feature.
mods.alphainfo = Keep in mind that mods are in alpha, and[scarlet] may be very buggy[].\nReport any issues you find to the Mindustry GitHub.
mods.initfailed = [red]⚠[] The previous Mindustry instance failed to initialize. This was likely caused by misbehaving mods.\n\nTo prevent a crash loop, [red]all mods have been disabled.[]\n\nTo disable this feature, turn it off in [accent]Settings->Game->Disable Mods On Startup Crash[].
mods = Mods
mods.none = [lightgray]No mods found!
mods.guide = Modding Guide
@ -800,6 +800,7 @@ setting.logichints.name = Logic Hints
setting.flow.name = Display Resource Flow Rate
setting.backgroundpause.name = Pause In Background
setting.buildautopause.name = Auto-Pause Building
setting.modcrashdisable = Disable Mods On Startup Crash
setting.animatedwater.name = Animated Surfaces
setting.animatedshields.name = Animated Shields
setting.antialias.name = Antialias[lightgray] (requires restart)[]

View file

@ -34,6 +34,7 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
@Override
public void setup(){
checkLaunch();
loadLogger();
loader = new LoadRenderer();
@ -145,7 +146,12 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
finished = true;
Events.fire(new ClientLoadEvent());
super.resize(graphics.getWidth(), graphics.getHeight());
app.post(() -> app.post(() -> app.post(() -> app.post(() -> super.resize(graphics.getWidth(), graphics.getHeight())))));
app.post(() -> app.post(() -> app.post(() -> app.post(() -> {
super.resize(graphics.getWidth(), graphics.getHeight());
//mark initialization as complete
finishLaunch();
}))));
}
}else{
asyncCore.begin();
@ -168,6 +174,12 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
lastTime = Time.nanos();
}
@Override
public void exit(){
//on graceful exit, finish the launch normally.
Vars.finishLaunch();
}
@Override
public void init(){
setup();
@ -182,6 +194,11 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
@Override
public void pause(){
//when the user tabs out on mobile, the exit() event doesn't fire reliably - in that case, just assume they're about to kill the app
//this isn't 100% reliable but it should work for most cases
if(mobile){
Vars.finishLaunch();
}
if(finished){
super.pause();
}

View file

@ -32,6 +32,8 @@ import java.util.*;
import static arc.Core.*;
public class Vars implements Loadable{
/** Whether the game failed to launch last time. */
public static boolean failedToLaunch = false;
/** Whether to load locales.*/
public static boolean loadLocales = true;
/** Whether the logger is loaded. */
@ -172,6 +174,8 @@ public class Vars implements Loadable{
public static Fi schematicDirectory;
/** data subdirectory used for bleeding edge build versions */
public static Fi bebuildDirectory;
/** file used to store launch ID */
public static Fi launchIDFile;
/** empty map, indicates no current map */
public static Map emptyMap;
/** map file extension */
@ -284,6 +288,27 @@ public class Vars implements Loadable{
maps.load();
}
/** Checks if a launch failure occurred.
* If this is the case, failedToLaunch is set to true. */
public static void checkLaunch(){
settings.setAppName(appName);
launchIDFile = settings.getDataDirectory().child("launchid.dat");
if(launchIDFile.exists()){
failedToLaunch = true;
}else{
failedToLaunch = false;
launchIDFile.writeString("go away");
}
}
/** Cleans up after a successful launch. */
public static void finishLaunch(){
if(launchIDFile != null){
launchIDFile.delete();
}
}
public static void loadLogger(){
if(loadedLogger) return;

View file

@ -59,6 +59,15 @@ public class Control implements ApplicationListener, Loadable{
saves = new Saves();
sound = new SoundControl();
//show dialog saying that mod loading was skipped.
Events.on(ClientLoadEvent.class, e -> {
if(Vars.mods.skipModLoading() && Vars.mods.list().any()){
Time.runTask(4f, () -> {
ui.showInfo("@mods.initfailed");
});
}
});
Events.on(StateChangeEvent.class, event -> {
if((event.from == State.playing && event.to == State.menu) || (event.from == State.menu && event.to != State.menu)){
Time.runTask(5f, platform::updateRPC);

View file

@ -406,6 +406,12 @@ public class ContentParser{
this.currentMod = mod;
this.currentContent = cont;
run.run();
//check nulls after parsing
if(cont != null){
toBeParsed.remove(cont);
checkNullFields(cont);
}
});
}

View file

@ -259,6 +259,11 @@ public class Mods implements Loadable{
return requiresReload;
}
/** @return whether to skip mod loading due to previous initialization failure. */
public boolean skipModLoading(){
return failedToLaunch && Core.settings.getBool("modcrashdisable", true);
}
/** Loads all mods from the folder, but does not call any methods on them.*/
public void load(){
for(Fi file : modDirectory.list()){
@ -683,8 +688,13 @@ public class Mods implements Loadable{
//make sure the main class exists before loading it; if it doesn't just don't put it there
//if the mod is explicitly marked as java, try loading it anyway
if((mainFile.exists() || meta.java) &&
Core.settings.getBool("mod-" + baseName + "-enabled", true) && Version.isAtLeast(meta.minGameVersion) && (meta.getMinMajor() >= 105 || headless)){
if(
(mainFile.exists() || meta.java) &&
!skipModLoading() &&
Core.settings.getBool("mod-" + baseName + "-enabled", true) &&
Version.isAtLeast(meta.minGameVersion) &&
(meta.getMinMajor() >= 105 || headless)
){
if(ios){
throw new IllegalArgumentException("Java class mods are not supported on iOS.");
@ -711,11 +721,16 @@ public class Mods implements Loadable{
}
}
//skip mod loading if it failed
if(skipModLoading()){
Core.settings.put("mod-" + baseName + "-enabled", false);
}
if(!headless){
Log.info("Loaded mod '@' in @ms", meta.name, Time.elapsed());
}
return new LoadedMod(sourceFile, zip, mainMod, loader, meta);
return new LoadedMod(sourceFile, zip, mainMod, loader, meta);
}catch(Exception e){
//delete root zip file so it can be closed on windows
if(rootZip != null) rootZip.delete();

View file

@ -106,11 +106,6 @@ public class ModsDialog extends BaseDialog{
}
});
shown(() -> Core.app.post(() -> {
Core.settings.getBoolOnce("modsalpha", () -> {
ui.showText("@mods", "@mods.alphainfo");
});
}));
}
void modError(Throwable error){

View file

@ -327,6 +327,10 @@ public class SettingsMenuDialog extends SettingsDialog{
game.checkPref("buildautopause", false);
}
if(!ios){
game.checkPref("modcrashdisable", true);
}
if(steam){
game.sliderPref("playerlimit", 16, 2, 32, i -> {
platform.updateLobby();

View file

@ -1,10 +1,14 @@
package mindustry.world.blocks.environment;
import arc.util.*;
import mindustry.world.*;
//do not use in mods!
/**
* Do not use in mods. This class provides no new functionality, and is only used for the Mindustry sprite generator.
* Use the standard Floor class instead.
* */
public class ShallowLiquid extends Floor{
public Floor liquidBase, floorBase;
public @Nullable Floor liquidBase, floorBase;
public float liquidOpacity = 0.35f;
public ShallowLiquid(String name){

View file

@ -38,7 +38,7 @@ public class Drill extends Block{
public float warmupSpeed = 0.02f;
//return variables for countOre
protected Item returnItem;
protected @Nullable Item returnItem;
protected int returnCount;
/** Whether to draw the item this drill is mining. */

View file

@ -3,6 +3,7 @@ package mindustry.world.blocks.production;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.struct.*;
import arc.util.*;
import arc.util.io.*;
import mindustry.content.*;
import mindustry.entities.*;
@ -13,8 +14,8 @@ import mindustry.world.draw.*;
import mindustry.world.meta.*;
public class GenericCrafter extends Block{
public ItemStack outputItem;
public LiquidStack outputLiquid;
public @Nullable ItemStack outputItem;
public @Nullable LiquidStack outputLiquid;
public float craftTime = 80;
public Effect craftEffect = Fx.none;

View file

@ -1,3 +1,3 @@
org.gradle.daemon=true
org.gradle.jvmargs=-Xms256m -Xmx1024m
archash=e402c9743e107fb53ed6f069ae72a19a59fbfd3b
archash=f6caa903fd34c016a902b70c4d41690f5f022431