diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/Main.java b/HMCL/src/main/java/org/jackhuang/hmcl/Main.java index ab0a06685..b32879ece 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/Main.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/Main.java @@ -32,6 +32,7 @@ import org.jackhuang.hmcl.util.*; import java.io.File; import java.util.Arrays; import java.util.ResourceBundle; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; public final class Main extends Application { @@ -89,6 +90,10 @@ public final class Main extends Application { Logging.LOG.info("Shutting down executor services."); Schedulers.shutdown(); + + Controllers.shutdown(); + + Lang.executeDelayed(OperatingSystem::forceGC, TimeUnit.SECONDS, 5, true); }); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java index ce429aea3..09134092e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -143,21 +143,26 @@ public final class LauncherHelper { @Override public void onFinished(Task task) { finished.incrementAndGet(); - Platform.runLater(() -> { - launchingStepsPane.setProgress(1.0 * finished.get() / executor.getRunningTasks()); - }); + int runningTasks = executor.getRunningTasks(); + Platform.runLater(() -> launchingStepsPane.setProgress(1.0 * finished.get() / runningTasks)); } @Override - public void onTerminate() { - Platform.runLater(() -> { - if (executor.getLastException() != null) - Controllers.dialog(I18nException.getStackTrace(executor.getLastException()), - scriptFile == null ? Main.i18n("launch.failed") : Main.i18n("version.launch_script.failed"), - MessageBox.ERROR_MESSAGE, Controllers::closeDialog); - else - Controllers.closeDialog(); - }); + public void onStop(boolean success, TaskExecutor executor) { + if (success) { + Controllers.closeDialog(); + } else { + Platform.runLater(() -> { + if (executor.getLastException() != null) + Controllers.dialog(I18nException.getStackTrace(executor.getLastException()), + scriptFile == null ? Main.i18n("launch.failed") : Main.i18n("version.launch_script.failed"), + MessageBox.ERROR_MESSAGE, Controllers::closeDialog); + else + Controllers.closeDialog(); + }); + } + + LauncherHelper.this.executor = null; } }); @@ -217,8 +222,6 @@ public final class LauncherHelper { } public void emitStatus(LoadingState state) { - if (state == LoadingState.DONE) - Controllers.closeDialog(); launchingStepsPane.setTitle(state.getLocalizedMessage()); launchingStepsPane.setSubtitle((state.ordinal() + 1) + " / " + LoadingState.values().length); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java index 6173a4818..1775d91fd 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -39,7 +39,7 @@ public final class Controllers { private static Scene scene; private static Stage stage; - private static MainPage mainPage = new MainPage(); + private static MainPage mainPage = null; private static SettingsPage settingsPage = null; private static VersionPage versionPage = null; private static AuthlibInjectorServersPage serversPage = null; @@ -75,11 +75,14 @@ public final class Controllers { return serversPage; } + // FXThread public static Decorator getDecorator() { return decorator; } public static MainPage getMainPage() { + if (mainPage == null) + mainPage = new MainPage(); return mainPage; } @@ -90,7 +93,7 @@ public final class Controllers { public static void initialize(Stage stage) { Controllers.stage = stage; - decorator = new Decorator(stage, mainPage, Main.TITLE, false, true); + decorator = new Decorator(stage, getMainPage(), Main.TITLE, false, true); decorator.showPage(null); leftPaneController = new LeftPaneController(decorator.getLeftPane()); @@ -167,4 +170,14 @@ public final class Controllers { public static void showUpdate() { getDecorator().showUpdate(); } + + public static void shutdown() { + mainPage = null; + settingsPage = null; + versionPage = null; + serversPage = null; + decorator = null; + stage = null; + scene = null; + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MultiFileItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MultiFileItem.java index 2c6d43046..720610781 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MultiFileItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MultiFileItem.java @@ -32,6 +32,7 @@ import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.control.Toggle; import javafx.scene.control.ToggleGroup; +import javafx.scene.control.Tooltip; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; @@ -50,6 +51,7 @@ public class MultiFileItem extends ComponentList { private final StringProperty customTitle = new SimpleStringProperty(this, "customTitle", Main.i18n("selector.custom")); private final StringProperty chooserTitle = new SimpleStringProperty(this, "chooserTitle", Main.i18n("selector.choose_file")); private final BooleanProperty directory = new SimpleBooleanProperty(this, "directory", false); + private final SimpleStringProperty tooltip = new SimpleStringProperty(this, "tooltip"); private ObservableList extensionFilters = FXCollections.observableArrayList(); private final ToggleGroup group = new ToggleGroup(); @@ -109,6 +111,10 @@ public class MultiFileItem extends ComponentList { if (toggleSelectedListener != null) toggleSelectedListener.accept(newValue); }); + + Tooltip tip = new Tooltip(); + tip.textProperty().bind(tooltipProperty()); + Tooltip.install(this, tip); } public Node createChildren(String title) { @@ -223,4 +229,16 @@ public class MultiFileItem extends ComponentList { public ObservableList getExtensionFilters() { return extensionFilters; } + + public String getTooltip() { + return tooltip.get(); + } + + public StringProperty tooltipProperty() { + return tooltip; + } + + public void setTooltip(String tooltip) { + this.tooltip.set(tooltip); + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/NumberValidator.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/NumberValidator.java index 5a3cc6169..c1feb0f13 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/NumberValidator.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/NumberValidator.java @@ -49,7 +49,7 @@ public class NumberValidator extends ValidatorBase { TextInputControl textField = ((TextInputControl) srcControl.get()); if (StringUtils.isBlank(textField.getText())) - hasErrors.set(nullable); + hasErrors.set(!nullable); else try { Integer.parseInt(textField.getText()); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskExecutorDialogWizardDisplayer.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskExecutorDialogWizardDisplayer.java index 8c59e7891..a1dea6b39 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskExecutorDialogWizardDisplayer.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskExecutorDialogWizardDisplayer.java @@ -57,24 +57,23 @@ public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplay } JFXUtilities.runInFX(() -> { - TaskExecutor executor = task.executor(e -> new TaskListener() { + TaskExecutor executor = task.executor(new TaskListener() { @Override - public void onSucceed() { - if (settings.containsKey("success_message") && settings.get("success_message") instanceof String) - JFXUtilities.runInFX(() -> Controllers.dialog((String) settings.get("success_message"), null, MessageBox.FINE_MESSAGE, () -> Controllers.navigate(null))); - else if (!settings.containsKey("forbid_success_message")) - JFXUtilities.runInFX(() -> Controllers.dialog(Main.i18n("message.success"), null, MessageBox.FINE_MESSAGE, () -> Controllers.navigate(null))); - } - - @Override - public void onTerminate() { - if (e.getLastException() == null) - return; - String appendix = StringUtils.getStackTrace(e.getLastException()); - if (settings.containsKey("failure_message") && settings.get("failure_message") instanceof String) - JFXUtilities.runInFX(() -> Controllers.dialog(appendix, (String) settings.get("failure_message"), MessageBox.ERROR_MESSAGE, () -> Controllers.navigate(null))); - else if (!settings.containsKey("forbid_failure_message")) - JFXUtilities.runInFX(() -> Controllers.dialog(appendix, Main.i18n("wizard.failed"), MessageBox.ERROR_MESSAGE, () -> Controllers.navigate(null))); + public void onStop(boolean success, TaskExecutor executor) { + if (success) { + if (settings.containsKey("success_message") && settings.get("success_message") instanceof String) + JFXUtilities.runInFX(() -> Controllers.dialog((String) settings.get("success_message"), null, MessageBox.FINE_MESSAGE, () -> Controllers.navigate(null))); + else if (!settings.containsKey("forbid_success_message")) + JFXUtilities.runInFX(() -> Controllers.dialog(Main.i18n("message.success"), null, MessageBox.FINE_MESSAGE, () -> Controllers.navigate(null))); + } else { + if (executor.getLastException() == null) + return; + String appendix = StringUtils.getStackTrace(executor.getLastException()); + if (settings.containsKey("failure_message") && settings.get("failure_message") instanceof String) + JFXUtilities.runInFX(() -> Controllers.dialog(appendix, (String) settings.get("failure_message"), MessageBox.ERROR_MESSAGE, () -> Controllers.navigate(null))); + else if (!settings.containsKey("forbid_failure_message")) + JFXUtilities.runInFX(() -> Controllers.dialog(appendix, Main.i18n("wizard.failed"), MessageBox.ERROR_MESSAGE, () -> Controllers.navigate(null))); + } } }); pane.setExecutor(executor); diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 55a34ed05..8a1309b17 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -187,7 +187,6 @@ launcher=Launcher launcher.background=Background Image launcher.background.choose=Choose background path. launcher.background.default=Default -launcher.background.tooltip=The laucher uses a default background.\nIf you use custom background.png, link it and it will be used.\nIf there is "bg" subdirectory, this app will chooses one picture in "bgskin" randomly.\nIf you set the background setting, this app will use it. launcher.common_directory=Common Directory launcher.common_directory.choose=Choose common directory. launcher.contact=Contact Us diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 266ca838a..24a065c15 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -186,8 +186,7 @@ launch.wrong_javadir=错误的Java路径,将自动重置为默认Java路径。 launcher=启动器 launcher.background=背景地址 launcher.background.choose=选择背景路径 -launcher.background.default=默认 -launcher.background.tooltip=启动器默认使用自带的背景\n如果当前目录有background.png,则会使用该文件作为背景\n如果当前目录有bg子目录,则会随机使用里面的一张图作为背景\n如果该背景地址被修改,则会使用背景地址里的一张图作为背景\n背景地址允许有多个地址,使用半角分号";"(不包含双引号)分隔 +launcher.background.default=默认(自动检索启动器同目录下的background.png/jpg及bg文件夹内的图片) launcher.common_directory=公共文件夹 launcher.common_directory.choose=选择公共文件夹 launcher.contact=联系我们 diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/AuthlibInjectorAccount.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/AuthlibInjectorAccount.java index 9407b8bf0..570782f72 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/AuthlibInjectorAccount.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/AuthlibInjectorAccount.java @@ -40,7 +40,7 @@ public class AuthlibInjectorAccount extends YggdrasilAccount { Arguments arguments = Arguments.addJVMArguments(null, arg); if (flag.get()) - arguments = Arguments.addJVMArguments(arguments, "-Dorg.to2mbn.authlibinjector.config.prefetched=" + getTask.getResult()); + arguments = Arguments.addJVMArguments(arguments, "-Dorg.to2mbn.authlibinjector.config.prefetched=" + new String(Base64.getEncoder().encode(getTask.getResult().getBytes()))); return info.setArguments(arguments); } catch (Exception e) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilAccount.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilAccount.java index e8f49e673..10bd58e6f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilAccount.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilAccount.java @@ -166,7 +166,7 @@ public class YggdrasilAccount extends Account { isOnline = true; return; } - logIn1(routeRefresh, new RefreshRequest(accessToken, clientToken), proxy); + logIn1(routeRefresh, new RefreshRequest(accessToken, clientToken, getSelectedProfile()), proxy); } public void logInWithPassword0(String password, Proxy proxy) throws AuthenticationException { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java index ec8dc050f..34811fda2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java @@ -208,9 +208,9 @@ public abstract class Task { return new TaskExecutor(this); } - public final TaskExecutor executor(Function taskListener) { + public final TaskExecutor executor(TaskListener taskListener) { TaskExecutor executor = new TaskExecutor(this); - executor.addTaskListener(taskListener.apply(executor)); + executor.addTaskListener(taskListener); return executor; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java index bef84a607..c1397dd4d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java @@ -66,13 +66,8 @@ public final class TaskExecutor { public TaskExecutor start() { taskListeners.forEach(TaskListener::onStart); workerQueue.add(scheduler.schedule(() -> { - if (executeTasks(Collections.singleton(firstTask))) - taskListeners.forEach(TaskListener::onSucceed); - else - taskListeners.forEach(it -> { - it.onTerminate(); - it.onTerminate(variables); - }); + boolean flag = executeTasks(Collections.singleton(firstTask)); + taskListeners.forEach(it -> it.onStop(flag, this)); })); return this; } @@ -81,14 +76,8 @@ public final class TaskExecutor { taskListeners.forEach(TaskListener::onStart); AtomicBoolean flag = new AtomicBoolean(true); Future future = scheduler.schedule(() -> { - if (!executeTasks(Collections.singleton(firstTask))) { - taskListeners.forEach(it -> { - it.onTerminate(); - it.onTerminate(variables); - }); - flag.set(false); - } else - taskListeners.forEach(TaskListener::onSucceed); + flag.set(executeTasks(Collections.singleton(firstTask))); + taskListeners.forEach(it -> it.onStop(flag.get(), this)); }); workerQueue.add(future); Lang.invoke(() -> future.get()); @@ -207,6 +196,10 @@ public final class TaskExecutor { return totTask.get(); } + public AutoTypingMap getVariables() { + return variables; + } + private class Invoker implements ExceptionalRunnable { private final Task task; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskListener.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskListener.java index 5f160bf14..4c8bd0872 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskListener.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskListener.java @@ -39,16 +39,12 @@ public abstract class TaskListener implements EventListener { public void onFailed(Task task, Throwable throwable) { } - public void onTerminate() { - } - - public void onTerminate(AutoTypingMap variables) { - } - - public void onSucceed() { + public void onStop(boolean success, TaskExecutor executor) { } public static class DefaultTaskListener extends TaskListener { + private DefaultTaskListener() { + } public static final DefaultTaskListener INSTANCE = new DefaultTaskListener(); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java index 40b7f4256..c41211ed9 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java @@ -8,6 +8,7 @@ package org.jackhuang.hmcl.util; import com.google.gson.JsonParseException; import java.util.*; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; @@ -249,6 +250,17 @@ public final class Lang { return result; } + public static void executeDelayed(Runnable runnable, TimeUnit timeUnit, long timeout, boolean isDaemon) { + thread(() -> { + try { + timeUnit.sleep(timeout); + runnable.run(); + } catch (InterruptedException ignore) { + } + + }, null, isDaemon); + } + public static Thread thread(Runnable runnable) { return thread(runnable, null); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/OperatingSystem.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/OperatingSystem.java index abe9af5d2..70389422f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/OperatingSystem.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/OperatingSystem.java @@ -122,4 +122,10 @@ public enum OperatingSystem { c.putString(string); Clipboard.getSystemClipboard().setContent(c); } + + public static void forceGC() { + System.gc(); + System.runFinalization(); + System.gc(); + } }