check sha1 of assets (optionally), hide all other windows, when fatal error occurred

This commit is contained in:
Bixilon 2020-10-23 20:08:05 +02:00
parent 16ef8d7b06
commit 9d12a40596
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
8 changed files with 107 additions and 25 deletions

View File

@ -34,6 +34,7 @@ import de.bixilon.minosoft.util.task.Task;
import de.bixilon.minosoft.util.task.TaskImportance; import de.bixilon.minosoft.util.task.TaskImportance;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.scene.control.Dialog; import javafx.scene.control.Dialog;
import javafx.stage.Stage;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
@ -85,11 +86,19 @@ public final class Minosoft {
System.exit(1); System.exit(1);
} }
} }
// hide all other gui parts
StartProgressWindow.hideDialog();
Launcher.exit();
Platform.runLater(() -> { Platform.runLater(() -> {
Dialog<Boolean> dialog = new Dialog<>(); Dialog<Boolean> dialog = new Dialog<>();
dialog.setTitle("Critical Error"); dialog.setTitle("Critical Error");
dialog.setHeaderText("An error occurred while starting Minosoft"); 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(); dialog.showAndWait();
System.exit(1); System.exit(1);
}); });
@ -101,7 +110,7 @@ public final class Minosoft {
taskWorker.addTask(new Task(progress -> { taskWorker.addTask(new Task(progress -> {
progress.countUp(); progress.countUp();
LocaleManager.load(config.getString(ConfigurationPaths.LANGUAGE)); LocaleManager.load(config.getString(ConfigurationPaths.GENERAL_LANGUAGE));
progress.countDown(); progress.countDown();
}, "Minosoft Language", "", Priorities.HIGH, TaskImportance.REQUIRED)); }, "Minosoft Language", "", Priorities.HIGH, TaskImportance.REQUIRED));
@ -143,7 +152,7 @@ public final class Minosoft {
taskWorker.addTask(new Task(progress -> { taskWorker.addTask(new Task(progress -> {
progress.countUp(); progress.countUp();
MinecraftLocaleManager.load(config.getString(ConfigurationPaths.LANGUAGE)); MinecraftLocaleManager.load(config.getString(ConfigurationPaths.GENERAL_LANGUAGE));
progress.countDown(); progress.countDown();
}, "Mojang language", "", Priorities.HIGH, TaskImportance.REQUIRED, new HashSet<>(Collections.singleton("Assets")))); }, "Mojang language", "", Priorities.HIGH, TaskImportance.REQUIRED, new HashSet<>(Collections.singleton("Assets"))));

View File

@ -91,6 +91,7 @@ public class Configuration {
public boolean getBoolean(ConfigurationPaths path) { public boolean getBoolean(ConfigurationPaths path) {
return switch (path) { return switch (path) {
case NETWORK_FAKE_CLIENT_BRAND -> config.getAsJsonObject("network").get("fake-network-brand").getAsBoolean(); 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)); 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) { public void putBoolean(ConfigurationPaths path, boolean value) {
switch (path) { switch (path) {
case NETWORK_FAKE_CLIENT_BRAND -> config.getAsJsonObject("network").addProperty("fake-network-brand", value); 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)); default -> throw new RuntimeException(String.format("Illegal boolean value: %s", path));
} }
} }
@ -122,7 +124,7 @@ public class Configuration {
return switch (path) { return switch (path) {
case ACCOUNT_SELECTED -> config.getAsJsonObject("accounts").get("selected").getAsString(); case ACCOUNT_SELECTED -> config.getAsJsonObject("accounts").get("selected").getAsString();
case GENERAL_LOG_LEVEL -> config.getAsJsonObject("general").get("log-level").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 MAPPINGS_URL -> config.getAsJsonObject("download").getAsJsonObject("urls").get("mappings").getAsString();
case CLIENT_TOKEN -> config.getAsJsonObject("accounts").get("client-token").getAsString(); case CLIENT_TOKEN -> config.getAsJsonObject("accounts").get("client-token").getAsString();
default -> throw new RuntimeException(String.format("Illegal String value: %s", path)); default -> throw new RuntimeException(String.format("Illegal String value: %s", path));
@ -133,7 +135,7 @@ public class Configuration {
switch (path) { switch (path) {
case ACCOUNT_SELECTED -> config.getAsJsonObject("accounts").addProperty("selected", value); case ACCOUNT_SELECTED -> config.getAsJsonObject("accounts").addProperty("selected", value);
case GENERAL_LOG_LEVEL -> config.getAsJsonObject("general").addProperty("log-level", 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 MAPPINGS_URL -> config.getAsJsonObject("download").getAsJsonObject("urls").addProperty("mappings", value);
case CLIENT_TOKEN -> config.getAsJsonObject("accounts").addProperty("client-token", value); case CLIENT_TOKEN -> config.getAsJsonObject("accounts").addProperty("client-token", value);
default -> throw new RuntimeException(String.format("Illegal String value: %s", path)); default -> throw new RuntimeException(String.format("Illegal String value: %s", path));

View File

@ -21,5 +21,6 @@ public enum ConfigurationPaths {
CLIENT_TOKEN, CLIENT_TOKEN,
MAPPINGS_URL, MAPPINGS_URL,
ACCOUNT_SELECTED, ACCOUNT_SELECTED,
LANGUAGE, GENERAL_LANGUAGE,
DEBUG_VERIFY_ASSETS,
} }

View File

@ -19,6 +19,8 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonReader;
import de.bixilon.minosoft.Config; 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.Log;
import de.bixilon.minosoft.logging.LogLevels; import de.bixilon.minosoft.logging.LogLevels;
import de.bixilon.minosoft.util.CountUpAndDownLatch; import de.bixilon.minosoft.util.CountUpAndDownLatch;
@ -146,7 +148,17 @@ public class AssetsManager {
private static boolean verifyAssetHash(String hash) { private static boolean verifyAssetHash(String hash) {
// file does not exist // 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 { public static void generateJarAssets() throws IOException {

View File

@ -33,11 +33,16 @@ import java.io.IOException;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
public class Launcher { public class Launcher {
private static Stage stage;
private static boolean exit = false;
public static void start() throws Exception { public static void start() throws Exception {
Log.info("Starting launcher..."); Log.info("Starting launcher...");
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
Platform.runLater(() -> { Platform.runLater(() -> {
if (exit) {
return;
}
Stage stage = new Stage(); Stage stage = new Stage();
GUITools.versionList.setCellFactory(new Callback<>() { GUITools.versionList.setCellFactory(new Callback<>() {
@ -73,14 +78,27 @@ public class Launcher {
stage.setTitle(LocaleManager.translate(Strings.MAIN_WINDOW_TITLE)); stage.setTitle(LocaleManager.translate(Strings.MAIN_WINDOW_TITLE));
stage.getIcons().add(GUITools.logo); stage.getIcons().add(GUITools.logo);
stage.show();
stage.setOnCloseRequest(windowEvent -> System.exit(0)); stage.setOnCloseRequest(windowEvent -> System.exit(0));
if (Minosoft.getSelectedAccount() == null) { if (Minosoft.getSelectedAccount() == null) {
MainWindow.manageAccounts(); MainWindow.manageAccounts();
} }
if (exit) {
return;
}
stage.show();
Launcher.stage = stage;
latch.countDown(); latch.countDown();
}); });
latch.await(); latch.await();
Log.info("Launcher started!"); Log.info("Launcher started!");
} }
public static void exit() {
exit = true;
if (stage == null) {
return;
}
Platform.runLater(() -> stage.close());
}
} }

View File

@ -29,26 +29,33 @@ import java.util.concurrent.atomic.AtomicReference;
public class StartProgressWindow extends Application { public class StartProgressWindow extends Application {
public static CountDownLatch toolkitLatch = new CountDownLatch(2); public static CountDownLatch toolkitLatch = new CountDownLatch(2);
private static Dialog<Boolean> progressDialog;
private static boolean exit = false;
public static void show(CountUpAndDownLatch progress) { public static void show(CountUpAndDownLatch progress) {
if (exit) {
return;
}
new Thread(() -> { new Thread(() -> {
if (progress.getCount() == 0) { if (progress.getCount() == 0) {
return; return;
} }
AtomicReference<ProgressBar> progressBar = new AtomicReference<>(); AtomicReference<ProgressBar> progressBar = new AtomicReference<>();
AtomicReference<Dialog<Boolean>> progressDialog = new AtomicReference<>();
Platform.runLater(() -> { Platform.runLater(() -> {
progressDialog.set(new Dialog<>()); progressDialog = new Dialog<>();
progressDialog.get().setTitle(LocaleManager.translate(Strings.MINOSOFT_STILL_STARTING_TITLE)); progressDialog.setTitle(LocaleManager.translate(Strings.MINOSOFT_STILL_STARTING_TITLE));
progressDialog.get().setHeaderText(LocaleManager.translate(Strings.MINOSOFT_STILL_STARTING_HEADER)); progressDialog.setHeaderText(LocaleManager.translate(Strings.MINOSOFT_STILL_STARTING_HEADER));
GridPane grid = new GridPane(); GridPane grid = new GridPane();
progressBar.set(new ProgressBar()); progressBar.set(new ProgressBar());
progressBar.get().setProgress(1.0F - ((float) progress.getCount() / progress.getTotal())); progressBar.get().setProgress(1.0F - ((float) progress.getCount() / progress.getTotal()));
grid.add(progressBar.get(), 0, 0); grid.add(progressBar.get(), 0, 0);
progressDialog.get().getDialogPane().setContent(grid); progressDialog.getDialogPane().setContent(grid);
progressDialog.get().show(); if (exit) {
return;
}
progressDialog.show();
Stage stage = (Stage) progressDialog.get().getDialogPane().getScene().getWindow(); Stage stage = (Stage) progressDialog.getDialogPane().getScene().getWindow();
stage.setAlwaysOnTop(true); stage.setAlwaysOnTop(true);
stage.toFront(); 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(() -> progressBar.get().setProgress(1.0F - ((float) progress.getCount() / progress.getTotal())));
} }
hideDialog();
Platform.runLater(() -> {
progressDialog.get().setResult(Boolean.TRUE);
progressDialog.get().hide();
});
}).start(); }).start();
} }
@ -77,6 +80,17 @@ public class StartProgressWindow extends Application {
Log.debug("Initialized JavaFX Toolkit!"); Log.debug("Initialized JavaFX Toolkit!");
} }
public static void hideDialog() {
exit = true;
if (progressDialog == null) {
return;
}
Platform.runLater(() -> {
progressDialog.setResult(Boolean.TRUE);
progressDialog.hide();
});
}
@Override @Override
public void start(Stage stage) { public void start(Stage stage) {
toolkitLatch.countDown(); toolkitLatch.countDown();

View File

@ -102,17 +102,40 @@ public final class Util {
} }
public static String sha1(byte[] data) { public static String sha1(byte[] data) {
ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
try { try {
MessageDigest crypt = MessageDigest.getInstance("SHA-1"); return sha1(inputStream);
crypt.reset(); } catch (IOException e) {
crypt.update(data);
return byteArrayToHexString(crypt.digest());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace(); e.printStackTrace();
} }
return null; 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) { public static String byteArrayToHexString(byte[] b) {
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
for (byte value : b) { for (byte value : b) {

View File

@ -23,5 +23,8 @@
"urls": { "urls": {
"mappings": "https://gitlab.com/Bixilon/minosoft/-/raw/master/data/mcdata/%s.tar.gz?inline=false" "mappings": "https://gitlab.com/Bixilon/minosoft/-/raw/master/data/mcdata/%s.tar.gz?inline=false"
} }
},
"debug": {
"verify-assets": true
} }
} }