diff --git a/src/main/java/de/bixilon/minosoft/Minosoft.java b/src/main/java/de/bixilon/minosoft/Minosoft.java index aba9f2eee..46b8de543 100644 --- a/src/main/java/de/bixilon/minosoft/Minosoft.java +++ b/src/main/java/de/bixilon/minosoft/Minosoft.java @@ -34,6 +34,7 @@ import de.bixilon.minosoft.util.task.Task; import de.bixilon.minosoft.util.task.TaskImportance; import javafx.application.Platform; import javafx.scene.control.Dialog; +import javafx.stage.Stage; import java.io.IOException; import java.util.*; @@ -85,11 +86,19 @@ public final class Minosoft { System.exit(1); } } + // hide all other gui parts + StartProgressWindow.hideDialog(); + Launcher.exit(); Platform.runLater(() -> { Dialog dialog = new Dialog<>(); dialog.setTitle("Critical Error"); dialog.setHeaderText("An error occurred while starting Minosoft"); - dialog.setContentText(exception.getMessage()); + dialog.setContentText(exception.getLocalizedMessage()); + + Stage stage = (Stage) dialog.getDialogPane().getScene().getWindow(); + stage.setAlwaysOnTop(true); + stage.toFront(); + dialog.setOnCloseRequest(dialogEvent -> System.exit(1)); dialog.showAndWait(); System.exit(1); }); @@ -101,7 +110,7 @@ public final class Minosoft { taskWorker.addTask(new Task(progress -> { progress.countUp(); - LocaleManager.load(config.getString(ConfigurationPaths.LANGUAGE)); + LocaleManager.load(config.getString(ConfigurationPaths.GENERAL_LANGUAGE)); progress.countDown(); }, "Minosoft Language", "", Priorities.HIGH, TaskImportance.REQUIRED)); @@ -143,7 +152,7 @@ public final class Minosoft { taskWorker.addTask(new Task(progress -> { progress.countUp(); - MinecraftLocaleManager.load(config.getString(ConfigurationPaths.LANGUAGE)); + MinecraftLocaleManager.load(config.getString(ConfigurationPaths.GENERAL_LANGUAGE)); progress.countDown(); }, "Mojang language", "", Priorities.HIGH, TaskImportance.REQUIRED, new HashSet<>(Collections.singleton("Assets")))); diff --git a/src/main/java/de/bixilon/minosoft/config/Configuration.java b/src/main/java/de/bixilon/minosoft/config/Configuration.java index 438f4e4af..aca3dab48 100644 --- a/src/main/java/de/bixilon/minosoft/config/Configuration.java +++ b/src/main/java/de/bixilon/minosoft/config/Configuration.java @@ -91,6 +91,7 @@ public class Configuration { public boolean getBoolean(ConfigurationPaths path) { return switch (path) { case NETWORK_FAKE_CLIENT_BRAND -> config.getAsJsonObject("network").get("fake-network-brand").getAsBoolean(); + case DEBUG_VERIFY_ASSETS -> config.getAsJsonObject("debug").get("verify-assets").getAsBoolean(); default -> throw new RuntimeException(String.format("Illegal boolean value: %s", path)); }; } @@ -98,6 +99,7 @@ public class Configuration { public void putBoolean(ConfigurationPaths path, boolean value) { switch (path) { case NETWORK_FAKE_CLIENT_BRAND -> config.getAsJsonObject("network").addProperty("fake-network-brand", value); + case DEBUG_VERIFY_ASSETS -> config.getAsJsonObject("debug").addProperty("verify-assets", value); default -> throw new RuntimeException(String.format("Illegal boolean value: %s", path)); } } @@ -122,7 +124,7 @@ public class Configuration { return switch (path) { case ACCOUNT_SELECTED -> config.getAsJsonObject("accounts").get("selected").getAsString(); case GENERAL_LOG_LEVEL -> config.getAsJsonObject("general").get("log-level").getAsString(); - case LANGUAGE -> config.getAsJsonObject("general").get("language").getAsString(); + case GENERAL_LANGUAGE -> config.getAsJsonObject("general").get("language").getAsString(); case MAPPINGS_URL -> config.getAsJsonObject("download").getAsJsonObject("urls").get("mappings").getAsString(); case CLIENT_TOKEN -> config.getAsJsonObject("accounts").get("client-token").getAsString(); default -> throw new RuntimeException(String.format("Illegal String value: %s", path)); @@ -133,7 +135,7 @@ public class Configuration { switch (path) { case ACCOUNT_SELECTED -> config.getAsJsonObject("accounts").addProperty("selected", value); case GENERAL_LOG_LEVEL -> config.getAsJsonObject("general").addProperty("log-level", value); - case LANGUAGE -> config.getAsJsonObject("general").addProperty("language", value); + case GENERAL_LANGUAGE -> config.getAsJsonObject("general").addProperty("language", value); case MAPPINGS_URL -> config.getAsJsonObject("download").getAsJsonObject("urls").addProperty("mappings", value); case CLIENT_TOKEN -> config.getAsJsonObject("accounts").addProperty("client-token", value); default -> throw new RuntimeException(String.format("Illegal String value: %s", path)); diff --git a/src/main/java/de/bixilon/minosoft/config/ConfigurationPaths.java b/src/main/java/de/bixilon/minosoft/config/ConfigurationPaths.java index 3adf3fb1b..2e22acbf3 100644 --- a/src/main/java/de/bixilon/minosoft/config/ConfigurationPaths.java +++ b/src/main/java/de/bixilon/minosoft/config/ConfigurationPaths.java @@ -21,5 +21,6 @@ public enum ConfigurationPaths { CLIENT_TOKEN, MAPPINGS_URL, ACCOUNT_SELECTED, - LANGUAGE, + GENERAL_LANGUAGE, + DEBUG_VERIFY_ASSETS, } \ No newline at end of file diff --git a/src/main/java/de/bixilon/minosoft/data/assets/AssetsManager.java b/src/main/java/de/bixilon/minosoft/data/assets/AssetsManager.java index 0bd9b1fde..e8bc4382d 100644 --- a/src/main/java/de/bixilon/minosoft/data/assets/AssetsManager.java +++ b/src/main/java/de/bixilon/minosoft/data/assets/AssetsManager.java @@ -19,6 +19,8 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.stream.JsonReader; import de.bixilon.minosoft.Config; +import de.bixilon.minosoft.Minosoft; +import de.bixilon.minosoft.config.ConfigurationPaths; import de.bixilon.minosoft.logging.Log; import de.bixilon.minosoft.logging.LogLevels; import de.bixilon.minosoft.util.CountUpAndDownLatch; @@ -146,7 +148,17 @@ public class AssetsManager { private static boolean verifyAssetHash(String hash) { // file does not exist - return getAssetSize(hash) != -1;// ToDo + if (getAssetSize(hash) == -1) { + return false; + } + if (!Minosoft.config.getBoolean(ConfigurationPaths.DEBUG_VERIFY_ASSETS)) { + return true; + } + try { + return hash.equals(Util.sha1Gzip(new File(getAssetDiskPath(hash)))); + } catch (IOException ignored) { + } + return false; } public static void generateJarAssets() throws IOException { diff --git a/src/main/java/de/bixilon/minosoft/gui/main/Launcher.java b/src/main/java/de/bixilon/minosoft/gui/main/Launcher.java index 09e941856..6eae8ca5f 100644 --- a/src/main/java/de/bixilon/minosoft/gui/main/Launcher.java +++ b/src/main/java/de/bixilon/minosoft/gui/main/Launcher.java @@ -33,11 +33,16 @@ import java.io.IOException; import java.util.concurrent.CountDownLatch; public class Launcher { + private static Stage stage; + private static boolean exit = false; public static void start() throws Exception { Log.info("Starting launcher..."); CountDownLatch latch = new CountDownLatch(1); Platform.runLater(() -> { + if (exit) { + return; + } Stage stage = new Stage(); GUITools.versionList.setCellFactory(new Callback<>() { @@ -73,14 +78,27 @@ public class Launcher { stage.setTitle(LocaleManager.translate(Strings.MAIN_WINDOW_TITLE)); stage.getIcons().add(GUITools.logo); - stage.show(); stage.setOnCloseRequest(windowEvent -> System.exit(0)); if (Minosoft.getSelectedAccount() == null) { MainWindow.manageAccounts(); } + if (exit) { + return; + } + stage.show(); + Launcher.stage = stage; latch.countDown(); }); latch.await(); Log.info("Launcher started!"); } + + public static void exit() { + exit = true; + if (stage == null) { + return; + } + + Platform.runLater(() -> stage.close()); + } } \ No newline at end of file diff --git a/src/main/java/de/bixilon/minosoft/gui/main/StartProgressWindow.java b/src/main/java/de/bixilon/minosoft/gui/main/StartProgressWindow.java index ea2a8797e..0f3ca694f 100644 --- a/src/main/java/de/bixilon/minosoft/gui/main/StartProgressWindow.java +++ b/src/main/java/de/bixilon/minosoft/gui/main/StartProgressWindow.java @@ -29,26 +29,33 @@ import java.util.concurrent.atomic.AtomicReference; public class StartProgressWindow extends Application { public static CountDownLatch toolkitLatch = new CountDownLatch(2); + private static Dialog progressDialog; + private static boolean exit = false; public static void show(CountUpAndDownLatch progress) { + if (exit) { + return; + } new Thread(() -> { if (progress.getCount() == 0) { return; } AtomicReference progressBar = new AtomicReference<>(); - AtomicReference> progressDialog = new AtomicReference<>(); Platform.runLater(() -> { - progressDialog.set(new Dialog<>()); - progressDialog.get().setTitle(LocaleManager.translate(Strings.MINOSOFT_STILL_STARTING_TITLE)); - progressDialog.get().setHeaderText(LocaleManager.translate(Strings.MINOSOFT_STILL_STARTING_HEADER)); + progressDialog = new Dialog<>(); + progressDialog.setTitle(LocaleManager.translate(Strings.MINOSOFT_STILL_STARTING_TITLE)); + progressDialog.setHeaderText(LocaleManager.translate(Strings.MINOSOFT_STILL_STARTING_HEADER)); GridPane grid = new GridPane(); progressBar.set(new ProgressBar()); progressBar.get().setProgress(1.0F - ((float) progress.getCount() / progress.getTotal())); grid.add(progressBar.get(), 0, 0); - progressDialog.get().getDialogPane().setContent(grid); - progressDialog.get().show(); + progressDialog.getDialogPane().setContent(grid); + if (exit) { + return; + } + progressDialog.show(); - Stage stage = (Stage) progressDialog.get().getDialogPane().getScene().getWindow(); + Stage stage = (Stage) progressDialog.getDialogPane().getScene().getWindow(); stage.setAlwaysOnTop(true); stage.toFront(); @@ -61,11 +68,7 @@ public class StartProgressWindow extends Application { } Platform.runLater(() -> progressBar.get().setProgress(1.0F - ((float) progress.getCount() / progress.getTotal()))); } - - Platform.runLater(() -> { - progressDialog.get().setResult(Boolean.TRUE); - progressDialog.get().hide(); - }); + hideDialog(); }).start(); } @@ -77,6 +80,17 @@ public class StartProgressWindow extends Application { Log.debug("Initialized JavaFX Toolkit!"); } + public static void hideDialog() { + exit = true; + if (progressDialog == null) { + return; + } + Platform.runLater(() -> { + progressDialog.setResult(Boolean.TRUE); + progressDialog.hide(); + }); + } + @Override public void start(Stage stage) { toolkitLatch.countDown(); diff --git a/src/main/java/de/bixilon/minosoft/util/Util.java b/src/main/java/de/bixilon/minosoft/util/Util.java index 27ac9f876..26de0a9bf 100644 --- a/src/main/java/de/bixilon/minosoft/util/Util.java +++ b/src/main/java/de/bixilon/minosoft/util/Util.java @@ -102,17 +102,40 @@ public final class Util { } public static String sha1(byte[] data) { + ByteArrayInputStream inputStream = new ByteArrayInputStream(data); try { - MessageDigest crypt = MessageDigest.getInstance("SHA-1"); - crypt.reset(); - crypt.update(data); - return byteArrayToHexString(crypt.digest()); - } catch (NoSuchAlgorithmException e) { + return sha1(inputStream); + } catch (IOException e) { e.printStackTrace(); } return null; } + public static String sha1(File file) throws IOException { + return sha1(new FileInputStream(file)); + } + + public static String sha1Gzip(File file) throws IOException { + return sha1(new GZIPInputStream(new FileInputStream(file))); + } + + public static String sha1(InputStream inputStream) throws IOException { + try { + MessageDigest crypt = MessageDigest.getInstance("SHA-1"); + crypt.reset(); + + byte[] buffer = new byte[4096]; + int length; + while ((length = inputStream.read(buffer, 0, 4096)) != -1) { + crypt.update(buffer, 0, length); + } + return byteArrayToHexString(crypt.digest()); + } catch (NoSuchAlgorithmException | FileNotFoundException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + public static String byteArrayToHexString(byte[] b) { StringBuilder result = new StringBuilder(); for (byte value : b) { diff --git a/src/main/resources/config/config.json b/src/main/resources/config/config.json index 47c88d3c7..4a0d77d74 100644 --- a/src/main/resources/config/config.json +++ b/src/main/resources/config/config.json @@ -23,5 +23,8 @@ "urls": { "mappings": "https://gitlab.com/Bixilon/minosoft/-/raw/master/data/mcdata/%s.tar.gz?inline=false" } + }, + "debug": { + "verify-assets": true } } \ No newline at end of file