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 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<Boolean> 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"))));

View File

@ -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));

View File

@ -21,5 +21,6 @@ public enum ConfigurationPaths {
CLIENT_TOKEN,
MAPPINGS_URL,
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.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 {

View File

@ -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());
}
}

View File

@ -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<Boolean> progressDialog;
private static boolean exit = false;
public static void show(CountUpAndDownLatch progress) {
if (exit) {
return;
}
new Thread(() -> {
if (progress.getCount() == 0) {
return;
}
AtomicReference<ProgressBar> progressBar = new AtomicReference<>();
AtomicReference<Dialog<Boolean>> 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();

View File

@ -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) {

View File

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