add: allow customize LWJGL path (runs on M1)

This commit is contained in:
yaoxi-std 2021-05-27 22:58:55 +08:00 committed by Yuhui Huang
parent b7da21e5ce
commit 7cbe24b35b
14 changed files with 131 additions and 16 deletions

5
.gitignore vendored
View File

@ -39,4 +39,7 @@ NVIDIA
.nb-gradle
*.exe
!/HMCL/src/main/resources/assets/HMCLauncher.exe
!/HMCL/src/main/resources/assets/HMCLauncher.exe
# macos
.DS_Store

View File

@ -42,11 +42,15 @@ public final class HMCLGameLauncher extends DefaultLauncher {
}
public HMCLGameLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) {
this(repository, version, authInfo, options, listener, true);
this(repository, version, authInfo, options, listener, false, null, true);
}
public HMCLGameLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) {
super(repository, version, authInfo, options, listener, daemon);
public HMCLGameLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean customized_natives, String customized_natives_path) {
this(repository, version, authInfo, options, listener, customized_natives, customized_natives_path, true);
}
public HMCLGameLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean customized_natives, String customized_natives_path, boolean daemon) {
super(repository, version, authInfo, options, listener, customized_natives, customized_natives_path, daemon);
}
@Override

View File

@ -178,7 +178,10 @@ public final class LauncherHelper {
repository.getLaunchOptions(selectedVersion, profile.getGameDir(), !setting.isNotCheckJVM()),
launcherVisibility == LauncherVisibility.CLOSE
? null // Unnecessary to start listening to game process output when close launcher immediately after game launched.
: new HMCLProcessListener(repository, selectedVersion, authInfo, launchingLatch, gameVersion.isPresent())
: new HMCLProcessListener(repository, selectedVersion, authInfo, launchingLatch, gameVersion.isPresent()),
NativesDirectoryType.CUSTOM.equals(setting.getNativesDirType()),
setting.getNativesDir()
// TODO: yaoxi-std ADD custom natives path checking
);
}).thenComposeAsync(launcher -> { // launcher is prev task's result
if (scriptFile == null) {

View File

@ -22,6 +22,7 @@ import com.google.gson.annotations.JsonAdapter;
import javafx.beans.InvalidationListener;
import javafx.beans.property.*;
import org.jackhuang.hmcl.game.GameDirectoryType;
import org.jackhuang.hmcl.game.NativesDirectoryType;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.platform.JavaVersion;
@ -119,6 +120,39 @@ public final class VersionSetting implements Cloneable {
defaultJavaPathProperty.set(defaultJavaPath);
}
/**
* 0 - .minecraft/versions/&lt;version&gt;/natives/<br/>
*/
private final ObjectProperty<NativesDirectoryType> nativesDirTypeProperty = new SimpleObjectProperty<>(this, "nativesDirType", NativesDirectoryType.VERSION_FOLDER);
public ObjectProperty<NativesDirectoryType> nativesDirTypeProperty() {
return nativesDirTypeProperty;
}
public NativesDirectoryType getNativesDirType() {
return nativesDirTypeProperty.get();
}
public void setNativesDirType(NativesDirectoryType nativesDirType) {
nativesDirTypeProperty.set(nativesDirType);
}
// Path to lwjgl natives directory
private final StringProperty nativesDirProperty = new SimpleStringProperty(this, "nativesDirProperty", "");
public StringProperty nativesDirProperty(){
return nativesDirProperty;
}
public String getNativesDir(){
return nativesDirProperty.get();
}
public void setNativesDir(String nativesDir){
nativesDirProperty.set(nativesDir);
}
private final StringProperty javaDirProperty = new SimpleStringProperty(this, "javaDir", "");
public StringProperty javaDirProperty() {
@ -526,6 +560,7 @@ public final class VersionSetting implements Cloneable {
gameDirProperty.addListener(listener);
launcherVisibilityProperty.addListener(listener);
defaultJavaPathProperty.addListener(listener);
nativesDirProperty.addListener(listener);
}
@Override
@ -553,6 +588,7 @@ public final class VersionSetting implements Cloneable {
versionSetting.setGameDirType(getGameDirType());
versionSetting.setGameDir(getGameDir());
versionSetting.setLauncherVisibility(getLauncherVisibility());
versionSetting.setNativesDir(getNativesDir());
return versionSetting;
}
@ -584,6 +620,8 @@ public final class VersionSetting implements Cloneable {
obj.addProperty("launcherVisibility", src.getLauncherVisibility().ordinal());
obj.addProperty("gameDirType", src.getGameDirType().ordinal());
obj.addProperty("defaultJavaPath", src.getDefaultJavaPath());
obj.addProperty("nativesDir", src.getNativesDir());
obj.addProperty("nativesDirType", src.getNativesDirType().ordinal());
return obj;
}
@ -613,6 +651,7 @@ public final class VersionSetting implements Cloneable {
vs.setJava(Optional.ofNullable(obj.get("java")).map(JsonElement::getAsString).orElse(""));
vs.setWrapper(Optional.ofNullable(obj.get("wrapper")).map(JsonElement::getAsString).orElse(""));
vs.setGameDir(Optional.ofNullable(obj.get("gameDir")).map(JsonElement::getAsString).orElse(""));
vs.setNativesDir(Optional.ofNullable(obj.get("nativesDir")).map(JsonElement::getAsString).orElse(""));
vs.setFullscreen(Optional.ofNullable(obj.get("fullscreen")).map(JsonElement::getAsBoolean).orElse(false));
vs.setNoJVMArgs(Optional.ofNullable(obj.get("noJVMArgs")).map(JsonElement::getAsBoolean).orElse(false));
vs.setNotCheckGame(Optional.ofNullable(obj.get("notCheckGame")).map(JsonElement::getAsBoolean).orElse(false));
@ -621,6 +660,7 @@ public final class VersionSetting implements Cloneable {
vs.setLauncherVisibility(LauncherVisibility.values()[Optional.ofNullable(obj.get("launcherVisibility")).map(JsonElement::getAsInt).orElse(1)]);
vs.setGameDirType(GameDirectoryType.values()[Optional.ofNullable(obj.get("gameDirType")).map(JsonElement::getAsInt).orElse(0)]);
vs.setDefaultJavaPath(Optional.ofNullable(obj.get("defaultJavaPath")).map(JsonElement::getAsString).orElse(null));
vs.setNativesDirType(NativesDirectoryType.values()[Optional.ofNullable(obj.get("nativesDirType")).map(JsonElement::getAsInt).orElse(0)]);
return vs;
}

View File

@ -35,6 +35,7 @@ import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.game.GameDirectoryType;
import org.jackhuang.hmcl.game.NativesDirectoryType;
import org.jackhuang.hmcl.setting.*;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
@ -76,6 +77,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
@FXML private JFXTextField txtWidth;
@FXML private JFXTextField txtHeight;
@FXML private JFXTextField txtMaxMemory;
@FXML private JFXTextField txtNativesPath;
@FXML private JFXTextField txtJVMArgs;
@FXML private JFXTextField txtGameArgs;
@FXML private JFXTextField txtMetaspace;
@ -88,11 +90,13 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
@FXML private JFXComboBox<LauncherVisibility> cboLauncherVisibility;
@FXML private JFXCheckBox chkFullscreen;
@FXML private Label lblPhysicalMemory;
@FXML private Label lblCustomizedNativesPath;
@FXML private JFXToggleButton chkNoJVMArgs;
@FXML private JFXToggleButton chkNoGameCheck;
@FXML private JFXToggleButton chkNoJVMCheck;
@FXML private MultiFileItem<JavaVersion> javaItem;
@FXML private MultiFileItem<GameDirectoryType> gameDirItem;
@FXML private MultiFileItem<NativesDirectoryType> nativesDirItem;
@FXML private JFXToggleButton chkShowLogs;
@FXML private ImagePickerItem iconPickerItem;
@FXML private JFXCheckBox chkEnableSpecificSettings;
@ -138,6 +142,11 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
gameDirItem.createChildren(i18n("settings.advanced.game_dir.independent"), GameDirectoryType.VERSION_FOLDER)
));
nativesDirItem.setCustomUserData(NativesDirectoryType.CUSTOM);
nativesDirItem.loadChildren(Arrays.asList(
nativesDirItem.createChildren(i18n("settings.advanced.natives_dir.default"), NativesDirectoryType.VERSION_FOLDER)
));
chkEnableSpecificSettings.selectedProperty().addListener((a, b, newValue) -> {
if (versionId == null) return;
@ -179,6 +188,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
FXUtils.unbindInt(txtMaxMemory, lastVersionSetting.maxMemoryProperty());
FXUtils.unbindString(javaItem.getTxtCustom(), lastVersionSetting.javaDirProperty());
FXUtils.unbindString(gameDirItem.getTxtCustom(), lastVersionSetting.gameDirProperty());
FXUtils.unbindString(nativesDirItem.getTxtCustom(), lastVersionSetting.nativesDirProperty());
FXUtils.unbindString(txtJVMArgs, lastVersionSetting.javaArgsProperty());
FXUtils.unbindString(txtGameArgs, lastVersionSetting.minecraftArgsProperty());
FXUtils.unbindString(txtMetaspace, lastVersionSetting.permSizeProperty());
@ -198,6 +208,9 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
gameDirItem.selectedDataProperty().unbindBidirectional(lastVersionSetting.gameDirTypeProperty());
gameDirItem.subtitleProperty().unbind();
nativesDirItem.selectedDataProperty().unbindBidirectional(lastVersionSetting.nativesDirTypeProperty());
nativesDirItem.subtitleProperty().unbind();
}
// unbind data fields
@ -209,6 +222,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
FXUtils.bindInt(txtMaxMemory, versionSetting.maxMemoryProperty());
FXUtils.bindString(javaItem.getTxtCustom(), versionSetting.javaDirProperty());
FXUtils.bindString(gameDirItem.getTxtCustom(), versionSetting.gameDirProperty());
FXUtils.bindString(nativesDirItem.getTxtCustom(), versionSetting.nativesDirProperty());
FXUtils.bindString(txtJVMArgs, versionSetting.javaArgsProperty());
FXUtils.bindString(txtGameArgs, versionSetting.minecraftArgsProperty());
FXUtils.bindString(txtMetaspace, versionSetting.permSizeProperty());
@ -240,6 +254,10 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
gameDirItem.selectedDataProperty().bindBidirectional(versionSetting.gameDirTypeProperty());
gameDirItem.subtitleProperty().bind(Bindings.createStringBinding(() -> Paths.get(profile.getRepository().getRunDirectory(versionId).getAbsolutePath()).normalize().toString(),
versionSetting.gameDirProperty(), versionSetting.gameDirTypeProperty()));
nativesDirItem.selectedDataProperty().bindBidirectional(versionSetting.nativesDirTypeProperty());
nativesDirItem.subtitleProperty().bind(Bindings.createStringBinding(() -> Paths.get(profile.getRepository().getRunDirectory(versionId).getAbsolutePath() + "/natives").normalize().toString(),
versionSetting.nativesDirProperty(), versionSetting.nativesDirTypeProperty()));
lastVersionSetting = versionSetting;

View File

@ -38,6 +38,9 @@
<MultiFileItem fx:id="gameDirItem" title="%settings.game.working_directory" chooserTitle="%settings.game.working_directory.choose"
hasSubtitle="true" customText="%settings.custom" directory="true" />
<MultiFileItem fx:id="nativesDirItem" title="%settings.game.natives_directory" chooserTitle="%settings.game.natives_directory.choose"
hasSubtitle="true" customText="%settings.custom" directory="true" />
<BorderPane> <!-- Max Memory -->
<left>
<VBox>
@ -53,7 +56,7 @@
</JFXTextField>
</right>
</BorderPane>
<BorderPane> <!-- Launcher Visibility -->
<left>
<Label text="%settings.advanced.launcher_visible" BorderPane.alignment="CENTER_LEFT"/>

View File

@ -371,6 +371,7 @@ settings.advanced.launcher_visibility.hide_and_reopen=Hide the launcher and re-o
settings.advanced.launcher_visibility.keep=Keep the launcher visible.
settings.advanced.launcher_visible=Launcher Visibility
settings.advanced.minecraft_arguments=Minecraft Arguments
settings.advanced.natives_dir.default=Standerd (.minecraft/versions/<version name>/natives/)
settings.advanced.no_jvm_args=No Default JVM Args
settings.advanced.precall_command=Pre-Launch command (will be executed before game starts)
settings.advanced.server_ip=Server Address
@ -386,6 +387,8 @@ settings.game.java_directory=Java Directory
settings.game.java_directory.bit=, %s-Bit
settings.game.java_directory.choose=Choose Java Directory.
settings.game.management=Manage
settings.game.natives_directory=Local Library Path (LWJGL)
settings.game.natives_directory.choose=Choose Local Library Path
settings.game.working_directory=Working Directory
settings.game.working_directory.choose=Choose Working Directory

View File

@ -342,6 +342,7 @@ settings.advanced.launcher_visibility.hide_and_reopen=Esconder launcher y reabri
settings.advanced.launcher_visibility.keep=Mantener launcher visible.
settings.advanced.launcher_visible=Visibilidad de launcher
settings.advanced.minecraft_arguments=Minecraft Arguments
settings.advanced.natives_dir.default=Por defecto (.minecraft/versions/<version name>/natives/)
settings.advanced.no_jvm_args=No JVM Args por defecto
settings.advanced.precall_command=Comando pre-inicio (será ejecutado antes de inicio del juego)
settings.advanced.server_ip=Host de servidor
@ -357,6 +358,8 @@ settings.game.java_directory=Directorio Java
settings.game.java_directory.bit=, %s-Bit
settings.game.java_directory.choose=Escoja directorio Java.
settings.game.management=Gestionar
settings.game.natives_directory=Loka biblioteko- vojo (LWJGL)
settings.game.natives_directory.choose=Elektu lokan bibliotekvojn
settings.game.working_directory=Directorio de función
settings.game.working_directory.choose=Escoja directory de función

View File

@ -359,6 +359,7 @@ settings.advanced.launcher_visibility.hide_and_reopen=Сворачивать л
settings.advanced.launcher_visibility.keep=Оставлять лаунчер видимым.
settings.advanced.launcher_visible=Видимость лаунчера
settings.advanced.minecraft_arguments=Параметры Minecraft
settings.advanced.natives_dir.default=По умолчанию (.minecraft/versions/<версия>/natives/)
settings.advanced.no_jvm_args=По умолчанию параметры JVM отсутствуют
settings.advanced.precall_command=Команда предзапуска (будет выполнено до запуска игры)
settings.advanced.server_ip=Адрес сервера
@ -374,6 +375,8 @@ settings.game.java_directory=Каталог Java
settings.game.java_directory.bit=, %s-бит
settings.game.java_directory.choose=Выбор каталога Java.
settings.game.management=Управление
settings.game.natives_directory=путь к локальной библиотеке (LWJGL)
settings.game.natives_directory.choose=Выберите путь к локальной библиотеке
settings.game.working_directory=Рабочий каталог
settings.game.working_directory.choose=Выбор рабочего каталога

View File

@ -365,6 +365,7 @@ settings.advanced.launcher_visibility.hide_and_reopen=隱藏啟動器並在遊
settings.advanced.launcher_visibility.keep=不隱藏啟動器
settings.advanced.launcher_visible=啟動器可見性
settings.advanced.minecraft_arguments=Minecraft 額外參數(不必填寫)
settings.advanced.natives_dir.default=預設(.minecraft/versions/<版本名>/natives/
settings.advanced.no_jvm_args=不使用預設的 JVM 參數
settings.advanced.precall_command=啟動前執行指令(不必填寫,將在遊戲啟動前呼叫使用)
settings.advanced.server_ip=直接進入伺服器 IP 位址(不必填寫,啟動遊戲後直接進入對應伺服器)
@ -380,6 +381,8 @@ settings.game.java_directory=Java 路徑
settings.game.java_directory.bit=%s 位
settings.game.java_directory.choose=選擇 Java 路徑
settings.game.management=管理
settings.game.natives_directory=本地庫路徑LWJGL
settings.game.natives_directory.choose=選擇本地庫路徑
settings.game.working_directory=執行路徑(版本隔離,修改後請自行移動相關遊戲檔案,如存檔模組設定等)
settings.game.working_directory.choose=選擇執行路徑

View File

@ -371,6 +371,7 @@ settings.advanced.launcher_visibility.hide_and_reopen=隐藏启动器并在游
settings.advanced.launcher_visibility.keep=保持启动器可见
settings.advanced.launcher_visible=启动器可见性
settings.advanced.minecraft_arguments=Minecraft 额外参数(不必填写)
settings.advanced.natives_dir.default=预设(.minecraft/versions/<版本名>/natives/
settings.advanced.no_jvm_args=不添加默认的 JVM 参数
settings.advanced.precall_command=启动前执行命令(不必填写,将在游戏启动前调用)
settings.advanced.server_ip=直入服务器 IP 地址(不必填写,启动游戏后直接进入对应服务器)
@ -386,6 +387,8 @@ settings.game.java_directory=Java 路径
settings.game.java_directory.bit=%s 位
settings.game.java_directory.choose=选择 Java 路径
settings.game.management=管理
settings.game.natives_directory=本地库路径LWJGL
settings.game.natives_directory.choose=选择本地库路径
settings.game.working_directory=运行路径(版本隔离,修改后请自行移动相关游戏文件,如存档模组配置等)
settings.game.working_directory.choose=选择运行路径

View File

@ -0,0 +1,12 @@
package org.jackhuang.hmcl.game;
public enum NativesDirectoryType {
/**
* .minecraft/versions/&lt;version name&gt/natives;
*/
VERSION_FOLDER,
/**
* user customized directory.
*/
CUSTOM
}

View File

@ -66,11 +66,15 @@ public class DefaultLauncher extends Launcher {
}
public DefaultLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) {
this(repository, version, authInfo, options, listener, true);
this(repository, version, authInfo, options, listener, false, null, true);
}
public DefaultLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) {
super(repository, version, authInfo, options, listener, daemon);
public DefaultLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean customized_natives, String customized_natives_path) {
this(repository, version, authInfo, options, listener, customized_natives, customized_natives_path, true);
}
public DefaultLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean customized_natives, String customized_natives_path, boolean daemon) {
super(repository, version, authInfo, options, listener, customized_natives, customized_natives_path, daemon);
}
private CommandBuilder generateCommandLine(File nativeFolder) throws IOException {
@ -299,8 +303,10 @@ public class DefaultLauncher extends Launcher {
@Override
public ManagedProcess launch() throws IOException, InterruptedException {
File nativeFolder = repository.getNativeDirectory(version.getId());
File nativeFolder = null;
if (!customized_natives) nativeFolder = repository.getNativeDirectory(version.getId());
else nativeFolder = new File(customized_natives_path);
// To guarantee that when failed to generate launch command line, we will not call pre-launch command
List<String> rawCommandLine = generateCommandLine(nativeFolder).asList();
@ -308,7 +314,7 @@ public class DefaultLauncher extends Launcher {
throw new IllegalStateException("Illegal command line " + rawCommandLine);
}
decompressNatives(nativeFolder);
if (!customized_natives) decompressNatives(nativeFolder);
File runDirectory = repository.getRunDirectory(version.getId());
@ -348,8 +354,11 @@ public class DefaultLauncher extends Launcher {
public void makeLaunchScript(File scriptFile) throws IOException {
boolean isWindows = OperatingSystem.WINDOWS == OperatingSystem.CURRENT_OS;
File nativeFolder = repository.getNativeDirectory(version.getId());
decompressNatives(nativeFolder);
File nativeFolder = null;
if (!customized_natives) nativeFolder = repository.getNativeDirectory(version.getId());
else nativeFolder = new File(customized_natives_path);
if (!customized_natives) decompressNatives(nativeFolder);
if (isWindows && !FileUtils.getExtension(scriptFile).equals("bat"))
throw new IllegalArgumentException("The extension of " + scriptFile + " is not 'bat' in Windows");

View File

@ -38,22 +38,30 @@ public abstract class Launcher {
protected final LaunchOptions options;
protected final ProcessListener listener;
protected final boolean daemon;
protected final boolean customized_natives;
protected final String customized_natives_path;
public Launcher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options) {
this(repository, version, authInfo, options, null);
}
public Launcher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) {
this(repository, version, authInfo, options, listener, true);
this(repository, version, authInfo, options, listener, false, null, true);
}
public Launcher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) {
public Launcher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean customized_natives, String customized_natives_path) {
this(repository, version, authInfo, options, listener, customized_natives, customized_natives_path, true);
}
public Launcher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean customized_natives, String customized_natives_path, boolean daemon) {
this.repository = repository;
this.version = version;
this.authInfo = authInfo;
this.options = options;
this.listener = listener;
this.daemon = daemon;
this.customized_natives = customized_natives;
this.customized_natives_path = customized_natives_path;
}
/**