mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-08-04 03:46:57 -04:00
GC after game launched
This commit is contained in:
parent
4ad172dda4
commit
20a7039f59
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -143,13 +143,15 @@ 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() {
|
||||
public void onStop(boolean success, TaskExecutor executor) {
|
||||
if (success) {
|
||||
Controllers.closeDialog();
|
||||
} else {
|
||||
Platform.runLater(() -> {
|
||||
if (executor.getLastException() != null)
|
||||
Controllers.dialog(I18nException.getStackTrace(executor.getLastException()),
|
||||
@ -159,6 +161,9 @@ public final class LauncherHelper {
|
||||
Controllers.closeDialog();
|
||||
});
|
||||
}
|
||||
|
||||
LauncherHelper.this.executor = null;
|
||||
}
|
||||
});
|
||||
|
||||
executor.start();
|
||||
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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<FileChooser.ExtensionFilter> 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<FileChooser.ExtensionFilter> getExtensionFilters() {
|
||||
return extensionFilters;
|
||||
}
|
||||
|
||||
public String getTooltip() {
|
||||
return tooltip.get();
|
||||
}
|
||||
|
||||
public StringProperty tooltipProperty() {
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
public void setTooltip(String tooltip) {
|
||||
this.tooltip.set(tooltip);
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -57,25 +57,24 @@ public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplay
|
||||
}
|
||||
|
||||
JFXUtilities.runInFX(() -> {
|
||||
TaskExecutor executor = task.executor(e -> new TaskListener() {
|
||||
TaskExecutor executor = task.executor(new TaskListener() {
|
||||
@Override
|
||||
public void onSucceed() {
|
||||
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)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTerminate() {
|
||||
if (e.getLastException() == null)
|
||||
} else {
|
||||
if (executor.getLastException() == null)
|
||||
return;
|
||||
String appendix = StringUtils.getStackTrace(e.getLastException());
|
||||
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);
|
||||
Controllers.dialog(pane);
|
||||
|
@ -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
|
||||
|
@ -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=联系我们
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -208,9 +208,9 @@ public abstract class Task {
|
||||
return new TaskExecutor(this);
|
||||
}
|
||||
|
||||
public final TaskExecutor executor(Function<TaskExecutor, TaskListener> taskListener) {
|
||||
public final TaskExecutor executor(TaskListener taskListener) {
|
||||
TaskExecutor executor = new TaskExecutor(this);
|
||||
executor.addTaskListener(taskListener.apply(executor));
|
||||
executor.addTaskListener(taskListener);
|
||||
return executor;
|
||||
}
|
||||
|
||||
|
@ -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<String> getVariables() {
|
||||
return variables;
|
||||
}
|
||||
|
||||
private class Invoker implements ExceptionalRunnable<Exception> {
|
||||
|
||||
private final Task task;
|
||||
|
@ -39,16 +39,12 @@ public abstract class TaskListener implements EventListener {
|
||||
public void onFailed(Task task, Throwable throwable) {
|
||||
}
|
||||
|
||||
public void onTerminate() {
|
||||
}
|
||||
|
||||
public void onTerminate(AutoTypingMap<String> 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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -122,4 +122,10 @@ public enum OperatingSystem {
|
||||
c.putString(string);
|
||||
Clipboard.getSystemClipboard().setContent(c);
|
||||
}
|
||||
|
||||
public static void forceGC() {
|
||||
System.gc();
|
||||
System.runFinalization();
|
||||
System.gc();
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user