feat: WIP: auto java.
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import org.jackhuang.hmcl.util.Range;
|
||||
import org.jackhuang.hmcl.util.platform.JavaVersion;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LAUNCH_WRAPPER_MAIN;
|
||||
|
||||
public enum HMCLJavaVersion {
|
||||
|
||||
// Minecraft>=1.17 requires Java 16
|
||||
VANILLA_JAVA_16(HMCLJavaVersion.RULE_MANDATORY, versionRange("1.17", HMCLJavaVersion.MAX), versionRange("16", HMCLJavaVersion.MAX)),
|
||||
// Minecraft>=1.13 requires Java 8
|
||||
VANILLA_JAVA_8(HMCLJavaVersion.RULE_MANDATORY, versionRange("1.13", HMCLJavaVersion.MAX), versionRange("8", HMCLJavaVersion.MAX)),
|
||||
// Minecraft>=1.7.10+Forge accepts Java 8
|
||||
SUGGEST_JAVA_8(HMCLJavaVersion.RULE_SUGGESTED, versionRange("1.7.10", HMCLJavaVersion.MAX), versionRange("8", HMCLJavaVersion.MAX)),
|
||||
// LaunchWrapper<=1.12 will crash because of assuming the system class loader is an instance of URLClassLoader (Java 8)
|
||||
LAUNCH_WRAPPER(HMCLJavaVersion.RULE_MANDATORY, versionRange("0", "1.12"), versionRange("0", "8")) {
|
||||
@Override
|
||||
public boolean test(Version version) {
|
||||
return LAUNCH_WRAPPER_MAIN.equals(version.getMainClass()) &&
|
||||
version.getLibraries().stream()
|
||||
.filter(library -> "launchwrapper".equals(library.getArtifactId()))
|
||||
.anyMatch(library -> VersionNumber.asVersion(library.getVersion()).compareTo(VersionNumber.asVersion("1.13")) < 0);
|
||||
}
|
||||
},
|
||||
// Minecraft>=1.13 may crash when generating world on Java [1.8,1.8.0_51)
|
||||
VANILLA_JAVA_8_51(HMCLJavaVersion.RULE_SUGGESTED, versionRange("1.13", HMCLJavaVersion.MAX), versionRange("1.8.0_51", HMCLJavaVersion.MAX)),
|
||||
|
||||
;
|
||||
|
||||
private final int type;
|
||||
private final Range<VersionNumber> gameVersion;
|
||||
private final Range<VersionNumber> javaVersion;
|
||||
|
||||
HMCLJavaVersion(int type, Range<VersionNumber> gameVersion, Range<VersionNumber> javaVersion) {
|
||||
this.type = type;
|
||||
this.gameVersion = gameVersion;
|
||||
this.javaVersion = javaVersion;
|
||||
}
|
||||
|
||||
public boolean test(Version version) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static JavaVersion findSuitableJavaVersion(VersionNumber gameVersion, Version version) throws InterruptedException {
|
||||
Range<VersionNumber> mandatoryJavaRange = versionRange(MIN, MAX);
|
||||
Range<VersionNumber> suggestedJavaRange = versionRange(MIN, MAX);
|
||||
for (HMCLJavaVersion java : values()) {
|
||||
if (java.gameVersion.contains(gameVersion) && java.test(version)) {
|
||||
if (java.type == RULE_MANDATORY) {
|
||||
mandatoryJavaRange = mandatoryJavaRange.intersectionWith(java.javaVersion);
|
||||
suggestedJavaRange = suggestedJavaRange.intersectionWith(java.javaVersion);
|
||||
} else if (java.type == RULE_SUGGESTED) {
|
||||
suggestedJavaRange = suggestedJavaRange.intersectionWith(java.javaVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JavaVersion mandatory = null;
|
||||
JavaVersion suggested = null;
|
||||
for (JavaVersion javaVersion : JavaVersion.getJavas()) {
|
||||
// select the latest java version that this version accepts.
|
||||
if (mandatoryJavaRange.contains(javaVersion.getVersionNumber())) {
|
||||
if (mandatory == null) mandatory = javaVersion;
|
||||
else if (javaVersion.getVersionNumber().compareTo(mandatory.getVersionNumber()) > 0) {
|
||||
mandatory = javaVersion;
|
||||
}
|
||||
}
|
||||
if (suggestedJavaRange.contains(javaVersion.getVersionNumber())) {
|
||||
if (suggested == null) suggested = javaVersion;
|
||||
else if (javaVersion.getVersionNumber().compareTo(suggested.getVersionNumber()) > 0) {
|
||||
suggested = javaVersion;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (suggested != null) return suggested;
|
||||
else return mandatory;
|
||||
}
|
||||
|
||||
public static final int RULE_MANDATORY = 1;
|
||||
public static final int RULE_SUGGESTED = 2;
|
||||
|
||||
public static final String MIN = "0";
|
||||
public static final String MAX = "10000";
|
||||
|
||||
private static Range<VersionNumber> versionRange(String fromInclusive, String toExclusive) {
|
||||
return Range.between(VersionNumber.asVersion(fromInclusive), VersionNumber.asVersion(toExclusive));
|
||||
}
|
||||
}
|
||||
@@ -21,14 +21,13 @@ import com.google.gson.*;
|
||||
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.game.ProcessPriority;
|
||||
import org.jackhuang.hmcl.game.*;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.platform.JavaVersion;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.platform.Platform;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
@@ -36,6 +35,8 @@ import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -105,6 +106,15 @@ public final class VersionSetting implements Cloneable {
|
||||
setDefaultJavaPath(null);
|
||||
}
|
||||
|
||||
public boolean isJavaAutoSelected() {
|
||||
return "Auto".equals(getJava());
|
||||
}
|
||||
|
||||
public void setJavaAutoSelected() {
|
||||
setJava("Auto");
|
||||
setDefaultJavaPath(null);
|
||||
}
|
||||
|
||||
private final StringProperty defaultJavaPathProperty = new SimpleStringProperty(this, "defaultJavaPath", "");
|
||||
|
||||
/**
|
||||
@@ -571,38 +581,46 @@ public final class VersionSetting implements Cloneable {
|
||||
launcherVisibilityProperty.set(launcherVisibility);
|
||||
}
|
||||
|
||||
public JavaVersion getJavaVersion() throws InterruptedException {
|
||||
return getJavaVersion(true);
|
||||
public CompletableFuture<JavaVersion> getJavaVersion(String gameVersion, Version version) {
|
||||
return getJavaVersion(gameVersion, version, true);
|
||||
}
|
||||
|
||||
public JavaVersion getJavaVersion(boolean checkJava) throws InterruptedException {
|
||||
// TODO: lazy initialization may result in UI suspension.
|
||||
if (StringUtils.isBlank(getJava()))
|
||||
setJava(StringUtils.isBlank(getJavaDir()) ? "Default" : "Custom");
|
||||
if ("Default".equals(getJava())) return JavaVersion.fromCurrentEnvironment();
|
||||
else if (isUsesCustomJavaDir()) {
|
||||
public CompletableFuture<JavaVersion> getJavaVersion(String gameVersion, Version version, boolean checkJava) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
if (checkJava)
|
||||
return JavaVersion.fromExecutable(Paths.get(getJavaDir()));
|
||||
else
|
||||
return new JavaVersion(Paths.get(getJavaDir()), "", Platform.getPlatform());
|
||||
} catch (IOException | InvalidPathException e) {
|
||||
return null; // Custom Java Directory not found,
|
||||
if (StringUtils.isBlank(getJava()))
|
||||
setJava(StringUtils.isBlank(getJavaDir()) ? "Default" : "Custom");
|
||||
if ("Default".equals(getJava())) {
|
||||
return JavaVersion.fromCurrentEnvironment();
|
||||
} else if (isJavaAutoSelected()) {
|
||||
return HMCLJavaVersion.findSuitableJavaVersion(VersionNumber.asVersion(gameVersion), version);
|
||||
} else if (isUsesCustomJavaDir()) {
|
||||
try {
|
||||
if (checkJava)
|
||||
return JavaVersion.fromExecutable(Paths.get(getJavaDir()));
|
||||
else
|
||||
return new JavaVersion(Paths.get(getJavaDir()), "", Platform.getPlatform());
|
||||
} catch (IOException | InvalidPathException e) {
|
||||
return null; // Custom Java Directory not found,
|
||||
}
|
||||
} else if (StringUtils.isNotBlank(getJava())) {
|
||||
List<JavaVersion> matchedJava = JavaVersion.getJavas().stream()
|
||||
.filter(java -> java.getVersion().equals(getJava()))
|
||||
.collect(Collectors.toList());
|
||||
if (matchedJava.isEmpty()) {
|
||||
setJava("Default");
|
||||
return JavaVersion.fromCurrentEnvironment();
|
||||
} else {
|
||||
return matchedJava.stream()
|
||||
.filter(java -> java.getBinary().toString().equals(getDefaultJavaPath()))
|
||||
.findFirst()
|
||||
.orElse(matchedJava.get(0));
|
||||
}
|
||||
} else throw new Error();
|
||||
} catch (InterruptedException e) {
|
||||
throw new CancellationException();
|
||||
}
|
||||
} else if (StringUtils.isNotBlank(getJava())) {
|
||||
List<JavaVersion> matchedJava = JavaVersion.getJavas().stream()
|
||||
.filter(java -> java.getVersion().equals(getJava()))
|
||||
.collect(Collectors.toList());
|
||||
if (matchedJava.isEmpty()) {
|
||||
setJava("Default");
|
||||
return JavaVersion.fromCurrentEnvironment();
|
||||
} else {
|
||||
return matchedJava.stream()
|
||||
.filter(java -> java.getBinary().toString().equals(getDefaultJavaPath()))
|
||||
.findFirst()
|
||||
.orElse(matchedJava.get(0));
|
||||
}
|
||||
} else throw new Error();
|
||||
});
|
||||
}
|
||||
|
||||
public void setJavaVersion(JavaVersion java) {
|
||||
|
||||
@@ -115,6 +115,7 @@ public class MultiFileItem<T> extends VBox {
|
||||
protected final String title;
|
||||
protected String subtitle;
|
||||
protected final T data;
|
||||
protected final BooleanProperty selected = new SimpleBooleanProperty();
|
||||
|
||||
public Option(String title, T data) {
|
||||
this.title = title;
|
||||
@@ -138,6 +139,18 @@ public class MultiFileItem<T> extends VBox {
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isSelected() {
|
||||
return selected.get();
|
||||
}
|
||||
|
||||
public BooleanProperty selectedProperty() {
|
||||
return selected;
|
||||
}
|
||||
|
||||
public void setSelected(boolean selected) {
|
||||
this.selected.set(selected);
|
||||
}
|
||||
|
||||
protected Node createItem(ToggleGroup group) {
|
||||
BorderPane pane = new BorderPane();
|
||||
pane.setPadding(new Insets(3));
|
||||
@@ -147,6 +160,7 @@ public class MultiFileItem<T> extends VBox {
|
||||
BorderPane.setAlignment(left, Pos.CENTER_LEFT);
|
||||
left.setToggleGroup(group);
|
||||
left.setUserData(data);
|
||||
selected.bind(left.selectedProperty());
|
||||
pane.setLeft(left);
|
||||
|
||||
if (StringUtils.isNotBlank(subtitle)) {
|
||||
@@ -208,6 +222,7 @@ public class MultiFileItem<T> extends VBox {
|
||||
BorderPane.setAlignment(left, Pos.CENTER_LEFT);
|
||||
left.setToggleGroup(group);
|
||||
left.setUserData(data);
|
||||
selected.bind(left.selectedProperty());
|
||||
pane.setLeft(left);
|
||||
|
||||
BorderPane.setAlignment(customField, Pos.CENTER_RIGHT);
|
||||
@@ -266,6 +281,7 @@ public class MultiFileItem<T> extends VBox {
|
||||
BorderPane.setAlignment(left, Pos.CENTER_LEFT);
|
||||
left.setToggleGroup(group);
|
||||
left.setUserData(data);
|
||||
selected.bind(left.selectedProperty());
|
||||
pane.setLeft(left);
|
||||
|
||||
selector.disableProperty().bind(left.selectedProperty().not());
|
||||
|
||||
@@ -101,6 +101,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
private final OptionToggleButton useNativeOpenALPane;
|
||||
private final ComponentSublist javaSublist;
|
||||
private final MultiFileItem<JavaVersion> javaItem;
|
||||
private final MultiFileItem.Option<JavaVersion> javaAutoDeterminedOption;
|
||||
private final MultiFileItem.FileOption<JavaVersion> javaCustomOption;
|
||||
private final ComponentSublist gameDirSublist;
|
||||
private final MultiFileItem<GameDirectoryType> gameDirItem;
|
||||
@@ -199,6 +200,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
javaSublist.getContent().add(javaItem);
|
||||
javaSublist.setTitle(i18n("settings.game.java_directory"));
|
||||
javaSublist.setHasSubtitle(true);
|
||||
javaAutoDeterminedOption = new MultiFileItem.Option<>(i18n("settings.game.java_directory.auto"), null);
|
||||
javaCustomOption = new MultiFileItem.FileOption<JavaVersion>(i18n("settings.custom"), null)
|
||||
.setChooserTitle(i18n("settings.game.java_directory.choose"));
|
||||
|
||||
@@ -541,6 +543,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
javaVersion.getPlatform().getBit()), javaVersion)
|
||||
.setSubtitle(javaVersion.getBinary().toString()))
|
||||
.collect(Collectors.toList());
|
||||
options.add(0, javaAutoDeterminedOption);
|
||||
options.add(javaCustomOption);
|
||||
javaItem.loadChildren(options);
|
||||
javaItemsLoaded = true;
|
||||
@@ -661,8 +664,10 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
enableSpecificSettings.set(!versionSetting.isUsesGlobal());
|
||||
|
||||
javaItem.setToggleSelectedListener(newValue -> {
|
||||
if (newValue.getUserData() == null) {
|
||||
if (javaCustomOption.isSelected()) {
|
||||
versionSetting.setUsesCustomJavaDir();
|
||||
} else if (javaAutoDeterminedOption.isSelected()) {
|
||||
versionSetting.setJavaAutoSelected();
|
||||
} else {
|
||||
versionSetting.setJavaVersion((JavaVersion) newValue.getUserData());
|
||||
}
|
||||
@@ -711,7 +716,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
VersionSetting versionSetting = lastVersionSetting;
|
||||
if (versionSetting == null)
|
||||
return;
|
||||
Task.supplyAsync(versionSetting::getJavaVersion)
|
||||
Task.fromCompletableFuture(versionSetting.getJavaVersion())
|
||||
.thenAcceptAsync(Schedulers.javafx(), javaVersion -> javaSublist.setSubtitle(Optional.ofNullable(javaVersion)
|
||||
.map(JavaVersion::getBinary).map(Path::toString).orElse("Invalid Java Path")))
|
||||
.start();
|
||||
|
||||
@@ -424,6 +424,7 @@ launch.failed.decompressing_natives=Unable to decompress native libraries.
|
||||
launch.failed.download_library=Unable to download library %s.
|
||||
launch.failed.executable_permission=Unable to add permission to the launch script
|
||||
launch.failed.exited_abnormally=Game exited abnormally, please check the log, or ask someone for help.
|
||||
launch.failed.no_accepted_java=Cannot find the Java installation suitable for current game. If you think you have installed a suitable Java VM, you can manually select it in game settings.
|
||||
launch.state.dependencies=Dependencies
|
||||
launch.state.done=Done
|
||||
launch.state.logging_in=Logging In
|
||||
@@ -765,6 +766,7 @@ settings.game.dimension=Game Window Dimension
|
||||
settings.game.exploration=Explore
|
||||
settings.game.fullscreen=Fullscreen
|
||||
settings.game.java_directory=Java Directory
|
||||
settings.game.java_directory.auto=Automatically selected
|
||||
settings.game.java_directory.bit=, %s-Bit
|
||||
settings.game.java_directory.choose=Choose Java Directory.
|
||||
settings.game.management=Manage
|
||||
|
||||
@@ -424,6 +424,7 @@ launch.failed.decompressing_natives=無法解壓縮遊戲資源庫。
|
||||
launch.failed.download_library=無法下載遊戲相依元件 %s。
|
||||
launch.failed.executable_permission=無法為啟動檔案新增執行權限。
|
||||
launch.failed.exited_abnormally=遊戲非正常退出,請查看記錄檔案,或聯絡他人尋求幫助。
|
||||
launch.failed.no_accepted_java=找不到適合當前遊戲使用的 Java。如果您認為實際存在合適的 Java,您可以在遊戲設置中手動設置 Java。
|
||||
launch.state.dependencies=處理遊戲相依元件
|
||||
launch.state.done=啟動完成
|
||||
launch.state.logging_in=登入
|
||||
@@ -764,6 +765,7 @@ settings.game.dimension=遊戲介面解析度大小
|
||||
settings.game.exploration=瀏覽
|
||||
settings.game.fullscreen=全螢幕
|
||||
settings.game.java_directory=Java 路徑
|
||||
settings.game.java_directory.auto=自動選擇合適的 Java
|
||||
settings.game.java_directory.bit=,%s 位
|
||||
settings.game.java_directory.choose=選擇 Java 路徑
|
||||
settings.game.management=管理
|
||||
|
||||
@@ -424,6 +424,7 @@ launch.failed.decompressing_natives=未能解压游戏本地库。
|
||||
launch.failed.download_library=未能下载游戏依赖 %s.
|
||||
launch.failed.executable_permission=未能为启动文件添加执行权限。
|
||||
launch.failed.exited_abnormally=游戏非正常退出,请查看日志文件,或联系他人寻求帮助。
|
||||
launch.failed.no_accepted_java=找不到适合当前游戏使用的 Java。如果您认为实际存在合适的 Java,您可以在游戏设置中手动设置 Java。
|
||||
launch.state.dependencies=处理游戏依赖
|
||||
launch.state.done=启动完成
|
||||
launch.state.logging_in=登录
|
||||
@@ -764,6 +765,7 @@ settings.game.dimension=游戏窗口分辨率
|
||||
settings.game.exploration=浏览
|
||||
settings.game.fullscreen=全屏
|
||||
settings.game.java_directory=Java 路径
|
||||
settings.game.java_directory.auto=自动选择合适的 Java
|
||||
settings.game.java_directory.bit=,%s 位
|
||||
settings.game.java_directory.choose=选择 Java 路径
|
||||
settings.game.management=管理
|
||||
|
||||
Reference in New Issue
Block a user