mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-18 00:10:33 -04:00
Launching messages
This commit is contained in:
parent
0a4944f120
commit
f144dd888d
@ -24,8 +24,7 @@ import org.jackhuang.hmcl.auth.Account;
|
|||||||
import org.jackhuang.hmcl.auth.AuthInfo;
|
import org.jackhuang.hmcl.auth.AuthInfo;
|
||||||
import org.jackhuang.hmcl.auth.AuthenticationException;
|
import org.jackhuang.hmcl.auth.AuthenticationException;
|
||||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||||
import org.jackhuang.hmcl.launch.DefaultLauncher;
|
import org.jackhuang.hmcl.launch.*;
|
||||||
import org.jackhuang.hmcl.launch.ProcessListener;
|
|
||||||
import org.jackhuang.hmcl.mod.CurseCompletionTask;
|
import org.jackhuang.hmcl.mod.CurseCompletionTask;
|
||||||
import org.jackhuang.hmcl.setting.LauncherVisibility;
|
import org.jackhuang.hmcl.setting.LauncherVisibility;
|
||||||
import org.jackhuang.hmcl.setting.Profile;
|
import org.jackhuang.hmcl.setting.Profile;
|
||||||
@ -34,6 +33,7 @@ import org.jackhuang.hmcl.setting.VersionSetting;
|
|||||||
import org.jackhuang.hmcl.task.*;
|
import org.jackhuang.hmcl.task.*;
|
||||||
import org.jackhuang.hmcl.ui.Controllers;
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
import org.jackhuang.hmcl.ui.DialogController;
|
import org.jackhuang.hmcl.ui.DialogController;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
||||||
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
|
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
|
||||||
import org.jackhuang.hmcl.ui.LogWindow;
|
import org.jackhuang.hmcl.ui.LogWindow;
|
||||||
import org.jackhuang.hmcl.ui.construct.MessageBox;
|
import org.jackhuang.hmcl.ui.construct.MessageBox;
|
||||||
@ -62,10 +62,12 @@ public final class LauncherHelper {
|
|||||||
Version version = repository.getVersion(selectedVersion);
|
Version version = repository.getVersion(selectedVersion);
|
||||||
VersionSetting setting = profile.getVersionSetting(selectedVersion);
|
VersionSetting setting = profile.getVersionSetting(selectedVersion);
|
||||||
|
|
||||||
try {
|
Platform.runLater(() -> {
|
||||||
checkGameState(profile, setting, version, () -> launch0(selectedVersion, scriptFile));
|
try {
|
||||||
} catch (InterruptedException ignore) {
|
checkGameState(profile, setting, version, () -> Schedulers.newThread().schedule(() -> launch0(selectedVersion, scriptFile)));
|
||||||
}
|
} catch (InterruptedException ignore) {
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void launch0(String selectedVersion, File scriptFile) {
|
private void launch0(String selectedVersion, File scriptFile) {
|
||||||
@ -97,20 +99,31 @@ public final class LauncherHelper {
|
|||||||
));
|
));
|
||||||
}))
|
}))
|
||||||
.then(variables -> {
|
.then(variables -> {
|
||||||
|
DefaultLauncher launcher = variables.<DefaultLauncher>get("launcher");
|
||||||
if (scriptFile == null) {
|
if (scriptFile == null) {
|
||||||
return variables.<DefaultLauncher>get("launcher").launchAsync().setName(Main.i18n("version.launch"));
|
return new LaunchTask<>(launcher::launch).setName(Main.i18n("version.launch"));
|
||||||
} else {
|
} else {
|
||||||
return variables.<DefaultLauncher>get("launcher").makeLaunchScriptAsync(scriptFile).setName(Main.i18n("version.launch"));
|
return new LaunchTask<>(() -> {
|
||||||
|
launcher.makeLaunchScript(scriptFile);
|
||||||
|
return null;
|
||||||
|
}).setName(Main.i18n("version.launch_script"));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(Task.of(variables -> {
|
.then(Task.of(variables -> {
|
||||||
if (scriptFile == null) {
|
if (scriptFile == null) {
|
||||||
PROCESSES.add(variables.get(DefaultLauncher.LAUNCH_ASYNC_ID));
|
ManagedProcess process = variables.get(LaunchTask.LAUNCH_ID);
|
||||||
|
PROCESSES.add(process);
|
||||||
if (setting.getLauncherVisibility() == LauncherVisibility.CLOSE)
|
if (setting.getLauncherVisibility() == LauncherVisibility.CLOSE)
|
||||||
Main.stopApplication();
|
Main.stopApplication();
|
||||||
|
else
|
||||||
|
launchingStepsPane.setCancel(() -> {
|
||||||
|
process.stop();
|
||||||
|
Controllers.closeDialog();
|
||||||
|
});
|
||||||
} else
|
} else
|
||||||
Platform.runLater(() ->
|
Platform.runLater(() ->
|
||||||
Controllers.dialog(Main.i18n("version.launch_script.success", scriptFile.getAbsolutePath())));
|
Controllers.dialog(Main.i18n("version.launch_script.success", scriptFile.getAbsolutePath())));
|
||||||
|
|
||||||
}))
|
}))
|
||||||
.executor();
|
.executor();
|
||||||
|
|
||||||
@ -129,7 +142,9 @@ public final class LauncherHelper {
|
|||||||
@Override
|
@Override
|
||||||
public void onTerminate() {
|
public void onTerminate() {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
Controllers.dialog(StringUtils.getStackTrace(executor.getLastException()), Main.i18n("launch.failed"), MessageBox.ERROR_MESSAGE, Controllers::closeDialog);
|
Controllers.dialog(I18nException.getStackTrace(executor.getLastException()),
|
||||||
|
scriptFile == null ? Main.i18n("launch.failed") : Main.i18n("version.launch_script.failed"),
|
||||||
|
MessageBox.ERROR_MESSAGE, Controllers::closeDialog);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -138,7 +153,7 @@ public final class LauncherHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void checkGameState(Profile profile, VersionSetting setting, Version version, Runnable onAccept) throws InterruptedException {
|
private static void checkGameState(Profile profile, VersionSetting setting, Version version, Runnable onAccept) throws InterruptedException {
|
||||||
boolean flag = false;
|
boolean flag = false, suggest = true;
|
||||||
|
|
||||||
VersionNumber gameVersion = VersionNumber.asVersion(GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version)));
|
VersionNumber gameVersion = VersionNumber.asVersion(GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version)));
|
||||||
JavaVersion java = setting.getJavaVersion();
|
JavaVersion java = setting.getJavaVersion();
|
||||||
@ -156,6 +171,7 @@ public final class LauncherHelper {
|
|||||||
|
|
||||||
if (java.getParsedVersion() >= JavaVersion.JAVA_9 && gameVersion.compareTo(VersionNumber.asVersion("1.12.5")) < 0 && version.getMainClass().contains("launchwrapper")) {
|
if (java.getParsedVersion() >= JavaVersion.JAVA_9 && gameVersion.compareTo(VersionNumber.asVersion("1.12.5")) < 0 && version.getMainClass().contains("launchwrapper")) {
|
||||||
Controllers.dialog(Main.i18n("launch.advice.java9"), Main.i18n("message.error"), MessageBox.ERROR_MESSAGE, null);
|
Controllers.dialog(Main.i18n("launch.advice.java9"), Main.i18n("message.error"), MessageBox.ERROR_MESSAGE, null);
|
||||||
|
suggest = false;
|
||||||
flag = true;
|
flag = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,7 +190,10 @@ public final class LauncherHelper {
|
|||||||
flag = true;
|
flag = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!flag)
|
if (flag) {
|
||||||
|
if (suggest && Controllers.getDialogContent() instanceof MessageDialogPane)
|
||||||
|
((MessageDialogPane) Controllers.getDialogContent()).disableClosingDialog();
|
||||||
|
} else
|
||||||
onAccept.run();
|
onAccept.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,6 +234,34 @@ public final class LauncherHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LaunchTask<T> extends TaskResult<T> {
|
||||||
|
private final ExceptionalSupplier<T, ?> supplier;
|
||||||
|
|
||||||
|
public LaunchTask(ExceptionalSupplier<T, ?> supplier) {
|
||||||
|
this.supplier = supplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
try {
|
||||||
|
setResult(supplier.get());
|
||||||
|
} catch (PermissionException e) {
|
||||||
|
throw new I18nException(Main.i18n("launch.failed.executable_permission"), e);
|
||||||
|
} catch (ProcessCreationException e) {
|
||||||
|
throw new I18nException(Main.i18n("launch.failed.creating_process") + e.getLocalizedMessage(), e);
|
||||||
|
} catch (NotDecompressingNativesException e) {
|
||||||
|
throw new I18nException(Main.i18n("launch.failed.decompressing_natives") + e.getLocalizedMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return LAUNCH_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
static final String LAUNCH_ID = "launch";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The managed process listener.
|
* The managed process listener.
|
||||||
* Guarantee that one [JavaProcess], one [HMCLProcessListener].
|
* Guarantee that one [JavaProcess], one [HMCLProcessListener].
|
||||||
@ -302,9 +349,22 @@ public final class LauncherHelper {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onExit(int exitCode, ExitType exitType) {
|
public void onExit(int exitCode, ExitType exitType) {
|
||||||
|
if (exitType == ExitType.INTERRUPTED)
|
||||||
|
return;
|
||||||
|
|
||||||
if (exitType != ExitType.NORMAL && logWindow == null)
|
if (exitType != ExitType.NORMAL && logWindow == null)
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
logWindow = new LogWindow();
|
logWindow = new LogWindow();
|
||||||
|
|
||||||
|
switch (exitType) {
|
||||||
|
case JVM_ERROR:
|
||||||
|
logWindow.setTitle(Main.i18n("launch.failed.cannot_create_jvm"));
|
||||||
|
break;
|
||||||
|
case APPLICATION_ERROR:
|
||||||
|
logWindow.setTitle(Main.i18n("launch.failed.exited_abnormally"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
logWindow.show();
|
logWindow.show();
|
||||||
logWindow.onDone.register(() -> {
|
logWindow.onDone.register(() -> {
|
||||||
for (Map.Entry<String, Log4jLevel> entry : logs)
|
for (Map.Entry<String, Log4jLevel> entry : logs)
|
||||||
|
@ -33,6 +33,8 @@ public final class MessageDialogPane extends StackPane {
|
|||||||
private final String text;
|
private final String text;
|
||||||
private final JFXDialog dialog;
|
private final JFXDialog dialog;
|
||||||
|
|
||||||
|
private boolean closingDialog = true;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private JFXButton acceptButton;
|
private JFXButton acceptButton;
|
||||||
@FXML
|
@FXML
|
||||||
@ -57,7 +59,8 @@ public final class MessageDialogPane extends StackPane {
|
|||||||
|
|
||||||
content.setText(text);
|
content.setText(text);
|
||||||
acceptButton.setOnMouseClicked(e -> {
|
acceptButton.setOnMouseClicked(e -> {
|
||||||
dialog.close();
|
if (closingDialog)
|
||||||
|
dialog.close();
|
||||||
Optional.ofNullable(onAccept).ifPresent(Runnable::run);
|
Optional.ofNullable(onAccept).ifPresent(Runnable::run);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -84,7 +87,8 @@ public final class MessageDialogPane extends StackPane {
|
|||||||
|
|
||||||
cancelButton.setVisible(true);
|
cancelButton.setVisible(true);
|
||||||
cancelButton.setOnMouseClicked(e -> {
|
cancelButton.setOnMouseClicked(e -> {
|
||||||
dialog.close();
|
if (closingDialog)
|
||||||
|
dialog.close();
|
||||||
Optional.ofNullable(onCancel).ifPresent(Runnable::run);
|
Optional.ofNullable(onCancel).ifPresent(Runnable::run);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -95,4 +99,8 @@ public final class MessageDialogPane extends StackPane {
|
|||||||
|
|
||||||
graphic.setGraphic(SVG.help_circle("black", 40, 40));
|
graphic.setGraphic(SVG.help_circle("black", 40, 40));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void disableClosingDialog() {
|
||||||
|
closingDialog = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui.construct;
|
package org.jackhuang.hmcl.ui.construct;
|
||||||
|
|
||||||
|
import com.jfoenix.concurrency.JFXUtilities;
|
||||||
import com.jfoenix.controls.JFXButton;
|
import com.jfoenix.controls.JFXButton;
|
||||||
import com.jfoenix.controls.JFXProgressBar;
|
import com.jfoenix.controls.JFXProgressBar;
|
||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
@ -30,6 +31,7 @@ import java.util.Optional;
|
|||||||
|
|
||||||
public class TaskExecutorDialogPane extends StackPane {
|
public class TaskExecutorDialogPane extends StackPane {
|
||||||
private TaskExecutor executor;
|
private TaskExecutor executor;
|
||||||
|
private Runnable onCancel;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private JFXProgressBar progressBar;
|
private JFXProgressBar progressBar;
|
||||||
@ -48,12 +50,11 @@ public class TaskExecutorDialogPane extends StackPane {
|
|||||||
FXUtils.limitHeight(this, 200);
|
FXUtils.limitHeight(this, 200);
|
||||||
FXUtils.limitWidth(this, 400);
|
FXUtils.limitWidth(this, 400);
|
||||||
|
|
||||||
if (cancel == null)
|
setCancel(cancel);
|
||||||
btnCancel.setDisable(true);
|
|
||||||
|
|
||||||
btnCancel.setOnMouseClicked(e -> {
|
btnCancel.setOnMouseClicked(e -> {
|
||||||
Optional.ofNullable(executor).ifPresent(TaskExecutor::cancel);
|
Optional.ofNullable(executor).ifPresent(TaskExecutor::cancel);
|
||||||
cancel.run();
|
onCancel.run();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,4 +93,10 @@ public class TaskExecutorDialogPane extends StackPane {
|
|||||||
else
|
else
|
||||||
progressBar.setProgress(progress);
|
progressBar.setProgress(progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setCancel(Runnable onCancel) {
|
||||||
|
this.onCancel = onCancel;
|
||||||
|
|
||||||
|
JFXUtilities.runInFX(() -> btnCancel.setDisable(onCancel == null));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.util;
|
||||||
|
|
||||||
|
public class I18nException extends Exception {
|
||||||
|
private final String localizedMessage;
|
||||||
|
|
||||||
|
public I18nException(String localizedMessage) {
|
||||||
|
this.localizedMessage = localizedMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public I18nException(String localizedMessage, Throwable suppressed) {
|
||||||
|
addSuppressed(suppressed);
|
||||||
|
|
||||||
|
this.localizedMessage = localizedMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLocalizedMessage() {
|
||||||
|
return localizedMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getStackTrace(Throwable e) {
|
||||||
|
if (e instanceof I18nException)
|
||||||
|
return e.getLocalizedMessage();
|
||||||
|
else
|
||||||
|
return StringUtils.getStackTrace(e);
|
||||||
|
}
|
||||||
|
}
|
@ -145,13 +145,13 @@ launch.advice.not_enough_space=You have allocated too much memory, because the p
|
|||||||
launch.advice.too_large_memory_for_32bit=You have allocated too much memory, because of your 32-Bit Java Runtime Environment, your game probably crash. The maximum memory is 1024MB. The launcher will try to launch it.
|
launch.advice.too_large_memory_for_32bit=You have allocated too much memory, because of your 32-Bit Java Runtime Environment, your game probably crash. The maximum memory is 1024MB. The launcher will try to launch it.
|
||||||
launch.circular_dependency_versions=Found circular dependency versions, please check if your client has been modified.
|
launch.circular_dependency_versions=Found circular dependency versions, please check if your client has been modified.
|
||||||
launch.failed=Unable to launch
|
launch.failed=Unable to launch
|
||||||
launch.failed.cannot_create_jvm=We find that it cannot create java virutal machine. The Java argements may have problems. You can enable the no args mode in the settings.
|
launch.failed.cannot_create_jvm=Java virtual machine cannot be created. Java arguments may have problems. You can enable the no args mode in the settings.
|
||||||
launch.failed.decompressing_natives=Did not finish decompressing native libraries, continue launching game?
|
launch.failed.creating_process=Failed to create process, maybe your java path is wrong, please modify your java path.
|
||||||
|
launch.failed.decompressing_natives=Unable to decompress native libraries.
|
||||||
launch.failed.downloading_libraries=Did not finish downloading libraries, continue launching game?
|
launch.failed.downloading_libraries=Did not finish downloading libraries, continue launching game?
|
||||||
|
launch.failed.executable_permission=Unable to add permission to the launch script
|
||||||
launch.failed.exited_abnormally=Game exited abnormally, please visit the log, or ask someone for help.
|
launch.failed.exited_abnormally=Game exited abnormally, please visit the log, or ask someone for help.
|
||||||
launch.failed.packing_jar=Failed to pack the jar.
|
launch.failed.prelaunch_command=Prelaunch command fail.
|
||||||
launch.failed.sh_permission=Failed to add permission to the launch script.
|
|
||||||
launch.failed_creating_process=Failed to create process, maybe your java path is wrong, please modify your java path.
|
|
||||||
launch.state.dependencies=Decompressing natives
|
launch.state.dependencies=Decompressing natives
|
||||||
launch.state.done=Done
|
launch.state.done=Done
|
||||||
launch.state.logging_in=Logging In
|
launch.state.logging_in=Logging In
|
||||||
@ -366,8 +366,8 @@ version.game.old_beta=Beta
|
|||||||
version.game.release=Release
|
version.game.release=Release
|
||||||
version.game.snapshot=Snapshot
|
version.game.snapshot=Snapshot
|
||||||
version.launch=Play
|
version.launch=Play
|
||||||
version.launch_script=Make Launching Script.
|
version.launch_script=Make Launching Script
|
||||||
version.launch_script.failed=Failed to make script.
|
version.launch_script.failed=Unable to make launch script.
|
||||||
version.launch_script.save=Save the launch script
|
version.launch_script.save=Save the launch script
|
||||||
version.launch_script.success=Finished script creation, %s.
|
version.launch_script.success=Finished script creation, %s.
|
||||||
version.manage.redownload_assets_index=Redownload Assets Index
|
version.manage.redownload_assets_index=Redownload Assets Index
|
||||||
|
@ -146,12 +146,12 @@ launch.advice.too_large_memory_for_32bit=您设置的内存大小过大,由于
|
|||||||
launch.circular_dependency_versions=发现游戏版本循环引用,请确认您的客户端未被修改或修改导致出现此问题。
|
launch.circular_dependency_versions=发现游戏版本循环引用,请确认您的客户端未被修改或修改导致出现此问题。
|
||||||
launch.failed=启动失败
|
launch.failed=启动失败
|
||||||
launch.failed.cannot_create_jvm=截获到无法创建Java虚拟机,可能是Java参数有问题,可以在设置中开启无参数模式启动
|
launch.failed.cannot_create_jvm=截获到无法创建Java虚拟机,可能是Java参数有问题,可以在设置中开启无参数模式启动
|
||||||
launch.failed.decompressing_natives=未能解压游戏本地库,还要继续启动游戏吗?
|
launch.failed.creating_process=启动失败,在创建新进程时发生错误,可能是Java路径错误。
|
||||||
|
launch.failed.decompressing_natives=未能解压游戏本地库。
|
||||||
launch.failed.downloading_libraries=未完成游戏依赖库的下载,还要继续启动游戏吗?
|
launch.failed.downloading_libraries=未完成游戏依赖库的下载,还要继续启动游戏吗?
|
||||||
|
launch.failed.executable_permission=未能为启动文件添加执行权限
|
||||||
launch.failed.exited_abnormally=游戏非正常退出,请查看日志文件,或联系他人寻求帮助。
|
launch.failed.exited_abnormally=游戏非正常退出,请查看日志文件,或联系他人寻求帮助。
|
||||||
launch.failed.packing_jar=在打包jar时发生错误
|
launch.failed.prelaunch_command=启动前执行命令执行失败。
|
||||||
launch.failed.sh_permission=为启动文件添加权限时发生错误
|
|
||||||
launch.failed_creating_process=启动失败,在创建新进程时发生错误,可能是Java路径错误。
|
|
||||||
launch.state.dependencies=正在处理游戏依赖
|
launch.state.dependencies=正在处理游戏依赖
|
||||||
launch.state.done=启动完成
|
launch.state.done=启动完成
|
||||||
launch.state.logging_in=登录中
|
launch.state.logging_in=登录中
|
||||||
|
@ -19,9 +19,6 @@ package org.jackhuang.hmcl.launch;
|
|||||||
|
|
||||||
import org.jackhuang.hmcl.auth.AuthInfo;
|
import org.jackhuang.hmcl.auth.AuthInfo;
|
||||||
import org.jackhuang.hmcl.game.*;
|
import org.jackhuang.hmcl.game.*;
|
||||||
import org.jackhuang.hmcl.task.SimpleTaskResult;
|
|
||||||
import org.jackhuang.hmcl.task.Task;
|
|
||||||
import org.jackhuang.hmcl.task.TaskResult;
|
|
||||||
import org.jackhuang.hmcl.util.*;
|
import org.jackhuang.hmcl.util.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
@ -220,14 +217,18 @@ public class DefaultLauncher extends Launcher {
|
|||||||
protected void appendJvmArgs(List<String> result) {
|
protected void appendJvmArgs(List<String> result) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void decompressNatives(File destination) throws IOException {
|
public void decompressNatives(File destination) throws NotDecompressingNativesException {
|
||||||
for (Library library : version.getLibraries())
|
try {
|
||||||
if (library.isNative())
|
for (Library library : version.getLibraries())
|
||||||
CompressingUtils.unzip(repository.getLibraryFile(version, library),
|
if (library.isNative())
|
||||||
destination,
|
CompressingUtils.unzip(repository.getLibraryFile(version, library),
|
||||||
"",
|
destination,
|
||||||
library.getExtract()::shouldExtract,
|
"",
|
||||||
false);
|
library.getExtract()::shouldExtract,
|
||||||
|
false);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new NotDecompressingNativesException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Map<String, String> getConfigurations() {
|
protected Map<String, String> getConfigurations() {
|
||||||
@ -249,19 +250,26 @@ public class DefaultLauncher extends Launcher {
|
|||||||
@Override
|
@Override
|
||||||
public ManagedProcess launch() throws IOException, InterruptedException {
|
public ManagedProcess launch() throws IOException, InterruptedException {
|
||||||
File nativeFolder = Files.createTempDirectory("minecraft").toFile();
|
File nativeFolder = Files.createTempDirectory("minecraft").toFile();
|
||||||
List<String> rawCommandLine = generateCommandLine(nativeFolder);
|
|
||||||
|
|
||||||
// To guarantee that when failed to generate code, we will not call precalled command
|
// To guarantee that when failed to generate launch command line, we will not call pre-launch command
|
||||||
ProcessBuilder builder = new ProcessBuilder(rawCommandLine);
|
List<String> rawCommandLine = generateCommandLine(nativeFolder);
|
||||||
|
|
||||||
decompressNatives(nativeFolder);
|
decompressNatives(nativeFolder);
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(options.getPrecalledCommand()))
|
if (StringUtils.isNotBlank(options.getPrecalledCommand()))
|
||||||
Runtime.getRuntime().exec(options.getPrecalledCommand()).waitFor();
|
Runtime.getRuntime().exec(options.getPrecalledCommand()).waitFor();
|
||||||
|
|
||||||
builder.directory(repository.getRunDirectory(version.getId()))
|
Process process;
|
||||||
.environment().put("APPDATA", options.getGameDir().getAbsoluteFile().getParent());
|
try {
|
||||||
ManagedProcess p = new ManagedProcess(builder.start(), rawCommandLine);
|
ProcessBuilder builder = new ProcessBuilder(rawCommandLine);
|
||||||
|
builder.directory(repository.getRunDirectory(version.getId()))
|
||||||
|
.environment().put("APPDATA", options.getGameDir().getAbsoluteFile().getParent());
|
||||||
|
process = builder.start();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ProcessCreationException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
ManagedProcess p = new ManagedProcess(process, rawCommandLine);
|
||||||
if (listener == null)
|
if (listener == null)
|
||||||
startMonitors(p);
|
startMonitors(p);
|
||||||
else
|
else
|
||||||
@ -269,10 +277,6 @@ public class DefaultLauncher extends Launcher {
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final TaskResult<ManagedProcess> launchAsync() {
|
|
||||||
return new SimpleTaskResult<>(LAUNCH_ASYNC_ID, this::launch);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void makeLaunchScript(File scriptFile) throws IOException {
|
public void makeLaunchScript(File scriptFile) throws IOException {
|
||||||
boolean isWindows = OperatingSystem.WINDOWS == OperatingSystem.CURRENT_OS;
|
boolean isWindows = OperatingSystem.WINDOWS == OperatingSystem.CURRENT_OS;
|
||||||
@ -303,11 +307,7 @@ public class DefaultLauncher extends Launcher {
|
|||||||
writer.write(StringUtils.makeCommand(generateCommandLine(nativeFolder)));
|
writer.write(StringUtils.makeCommand(generateCommandLine(nativeFolder)));
|
||||||
}
|
}
|
||||||
if (!scriptFile.setExecutable(true))
|
if (!scriptFile.setExecutable(true))
|
||||||
throw new IOException("Cannot make script file '" + scriptFile + "' executable.");
|
throw new PermissionException();
|
||||||
}
|
|
||||||
|
|
||||||
public final Task makeLaunchScriptAsync(File file) {
|
|
||||||
return Task.of(() -> makeLaunchScript(file));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startMonitors(ManagedProcess managedProcess) {
|
private void startMonitors(ManagedProcess managedProcess) {
|
||||||
@ -362,7 +362,4 @@ public class DefaultLauncher extends Launcher {
|
|||||||
managedProcess.addRelatedThread(stderr);
|
managedProcess.addRelatedThread(stderr);
|
||||||
managedProcess.addRelatedThread(Lang.thread(new ExitWaiter(managedProcess, Arrays.asList(stdout, stderr), (exitCode, exitType) -> processListener.onExit(exitCode, exitType)), "exit-waiter", isDaemon));
|
managedProcess.addRelatedThread(Lang.thread(new ExitWaiter(managedProcess, Arrays.asList(stdout, stderr), (exitCode, exitType) -> processListener.onExit(exitCode, exitType)), "exit-waiter", isDaemon));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String LAUNCH_ASYNC_ID = "process";
|
|
||||||
public static final String LAUNCH_SCRIPT_ASYNC_ID = "script";
|
|
||||||
}
|
}
|
||||||
|
@ -65,16 +65,15 @@ final class ExitWaiter implements Runnable {
|
|||||||
ProcessListener.ExitType exitType;
|
ProcessListener.ExitType exitType;
|
||||||
|
|
||||||
// LaunchWrapper will catch the exception logged and will exit normally.
|
// LaunchWrapper will catch the exception logged and will exit normally.
|
||||||
if (exitCode != 0 || StringUtils.containsOne(errorLines, "Unable to launch")) {
|
if (exitCode != 0 && StringUtils.containsOne(errorLines,
|
||||||
EventBus.EVENT_BUS.fireEvent(new ProcessExitedAbnormallyEvent(this, process));
|
|
||||||
exitType = ProcessListener.ExitType.APPLICATION_ERROR;
|
|
||||||
} else if (exitCode != 0 && StringUtils.containsOne(errorLines,
|
|
||||||
"Could not create the Java Virtual Machine.",
|
"Could not create the Java Virtual Machine.",
|
||||||
"Error occurred during initialization of VM",
|
"Error occurred during initialization of VM",
|
||||||
"A fatal exception has occurred. Program will exit.",
|
"A fatal exception has occurred. Program will exit.")) {
|
||||||
"Unable to launch")) {
|
|
||||||
EventBus.EVENT_BUS.fireEvent(new JVMLaunchFailedEvent(this, process));
|
EventBus.EVENT_BUS.fireEvent(new JVMLaunchFailedEvent(this, process));
|
||||||
exitType = ProcessListener.ExitType.JVM_ERROR;
|
exitType = ProcessListener.ExitType.JVM_ERROR;
|
||||||
|
} else if (exitCode != 0 || StringUtils.containsOne(errorLines, "Unable to launch")) {
|
||||||
|
EventBus.EVENT_BUS.fireEvent(new ProcessExitedAbnormallyEvent(this, process));
|
||||||
|
exitType = ProcessListener.ExitType.APPLICATION_ERROR;
|
||||||
} else
|
} else
|
||||||
exitType = ProcessListener.ExitType.NORMAL;
|
exitType = ProcessListener.ExitType.NORMAL;
|
||||||
|
|
||||||
@ -82,7 +81,7 @@ final class ExitWaiter implements Runnable {
|
|||||||
|
|
||||||
watcher.accept(exitCode, exitType);
|
watcher.accept(exitCode, exitType);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
watcher.accept(1, ProcessListener.ExitType.NORMAL);
|
watcher.accept(1, ProcessListener.ExitType.INTERRUPTED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.launch;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class NotDecompressingNativesException extends IOException {
|
||||||
|
public NotDecompressingNativesException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public NotDecompressingNativesException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NotDecompressingNativesException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NotDecompressingNativesException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.launch;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Threw if unable to make file executable.
|
||||||
|
*/
|
||||||
|
public class PermissionException extends IOException {
|
||||||
|
public PermissionException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public PermissionException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PermissionException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PermissionException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.launch;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class ProcessCreationException extends IOException {
|
||||||
|
public ProcessCreationException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProcessCreationException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProcessCreationException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProcessCreationException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
@ -52,6 +52,7 @@ public interface ProcessListener {
|
|||||||
enum ExitType {
|
enum ExitType {
|
||||||
JVM_ERROR,
|
JVM_ERROR,
|
||||||
APPLICATION_ERROR,
|
APPLICATION_ERROR,
|
||||||
NORMAL
|
NORMAL,
|
||||||
|
INTERRUPTED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user