Compare commits

..
Author SHA1 Message Date
Haowei Wen 4b2e857f08 Fix chat is broken on Minecraft 1.20 2023-06-12 00:42:41 +08:00
Glavo 0280a34d00 fix #2267: filter duplicate jars in the class path (#2280) 2023-06-11 15:47:43 +08:00
Glavo 811707b611 代码清理以及微优化 (#2244) 2023-05-26 00:52:46 +08:00
Glavo 9152550bda Suppress compilation warnings (#2243)
* Suppress compilation warnings

* update
2023-05-24 22:05:30 +08:00
Glavo 6cb4d24600 Log XDG_SESSION_TYPE (#2242) 2023-05-24 21:51:22 +08:00
Glavo 49287453eb 优化 VersionNumber (#2240)
* 优化 VersionNumber

* Add more tests

* update

* update tests
2023-05-24 21:28:22 +08:00
Glavo e7c108d843 修复使用系统 GLFW 选项对 1.19+ 不生效的问题 (#2241)
* 修复使用系统 GLFW 选项对 1.19+ 不生效的问题

* Use UTF-8 in NativePatcher

* update
2023-05-24 21:15:09 +08:00
Glavo 6c9bf6cb09 修复翻译 (#2239) 2023-05-23 15:07:53 +08:00
Glavo efc27c8e21 添加 Linux RISC-V 64 平台支持 (#2238) 2023-05-23 05:17:26 +08:00
Glavo 7c5ccc906e 优化 SSL 异常报错信息 (#2234)
* 改进 SSL 异常报错

* update
2023-05-22 16:40:34 +08:00
zkitefly 69a6ff6053 Fix 2226 (#2227) 2023-05-20 02:18:25 +08:00
zkitefly 605d0c6a92 Fix #2204 (#2228)
https://github.com/huanghongxun/HMCL/issues/2204#issuecomment-1519823562
2023-05-20 02:17:18 +08:00
Glavo 9d54ddf456 修复无法使用 macOS aarch64 JRE 启动部分版本的问题 (#2222) 2023-05-10 20:08:53 +08:00
Glavo c6afd53e73 Bump dependencies (#2191) 2023-04-06 02:41:57 +08:00
Glavo fd9bb88033 添加隐藏提示按钮 (#2018)
* 添加隐藏提示按钮

* update

* update

* update

* update
2023-04-06 02:33:22 +08:00
Glavo 316607b333 添加对 Linux RISC-V 64 平台的基本支持 (#2187) 2023-04-01 06:29:51 +08:00
Glavo af36a0ac2a Bump Gradle to 8.0.1 (#2158) 2023-03-01 23:32:24 +08:00
Glavo 7356335106 添加环境变量设置 (#2157)
* Remove unused methods

* 添加环境变量设置

* update

* update
2023-02-26 23:46:00 +08:00
Glavo d8c96769ea Bump dependencies (#2149) 2023-02-24 04:31:45 +08:00
Glavo d71815aa3d 清理编译警告 (#2148) 2023-02-24 04:14:23 +08:00
Glavo 9a4286620f 简化 JFXHyperlink 用法 (#2147)
* 简化 JFXHyperlink 用法

* fix checkstyle
2023-02-24 03:49:13 +08:00
Glavo 0eac7b5d12 Use MESA_LOADER_DRIVER_OVERRIDE on Linux (#2146) 2023-02-24 01:56:01 +08:00
Glavo af63c3aa3a 添加渲染器设置 (#2145)
close #2143
2023-02-24 01:32:15 +08:00
Glavo 72a8199453 将版本高级设置拆分至单独页面中 (#2142)
* update

* update

* update

* update
2023-02-22 20:37:27 +08:00
Glavo 090b83bf9c fix #2129: Fix computePerfHeight of ComponentList (#2139)
* update

* update style

* update

* update

* update

* update
2023-02-19 19:22:01 +08:00
Glavo 6f70da51f9 Use computeIfAbsent in EventBus (#2138) 2023-02-19 13:25:20 +08:00
Glavo ed0a4c8cf4 fix #2131: Hide feedbackTextFlow from gameDirPane if possible (#2136)
* Add scroll bar to GameCrashWindow

* Hide feedbackTextFlow when it is not visible

* fix checkstyle

* update I18N

* update I18N
2023-02-19 12:36:35 +08:00
54 changed files with 1294 additions and 893 deletions

No files matched your search

@@ -19,6 +19,7 @@ public class SkinTransition extends Transition {
return count;
}
@SafeVarargs
public SkinTransition(Duration duration, Function<Double, Double> expression, WritableValue<Number>... observables) {
setCycleDuration(duration);
this.expression = expression;
@@ -222,7 +222,7 @@ public final class Launcher extends Application {
LOG.info("Java Version: " + System.getProperty("java.version") + ", " + System.getProperty("java.vendor"));
LOG.info("Java VM Version: " + System.getProperty("java.vm.name") + " (" + System.getProperty("java.vm.info") + "), " + System.getProperty("java.vm.vendor"));
LOG.info("Java Home: " + System.getProperty("java.home"));
LOG.info("Current Directory: " + Paths.get("").toAbsolutePath());
LOG.info("Current Directory: " + System.getProperty("user.dir"));
LOG.info("HMCL Directory: " + Metadata.HMCL_DIRECTORY);
LOG.info("HMCL Jar Path: " + JarUtils.thisJar().map(it -> it.toAbsolutePath().toString()).orElse("Not Found"));
LOG.info("Memory: " + Runtime.getRuntime().maxMemory() / 1024 / 1024 + "MB");
@@ -231,6 +231,8 @@ public final class Launcher extends Application {
.findAny()
.map(bean -> bean.getUsage().getUsed() / 1024 / 1024 + "MB")
.orElse("Unknown"));
if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX)
LOG.info("XDG Session Type: " + System.getenv("XDG_SESSION_TYPE"));
launch(Launcher.class, args);
} catch (Throwable e) { // Fucking JavaFX will suppress the exception and will break our crash reporter.
@@ -48,11 +48,13 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
import static org.jackhuang.hmcl.ui.FXUtils.newImage;
import static org.jackhuang.hmcl.util.Logging.LOG;
import static org.jackhuang.hmcl.util.Pair.pair;
public class HMCLGameRepository extends DefaultGameRepository {
private final Profile profile;
@@ -335,6 +337,16 @@ public class HMCLGameRepository extends DefaultGameRepository {
) / 1024 / 1024))
.setMinMemory(vs.getMinMemory())
.setMetaspace(Lang.toIntOrNull(vs.getPermSize()))
.setEnvironmentVariables(
Lang.mapOf(StringUtils.tokenize(vs.getEnvironmentVariables())
.stream()
.map(it -> {
int idx = it.indexOf('=');
return idx >= 0 ? pair(it.substring(0, idx), it.substring(idx + 1)) : pair(it, "");
})
.collect(Collectors.toList())
)
)
.setWidth(vs.getWidth())
.setHeight(vs.getHeight())
.setFullscreen(vs.isFullscreen())
@@ -346,7 +358,7 @@ public class HMCLGameRepository extends DefaultGameRepository {
.setNativesDirType(vs.getNativesDirType())
.setNativesDir(vs.getNativesDir())
.setProcessPriority(vs.getProcessPriority())
.setUseSoftwareRenderer(vs.isUseSoftwareRenderer())
.setRenderer(vs.getRenderer())
.setUseNativeGLFW(vs.isUseNativeGLFW())
.setUseNativeOpenAL(vs.isUseNativeOpenAL())
.setDaemon(!makeLaunchScript && vs.getLauncherVisibility().isDaemon())
@@ -150,8 +150,9 @@ public final class LauncherHelper {
}
}),
Task.composeAsync(() -> {
if (setting.isUseSoftwareRenderer() && OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
Library lib = NativePatcher.getSoftwareRendererLoader(javaVersion);
Renderer renderer = setting.getRenderer();
if (renderer != Renderer.DEFAULT && OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
Library lib = NativePatcher.getMesaLoader(javaVersion, renderer);
if (lib == null)
return null;
File file = dependencyManager.getGameRepository().getLibraryFile(version.get(), lib);
@@ -160,11 +161,13 @@ public final class LauncherHelper {
return null;
}
String agent = file.getAbsolutePath() + "=" + renderer.name().toLowerCase(Locale.ROOT);
if (GameLibrariesTask.shouldDownloadLibrary(repository, version.get(), lib, integrityCheck)) {
return new LibraryDownloadTask(dependencyManager, file, lib)
.thenRunAsync(() -> javaAgents.add(file.getAbsolutePath()));
.thenRunAsync(() -> javaAgents.add(agent));
} else {
javaAgents.add(file.getAbsolutePath());
javaAgents.add(agent);
return null;
}
} else {
@@ -42,6 +42,7 @@ import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.skin.InvalidSkinException;
import javax.net.ssl.SSLException;
import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;
@@ -427,7 +428,11 @@ public final class Accounts {
if (exception instanceof NoCharacterException) {
return i18n("account.failed.no_character");
} else if (exception instanceof ServerDisconnectException) {
return i18n("account.failed.connect_authentication_server");
if (exception.getCause() instanceof SSLException) {
return i18n("account.failed.ssl");
} else {
return i18n("account.failed.connect_authentication_server");
}
} else if (exception instanceof ServerResponseMalformedException) {
return i18n("account.failed.server_response_malformed");
} else if (exception instanceof RemoteAuthenticationException) {
@@ -172,6 +172,9 @@ public final class Config implements Cloneable, Observable {
@SerializedName("promptedVersion")
private StringProperty promptedVersion = new SimpleStringProperty();
@SerializedName("hideAnnouncementVersion")
private StringProperty hideAnnouncementVersion = new SimpleStringProperty();
@SerializedName("_version")
private IntegerProperty configVersion = new SimpleIntegerProperty(0);
@@ -639,4 +642,16 @@ public final class Config implements Cloneable, Observable {
public void setPromptedVersion(String promptedVersion) {
this.promptedVersion.set(promptedVersion);
}
public String getHideAnnouncementVersion() {
return hideAnnouncementVersion.get();
}
public StringProperty hideAnnouncementVersionProperty() {
return hideAnnouncementVersion;
}
public void setHideAnnouncementVersion(String hideAnnouncementDevVersion) {
this.hideAnnouncementVersion.set(hideAnnouncementDevVersion);
}
}
@@ -38,6 +38,7 @@ import java.lang.reflect.Type;
import java.nio.file.InvalidPathException;
import java.nio.file.Paths;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.concurrent.CancellationException;
import java.util.stream.Collectors;
@@ -340,6 +341,20 @@ public final class VersionSetting implements Cloneable {
minecraftArgsProperty.set(minecraftArgs);
}
private final StringProperty environmentVariablesProperty = new SimpleStringProperty(this, "environmentVariables", "");
public StringProperty environmentVariablesProperty() {
return environmentVariablesProperty;
}
public String getEnvironmentVariables() {
return environmentVariablesProperty.get();
}
public void setEnvironmentVariables(String env) {
environmentVariablesProperty.set(env);
}
private final BooleanProperty noJVMArgsProperty = new SimpleBooleanProperty(this, "noJVMArgs", false);
public BooleanProperty noJVMArgsProperty() {
@@ -482,7 +497,6 @@ public final class VersionSetting implements Cloneable {
widthProperty.set(width);
}
private final IntegerProperty heightProperty = new SimpleIntegerProperty(this, "height", 480);
public IntegerProperty heightProperty() {
@@ -553,18 +567,18 @@ public final class VersionSetting implements Cloneable {
processPriorityProperty.set(processPriority);
}
private final BooleanProperty useSoftwareRenderer = new SimpleBooleanProperty(this, "softwareRenderer", false);
private final ObjectProperty<Renderer> rendererProperty = new SimpleObjectProperty<>(this, "renderer", Renderer.DEFAULT);
public boolean isUseSoftwareRenderer() {
return useSoftwareRenderer.get();
public Renderer getRenderer() {
return rendererProperty.get();
}
public BooleanProperty useSoftwareRendererProperty() {
return useSoftwareRenderer;
public ObjectProperty<Renderer> rendererProperty() {
return rendererProperty;
}
public void setUseSoftwareRenderer(boolean useSoftwareRenderer) {
this.useSoftwareRenderer.set(useSoftwareRenderer);
public void setRenderer(Renderer renderer) {
this.rendererProperty.set(renderer);
}
private final BooleanProperty useNativeGLFW = new SimpleBooleanProperty(this, "nativeGLFW", false);
@@ -692,6 +706,7 @@ public final class VersionSetting implements Cloneable {
postExitCommand.addListener(listener);
javaArgsProperty.addListener(listener);
minecraftArgsProperty.addListener(listener);
environmentVariablesProperty.addListener(listener);
noJVMArgsProperty.addListener(listener);
notCheckGameProperty.addListener(listener);
notCheckJVMProperty.addListener(listener);
@@ -704,7 +719,7 @@ public final class VersionSetting implements Cloneable {
gameDirTypeProperty.addListener(listener);
gameDirProperty.addListener(listener);
processPriorityProperty.addListener(listener);
useSoftwareRenderer.addListener(listener);
rendererProperty.addListener(listener);
useNativeGLFW.addListener(listener);
useNativeOpenAL.addListener(listener);
launcherVisibilityProperty.addListener(listener);
@@ -730,6 +745,7 @@ public final class VersionSetting implements Cloneable {
versionSetting.setPostExitCommand(getPostExitCommand());
versionSetting.setJavaArgs(getJavaArgs());
versionSetting.setMinecraftArgs(getMinecraftArgs());
versionSetting.setEnvironmentVariables(getEnvironmentVariables());
versionSetting.setNoJVMArgs(isNoJVMArgs());
versionSetting.setNotCheckGame(isNotCheckGame());
versionSetting.setNotCheckJVM(isNotCheckJVM());
@@ -742,7 +758,7 @@ public final class VersionSetting implements Cloneable {
versionSetting.setGameDirType(getGameDirType());
versionSetting.setGameDir(getGameDir());
versionSetting.setProcessPriority(getProcessPriority());
versionSetting.setUseSoftwareRenderer(isUseSoftwareRenderer());
versionSetting.setRenderer(getRenderer());
versionSetting.setUseNativeGLFW(isUseNativeGLFW());
versionSetting.setUseNativeOpenAL(isUseNativeOpenAL());
versionSetting.setLauncherVisibility(getLauncherVisibility());
@@ -760,6 +776,7 @@ public final class VersionSetting implements Cloneable {
obj.addProperty("usesGlobal", src.isUsesGlobal());
obj.addProperty("javaArgs", src.getJavaArgs());
obj.addProperty("minecraftArgs", src.getMinecraftArgs());
obj.addProperty("environmentVariables", src.getEnvironmentVariables());
obj.addProperty("maxMemory", src.getMaxMemory() <= 0 ? OperatingSystem.SUGGESTED_MEMORY : src.getMaxMemory());
obj.addProperty("minMemory", src.getMinMemory());
obj.addProperty("autoMemory", src.isAutoMemory());
@@ -781,7 +798,6 @@ public final class VersionSetting implements Cloneable {
obj.addProperty("gameDir", src.getGameDir());
obj.addProperty("launcherVisibility", src.getLauncherVisibility().ordinal());
obj.addProperty("processPriority", src.getProcessPriority().ordinal());
obj.addProperty("useSoftwareRenderer", src.isUseSoftwareRenderer());
obj.addProperty("useNativeGLFW", src.isUseNativeGLFW());
obj.addProperty("useNativeOpenAL", src.isUseNativeOpenAL());
obj.addProperty("gameDirType", src.getGameDirType().ordinal());
@@ -790,6 +806,10 @@ public final class VersionSetting implements Cloneable {
obj.addProperty("nativesDirType", src.getNativesDirType().ordinal());
obj.addProperty("versionIcon", src.getVersionIcon().ordinal());
obj.addProperty("renderer", src.getRenderer().name());
if (src.getRenderer() == Renderer.LLVMPIPE)
obj.addProperty("useSoftwareRenderer", true);
return obj;
}
@@ -815,6 +835,7 @@ public final class VersionSetting implements Cloneable {
vs.setUsesGlobal(Optional.ofNullable(obj.get("usesGlobal")).map(JsonElement::getAsBoolean).orElse(false));
vs.setJavaArgs(Optional.ofNullable(obj.get("javaArgs")).map(JsonElement::getAsString).orElse(""));
vs.setMinecraftArgs(Optional.ofNullable(obj.get("minecraftArgs")).map(JsonElement::getAsString).orElse(""));
vs.setEnvironmentVariables(Optional.ofNullable(obj.get("environmentVariables")).map(JsonElement::getAsString).orElse(""));
vs.setMaxMemory(maxMemoryN);
vs.setMinMemory(Optional.ofNullable(obj.get("minMemory")).map(JsonElement::getAsInt).orElse(null));
vs.setAutoMemory(Optional.ofNullable(obj.get("autoMemory")).map(JsonElement::getAsBoolean).orElse(true));
@@ -836,7 +857,6 @@ public final class VersionSetting implements Cloneable {
vs.setShowLogs(Optional.ofNullable(obj.get("showLogs")).map(JsonElement::getAsBoolean).orElse(false));
vs.setLauncherVisibility(getOrDefault(LauncherVisibility.values(), obj.get("launcherVisibility"), LauncherVisibility.HIDE));
vs.setProcessPriority(getOrDefault(ProcessPriority.values(), obj.get("processPriority"), ProcessPriority.NORMAL));
vs.setUseSoftwareRenderer(Optional.ofNullable(obj.get("useSoftwareRenderer")).map(JsonElement::getAsBoolean).orElse(false));
vs.setUseNativeGLFW(Optional.ofNullable(obj.get("useNativeGLFW")).map(JsonElement::getAsBoolean).orElse(false));
vs.setUseNativeOpenAL(Optional.ofNullable(obj.get("useNativeOpenAL")).map(JsonElement::getAsBoolean).orElse(false));
vs.setGameDirType(getOrDefault(GameDirectoryType.values(), obj.get("gameDirType"), GameDirectoryType.ROOT_FOLDER));
@@ -844,6 +864,18 @@ public final class VersionSetting implements Cloneable {
vs.setNativesDirType(getOrDefault(NativesDirectoryType.values(), obj.get("nativesDirType"), NativesDirectoryType.VERSION_FOLDER));
vs.setVersionIcon(getOrDefault(VersionIconType.values(), obj.get("versionIcon"), VersionIconType.DEFAULT));
vs.setRenderer(Optional.ofNullable(obj.get("renderer")).map(JsonElement::getAsString)
.flatMap(name -> {
try {
return Optional.of(Renderer.valueOf(name.toUpperCase(Locale.ROOT)));
} catch (IllegalArgumentException ignored) {
return Optional.empty();
}
}).orElseGet(() -> {
boolean useSoftwareRenderer = Optional.ofNullable(obj.get("useSoftwareRenderer")).map(JsonElement::getAsBoolean).orElse(false);
return useSoftwareRenderer ? Renderer.LLVMPIPE : Renderer.DEFAULT;
}));
return vs;
}
@@ -227,7 +227,7 @@ public final class Controllers {
agreementPane.setHeading(new Label(i18n("launcher.agreement")));
agreementPane.setBody(new Label(i18n("launcher.agreement.hint")));
JFXHyperlink agreementLink = new JFXHyperlink(i18n("launcher.agreement"));
agreementLink.setOnAction(e -> FXUtils.openLink(Metadata.EULA_URL));
agreementLink.setExternalLink(Metadata.EULA_URL);
JFXButton yesButton = new JFXButton(i18n("launcher.agreement.accept"));
yesButton.getStyleClass().add("dialog-accept");
yesButton.setOnAction(e -> {
@@ -236,9 +236,7 @@ public final class Controllers {
});
JFXButton noButton = new JFXButton(i18n("launcher.agreement.decline"));
noButton.getStyleClass().add("dialog-cancel");
noButton.setOnAction(e -> {
System.exit(1);
});
noButton.setOnAction(e -> System.exit(1));
agreementPane.setActions(agreementLink, yesButton, noButton);
Controllers.dialog(agreementPane);
}
@@ -325,9 +323,14 @@ public final class Controllers {
public static void onHyperlinkAction(String href) {
if (href.startsWith("hmcl://")) {
if ("hmcl://settings/feedback".equals(href)) {
Controllers.getSettingsPage().showFeedback();
Controllers.navigate(Controllers.getSettingsPage());
switch (href) {
case "hmcl://settings/feedback":
Controllers.getSettingsPage().showFeedback();
Controllers.navigate(Controllers.getSettingsPage());
break;
case "hmcl://hide-announcement":
Controllers.getRootPage().getMainPage().hideAnnouncementPane();
break;
}
} else {
FXUtils.openLink(href);
@@ -625,16 +625,17 @@ public final class FXUtils {
* @param comboBox the combo box being bound with {@code property}.
* @param property the property being bound with {@code combo box}.
* @see #unbindEnum(JFXComboBox)
* @deprecated Use {@link ExtendedProperties#selectedItemPropertyFor(ComboBox)}
* @see ExtendedProperties#selectedItemPropertyFor(ComboBox)
*/
@SuppressWarnings("unchecked")
@Deprecated
public static void bindEnum(JFXComboBox<?> comboBox, Property<? extends Enum<?>> property) {
public static <T extends Enum<T>> void bindEnum(JFXComboBox<T> comboBox, Property<T> property) {
unbindEnum(comboBox);
@SuppressWarnings("rawtypes")
ChangeListener<Number> listener = (a, b, newValue) ->
((Property) property).setValue(property.getValue().getClass().getEnumConstants()[newValue.intValue()]);
comboBox.getSelectionModel().select(property.getValue().ordinal());
T currentValue = property.getValue();
@SuppressWarnings("unchecked")
T[] enumConstants = (T[]) currentValue.getClass().getEnumConstants();
ChangeListener<Number> listener = (a, b, newValue) -> property.setValue(enumConstants[newValue.intValue()]);
comboBox.getSelectionModel().select(currentValue.ordinal());
comboBox.getProperties().put("FXUtils.bindEnum.listener", listener);
comboBox.getSelectionModel().selectedIndexProperty().addListener(listener);
}
@@ -645,15 +646,13 @@ public final class FXUtils {
*
* @param comboBox the combo box being bound with the property which can be inferred by {@code bindEnum}.
* @see #bindEnum(JFXComboBox, Property)
* @deprecated Use {@link ExtendedProperties#selectedItemPropertyFor(ComboBox)}
* @see ExtendedProperties#selectedItemPropertyFor(ComboBox)
*/
@SuppressWarnings("unchecked")
@Deprecated
public static void unbindEnum(JFXComboBox<?> comboBox) {
@SuppressWarnings("rawtypes")
ChangeListener listener = tryCast(comboBox.getProperties().get("FXUtils.bindEnum.listener"), ChangeListener.class).orElse(null);
if (listener == null) return;
comboBox.getSelectionModel().selectedIndexProperty().removeListener(listener);
public static void unbindEnum(JFXComboBox<? extends Enum<?>> comboBox) {
@SuppressWarnings("unchecked")
ChangeListener<Number> listener = (ChangeListener<Number>) comboBox.getProperties().remove("FXUtils.bindEnum.listener");
if (listener != null)
comboBox.getSelectionModel().selectedIndexProperty().removeListener(listener);
}
/**
@@ -28,6 +28,7 @@ import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
@@ -347,9 +348,20 @@ public class GameCrashWindow extends Stage {
Label reasonTitle = new Label(i18n("game.crash.reason"));
reasonTitle.getStyleClass().add("two-line-item-second-large-title");
ScrollPane reasonPane = new ScrollPane(reasonTextFlow);
reasonPane.setFitToWidth(true);
reasonPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
reasonPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
gameDirPane.setPadding(new Insets(8));
VBox.setVgrow(gameDirPane, Priority.ALWAYS);
gameDirPane.getChildren().setAll(gameDir, javaDir, new VBox(reasonTitle, reasonTextFlow, feedbackTextFlow));
FXUtils.onChangeAndOperate(feedbackTextFlow.visibleProperty(), visible -> {
if (visible) {
gameDirPane.getChildren().setAll(gameDir, javaDir, new VBox(reasonTitle, reasonPane, feedbackTextFlow));
} else {
gameDirPane.getChildren().setAll(gameDir, javaDir, new VBox(reasonTitle, reasonPane));
}
});
}
HBox toolBar = new HBox();
@@ -33,6 +33,7 @@ import org.jackhuang.hmcl.ui.construct.SpinnerPane;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.io.NetworkUtils;
import javax.net.ssl.SSLException;
import java.io.IOException;
import java.util.logging.Level;
@@ -162,7 +163,9 @@ public final class AddAuthlibInjectorServerPane extends TransitionPane implement
}
private String resolveFetchExceptionMessage(Throwable exception) {
if (exception instanceof IOException) {
if (exception instanceof SSLException) {
return i18n("account.failed.ssl");
} else if (exception instanceof IOException) {
return i18n("account.failed.connect_injector_server");
} else {
return exception.getClass().getName() + ": " + exception.getLocalizedMessage();
@@ -36,7 +36,6 @@ import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilService;
import org.jackhuang.hmcl.setting.Accounts;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
import org.jackhuang.hmcl.ui.construct.JFXHyperlink;
import org.jackhuang.hmcl.ui.construct.RequiredValidator;
@@ -91,10 +90,10 @@ public class ClassicAccountLoginDialog extends StackPane {
body.getChildren().add(linkPane);
JFXHyperlink migrationLink = new JFXHyperlink(i18n("account.methods.yggdrasil.migration"));
migrationLink.setOnAction(e -> FXUtils.openLink(YggdrasilService.PROFILE_URL));
migrationLink.setExternalLink(YggdrasilService.PROFILE_URL);
JFXHyperlink migrationHowLink = new JFXHyperlink(i18n("account.methods.yggdrasil.migration.how"));
migrationHowLink.setOnAction(e -> FXUtils.openLink(YggdrasilService.MIGRATION_FAQ_URL));
migrationHowLink.setExternalLink(YggdrasilService.MIGRATION_FAQ_URL);
linkPane.getChildren().setAll(migrationLink, migrationHowLink);
}
@@ -512,13 +512,13 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
GridPane.setColumnSpan(box, 2);
JFXHyperlink migrationLink = new JFXHyperlink(i18n("account.methods.yggdrasil.migration"));
migrationLink.setOnAction(e -> FXUtils.openLink("https://aka.ms/MinecraftMigration"));
migrationLink.setExternalLink("https://aka.ms/MinecraftMigration");
JFXHyperlink migrationHowLink = new JFXHyperlink(i18n("account.methods.yggdrasil.migration.how"));
migrationHowLink.setOnAction(e -> FXUtils.openLink("https://help.minecraft.net/hc/articles/4411173197709"));
migrationHowLink.setExternalLink("https://help.minecraft.net/hc/articles/4411173197709");
JFXHyperlink purchaseLink = new JFXHyperlink(i18n("account.methods.yggdrasil.purchase"));
purchaseLink.setOnAction(e -> FXUtils.openLink(YggdrasilService.PURCHASE_URL));
purchaseLink.setExternalLink(YggdrasilService.PURCHASE_URL);
box.getChildren().setAll(migrationLink, migrationHowLink, purchaseLink);
add(box, 0, rowIndex);
@@ -530,7 +530,7 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
txtUsername.setPromptText(i18n("account.methods.offline.name.special_characters"));
JFXHyperlink purchaseLink = new JFXHyperlink(i18n("account.methods.yggdrasil.purchase"));
purchaseLink.setOnAction(e -> FXUtils.openLink(YggdrasilService.PURCHASE_URL));
purchaseLink.setExternalLink(YggdrasilService.PURCHASE_URL);
HBox linkPane = new HBox(purchaseLink);
GridPane.setColumnSpan(linkPane, 2);
add(linkPane, 0, rowIndex);
@@ -38,7 +38,8 @@ import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
import java.util.function.Consumer;
import java.util.List;
import java.util.function.Supplier;
@DefaultProperty("content")
public class ComponentList extends Control {
@@ -47,13 +48,17 @@ public class ComponentList extends Control {
private final IntegerProperty depth = new SimpleIntegerProperty(this, "depth", 0);
private boolean hasSubtitle = false;
public final ObservableList<Node> content = FXCollections.observableArrayList();
private boolean expanded = false;
private Consumer<ComponentList> lazyInitializer;
private Supplier<List<? extends Node>> lazyInitializer;
public ComponentList() {
getStyleClass().add("options-list");
}
public ComponentList(Supplier<List<? extends Node>> lazyInitializer) {
this();
this.lazyInitializer = lazyInitializer;
}
public String getTitle() {
return title.get();
}
@@ -102,17 +107,12 @@ public class ComponentList extends Control {
return content;
}
public void setLazyInitializer(Consumer<ComponentList> lazyInitializer) {
this.lazyInitializer = lazyInitializer;
}
public void onExpand() {
if (!expanded && lazyInitializer != null) {
lazyInitializer.accept(this);
void doLazyInit() {
if (lazyInitializer != null) {
this.getContent().setAll(lazyInitializer.get());
setNeedsLayout(true);
lazyInitializer = null;
}
expanded = true;
}
@Override
@@ -125,7 +125,7 @@ public class ComponentList extends Control {
return new Skin(this);
}
protected static class Skin extends ControlSkinBase<ComponentList> {
private static final class Skin extends ControlSkinBase<ComponentList> {
private static final PseudoClass PSEUDO_CLASS_FIRST = PseudoClass.getPseudoClass("first");
private static final PseudoClass PSEUDO_CLASS_LAST = PseudoClass.getPseudoClass("last");
@@ -133,7 +133,7 @@ public class ComponentList extends Control {
private final ObjectBinding<Node> firstItem;
private final ObjectBinding<Node> lastItem;
protected Skin(ComponentList control) {
Skin(ComponentList control) {
super(control);
list = MappedObservableList.create(control.getContent(), node -> {
@@ -43,7 +43,7 @@ import org.jackhuang.hmcl.ui.animation.AnimationUtils;
/**
* @author huangyuhui
*/
class ComponentListCell extends StackPane {
final class ComponentListCell extends StackPane {
private final Node content;
private Animation expandAnimation;
private Rectangle clipRect;
@@ -139,69 +139,53 @@ class ComponentListCell extends StackPane {
container.getChildren().setAll(content);
groupNode.getChildren().add(container);
EventHandler<Event> onExpand;
if (AnimationUtils.isAnimationEnabled()) {
onExpand = e -> {
if (expandAnimation != null && expandAnimation.getStatus() == Animation.Status.RUNNING) {
expandAnimation.stop();
EventHandler<Event> onExpand = e -> {
if (expandAnimation != null && expandAnimation.getStatus() == Animation.Status.RUNNING) {
expandAnimation.stop();
}
boolean expanded = !isExpanded();
setExpanded(expanded);
if (expanded) {
list.doLazyInit();
list.layout();
}
Platform.runLater(() -> {
double newAnimatedHeight = (list.prefHeight(-1) + 8 + 10) * (expanded ? 1 : -1);
double newHeight = expanded ? getHeight() + newAnimatedHeight : prefHeight(-1);
double contentHeight = expanded ? newAnimatedHeight : 0;
if (expanded) {
updateClip(newHeight);
}
setExpanded(!isExpanded());
if (isExpanded()) {
list.onExpand();
list.layout();
}
Platform.runLater(() -> {
double newAnimatedHeight = (content.prefHeight(-1) + 8 + 10) * (isExpanded() ? 1 : -1);
double newHeight = isExpanded() ? getHeight() + newAnimatedHeight : prefHeight(-1);
double contentHeight = isExpanded() ? newAnimatedHeight : 0;
if (isExpanded()) {
updateClip(newHeight);
}
if (AnimationUtils.isAnimationEnabled()) {
expandAnimation = new Timeline(new KeyFrame(new Duration(320.0),
new KeyValue(container.minHeightProperty(), contentHeight, FXUtils.SINE),
new KeyValue(container.maxHeightProperty(), contentHeight, FXUtils.SINE)
));
if (!isExpanded()) {
if (!expanded) {
expandAnimation.setOnFinished(e2 -> updateClip(newHeight));
}
expandAnimation.play();
});
};
} else {
onExpand = e -> {
setExpanded(!isExpanded());
} else {
container.setMinHeight(contentHeight);
container.setMaxHeight(contentHeight);
double newAnimatedHeight = (content.prefHeight(-1) + 8 + 10) * (isExpanded() ? 1 : -1);
double newHeight = isExpanded() ? getHeight() + newAnimatedHeight : prefHeight(-1);
double contentHeight = isExpanded() ? newAnimatedHeight : 0;
if (isExpanded()) {
list.onExpand();
list.layout();
updateClip(newHeight);
if (!expanded) {
updateClip(newHeight);
}
}
container.setMinHeight(contentHeight);
container.setMaxHeight(contentHeight);
if (!isExpanded()) {
updateClip(newHeight);
}
};
}
});
};
headerRippler.setOnMouseClicked(onExpand);
expandButton.setOnAction((EventHandler<ActionEvent>) (Object) onExpand);
expandedProperty().addListener((a, b, newValue) ->
expandIcon.setRotate(newValue ? 180 : 0));
expandedProperty().addListener((a, b, newValue) -> expandIcon.setRotate(newValue ? 180 : 0));
getChildren().setAll(groupNode);
} else {
@@ -39,6 +39,7 @@ import javafx.util.Duration;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
@SuppressWarnings("deprecation")
public class TabHeader extends Control implements TabControl, PageAware {
public TabHeader(Tab<?>... tabs) {
@@ -88,7 +88,9 @@ public class TaskExecutorDialogPane extends BorderPane {
btnCancel.setOnAction(e -> {
Optional.ofNullable(executor).ifPresent(TaskExecutor::cancel);
onCancel.getCancellationAction().accept(this);
if (onCancel.getCancellationAction() != null) {
onCancel.getCancellationAction().accept(this);
}
});
speedEventHandler = speedEvent -> {
@@ -33,6 +33,7 @@ import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Rectangle;
@@ -83,7 +84,7 @@ public final class MainPage extends StackPane implements DecoratorPage {
private final ObservableList<Node> versionNodes;
private Profile profile;
private final VBox announcementPane;
private VBox announcementPane;
private final StackPane updatePane;
private final JFXButton menuButton;
@@ -112,7 +113,15 @@ public final class MainPage extends StackPane implements DecoratorPage {
setPadding(new Insets(20));
announcementPane = new VBox(16);
if (Metadata.isNightly() || (Metadata.isDev() && !Objects.equals(Metadata.VERSION, config().getHideAnnouncementVersion()))) {
announcementPane = new VBox(16);
if (Metadata.isNightly()) {
announcementPane.getChildren().add(new AnnouncementCard(i18n("update.channel.nightly.title"), i18n("update.channel.nightly.hint")));
} else if (Metadata.isDev()) {
announcementPane.getChildren().add(new AnnouncementCard(i18n("update.channel.dev.title"), i18n("update.channel.dev.hint")));
}
getChildren().add(announcementPane);
}
updatePane = new StackPane();
updatePane.setVisible(false);
@@ -221,7 +230,7 @@ public final class MainPage extends StackPane implements DecoratorPage {
launchPane.getChildren().setAll(launchButton, separator, menuButton);
}
getChildren().setAll(announcementPane, updatePane, launchPane);
getChildren().addAll(updatePane, launchPane);
menu.setMaxHeight(365);
menu.setMaxWidth(545);
@@ -235,14 +244,6 @@ public final class MainPage extends StackPane implements DecoratorPage {
Bindings.bindContent(menu.getContent(), versionNodes);
}
public MainPage() {
if (Metadata.isNightly()) {
announcementPane.getChildren().add(new AnnouncementCard(i18n("update.channel.nightly.title"), i18n("update.channel.nightly.hint")));
} else if (Metadata.isDev()) {
announcementPane.getChildren().add(new AnnouncementCard(i18n("update.channel.dev.title"), i18n("update.channel.dev.hint")));
}
}
private void showUpdate(boolean show) {
doAnimation(show);
@@ -294,6 +295,16 @@ public final class MainPage extends StackPane implements DecoratorPage {
showUpdate.set(false);
}
public void hideAnnouncementPane() {
if (announcementPane != null) {
config().setHideAnnouncementVersion(Metadata.VERSION);
Pane parent = (Pane) announcementPane.getParent();
if (parent != null)
parent.getChildren().remove(announcementPane);
announcementPane = null;
}
}
@Override
public ReadOnlyObjectWrapper<State> stateProperty() {
return state;
@@ -82,7 +82,7 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
return new Skin(this);
}
private MainPage getMainPage() {
public MainPage getMainPage() {
if (mainPage == null) {
MainPage mainPage = new MainPage();
FXUtils.applyDragListener(mainPage, ModpackHelper::isFileModpackByExtension, modpacks -> {
@@ -157,7 +157,7 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
multiplayerItem.setActionButtonVisible(false);
multiplayerItem.setTitle(i18n("multiplayer"));
JFXHyperlink link = new JFXHyperlink(i18n("multiplayer.hint.details"));
link.setOnAction(e -> FXUtils.openLink("https://hmcl.huangyuhui.net/api/redirect/multiplayer-migrate"));
link.setExternalLink("https://hmcl.huangyuhui.net/api/redirect/multiplayer-migrate");
multiplayerItem.setOnAction(e -> Controllers.dialog(
new MessageDialogPane.Builder(i18n("multiplayer.hint"), null, MessageDialogPane.MessageType.INFO)
.addAction(link)
@@ -0,0 +1,258 @@
package org.jackhuang.hmcl.ui.versions;
import com.jfoenix.controls.JFXComboBox;
import com.jfoenix.controls.JFXTextField;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.*;
import org.jackhuang.hmcl.game.NativesDirectoryType;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.game.Renderer;
import org.jackhuang.hmcl.setting.VersionSetting;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Locale;
import static org.jackhuang.hmcl.ui.FXUtils.stringConverter;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class AdvancedVersionSettingPage extends StackPane implements DecoratorPage {
private final ObjectProperty<State> stateProperty;
private final Profile profile;
private final String versionId;
private final VersionSetting versionSetting;
private final JFXTextField txtJVMArgs;
private final JFXTextField txtGameArgs;
private final JFXTextField txtEnvironmentVariables;
private final JFXTextField txtMetaspace;
private final JFXTextField txtWrapper;
private final JFXTextField txtPreLaunchCommand;
private final JFXTextField txtPostExitCommand;
private final OptionToggleButton noJVMArgsPane;
private final OptionToggleButton noGameCheckPane;
private final OptionToggleButton noJVMCheckPane;
private final OptionToggleButton noNativesPatchPane;
private final OptionToggleButton useNativeGLFWPane;
private final OptionToggleButton useNativeOpenALPane;
private final ComponentSublist nativesDirSublist;
private final MultiFileItem<NativesDirectoryType> nativesDirItem;
private final MultiFileItem.FileOption<NativesDirectoryType> nativesDirCustomOption;
private final JFXComboBox<Renderer> cboRenderer;
public AdvancedVersionSettingPage(Profile profile, String versionId, VersionSetting versionSetting) {
this.profile = profile;
this.versionId = versionId;
this.versionSetting = versionSetting;
this.stateProperty = new SimpleObjectProperty<>(State.fromTitle(
versionId == null ? i18n("settings.advanced") : i18n("settings.advanced.title", versionId)
));
ScrollPane scrollPane = new ScrollPane();
scrollPane.setFitToHeight(true);
scrollPane.setFitToWidth(true);
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
getChildren().setAll(scrollPane);
VBox rootPane = new VBox();
rootPane.setFillWidth(true);
scrollPane.setContent(rootPane);
FXUtils.smoothScrolling(scrollPane);
rootPane.getStyleClass().add("card-list");
ComponentList customCommandsPane = new ComponentList();
{
GridPane pane = new GridPane();
pane.setHgap(16);
pane.setVgap(8);
pane.getColumnConstraints().setAll(new ColumnConstraints(), FXUtils.getColumnHgrowing());
txtGameArgs = new JFXTextField();
txtGameArgs.setPromptText(i18n("settings.advanced.minecraft_arguments.prompt"));
txtGameArgs.getStyleClass().add("fit-width");
pane.addRow(0, new Label(i18n("settings.advanced.minecraft_arguments")), txtGameArgs);
txtPreLaunchCommand = new JFXTextField();
txtPreLaunchCommand.setPromptText(i18n("settings.advanced.precall_command.prompt"));
txtPreLaunchCommand.getStyleClass().add("fit-width");
pane.addRow(1, new Label(i18n("settings.advanced.precall_command")), txtPreLaunchCommand);
txtWrapper = new JFXTextField();
txtWrapper.setPromptText(i18n("settings.advanced.wrapper_launcher.prompt"));
txtWrapper.getStyleClass().add("fit-width");
pane.addRow(2, new Label(i18n("settings.advanced.wrapper_launcher")), txtWrapper);
txtPostExitCommand = new JFXTextField();
txtPostExitCommand.setPromptText(i18n("settings.advanced.post_exit_command.prompt"));
txtPostExitCommand.getStyleClass().add("fit-width");
pane.addRow(3, new Label(i18n("settings.advanced.post_exit_command")), txtPostExitCommand);
HintPane hintPane = new HintPane();
hintPane.setText(i18n("settings.advanced.custom_commands.hint"));
GridPane.setColumnSpan(hintPane, 2);
pane.addRow(4, hintPane);
customCommandsPane.getContent().setAll(pane);
}
ComponentList jvmPane = new ComponentList();
{
GridPane pane = new GridPane();
ColumnConstraints title = new ColumnConstraints();
ColumnConstraints value = new ColumnConstraints();
value.setFillWidth(true);
value.setHgrow(Priority.ALWAYS);
pane.setHgap(16);
pane.setVgap(8);
pane.getColumnConstraints().setAll(title, value);
txtJVMArgs = new JFXTextField();
txtJVMArgs.getStyleClass().add("fit-width");
pane.addRow(0, new Label(i18n("settings.advanced.jvm_args")), txtJVMArgs);
HintPane hintPane = new HintPane();
hintPane.setText(i18n("settings.advanced.jvm_args.prompt"));
GridPane.setColumnSpan(hintPane, 2);
pane.addRow(4, hintPane);
txtMetaspace = new JFXTextField();
txtMetaspace.setPromptText(i18n("settings.advanced.java_permanent_generation_space.prompt"));
txtMetaspace.getStyleClass().add("fit-width");
FXUtils.setValidateWhileTextChanged(txtMetaspace, true);
txtMetaspace.setValidators(new NumberValidator(i18n("input.number"), true));
pane.addRow(1, new Label(i18n("settings.advanced.java_permanent_generation_space")), txtMetaspace);
txtEnvironmentVariables = new JFXTextField();
txtEnvironmentVariables.getStyleClass().add("fit-width");
pane.addRow(2, new Label(i18n("settings.advanced.environment_variables")), txtEnvironmentVariables);
jvmPane.getContent().setAll(pane);
}
ComponentList workaroundPane = new ComponentList();
HintPane workaroundWarning = new HintPane(MessageDialogPane.MessageType.WARNING);
workaroundWarning.setText(i18n("settings.advanced.workaround.warning"));
{
nativesDirItem = new MultiFileItem<>();
nativesDirSublist = new ComponentSublist();
nativesDirSublist.getContent().add(nativesDirItem);
nativesDirSublist.setTitle(i18n("settings.advanced.natives_directory"));
nativesDirSublist.setHasSubtitle(true);
nativesDirCustomOption = new MultiFileItem.FileOption<>(i18n("settings.advanced.natives_directory.custom"), NativesDirectoryType.CUSTOM)
.setChooserTitle(i18n("settings.advanced.natives_directory.choose"))
.setDirectory(true);
nativesDirItem.loadChildren(Arrays.asList(
new MultiFileItem.Option<>(i18n("settings.advanced.natives_directory.default"), NativesDirectoryType.VERSION_FOLDER),
nativesDirCustomOption
));
HintPane nativesDirHint = new HintPane(MessageDialogPane.MessageType.WARNING);
nativesDirHint.setText(i18n("settings.advanced.natives_directory.hint"));
nativesDirItem.getChildren().add(nativesDirHint);
BorderPane rendererPane = new BorderPane();
{
Label label = new Label(i18n("settings.advanced.renderer"));
rendererPane.setLeft(label);
BorderPane.setAlignment(label, Pos.CENTER_LEFT);
cboRenderer = new JFXComboBox<>();
cboRenderer.getItems().setAll(Renderer.values());
cboRenderer.setConverter(stringConverter(e -> i18n("settings.advanced.renderer." + e.name().toLowerCase(Locale.ROOT))));
rendererPane.setRight(cboRenderer);
BorderPane.setAlignment(cboRenderer, Pos.CENTER_RIGHT);
FXUtils.setLimitWidth(cboRenderer, 300);
}
noJVMArgsPane = new OptionToggleButton();
noJVMArgsPane.setTitle(i18n("settings.advanced.no_jvm_args"));
noGameCheckPane = new OptionToggleButton();
noGameCheckPane.setTitle(i18n("settings.advanced.dont_check_game_completeness"));
noJVMCheckPane = new OptionToggleButton();
noJVMCheckPane.setTitle(i18n("settings.advanced.dont_check_jvm_validity"));
noNativesPatchPane = new OptionToggleButton();
noNativesPatchPane.setTitle(i18n("settings.advanced.dont_patch_natives"));
useNativeGLFWPane = new OptionToggleButton();
useNativeGLFWPane.setTitle(i18n("settings.advanced.use_native_glfw"));
useNativeOpenALPane = new OptionToggleButton();
useNativeOpenALPane.setTitle(i18n("settings.advanced.use_native_openal"));
workaroundPane.getContent().setAll(
nativesDirSublist, rendererPane,
noJVMArgsPane, noGameCheckPane, noJVMCheckPane, noNativesPatchPane,
useNativeGLFWPane, useNativeOpenALPane);
}
rootPane.getChildren().addAll(
ComponentList.createComponentListTitle(i18n("settings.advanced.custom_commands")), customCommandsPane,
ComponentList.createComponentListTitle(i18n("settings.advanced.jvm")), jvmPane,
ComponentList.createComponentListTitle(i18n("settings.advanced.workaround")), workaroundWarning, workaroundPane
);
bindProperties();
}
void bindProperties() {
nativesDirCustomOption.bindBidirectional(versionSetting.nativesDirProperty());
FXUtils.bindString(txtJVMArgs, versionSetting.javaArgsProperty());
FXUtils.bindString(txtGameArgs, versionSetting.minecraftArgsProperty());
FXUtils.bindString(txtMetaspace, versionSetting.permSizeProperty());
FXUtils.bindString(txtEnvironmentVariables, versionSetting.environmentVariablesProperty());
FXUtils.bindString(txtWrapper, versionSetting.wrapperProperty());
FXUtils.bindString(txtPreLaunchCommand, versionSetting.preLaunchCommandProperty());
FXUtils.bindEnum(cboRenderer, versionSetting.rendererProperty());
noGameCheckPane.selectedProperty().bindBidirectional(versionSetting.notCheckGameProperty());
noJVMCheckPane.selectedProperty().bindBidirectional(versionSetting.notCheckJVMProperty());
noJVMArgsPane.selectedProperty().bindBidirectional(versionSetting.noJVMArgsProperty());
noNativesPatchPane.selectedProperty().bindBidirectional(versionSetting.notPatchNativesProperty());
useNativeGLFWPane.selectedProperty().bindBidirectional(versionSetting.useNativeGLFWProperty());
useNativeOpenALPane.selectedProperty().bindBidirectional(versionSetting.useNativeOpenALProperty());
nativesDirItem.selectedDataProperty().bindBidirectional(versionSetting.nativesDirTypeProperty());
nativesDirSublist.subtitleProperty().bind(Bindings.createStringBinding(() -> Paths.get(profile.getRepository().getRunDirectory(versionId).getAbsolutePath() + "/natives").normalize().toString(),
versionSetting.nativesDirProperty(), versionSetting.nativesDirTypeProperty()));
}
void unbindProperties() {
nativesDirCustomOption.valueProperty().unbindBidirectional(versionSetting.nativesDirProperty());
FXUtils.unbind(txtJVMArgs, versionSetting.javaArgsProperty());
FXUtils.unbind(txtGameArgs, versionSetting.minecraftArgsProperty());
FXUtils.unbind(txtMetaspace, versionSetting.permSizeProperty());
FXUtils.unbind(txtEnvironmentVariables, versionSetting.environmentVariablesProperty());
FXUtils.unbind(txtWrapper, versionSetting.wrapperProperty());
FXUtils.unbind(txtPreLaunchCommand, versionSetting.preLaunchCommandProperty());
FXUtils.unbind(txtPostExitCommand, versionSetting.postExitCommandProperty());
FXUtils.unbindEnum(cboRenderer);
noGameCheckPane.selectedProperty().unbindBidirectional(versionSetting.notCheckGameProperty());
noJVMCheckPane.selectedProperty().unbindBidirectional(versionSetting.notCheckJVMProperty());
noJVMArgsPane.selectedProperty().unbindBidirectional(versionSetting.noJVMArgsProperty());
noNativesPatchPane.selectedProperty().unbindBidirectional(versionSetting.notPatchNativesProperty());
useNativeGLFWPane.selectedProperty().unbindBidirectional(versionSetting.useNativeGLFWProperty());
useNativeOpenALPane.selectedProperty().unbindBidirectional(versionSetting.useNativeOpenALProperty());
nativesDirItem.selectedDataProperty().unbindBidirectional(versionSetting.nativesDirTypeProperty());
nativesDirSublist.subtitleProperty().unbind();
}
@Override
public ReadOnlyObjectProperty<State> stateProperty() {
return stateProperty;
}
}
@@ -260,18 +260,18 @@ public class DownloadPage extends Control implements DecoratorPage {
if (getSkinnable().mod != null) {
JFXHyperlink openMcmodButton = new JFXHyperlink(i18n("mods.mcmod"));
openMcmodButton.setOnAction(e -> FXUtils.openLink(getSkinnable().translations.getMcmodUrl(getSkinnable().mod)));
openMcmodButton.setExternalLink(getSkinnable().translations.getMcmodUrl(getSkinnable().mod));
descriptionPane.getChildren().add(openMcmodButton);
if (StringUtils.isNotBlank(getSkinnable().mod.getMcbbs())) {
JFXHyperlink openMcbbsButton = new JFXHyperlink(i18n("mods.mcbbs"));
openMcbbsButton.setOnAction(e -> FXUtils.openLink(ModManager.getMcbbsUrl(getSkinnable().mod.getMcbbs())));
openMcbbsButton.setExternalLink(ModManager.getMcbbsUrl(getSkinnable().mod.getMcbbs()));
descriptionPane.getChildren().add(openMcbbsButton);
}
}
JFXHyperlink openUrlButton = new JFXHyperlink(control.page.getLocalizedOfficialPage());
openUrlButton.setOnAction(e -> FXUtils.openLink(getSkinnable().addon.getPageUrl()));
openUrlButton.setExternalLink(getSkinnable().addon.getPageUrl());
descriptionPane.getChildren().add(openUrlButton);
}
@@ -322,12 +322,10 @@ public class DownloadPage extends Control implements DecoratorPage {
for (String gameVersion : control.versions.keys().stream()
.sorted(VersionNumber.VERSION_COMPARATOR.reversed())
.collect(Collectors.toList())) {
ComponentList sublist = new ComponentList();
sublist.setLazyInitializer(self -> {
self.getContent().setAll(control.versions.get(gameVersion).stream()
.map(version -> new ModItem(version, control))
.collect(Collectors.toList()));
});
ComponentList sublist = new ComponentList(() ->
control.versions.get(gameVersion).stream()
.map(version -> new ModItem(version, control))
.collect(Collectors.toList()));
sublist.getStyleClass().add("no-padding");
sublist.setTitle(gameVersion);
@@ -369,6 +367,7 @@ public class DownloadPage extends Control implements DecoratorPage {
}
private static final class ModItem extends StackPane {
ModItem(RemoteMod.Version dataItem, DownloadPage selfPage) {
HBox pane = new HBox(8);
pane.setPadding(new Insets(8));
@@ -378,9 +377,7 @@ public class DownloadPage extends Control implements DecoratorPage {
JFXButton saveAsButton = new JFXButton();
RipplerContainer container = new RipplerContainer(pane);
container.setOnMouseClicked(e -> {
selfPage.download(dataItem);
});
container.setOnMouseClicked(e -> selfPage.download(dataItem));
getChildren().setAll(container);
saveAsButton.getStyleClass().add("toggle-icon4");
@@ -391,7 +388,7 @@ public class DownloadPage extends Control implements DecoratorPage {
content.setTitle(dataItem.getName());
content.setSubtitle(FORMATTER.format(dataItem.getDatePublished().toInstant()));
saveAsButton.setOnMouseClicked(e -> selfPage.saveAs(dataItem));
saveAsButton.setOnAction(e -> selfPage.saveAs(dataItem));
switch (dataItem.getVersionType()) {
case Release:
@@ -407,6 +404,9 @@ public class DownloadPage extends Control implements DecoratorPage {
content.getTags().add(i18n("version.game.snapshot"));
break;
}
// Workaround for https://github.com/huanghongxun/HMCL/issues/2129
this.setMinHeight(50);
}
}
@@ -308,8 +308,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
setBody(description);
if (StringUtils.isNotBlank(modInfo.getModInfo().getUrl())) {
JFXHyperlink officialPageButton = new JFXHyperlink();
officialPageButton.setText(i18n("mods.url"));
JFXHyperlink officialPageButton = new JFXHyperlink(i18n("mods.url"));
officialPageButton.setOnAction(e -> {
fireEvent(new DialogCloseEvent());
FXUtils.openLink(modInfo.getModInfo().getUrl());
@@ -319,8 +318,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
}
if (modInfo.getMod() != null && StringUtils.isNotBlank(modInfo.getMod().getMcbbs())) {
JFXHyperlink mcbbsButton = new JFXHyperlink();
mcbbsButton.setText(i18n("mods.mcbbs"));
JFXHyperlink mcbbsButton = new JFXHyperlink(i18n("mods.mcbbs"));
mcbbsButton.setOnAction(e -> {
fireEvent(new DialogCloseEvent());
FXUtils.openLink(ModManager.getMcbbsUrl(modInfo.getMod().getMcbbs()));
@@ -329,8 +327,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
}
if (modInfo.getMod() == null || StringUtils.isBlank(modInfo.getMod().getMcmod())) {
JFXHyperlink searchButton = new JFXHyperlink();
searchButton.setText(i18n("mods.mcmod.search"));
JFXHyperlink searchButton = new JFXHyperlink(i18n("mods.mcmod.search"));
searchButton.setOnAction(e -> {
fireEvent(new DialogCloseEvent());
FXUtils.openLink(NetworkUtils.withQuery("https://search.mcmod.cn/s", mapOf(
@@ -341,8 +338,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
});
getActions().add(searchButton);
} else {
JFXHyperlink mcmodButton = new JFXHyperlink();
mcmodButton.setText(i18n("mods.mcmod.page"));
JFXHyperlink mcmodButton = new JFXHyperlink(i18n("mods.mcmod.page"));
mcmodButton.setOnAction(e -> {
fireEvent(new DialogCloseEvent());
FXUtils.openLink(ModTranslations.MOD.getMcmodUrl(modInfo.getMod()));
@@ -66,7 +66,7 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class VersionSettingsPage extends StackPane implements DecoratorPage, VersionPage.VersionLoadable, PageAware {
private final ReadOnlyObjectWrapper<State> state = new ReadOnlyObjectWrapper<>(new State("", null, false, false, false));
private final boolean globalSetting;
private AdvancedVersionSettingPage advancedVersionSettingPage;
private VersionSetting lastVersionSetting = null;
private Profile profile;
@@ -77,24 +77,11 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
private final VBox rootPane;
private final JFXTextField txtWidth;
private final JFXTextField txtHeight;
private final JFXTextField txtJVMArgs;
private final JFXTextField txtGameArgs;
private final JFXTextField txtMetaspace;
private final JFXTextField txtWrapper;
private final JFXTextField txtPreLaunchCommand;
private final JFXTextField txtPostExitCommand;
private final JFXTextField txtServerIP;
private final ComponentList componentList;
private final JFXComboBox<LauncherVisibility> cboLauncherVisibility;
private final JFXCheckBox chkAutoAllocate;
private final JFXCheckBox chkFullscreen;
private final OptionToggleButton noJVMArgsPane;
private final OptionToggleButton noGameCheckPane;
private final OptionToggleButton noJVMCheckPane;
private final OptionToggleButton noNativesPatchPane;
private final OptionToggleButton useSoftwareRenderer;
private final OptionToggleButton useNativeGLFWPane;
private final OptionToggleButton useNativeOpenALPane;
private final ComponentSublist javaSublist;
private final MultiFileItem<Pair<JavaVersionType, JavaVersion>> javaItem;
private final MultiFileItem.Option<Pair<JavaVersionType, JavaVersion>> javaAutoDeterminedOption;
@@ -102,12 +89,9 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
private final ComponentSublist gameDirSublist;
private final MultiFileItem<GameDirectoryType> gameDirItem;
private final MultiFileItem.FileOption<GameDirectoryType> gameDirCustomOption;
private final ComponentSublist nativesDirSublist;
private final MultiFileItem<NativesDirectoryType> nativesDirItem;
private final MultiFileItem.FileOption<NativesDirectoryType> nativesDirCustomOption;
private final JFXComboBox<ProcessPriority> cboProcessPriority;
private final OptionToggleButton showLogsPane;
private ImagePickerItem iconPickerItem;
private final ImagePickerItem iconPickerItem;
private final InvalidationListener specificSettingsListener;
@@ -121,8 +105,6 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
private final BooleanProperty modpack = new SimpleBooleanProperty();
public VersionSettingsPage(boolean globalSetting) {
this.globalSetting = globalSetting;
ScrollPane scrollPane = new ScrollPane();
scrollPane.setFitToHeight(true);
scrollPane.setFitToWidth(true);
@@ -145,18 +127,17 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
text.textProperty().bind(BindingMapping.of(selectedVersion)
.map(selectedVersion -> i18n("settings.type.special.edit.hint", selectedVersion)));
JFXHyperlink specificSettingsLink = new JFXHyperlink();
specificSettingsLink.setText(i18n("settings.type.special.edit"));
specificSettingsLink.setOnMouseClicked(e -> editSpecificSettings());
JFXHyperlink specificSettingsLink = new JFXHyperlink(i18n("settings.type.special.edit"));
specificSettingsLink.setOnAction(e -> editSpecificSettings());
specificSettingsHint.setChildren(text, specificSettingsLink);
specificSettingsHint.managedProperty().bind(navigateToSpecificSettings);
specificSettingsHint.visibleProperty().bind(navigateToSpecificSettings);
rootPane.getChildren().addAll(specificSettingsHint);
}
iconPickerItem = null;
if (!globalSetting) {
rootPane.getChildren().addAll(specificSettingsHint);
} else {
HintPane gameDirHint = new HintPane(MessageDialogPane.MessageType.INFO);
gameDirHint.setText(i18n("settings.game.working_directory.hint"));
rootPane.getChildren().add(gameDirHint);
@@ -171,7 +152,6 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
iconPickerItem.setOnDeleteButtonClicked(e -> onDeleteIcon());
iconPickerItemWrapper.getContent().setAll(iconPickerItem);
BorderPane settingsTypePane = new BorderPane();
settingsTypePane.disableProperty().bind(modpack);
rootPane.getChildren().add(settingsTypePane);
@@ -186,7 +166,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
settingsTypePane.setRight(editGlobalSettingsButton);
editGlobalSettingsButton.disableProperty().bind(enableSpecificCheckBox.selectedProperty());
BorderPane.setAlignment(editGlobalSettingsButton, Pos.CENTER_RIGHT);
editGlobalSettingsButton.setOnMouseClicked(e -> editGlobalSettings());
editGlobalSettingsButton.setOnAction(e -> editGlobalSettings());
}
{
@@ -401,146 +381,43 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
serverPane.addRow(0, new Label(i18n("settings.advanced.server_ip")), txtServerIP);
}
componentList.getContent().setAll(javaSublist, gameDirSublist, maxMemoryPane, launcherVisibilityPane, dimensionPane, showLogsPane, processPriorityPane, serverPane);
BorderPane showAdvancedSettingPane = new BorderPane();
{
Label label = new Label(i18n("settings.advanced"));
showAdvancedSettingPane.setLeft(label);
BorderPane.setAlignment(label, Pos.CENTER_LEFT);
JFXButton button = new JFXButton(i18n("settings.advanced.modify"));
button.setOnAction(e -> {
if (lastVersionSetting != null) {
if (advancedVersionSettingPage == null)
advancedVersionSettingPage = new AdvancedVersionSettingPage(profile, versionId, lastVersionSetting);
Controllers.navigate(advancedVersionSettingPage);
}
});
button.getStyleClass().add("jfx-button-border");
showAdvancedSettingPane.setRight(button);
}
componentList.getContent().setAll(
javaSublist,
gameDirSublist,
maxMemoryPane,
launcherVisibilityPane,
dimensionPane,
showLogsPane,
processPriorityPane,
serverPane,
showAdvancedSettingPane
);
}
HBox advancedHintPane = new HBox();
advancedHintPane.setAlignment(Pos.CENTER);
advancedHintPane.setPadding(new Insets(16, 0, 8, 0));
{
Label advanced = new Label(i18n("settings.advanced"));
advanced.setStyle("-fx-font-size: 12px;");
advancedHintPane.getChildren().setAll(advanced);
}
ComponentList customCommandsPane = new ComponentList();
customCommandsPane.disableProperty().bind(enableSpecificSettings.not());
{
GridPane pane = new GridPane();
pane.setHgap(16);
pane.setVgap(8);
pane.getColumnConstraints().setAll(new ColumnConstraints(), FXUtils.getColumnHgrowing());
txtGameArgs = new JFXTextField();
txtGameArgs.setPromptText(i18n("settings.advanced.minecraft_arguments.prompt"));
txtGameArgs.getStyleClass().add("fit-width");
pane.addRow(0, new Label(i18n("settings.advanced.minecraft_arguments")), txtGameArgs);
txtPreLaunchCommand = new JFXTextField();
txtPreLaunchCommand.setPromptText(i18n("settings.advanced.precall_command.prompt"));
txtPreLaunchCommand.getStyleClass().add("fit-width");
pane.addRow(1, new Label(i18n("settings.advanced.precall_command")), txtPreLaunchCommand);
txtWrapper = new JFXTextField();
txtWrapper.setPromptText(i18n("settings.advanced.wrapper_launcher.prompt"));
txtWrapper.getStyleClass().add("fit-width");
pane.addRow(2, new Label(i18n("settings.advanced.wrapper_launcher")), txtWrapper);
txtPostExitCommand = new JFXTextField();
txtPostExitCommand.setPromptText(i18n("settings.advanced.post_exit_command.prompt"));
txtPostExitCommand.getStyleClass().add("fit-width");
pane.addRow(3, new Label(i18n("settings.advanced.post_exit_command")), txtPostExitCommand);
HintPane hintPane = new HintPane();
hintPane.setText(i18n("settings.advanced.custom_commands.hint"));
GridPane.setColumnSpan(hintPane, 2);
pane.addRow(4, hintPane);
customCommandsPane.getContent().setAll(pane);
}
ComponentList jvmPane = new ComponentList();
jvmPane.disableProperty().bind(enableSpecificSettings.not());
{
GridPane pane = new GridPane();
ColumnConstraints title = new ColumnConstraints();
ColumnConstraints value = new ColumnConstraints();
value.setFillWidth(true);
value.setHgrow(Priority.ALWAYS);
pane.setHgap(16);
pane.setVgap(8);
pane.getColumnConstraints().setAll(title, value);
txtJVMArgs = new JFXTextField();
txtJVMArgs.getStyleClass().add("fit-width");
pane.addRow(0, new Label(i18n("settings.advanced.jvm_args")), txtJVMArgs);
HintPane hintPane = new HintPane();
hintPane.setText(i18n("settings.advanced.jvm_args.prompt"));
GridPane.setColumnSpan(hintPane, 2);
pane.addRow(4, hintPane);
txtMetaspace = new JFXTextField();
txtMetaspace.setPromptText(i18n("settings.advanced.java_permanent_generation_space.prompt"));
txtMetaspace.getStyleClass().add("fit-width");
FXUtils.setValidateWhileTextChanged(txtMetaspace, true);
txtMetaspace.setValidators(new NumberValidator(i18n("input.number"), true));
pane.addRow(1, new Label(i18n("settings.advanced.java_permanent_generation_space")), txtMetaspace);
jvmPane.getContent().setAll(pane);
}
ComponentList workaroundPane = new ComponentList();
workaroundPane.disableProperty().bind(enableSpecificSettings.not());
HintPane workaroundWarning = new HintPane(MessageDialogPane.MessageType.WARNING);
workaroundWarning.setText(i18n("settings.advanced.workaround.warning"));
{
nativesDirItem = new MultiFileItem<>();
nativesDirSublist = new ComponentSublist();
nativesDirSublist.getContent().add(nativesDirItem);
nativesDirSublist.setTitle(i18n("settings.advanced.natives_directory"));
nativesDirSublist.setHasSubtitle(true);
nativesDirCustomOption = new MultiFileItem.FileOption<>(i18n("settings.advanced.natives_directory.custom"), NativesDirectoryType.CUSTOM)
.setChooserTitle(i18n("settings.advanced.natives_directory.choose"))
.setDirectory(true);
nativesDirItem.loadChildren(Arrays.asList(
new MultiFileItem.Option<>(i18n("settings.advanced.natives_directory.default"), NativesDirectoryType.VERSION_FOLDER),
nativesDirCustomOption
));
HintPane nativesDirHint = new HintPane(MessageDialogPane.MessageType.WARNING);
nativesDirHint.setText(i18n("settings.advanced.natives_directory.hint"));
nativesDirItem.getChildren().add(nativesDirHint);
noJVMArgsPane = new OptionToggleButton();
noJVMArgsPane.setTitle(i18n("settings.advanced.no_jvm_args"));
noGameCheckPane = new OptionToggleButton();
noGameCheckPane.setTitle(i18n("settings.advanced.dont_check_game_completeness"));
noJVMCheckPane = new OptionToggleButton();
noJVMCheckPane.setTitle(i18n("settings.advanced.dont_check_jvm_validity"));
noNativesPatchPane = new OptionToggleButton();
noNativesPatchPane.setTitle(i18n("settings.advanced.dont_patch_natives"));
useSoftwareRenderer = new OptionToggleButton();
useSoftwareRenderer.setTitle(i18n("settings.advanced.use_software_renderer"));
useNativeGLFWPane = new OptionToggleButton();
useNativeGLFWPane.setTitle(i18n("settings.advanced.use_native_glfw"));
useNativeOpenALPane = new OptionToggleButton();
useNativeOpenALPane.setTitle(i18n("settings.advanced.use_native_openal"));
workaroundPane.getContent().setAll(
nativesDirSublist,
noJVMArgsPane, noGameCheckPane, noJVMCheckPane, noNativesPatchPane,
useSoftwareRenderer, useNativeGLFWPane, useNativeOpenALPane);
}
rootPane.getChildren().addAll(componentList,
advancedHintPane,
ComponentList.createComponentListTitle(i18n("settings.advanced.custom_commands")), customCommandsPane,
ComponentList.createComponentListTitle(i18n("settings.advanced.jvm")), jvmPane,
ComponentList.createComponentListTitle(i18n("settings.advanced.workaround")), workaroundWarning, workaroundPane);
rootPane.getChildren().add(componentList);
initialize();
specificSettingsListener = any -> {
enableSpecificSettings.set(!lastVersionSetting.isUsesGlobal());
};
specificSettingsListener = any -> enableSpecificSettings.set(!lastVersionSetting.isUsesGlobal());
addEventHandler(Navigator.NavigationEvent.NAVIGATED, this::onDecoratorPageNavigating);
@@ -595,6 +472,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
}
@Override
@SuppressWarnings("unchecked")
public void loadVersion(Profile profile, String versionId) {
this.profile = profile;
this.versionId = versionId;
@@ -631,24 +509,10 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
maxMemory.unbindBidirectional(lastVersionSetting.maxMemoryProperty());
javaCustomOption.valueProperty().unbindBidirectional(lastVersionSetting.javaDirProperty());
gameDirCustomOption.valueProperty().unbindBidirectional(lastVersionSetting.gameDirProperty());
nativesDirCustomOption.valueProperty().unbindBidirectional(lastVersionSetting.nativesDirProperty());
FXUtils.unbind(txtJVMArgs, lastVersionSetting.javaArgsProperty());
FXUtils.unbind(txtGameArgs, lastVersionSetting.minecraftArgsProperty());
FXUtils.unbind(txtMetaspace, lastVersionSetting.permSizeProperty());
FXUtils.unbind(txtWrapper, lastVersionSetting.wrapperProperty());
FXUtils.unbind(txtPreLaunchCommand, lastVersionSetting.preLaunchCommandProperty());
FXUtils.unbind(txtPostExitCommand, lastVersionSetting.postExitCommandProperty());
FXUtils.unbind(txtServerIP, lastVersionSetting.serverIpProperty());
FXUtils.unbindBoolean(chkAutoAllocate, lastVersionSetting.autoMemoryProperty());
FXUtils.unbindBoolean(chkFullscreen, lastVersionSetting.fullscreenProperty());
noGameCheckPane.selectedProperty().unbindBidirectional(lastVersionSetting.notCheckGameProperty());
noJVMCheckPane.selectedProperty().unbindBidirectional(lastVersionSetting.notCheckJVMProperty());
noJVMArgsPane.selectedProperty().unbindBidirectional(lastVersionSetting.noJVMArgsProperty());
noNativesPatchPane.selectedProperty().unbindBidirectional(lastVersionSetting.notPatchNativesProperty());
showLogsPane.selectedProperty().unbindBidirectional(lastVersionSetting.showLogsProperty());
useSoftwareRenderer.selectedProperty().unbindBidirectional(lastVersionSetting.useSoftwareRendererProperty());
useNativeGLFWPane.selectedProperty().unbindBidirectional(lastVersionSetting.useNativeGLFWProperty());
useNativeOpenALPane.selectedProperty().unbindBidirectional(lastVersionSetting.useNativeOpenALProperty());
FXUtils.unbindEnum(cboLauncherVisibility);
FXUtils.unbindEnum(cboProcessPriority);
@@ -659,8 +523,10 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
gameDirItem.selectedDataProperty().unbindBidirectional(lastVersionSetting.gameDirTypeProperty());
gameDirSublist.subtitleProperty().unbind();
nativesDirItem.selectedDataProperty().unbindBidirectional(lastVersionSetting.nativesDirTypeProperty());
nativesDirSublist.subtitleProperty().unbind();
if (advancedVersionSettingPage != null) {
advancedVersionSettingPage.unbindProperties();
advancedVersionSettingPage = null;
}
}
// unbind data fields
@@ -673,23 +539,10 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
javaCustomOption.bindBidirectional(versionSetting.javaDirProperty());
gameDirCustomOption.bindBidirectional(versionSetting.gameDirProperty());
nativesDirCustomOption.bindBidirectional(versionSetting.nativesDirProperty());
FXUtils.bindString(txtJVMArgs, versionSetting.javaArgsProperty());
FXUtils.bindString(txtGameArgs, versionSetting.minecraftArgsProperty());
FXUtils.bindString(txtMetaspace, versionSetting.permSizeProperty());
FXUtils.bindString(txtWrapper, versionSetting.wrapperProperty());
FXUtils.bindString(txtPreLaunchCommand, versionSetting.preLaunchCommandProperty());
FXUtils.bindString(txtServerIP, versionSetting.serverIpProperty());
FXUtils.bindBoolean(chkAutoAllocate, versionSetting.autoMemoryProperty());
FXUtils.bindBoolean(chkFullscreen, versionSetting.fullscreenProperty());
noGameCheckPane.selectedProperty().bindBidirectional(versionSetting.notCheckGameProperty());
noJVMCheckPane.selectedProperty().bindBidirectional(versionSetting.notCheckJVMProperty());
noJVMArgsPane.selectedProperty().bindBidirectional(versionSetting.noJVMArgsProperty());
noNativesPatchPane.selectedProperty().bindBidirectional(versionSetting.notPatchNativesProperty());
showLogsPane.selectedProperty().bindBidirectional(versionSetting.showLogsProperty());
useSoftwareRenderer.selectedProperty().bindBidirectional(versionSetting.useSoftwareRendererProperty());
useNativeGLFWPane.selectedProperty().bindBidirectional(versionSetting.useNativeGLFWProperty());
useNativeOpenALPane.selectedProperty().bindBidirectional(versionSetting.useNativeOpenALProperty());
FXUtils.bindEnum(cboLauncherVisibility, versionSetting.launcherVisibilityProperty());
FXUtils.bindEnum(cboProcessPriority, versionSetting.processPriorityProperty());
@@ -703,7 +556,6 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
} else if (javaAutoDeterminedOption.isSelected()) {
versionSetting.setJavaAutoSelected();
} else {
//noinspection unchecked
versionSetting.setJavaVersion(((Pair<JavaVersionType, JavaVersion>) newValue.getUserData()).getValue());
}
});
@@ -716,10 +568,6 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
gameDirSublist.subtitleProperty().bind(Bindings.createStringBinding(() -> Paths.get(profile.getRepository().getRunDirectory(versionId).getAbsolutePath()).normalize().toString(),
versionSetting.gameDirProperty(), versionSetting.gameDirTypeProperty()));
nativesDirItem.selectedDataProperty().bindBidirectional(versionSetting.nativesDirTypeProperty());
nativesDirSublist.subtitleProperty().bind(Bindings.createStringBinding(() -> Paths.get(profile.getRepository().getRunDirectory(versionId).getAbsolutePath() + "/natives").normalize().toString(),
versionSetting.nativesDirProperty(), versionSetting.nativesDirTypeProperty()));
lastVersionSetting = versionSetting;
initJavaSubtitle();
@@ -1,21 +1,22 @@
package org.jackhuang.hmcl.util;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.NativesDirectoryType;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.setting.VersionSetting;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.platform.Architecture;
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.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.logging.Level;
import java.util.stream.Collectors;
import static org.jackhuang.hmcl.util.Logging.LOG;
@@ -25,10 +26,24 @@ public final class NativePatcher {
private static final Library NONEXISTENT_LIBRARY = new Library(null);
public static Version patchNative(Version version, String gameVersion, JavaVersion javaVersion, VersionSetting settings) {
if (settings.isNotPatchNatives())
return version;
private static final Map<Platform, Map<String, Library>> natives = new HashMap<>();
private static Map<String, Library> getNatives(Platform platform) {
return natives.computeIfAbsent(platform, p -> {
//noinspection ConstantConditions
try (Reader reader = new InputStreamReader(NativePatcher.class.getResourceAsStream("/assets/natives.json"), StandardCharsets.UTF_8)) {
Map<String, Map<String, Library>> natives = JsonUtils.GSON.fromJson(reader, new TypeToken<Map<String, Map<String, Library>>>() {
}.getType());
return natives.getOrDefault(p.toString(), Collections.emptyMap());
} catch (IOException e) {
LOG.log(Level.WARNING, "Failed to load native library list", e);
return Collections.emptyMap();
}
});
}
public static Version patchNative(Version version, String gameVersion, JavaVersion javaVersion, VersionSetting settings) {
if (settings.getNativesDirType() == NativesDirectoryType.CUSTOM) {
if (gameVersion != null && VersionNumber.VERSION_COMPARATOR.compare(gameVersion, "1.19") < 0)
return version;
@@ -47,6 +62,34 @@ public final class NativePatcher {
return version.setLibraries(newLibraries);
}
final boolean useNativeGLFW = settings.isUseNativeGLFW();
final boolean useNativeOpenAL = settings.isUseNativeOpenAL();
if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX
&& (useNativeGLFW || useNativeOpenAL)
&& VersionNumber.VERSION_COMPARATOR.compare(gameVersion, "1.19") >= 0) {
version = version.setLibraries(version.getLibraries().stream()
.filter(library -> {
if (library.getClassifier() != null && library.getClassifier().startsWith("natives")
&& "org.lwjgl".equals(library.getGroupId())) {
if ((useNativeGLFW && "lwjgl-glfw".equals(library.getArtifactId()))
|| (useNativeOpenAL && "lwjgl-openal".equals(library.getArtifactId()))) {
LOG.info("Filter out " + library.getName());
return false;
}
}
return true;
})
.collect(Collectors.toList()));
}
// Try patch natives
if (settings.isNotPatchNatives())
return version;
if (javaVersion.getArchitecture().isX86())
return version;
@@ -56,8 +99,8 @@ public final class NativePatcher {
&& VersionNumber.VERSION_COMPARATOR.compare(gameVersion, "1.19") >= 0)
return version;
Map<String, Library> replacements = Hole.nativeReplacement.get(javaVersion.getPlatform().toString());
if (replacements == null) {
Map<String, Library> replacements = getNatives(javaVersion.getPlatform());
if (replacements.isEmpty()) {
LOG.warning("No alternative native library provided for platform " + javaVersion.getPlatform());
return version;
}
@@ -70,11 +113,7 @@ public final class NativePatcher {
if (library.isNative()) {
Library replacement = replacements.getOrDefault(library.getName() + ":natives", NONEXISTENT_LIBRARY);
if (replacement == NONEXISTENT_LIBRARY) {
if (!(settings.isUseNativeGLFW() && library.getArtifactId().contains("glfw"))
&& !(settings.isUseNativeOpenAL() && library.getArtifactId().contains("openal"))) {
LOG.warning("No alternative native library " + library.getName() + " provided for platform " + javaVersion.getPlatform());
return version;
}
LOG.warning("No alternative native library " + library.getName() + " provided for platform " + javaVersion.getPlatform());
newLibraries.add(library);
} else if (replacement != null) {
newLibraries.add(replacement);
@@ -92,23 +131,7 @@ public final class NativePatcher {
return version.setLibraries(newLibraries);
}
public static Library getSoftwareRendererLoader(JavaVersion javaVersion) {
Map<String, Library> map = Hole.nativeReplacement.get(javaVersion.getPlatform().toString());
return map != null ? map.get("software-renderer-loader") : null;
}
private static final class Hole {
static Map<String, Map<String, Library>> nativeReplacement;
static {
//noinspection ConstantConditions
try (Reader reader = new InputStreamReader(NativePatcher.class.getResourceAsStream("/assets/natives.json"))) {
nativeReplacement = JsonUtils.GSON.fromJson(reader, new TypeToken<Map<String, Map<String, Library>>>() {
}.getType());
} catch (IOException e) {
nativeReplacement = Collections.emptyMap();
LOG.log(Level.WARNING, "Failed to load native library list", e);
}
}
public static Library getMesaLoader(JavaVersion javaVersion, Renderer renderer) {
return getNatives(javaVersion.getPlatform()).get(renderer == Renderer.LLVMPIPE ? "software-renderer-loader" : "mesa-loader");
}
}
@@ -67,6 +67,7 @@ account.failed.migration=Your account needs to be migrated to a Microsoft accoun
account.failed.no_character=There are no characters linked to this account.
account.failed.server_disconnected=Cannot access authentication server. You can log in offline or try to re-login.
account.failed.server_response_malformed=Invalid server response, the authentication server may not be working.
account.failed.ssl=An SSL error occurred while connecting to the server. Please try updating your Java.
account.failed.wrong_account=You have logged in to the wrong account.
account.hmcl.hint=You need to click on "Login" and complete the process in the opened tab in your browser.
# avoid too long sequence.
@@ -952,6 +953,8 @@ selector.custom=Custom
settings=Settings
settings.advanced=Advanced Settings
settings.advanced.modify=Modify Advanced Settings
settings.advanced.title=Advanced Settings - %s
settings.advanced.custom_commands=Custom Commands
settings.advanced.custom_commands.hint=The following environment variables are provided\:\n\
- $INST_NAME: version name\n\
@@ -967,6 +970,7 @@ settings.advanced.custom_commands.hint=The following environment variables are p
settings.advanced.dont_check_game_completeness=Do not check game integrity
settings.advanced.dont_check_jvm_validity=Do not check JVM compatibility
settings.advanced.dont_patch_natives=Do not attempt to automatically replace native libraries
settings.advanced.environment_variables=Environment Variables
settings.advanced.game_dir.default=Default (.minecraft/)
settings.advanced.game_dir.independent=Isolated (.minecraft/versions/<instance name>/, except for assets and libraries)
settings.advanced.java_permanent_generation_space=PermGen Space
@@ -1001,11 +1005,15 @@ settings.advanced.process_priority.above_normal=Above Normal
settings.advanced.process_priority.high=High
settings.advanced.post_exit_command=Post-exit Command
settings.advanced.post_exit_command.prompt=Commands to execute after the game exits
settings.advanced.renderer=Renderer
settings.advanced.renderer.default=OpenGL (Default)
settings.advanced.renderer.d3d12=DirectX 12 (Poor performance and compatibility)
settings.advanced.renderer.llvmpipe=Software (Poor efficiency and best compatibility)
settings.advanced.renderer.zink=Vulkan (Best performance and poor compatibility)
settings.advanced.server_ip=Server Address
settings.advanced.server_ip.prompt=Join automatically after launching the game.
settings.advanced.use_native_glfw=[Linux Only] Use system GLFW
settings.advanced.use_native_openal=[Linux Only] Use system OpenAL
settings.advanced.use_software_renderer=Use OpenGL software renderer (better compatibility, but less performance)
settings.advanced.workaround=Workarounds
settings.advanced.workaround.warning=Workaround options are intended only for expert users. Tweaking with these options may crash the game. Unless you know what you are doing, please do not modify these options.
settings.advanced.wrapper_launcher=Wrapper Command
@@ -1107,14 +1115,12 @@ update.channel.dev.hint=You are currently using a beta build of the launcher, wh
\n\
If you encounter any bug or issue, you can go to <a href="hmcl://settings/feedback">the feedback page</a> to report it, or tell us in our <a href="https://discord.gg/jVvC7HfM6U">Discord</a> or <a href="https://kook.top/Kx7n3t">KOOK</a> community.\n\
\n\
To hide this tooltip, you need to make a <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">donation</a> or submit a valuable feedback.
<a href="hmcl://hide-announcement">Click here</a> to hide this tooltip for the current version.
update.channel.dev.title=Beta Version Notice
update.channel.nightly=Nightly
update.channel.nightly.hint=You are currently using a nightly build of the launcher, which may include some extra features, but is also always more unstable than the other versions.\n\
\n\
If you encounter any bug or issue, you can go to <a href="hmcl://settings/feedback">the feedback page</a> to report it, or tell us in our <a href="https://discord.gg/jVvC7HfM6U">Discord</a> or <a href="https://kook.top/Kx7n3t">KOOK</a> community.\n\
\n\
To hide this tooltip, you need to make a <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">donation</a> or submit a valuable feedback.
If you encounter any bug or issue, you can go to <a href="hmcl://settings/feedback">the feedback page</a> to report it, or tell us in our <a href="https://discord.gg/jVvC7HfM6U">Discord</a> or <a href="https://kook.top/Kx7n3t">KOOK</a> community.
update.channel.nightly.title=Nightly Version Notice
update.channel.stable=Release
update.checking=Checking for Updates
@@ -869,6 +869,7 @@ selector.custom=Personalizar
settings=Configuración
settings.advanced=Configuración avanzada
settings.advanced.title=Configuración avanzada - %s
settings.advanced.custom_commands=Comandos personalizados
settings.advanced.custom_commands.hint=Se proporcionan las siguientes variables de entorno\:\n\
- $INST_NAME: nombre de la versión\n\
@@ -1016,14 +1017,12 @@ update.channel.dev.hint=Actualmente está utilizando una versión beta del launc
\n\
Si encuentra algún error o problema, puede ir a la <a href="hmcl://settings/feedback">página de comentarios</a> para informar de ello, o bien para decírnoslo en nuestro <a href="https://discord.gg/jVvC7HfM6U">Discord</a> o en la comunidad <a href="https://kookapp.co/Kx7n3t">KOOK</a>.\n\
\n\
Para ocultar esta información, es necesario hacer una <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">donación</a> o enviar un valioso comentario.
<a href="hmcl://hide-announcement">Haga clic aquí</a> para ocultar el recordatorio para la versión de prueba actual.
update.channel.dev.title=Aviso de versión beta
update.channel.nightly=Nocturna
update.channel.nightly.hint=Estás utilizando una versión nocturna del launcher, que puede incluir algunas características adicionales, pero también es siempre más inestable que las otras versiones.\n\
\n\
Si encuentra algún error o problema, puede ir a la <a href="hmcl://settings/feedback">página de comentarios</a> para informar de ello, o bien para decírnoslo en nuestro <a href="https://discord.gg/jVvC7HfM6U">Discord</a> o en la comunidad <a href="https://kookapp.co/Kx7n3t">KOOK</a> community.\n\
\n\
Para ocultar esta información sobre herramientas, es necesario hacer una <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">donación</a> o enviar un valioso comentario.
Si encuentra algún error o problema, puede ir a la <a href="hmcl://settings/feedback">página de comentarios</a> para informar de ello, o bien para decírnoslo en nuestro <a href="https://discord.gg/jVvC7HfM6U">Discord</a> o en la comunidad <a href="https://kookapp.co/Kx7n3t">KOOK</a> community.
update.channel.nightly.title=Aviso de versión nocturna
update.channel.stable=Estable
update.checking=Buscando actualizaciones
@@ -703,6 +703,7 @@ selector.custom=カスタム
settings=設定
settings.advanced=詳細設定
settings.advanced.title=詳細設定 - %s
settings.advanced.custom_commands=カスタムコマンド
settings.advanced.custom_commands.hint=カスタムコマンドは、次の環境変数を使用して実行されます。\n \
\-$ INST_NAME:バージョンの名前\n \
@@ -843,16 +844,15 @@ update=更新
update.accept=更新
update.changelog=変更
update.channel.dev=ベータ
update.channel.dev.hint=ベータ版を使用しています。ベータ版には、リリースバージョンと比較していくつかの追加機能が含まれている可能性があり、テストにのみ使用されます。\n \
ベータ版は不安定なはずです!\n \
問題が発生した場合は、<a href="hmcl://settings/feedback">フィードバックページ</a>にアクセスして報告するか、チャット <a href="https://discord.gg/jVvC7HfM6U">Discord</a>または<a href="https://kook.top/Kx7n3t">KOOK</a>で問題を報告します。\n \n \
このヒントを非表示にするには、<a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">寄付</a>するか、フィードバックに貢献をする必要があります。
update.channel.dev.hint=ベータ版を使用しています。ベータ版には、リリースバージョンと比較していくつかの追加機能が含まれている可能性があり、テストにのみ使用されます。\n\
ベータ版は不安定なはずです!\n\
問題が発生した場合は、<a href="hmcl://settings/feedback">フィードバックページ</a>にアクセスして報告するか、チャット <a href="https://discord.gg/jVvC7HfM6U">Discord</a>または<a href="https://kook.top/Kx7n3t">KOOK</a>で問題を報告します。\n\n\
現在のテスト版のヒントを非表示にするには、<a href="hmcl://hide-announcement">ここをクリックしてください</a>。
update.channel.dev.title=ベータ版のヒント
update.channel.nightly=アルファ
update.channel.nightly.hint=アルファ版を使用しています。これには、ベータ版およびリリース版と比較していくつかの追加機能が含まれている可能性があり、テストにのみ使用されます。\n \
アルファ版は不安定です!\n \
問題が発生した場合は、<a href="hmcl://settings/feedback">フィードバックページ</a>にアクセスして報告するか、チャット <a href="https://discord.gg/jVvC7HfM6U">Discord</a>または<a href="https://kook.top/Kx7n3t">KOOK</a>で問題を報告します。\n \n \
このヒントを非表示にするには、<a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">寄付</a>するか、フィードバックに印象的な貢献をする必要があります。
update.channel.nightly.hint=アルファ版を使用しています。これには、ベータ版およびリリース版と比較していくつかの追加機能が含まれている可能性があり、テストにのみ使用されます。\n\
アルファ版は不安定です!\n\
問題が発生した場合は、<a href="hmcl://settings/feedback">フィードバックページ</a>にアクセスして報告するか、チャット <a href="https://discord.gg/jVvC7HfM6U">Discord</a>または<a href="https://kook.top/Kx7n3t">KOOK</a>で問題を報告します。
update.channel.nightly.title=アルファ版のヒント
update.channel.stable=リリース
update.checking=更新の確認
@@ -708,6 +708,7 @@ selector.custom=Пользовательский
settings=Настройки
settings.advanced=Расширенные настройки
settings.advanced.title=Расширенные настройки - %s
settings.advanced.custom_commands=Пользовательские команды
settings.advanced.custom_commands.hint=Пользовательские команды выполняются со следующими переменными окружения:\n\
\ - $INST_NAME: название версии\n\
@@ -852,13 +853,12 @@ update.channel.dev=Бета
update.channel.dev.hint=Вы используете бета-версию, которая может включать некоторые дополнительные функции по сравнению с релизной версией, используемой только для тестирования.\n\
Бета-версия может быть нестабильной!\n\
Если вы встретили какие-то проблемы, вы можете зайти на <a href="hmcl://settings/feedback">страницу обратной связи</a>, чтобы сообщить о них, или присоединиться к чату <a href="https://discord.gg/jVvC7HfM6U">Discord</a> или <a href="https://kook.top/Kx7n3t">KOOK</a>, чтобы сообщить о проблемах.\n\n\
Чтобы скрыть эту подсказку, нужно <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">пожертвовать</a> или сделать внушительный вклад через обратную связь.
<a href="hmcl://hide-announcement">Нажмите здесь</a>, чтобы скрыть подсказку для текущей тестовой версии.
update.channel.dev.title=Подсказки для бета-версии
update.channel.nightly=Альфа
update.channel.nightly.hint=Вы используете альфа-версию, которая может включать некоторые дополнительные функциональные возможности по сравнению с бета-версией и версией релиза, используемой только для тестирования.\n\
Альфа-версия может быть нестабильной!\n\
Если вы встретили какие-то проблемы, вы можете зайти на <a href="hmcl://settings/feedback">страницу обратной связи</a>, чтобы сообщить о них, или присоединиться к чату <a href="https://discord.gg/jVvC7HfM6U">Discord</a> или <a href="https://kook.top/Kx7n3t">KOOK</a>, чтобы сообщить о проблемах.\n\n\
Чтобы скрыть эту подсказку, нужно <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">пожертвовать</a> или сделать внушительный вклад через обратную связь.
Если вы встретили какие-то проблемы, вы можете зайти на <a href="hmcl://settings/feedback">страницу обратной связи</a>, чтобы сообщить о них, или присоединиться к чату <a href="https://discord.gg/jVvC7HfM6U">Discord</a> или <a href="https://kook.top/Kx7n3t">KOOK</a>, чтобы сообщить о проблемах.
update.channel.nightly.title=Подсказки для альфа-версии
update.channel.stable=Релиз
update.checking=Проверка наличия обновлений
@@ -69,6 +69,7 @@ account.failed.server_disconnected=無法訪問登錄伺服器,賬戶信息刷
若最近沒有刷新賬戶信息,則可能導致賬戶信息過期失效,\n\
若為 微軟賬戶 啟動遊戲,賬戶信息過期失效可能將無法進入需賬戶驗證的伺服器。
account.failed.server_response_malformed=無法解析認證伺服器回應,可能是伺服器故障
account.failed.ssl=連接服務器時發生了 SSL 錯誤,可能網站證書已過期或你使用的 Java 版本過低,請嘗試更新 Java
account.failed.wrong_account=登錄了錯誤的帳號
account.hmcl.hint=你需要點擊“登入”按鈕,並在打開的網頁中完成登入
account.injector.add=新增認證伺服器
@@ -81,7 +82,7 @@ account.injector.server_url=伺服器位址
account.injector.server_name=伺服器名稱
account.login=登入
account.login.hint=我們不會保存你的密碼
account.login.skip=重新刷新帳戶
account.login.skip=跳過刷新帳戶
account.login.retry=再次刷新帳戶
account.login.refresh=重新登錄
account.login.refresh.microsoft.hint=因為賬戶授權失效,你需要重新添加微軟賬戶
@@ -827,6 +828,8 @@ selector.custom=自訂
settings=遊戲設定
settings.advanced=進階設定
settings.advanced.modify=修改進階設定
settings.advanced.title=進階設定 - %s
settings.advanced.custom_commands=自訂命令
settings.advanced.custom_commands.hint=自訂命令被調用時將包含如下的環境變數:\n\
\ - $INST_NAME: 版本名稱\n\
@@ -842,6 +845,7 @@ settings.advanced.custom_commands.hint=自訂命令被調用時將包含如下
settings.advanced.dont_check_game_completeness=不檢查遊戲完整性
settings.advanced.dont_check_jvm_validity=不檢查 JVM 與遊戲的相容性
settings.advanced.dont_patch_natives=不嘗試自動替換本機庫
settings.advanced.environment_variables=環境變數
settings.advanced.game_dir.default=預設(.minecraft/)
settings.advanced.game_dir.independent=各版本獨立(.minecraft/versions/<版本名>/,除 assets、libraries)
settings.advanced.java_permanent_generation_space=記憶體永久儲存區域
@@ -874,13 +878,17 @@ settings.advanced.process_priority.above_normal=較高(優先保證遊戲運
settings.advanced.process_priority.high=高(優先保證遊戲運行,但可能會導致其他程式卡頓)
settings.advanced.post_exit_command=遊戲結束後執行命令
settings.advanced.post_exit_command.prompt=將在遊戲結束後呼叫使用
settings.advanced.renderer=渲染器
settings.advanced.renderer.default=OpenGL(默認)
settings.advanced.renderer.d3d12=DirectX 12(效能與相容性較差,用於調試)
settings.advanced.renderer.llvmpipe=軟渲染器(效能較差,相容性最好)
settings.advanced.renderer.zink=Vulkan(效能最好,相容性較差)
settings.advanced.server_ip=伺服器位址
settings.advanced.server_ip.prompt=預設,啟動遊戲後直接進入對應伺服器
settings.advanced.use_native_glfw=[Linux] 使用系統 GLFW
settings.advanced.use_native_openal=[Linux] 使用系統 OpenAL
settings.advanced.use_software_renderer=使用 OpenGL 軟渲染器(相容性更好,但效能較差)
settings.advanced.workaround=除錯選項
settings.advanced.workaround.warning=除錯選項僅提供給專業玩家使用。修改除錯選項可能會導致遊戲無法啟動。除非你知道你在做什麼,否則請你不要修改這些選項。
settings.advanced.workaround.warning=除錯選項僅提供給專業玩家使用。修改除錯選項可能會導致遊戲無法啟動。除非你知道你在做什麼,否則請不要修改這些選項。
settings.advanced.wrapper_launcher=前置指令
settings.advanced.wrapper_launcher.prompt=如填寫 optirun 後,啟動命令將從 "java ..." 變為 "optirun java ..."
@@ -976,13 +984,12 @@ update.channel.dev=測試版
update.channel.dev.hint=你正在使用測試版。測試版包含一些未在正式版中包含的測試性功能,僅用於體驗新功能。\n\
測試版功能未受充分驗證,使用起來可能不穩定!\n\
如果你遇到了使用問題,可以在設置的 <a href="hmcl://settings/feedback">回饋頁面</a> 中進行回饋,或加入回饋頁面中提供的 <a href="https://discord.gg/jVvC7HfM6U">Discord</a> 或 <a href="https://kook.top/Kx7n3t">KOOK 群</a>以回饋問題。\n\n\
你需要 <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">贊助</a> 或在回饋過程中作出貢獻以隱藏本提示。
<a href="hmcl://hide-announcement">點擊此處</a>為當前版本隱藏該提示。
update.channel.dev.title=測試版提示
update.channel.nightly=預覽版
update.channel.nightly.hint=你正在使用預覽版。預覽版可能會每天更新一次,包含一些未在正式版和測試版中包含的測試性功能,僅用於體驗新功能\n\
測試版功能未受充分驗證,使用起來可能不穩定!\n\
如果你遇到了使用問題,可以在設置的 <a href="hmcl://settings/feedback">回饋頁面</a> 中進行回饋,或加入回饋頁面中提供的 <a href="https://discord.gg/jVvC7HfM6U">Discord</a> 或 <a href="https://kook.top/Kx7n3t">KOOK 群</a>以回饋問題。\n\n\
你需要 <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">贊助</a> 或在回饋過程中作出貢獻以隱藏本提示。
如果你遇到了使用問題,可以在設置的 <a href="hmcl://settings/feedback">回饋頁面</a> 中進行回饋,或加入回饋頁面中提供的 <a href="https://discord.gg/jVvC7HfM6U">Discord</a> 或 <a href="https://kook.top/Kx7n3t">KOOK 群</a>以回饋問題。
update.channel.nightly.title=預覽版提示
update.channel.stable=建議版本
update.checking=正在檢查更新
@@ -69,6 +69,7 @@ account.failed.server_disconnected=无法访问登录服务器,账户信息刷
若最近没有刷新账户信息,则可能导致账户信息过期失效,\n\
若为 微软账户 启动游戏,账户信息过期失效可能将无法进入需账户验证的服务器。
account.failed.server_response_malformed=无法解析认证服务器响应,可能是服务器故障
account.failed.ssl=连接服务器时发生了 SSL 错误,可能网站证书已过期或你使用的 Java 版本过低,请尝试更新 Java
account.failed.wrong_account=登录了错误的帐号
account.hmcl.hint=你需要点击“登录”按钮,并在打开的网页中完成登录
account.injector.add=添加认证服务器
@@ -398,12 +399,12 @@ game.crash.reason.fabric_warnings=Fabric 提供了一些警告信息:\n%1$s \n
game.crash.reason.file_already_exists=当前游戏因为文件 %1$s 已经存在,无法继续运行。\n如果你认为这个文件可以删除,你可以在备份这个文件后尝试删除它,并重新启动游戏。 \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.file_changed=当前游戏因为文件校验失败,无法继续运行。\n如果你手动修改了 Minecraft.jar 文件,你需要回退修改,或者重新下载游戏。 \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.gl_operation_failure=当前游戏因为你使用的某些 Mod、光影包、材质包,无法继续运行。\n请先尝试禁用你所使用的Mod/光影包/材质包再试。 \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.graphics_driver=当前游戏因为你的显卡驱动存在问题崩溃了,请尝试:\n\
game.crash.reason.graphics_driver=当前游戏因为显卡驱动问题而崩溃,请尝试以下操作:\n\
- 如果你的电脑存在独立显卡,请尝试使用 独立显卡 而非 Intel 核显启动 HMCL 与游戏 <a href="https://www.bing.com/search?q=用独立显卡运行程序">详情</a>;\n\
- <a href="https://minecrafthopper.net/help/pixel-format-not-accelerated/">尝试升级你的 显卡驱动 到最新版本</a>,或回退到出厂版本;\n\
- 如果你确实需要使用核芯显卡,请检查你的电脑的 CPU 是否是 Intel(R) Core(TM) 3000 系列或更旧的处理器,如果是,对于 Minecraft 1.16.5 及更旧版本,请你将游戏所使用的 Java 版本降级至 1.8.0_51 及以下版本 <a href="https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html">Java 1.8.0 历史版本</a> ,否则请跳过;\n\
- 在全局(特定)游戏设置,菜单底部中打开“使用 OpenGL 软渲染器”选项可以尝试解决此问题,但打开此选项后在 CPU 性能不足的情况下帧数会显著降低,请慎重选择(仅推荐以调试为目的或应急时开启)\n\
如果仍有问题,你可能需要考虑换一个新显卡或新电脑。\n\
- 在游戏设置中打开“使用 OpenGL 软渲染器”(开启此选项后帧数会显著降低,仅推荐在以调试为目的或应急时开启)。\n\
如果仍有问题,你可能需要考虑换一张新显卡或一台新电脑。\n\
你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.illegal_access_error=当前游戏因为某些 Mod 的问题,无法继续运行。\n如果你认识:%1$s,你可以更新或删除对应 Mod 再试。\n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.jdk_9=当前游戏因为 Java 版本过高,无法继续运行。\n你需要下载安装 <a href="https://bell-sw.com/pages/downloads/#downloads">Java 8</a>,并在全局(特定)游戏设置中将 Java 设置为 1.8 的版本。\n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
@@ -436,7 +437,7 @@ game.crash.reason.no_class_def_found_error=当前游戏因为代码不完整,
game.crash.reason.no_such_method_error=当前游戏因为代码不完整,无法继续运行。\n你的游戏可能缺失了某个 Mod,或者某些 Mod 文件不完整,或者 Mod 与游戏的版本不匹配。\n你可能需要重新安装游戏和 Mod,或请求他人帮助。 \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.opengl_not_supported=当前游戏因为你的显卡驱动存在问题,无法继续运行。\n原因是 OpenGL 不受支持,你现在是否在远程桌面或者串流模式下?如果是,请直接使用原电脑启动游戏。\n或者尝试升级你的显卡驱动到最新版本后再尝试启动游戏。如果你的电脑存在独立显卡,你需要检查游戏是否使用集成/核心显卡启动,如果是,请尝试使用独立显卡启动 HMCL 与游戏。如果仍有问题,你可能需要考虑换一个新显卡或新电脑。 \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.openj9=当前游戏无法运行在 OpenJ9 虚拟机上,请你在全局(特定)游戏设置中更换 Hotspot Java 虚拟机,并重新启动游戏。如果没有下载安装,你可以在网上自行下载。 \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.out_of_memory=当前游戏因为内存不足,无法继续运行。\n这可能是内存分配太小,或者 Mod 数量过多导致的。\n你可以在全局(特定)游戏设置中调大游戏内存分配值以允许游戏在更大的内存下运行。\n如果仍然出现该错误,你可能需要换一台更好的电脑。 \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.out_of_memory=当前游戏因为内存不足,无法继续运行。\n这可能是内存分配太小,或者 Mod 数量过多导致的。\n你可以在全局(特定)游戏设置中调大游戏内存分配值以允许游戏在更大的内存下运行。\n如果仍然出现该错误,你可能需要换一台更好的电脑。 \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.resolution_too_high=当前游戏因为材质包分辨率过高,无法继续运行\n你可以更换一个分辨率更低的材质,或者更换一个显存更大的显卡。 \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.stacktrace=原因未知,请点击日志按钮查看详细信息。\n下面是一些关键词,其中可能包含 Mod 名称,你可以通过搜索的方式查找有关信息。\n%s
game.crash.reason.too_old_java=当前游戏因为 Java 虚拟机版本过低,无法继续运行。\n你需要在全局(特定)游戏设置中更换 Java %1$s 或更新版本的 Java 虚拟机,并重新启动游戏。如果没有下载安装,你可以点击 <a href="https://bell-sw.com/pages/downloads/#downloads">此处</a> 下载 Liberica JDK。\n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
@@ -825,6 +826,8 @@ selector.custom=自定义
settings=设置
settings.advanced=高级设置
settings.advanced.modify=修改高级设置
settings.advanced.title=高级设置 - %s
settings.advanced.custom_commands=自定义命令
settings.advanced.custom_commands.hint=自定义命令被调用时将包含如下的环境变量:\n\
\ - $INST_NAME: 版本名称\n\
@@ -840,6 +843,7 @@ settings.advanced.custom_commands.hint=自定义命令被调用时将包含如
settings.advanced.dont_check_game_completeness=不检查游戏完整性
settings.advanced.dont_check_jvm_validity=不检查 JVM 与游戏的兼容性
settings.advanced.dont_patch_natives=不尝试自动替换本地库
settings.advanced.environment_variables=环境变量
settings.advanced.game_dir.default=默认(.minecraft/)
settings.advanced.game_dir.independent=各版本独立(存放在 .minecraft/versions/<版本名>/,除 assets、libraries 外)
settings.advanced.java_permanent_generation_space=内存永久保存区域
@@ -872,13 +876,17 @@ settings.advanced.process_priority.above_normal=较高(优先保证游戏运
settings.advanced.process_priority.high=高(优先保证游戏运行,但可能会导致其他程序卡顿)
settings.advanced.post_exit_command=游戏结束后执行命令
settings.advanced.post_exit_command.prompt=将在游戏结束后调用
settings.advanced.renderer=渲染器
settings.advanced.renderer.default=OpenGL(默认)
settings.advanced.renderer.d3d12=DirectX 12(性能与兼容性较差,用于调试)
settings.advanced.renderer.llvmpipe=软渲染器(性能较差,兼容性最好)
settings.advanced.renderer.zink=Vulkan(性能最好,兼容性较差)
settings.advanced.server_ip=服务器地址
settings.advanced.server_ip.prompt=默认,启动游戏后可以直接进入对应服务器
settings.advanced.use_native_glfw=[Linux] 使用系统 GLFW
settings.advanced.use_native_openal=[Linux] 使用系统 OpenAL
settings.advanced.use_software_renderer=使用 OpenGL 软渲染器(兼容性更好,但性能较差)
settings.advanced.workaround=调试选项
settings.advanced.workaround.warning=调试选项仅提供给专业玩家使用。调试选项可能会导致游戏无法启动。除非你知道你在做什么,否则请你不要修改这些选项!
settings.advanced.workaround.warning=调试选项仅提供给专业玩家使用。调试选项可能会导致游戏无法启动。除非你知道你在做什么,否则请不要修改这些选项!
settings.advanced.wrapper_launcher=包装命令
settings.advanced.wrapper_launcher.prompt=如填写 optirun 后,启动命令将从 "java ..." 变为 "optirun java ..."
@@ -974,13 +982,12 @@ update.channel.dev=测试版
update.channel.dev.hint=你正在使用测试版。测试版包含一些未在正式版中包含的测试性功能,仅用于体验新功能。\n\
测试版功能未受充分验证,使用起来可能不稳定!<a href="https://hmcl.huangyuhui.net/download">下载稳定版</a>\n\
如果你遇到了使用问题,可以在设置的 <a href="hmcl://settings/feedback">反馈页面</a> 中进行反馈,或加入反馈页面中提供的 <a href="https://discord.gg/jVvC7HfM6U">Discord</a> 或 <a href="https://kook.top/Kx7n3t">KOOK</a>群以反馈问题。欢迎关注 B 站账号 <a href="https://space.bilibili.com/1445341">huanghongxun</a> 以关注 HMCL 的开发进展。\n\n\
你需要 <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">赞助</a> 或在反馈过程中作出贡献以隐藏本提示。
<a href="hmcl://hide-announcement">点击此处</a>为当前版本隐藏该提示。
update.channel.dev.title=测试版提示
update.channel.nightly=预览版
update.channel.nightly.hint=你正在使用预览版。预览版可能会每天更新一次,包含一些未在正式版和测试版中包含的测试性功能,仅用于体验新功能。\n\
测试版功能未受充分验证,使用起来可能不稳定!<a href="https://hmcl.huangyuhui.net/download">下载稳定版</a>\n\
如果你遇到了使用问题,可以在设置的 <a href="hmcl://settings/feedback">反馈页面</a> 中进行反馈,或加入反馈页面中提供的 <a href="https://discord.gg/jVvC7HfM6U">Discord</a> 或 <a href="https://kook.top/Kx7n3t">KOOK</a>群以反馈问题。欢迎关注 B 站账号 <a href="https://space.bilibili.com/1445341">huanghongxun</a> 以关注 HMCL 的开发进展。\n\n\
你需要 <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">赞助</a> 或在反馈过程中作出贡献以隐藏本提示。
如果你遇到了使用问题,可以在设置的 <a href="hmcl://settings/feedback">反馈页面</a> 中进行反馈,或加入反馈页面中提供的 <a href="https://discord.gg/jVvC7HfM6U">Discord</a> 或 <a href="https://kook.top/Kx7n3t">KOOK</a>群以反馈问题。欢迎关注 B 站账号 <a href="https://space.bilibili.com/1445341">huanghongxun</a> 以关注 HMCL 的开发进展。
update.channel.nightly.title=预览版提示
update.channel.stable=推荐版本
update.checking=正在检查更新
+280 -14
View File
@@ -1519,6 +1519,248 @@
"com.mojang:text2speech:1.12.4:natives": null,
"com.mojang:text2speech:1.13.9:natives-linux": null
},
"linux-riscv64": {
"org.lwjgl:lwjgl:3.1.6": {
"name": "org.lwjgl:lwjgl:3.3.1",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar",
"sha1": "ae58664f88e18a9bb2c77b063833ca7aaec484cb",
"size": 724243
}
}
},
"org.lwjgl:lwjgl:3.1.6:natives": {
"name": "org.glavo.hmcl:lwjgl3-natives:3.3.1-rc1",
"downloads": {
"classifiers": {
"linux-riscv64": {
"path": "org/glavo/hmcl/lwjgl3-natives/3.3.1-rc1/lwjgl3-natives-3.3.1-rc1-linux-riscv64.jar",
"url": "https://repo1.maven.org/maven2/org/glavo/hmcl/lwjgl3-natives/3.3.1-rc1-linux-riscv64/lwjgl3-natives-3.3.1-rc1-linux-riscv64.jar",
"sha1": "8544853aa77b10692548b2a728cd890afd740ba5",
"size": 11905844
}
}
},
"extract": {
"exclude": [
"META-INF/"
]
},
"natives": {
"linux": "linux-riscv64"
}
},
"org.lwjgl:lwjgl-jemalloc:3.1.6": {
"name": "org.lwjgl:lwjgl-jemalloc:3.3.1",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar",
"sha1": "a817bcf213db49f710603677457567c37d53e103",
"size": 36601
}
}
},
"org.lwjgl:lwjgl-jemalloc:3.1.6:natives": null,
"org.lwjgl:lwjgl-openal:3.1.6": {
"name": "org.lwjgl:lwjgl-openal:3.3.1",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar",
"sha1": "2623a6b8ae1dfcd880738656a9f0243d2e6840bd",
"size": 88237
}
}
},
"org.lwjgl:lwjgl-openal:3.1.6:natives": null,
"org.lwjgl:lwjgl-opengl:3.1.6": {
"name": "org.lwjgl:lwjgl-opengl:3.3.1",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar",
"sha1": "831a5533a21a5f4f81bbc51bb13e9899319b5411",
"size": 921563
}
}
},
"org.lwjgl:lwjgl-opengl:3.1.6:natives": null,
"org.lwjgl:lwjgl-glfw:3.1.6": {
"name": "org.lwjgl:lwjgl-glfw:3.3.1",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar",
"sha1": "cbac1b8d30cb4795149c1ef540f912671a8616d0",
"size": 128801
}
}
},
"org.lwjgl:lwjgl-glfw:3.1.6:natives": null,
"org.lwjgl:lwjgl-stb:3.1.6": {
"name": "org.lwjgl:lwjgl-stb:3.3.1",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar",
"sha1": "b119297cf8ed01f247abe8685857f8e7fcf5980f",
"size": 112380
}
}
},
"org.lwjgl:lwjgl-stb:3.1.6:natives": null,
"org.lwjgl:lwjgl-tinyfd:3.1.6": {
"name": "org.lwjgl:lwjgl-tinyfd:3.3.1",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar",
"sha1": "0ff1914111ef2e3e0110ef2dabc8d8cdaad82347",
"size": 6767
}
}
},
"org.lwjgl:lwjgl-tinyfd:3.1.6:natives": null,
"org.lwjgl:lwjgl:3.2.2": {
"name": "org.lwjgl:lwjgl:3.3.1",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar",
"sha1": "ae58664f88e18a9bb2c77b063833ca7aaec484cb",
"size": 724243
}
}
},
"org.lwjgl:lwjgl:3.2.2:natives": {
"name": "org.glavo.hmcl:lwjgl3-natives:3.3.1-rc1",
"downloads": {
"classifiers": {
"linux-riscv64": {
"path": "org/glavo/hmcl/lwjgl3-natives/3.3.1-rc1/lwjgl3-natives-3.3.1-rc1-linux-riscv64.jar",
"url": "https://repo1.maven.org/maven2/org/glavo/hmcl/lwjgl3-natives/3.3.1-rc1-linux-riscv64/lwjgl3-natives-3.3.1-rc1-linux-riscv64.jar",
"sha1": "8544853aa77b10692548b2a728cd890afd740ba5",
"size": 11905844
}
}
},
"extract": {
"exclude": [
"META-INF/"
]
},
"natives": {
"linux": "linux-riscv64"
}
},
"org.lwjgl:lwjgl-jemalloc:3.2.2": {
"name": "org.lwjgl:lwjgl-jemalloc:3.3.1",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar",
"sha1": "a817bcf213db49f710603677457567c37d53e103",
"size": 36601
}
}
},
"org.lwjgl:lwjgl-jemalloc:3.2.2:natives": null,
"org.lwjgl:lwjgl-openal:3.2.2": {
"name": "org.lwjgl:lwjgl-openal:3.3.1",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar",
"sha1": "2623a6b8ae1dfcd880738656a9f0243d2e6840bd",
"size": 88237
}
}
},
"org.lwjgl:lwjgl-openal:3.2.2:natives": null,
"org.lwjgl:lwjgl-opengl:3.2.2": {
"name": "org.lwjgl:lwjgl-opengl:3.3.1",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar",
"sha1": "831a5533a21a5f4f81bbc51bb13e9899319b5411",
"size": 921563
}
}
},
"org.lwjgl:lwjgl-opengl:3.2.2:natives": null,
"org.lwjgl:lwjgl-glfw:3.2.2": {
"name": "org.lwjgl:lwjgl-glfw:3.3.1",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar",
"sha1": "cbac1b8d30cb4795149c1ef540f912671a8616d0",
"size": 128801
}
}
},
"org.lwjgl:lwjgl-glfw:3.2.2:natives": null,
"org.lwjgl:lwjgl-stb:3.2.2": {
"name": "org.lwjgl:lwjgl-stb:3.3.1",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar",
"sha1": "b119297cf8ed01f247abe8685857f8e7fcf5980f",
"size": 112380
}
}
},
"org.lwjgl:lwjgl-stb:3.2.2:natives": null,
"org.lwjgl:lwjgl-tinyfd:3.2.2": {
"name": "org.lwjgl:lwjgl-tinyfd:3.3.1",
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar",
"sha1": "0ff1914111ef2e3e0110ef2dabc8d8cdaad82347",
"size": 6767
}
}
},
"org.lwjgl:lwjgl-tinyfd:3.2.2:natives": null,
"org.lwjgl:lwjgl:3.3.1:natives-linux": {
"name": "org.glavo.hmcl:lwjgl3-natives:3.3.1-rc1",
"downloads": {
"classifiers": {
"linux-riscv64": {
"path": "org/glavo/hmcl/lwjgl3-natives/3.3.1-rc1/lwjgl3-natives-3.3.1-rc1-linux-riscv64.jar",
"url": "https://repo1.maven.org/maven2/org/glavo/hmcl/lwjgl3-natives/3.3.1-rc1-linux-riscv64/lwjgl3-natives-3.3.1-rc1-linux-riscv64.jar",
"sha1": "8544853aa77b10692548b2a728cd890afd740ba5",
"size": 11905844
}
}
},
"extract": {
"exclude": [
"META-INF/"
]
},
"natives": {
"linux": "linux-riscv64"
}
},
"org.lwjgl:lwjgl-jemalloc:3.3.1:natives-linux": null,
"org.lwjgl:lwjgl-openal:3.3.1:natives-linux": null,
"org.lwjgl:lwjgl-opengl:3.3.1:natives-linux": null,
"org.lwjgl:lwjgl-glfw:3.3.1:natives-linux": null,
"org.lwjgl:lwjgl-stb:3.3.1:natives-linux": null,
"org.lwjgl:lwjgl-tinyfd:3.3.1:natives-linux": null,
"net.java.jinput:jinput-platform:2.0.5:natives": null,
"com.mojang:text2speech:1.10.3:natives": null,
"com.mojang:text2speech:1.11.3:natives": null,
"com.mojang:text2speech:1.12.4:natives": null,
"com.mojang:text2speech:1.13.9:natives-linux": null
},
"windows-x86_64": {
"software-renderer-loader": {
"name": "org.glavo:llvmpipe-loader:1.0",
@@ -1530,6 +1772,30 @@
"size": 12964773
}
}
},
"mesa-loader": {
"name": "org.glavo:mesa-loader-windows:0.2.0:x64",
"downloads": {
"artifact": {
"path": "org/glavo/mesa-loader-windows/0.2.0/mesa-loader-windows-0.2.0-x64.jar",
"url": "https://repo1.maven.org/maven2/org/glavo/mesa-loader-windows/0.2.0/mesa-loader-windows-0.2.0-x64.jar",
"sha1": "adb4a16ecb95c8944a016829b05d70f934ea1a29",
"size": 22823052
}
}
}
},
"windows-x86": {
"mesa-loader": {
"name": "org.glavo:mesa-loader-windows:0.2.0:x86",
"downloads": {
"artifact": {
"path": "org/glavo/mesa-loader-windows/0.2.0/mesa-loader-windows-0.2.0-x86.jar",
"url": "https://repo1.maven.org/maven2/org/glavo/mesa-loader-windows/0.2.0/mesa-loader-windows-0.2.0-x86.jar",
"sha1": "93c0b9b7382d984e713d3aa299ceb337fde2897b",
"size": 18273286
}
}
}
},
"windows-arm64": {
@@ -2074,7 +2340,7 @@
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-macos-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-macos-arm64.jar",
"url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-macos-arm64.jar",
"sha1": "71d0d5e469c9c95351eb949064497e3391616ac9",
"size": 42693
}
@@ -2096,7 +2362,7 @@
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-macos-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-macos-arm64.jar",
"url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-macos-arm64.jar",
"sha1": "e577b87d8ad2ade361aaea2fcf226c660b15dee8",
"size": 103475
}
@@ -2118,7 +2384,7 @@
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-macos-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-macos-arm64.jar",
"url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-macos-arm64.jar",
"sha1": "23d55e7490b57495320f6c9e1936d78fd72c4ef8",
"size": 346125
}
@@ -2140,7 +2406,7 @@
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-macos-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-macos-arm64.jar",
"url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-macos-arm64.jar",
"sha1": "eafe34b871d966292e8db0f1f3d6b8b110d4e91d",
"size": 41665
}
@@ -2162,7 +2428,7 @@
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-macos-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-macos-arm64.jar",
"url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-macos-arm64.jar",
"sha1": "cac0d3f712a3da7641fa174735a5f315de7ffe0a",
"size": 129077
}
@@ -2184,7 +2450,7 @@
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-macos-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-macos-arm64.jar",
"url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-macos-arm64.jar",
"sha1": "fcf073ed911752abdca5f0b00a53cfdf17ff8e8b",
"size": 178408
}
@@ -2206,7 +2472,7 @@
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-natives-macos-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-natives-macos-arm64.jar",
"url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-natives-macos-arm64.jar",
"sha1": "972ecc17bad3571e81162153077b4d47b7b9eaa9",
"size": 41380
}
@@ -2228,7 +2494,7 @@
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-macos-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-macos-arm64.jar",
"url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-macos-arm64.jar",
"sha1": "71d0d5e469c9c95351eb949064497e3391616ac9",
"size": 42693
}
@@ -2250,7 +2516,7 @@
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-macos-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-macos-arm64.jar",
"url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-macos-arm64.jar",
"sha1": "e577b87d8ad2ade361aaea2fcf226c660b15dee8",
"size": 103475
}
@@ -2272,7 +2538,7 @@
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-macos-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-macos-arm64.jar",
"url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-macos-arm64.jar",
"sha1": "23d55e7490b57495320f6c9e1936d78fd72c4ef8",
"size": 346125
}
@@ -2294,7 +2560,7 @@
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-macos-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-macos-arm64.jar",
"url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-macos-arm64.jar",
"sha1": "eafe34b871d966292e8db0f1f3d6b8b110d4e91d",
"size": 41665
}
@@ -2316,7 +2582,7 @@
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-macos-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-macos-arm64.jar",
"url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-macos-arm64.jar",
"sha1": "cac0d3f712a3da7641fa174735a5f315de7ffe0a",
"size": 129077
}
@@ -2338,7 +2604,7 @@
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-macos-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-macos-arm64.jar",
"url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-macos-arm64.jar",
"sha1": "fcf073ed911752abdca5f0b00a53cfdf17ff8e8b",
"size": 178408
}
@@ -2360,7 +2626,7 @@
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-natives-macos-arm64.jar",
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-natives-macos-arm64.jar",
"url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-natives-macos-arm64.jar",
"sha1": "972ecc17bad3571e81162153077b4d47b7b9eaa9",
"size": 41380
}
+6 -9
View File
@@ -4,16 +4,13 @@ plugins {
dependencies {
api("org.glavo:simple-png-javafx:0.3.0")
api("com.google.code.gson:gson:2.8.1")
api("com.google.code.gson:gson:2.10.1")
api("com.moandjiezana.toml:toml4j:0.7.2")
api("org.tukaani:xz:1.8")
api("org.hildan.fxgson:fx-gson:3.1.0") {
exclude(group = "org.jetbrains", module = "annotations")
}
api("org.tukaani:xz:1.9")
api("org.hildan.fxgson:fx-gson:5.0.0")
api("org.jenkins-ci:constant-pool-scanner:1.2")
api("com.github.steveice10:opennbt:1.1")
api("com.nqzero:permit-reflect:0.3")
api("com.github.steveice10:opennbt:1.5")
api("org.nanohttpd:nanohttpd:2.3.1")
api("org.apache.commons:commons-compress:1.21")
compileOnlyApi("org.jetbrains:annotations:16.0.3")
api("org.apache.commons:commons-compress:1.23.0")
compileOnlyApi("org.jetbrains:annotations:24.0.1")
}
@@ -30,6 +30,7 @@ import java.util.UUID;
*/
@Immutable
public class AuthInfo implements AutoCloseable {
public static final String USER_TYPE_MSA = "msa";
public static final String USER_TYPE_MOJANG = "mojang";
public static final String USER_TYPE_LEGACY = "legacy";
@@ -110,7 +110,7 @@ public class MicrosoftSession {
public AuthInfo toAuthInfo() {
requireNonNull(profile);
return new AuthInfo(profile.getName(), profile.getId(), accessToken, AuthInfo.USER_TYPE_MOJANG, "{}");
return new AuthInfo(profile.getName(), profile.getId(), accessToken, AuthInfo.USER_TYPE_MSA, "{}");
}
public static class User {
@@ -114,7 +114,7 @@ public class OfflineAccount extends Account {
@Override
public AuthInfo logIn() throws AuthenticationException {
// Using "legacy" user type here because "mojang" user type may cause "invalid session token" or "disconnected" when connecting to a game server.
AuthInfo authInfo = new AuthInfo(username, uuid, UUIDTypeAdapter.fromUUID(UUID.randomUUID()), AuthInfo.USER_TYPE_LEGACY, "{}");
AuthInfo authInfo = new AuthInfo(username, uuid, UUIDTypeAdapter.fromUUID(UUID.randomUUID()), AuthInfo.USER_TYPE_MSA, "{}");
if (loadAuthlibInjector(skin)) {
CompletableFuture<AuthlibInjectorArtifactInfo> artifactTask = CompletableFuture.supplyAsync(() -> {
@@ -154,7 +154,7 @@ public class OfflineAccount extends Account {
private YggdrasilServer server;
public OfflineAuthInfo(AuthInfo authInfo, AuthlibInjectorArtifactInfo artifact) {
super(authInfo.getUsername(), authInfo.getUUID(), authInfo.getAccessToken(), USER_TYPE_LEGACY, authInfo.getUserProperties());
super(authInfo.getUsername(), authInfo.getUUID(), authInfo.getAccessToken(), USER_TYPE_MSA, authInfo.getUserProperties());
this.artifact = artifact;
}
@@ -84,6 +84,7 @@ public class YggdrasilSession {
String name = tryCast(storage.get("displayName"), String.class).orElseThrow(() -> new IllegalArgumentException("displayName is missing"));
String clientToken = tryCast(storage.get("clientToken"), String.class).orElseThrow(() -> new IllegalArgumentException("clientToken is missing"));
String accessToken = tryCast(storage.get("accessToken"), String.class).orElseThrow(() -> new IllegalArgumentException("accessToken is missing"));
@SuppressWarnings("unchecked")
Map<String, String> userProperties = tryCast(storage.get("userProperties"), Map.class).orElse(null);
return new YggdrasilSession(clientToken, accessToken, new GameProfile(uuid, name), null, userProperties);
}
@@ -104,7 +105,7 @@ public class YggdrasilSession {
if (selectedProfile == null)
throw new IllegalStateException("No character is selected");
return new AuthInfo(selectedProfile.getName(), selectedProfile.getId(), accessToken, AuthInfo.USER_TYPE_MOJANG,
return new AuthInfo(selectedProfile.getName(), selectedProfile.getId(), accessToken, AuthInfo.USER_TYPE_MSA,
Optional.ofNullable(userProperties)
.map(properties -> properties.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey,
@@ -31,8 +31,7 @@ public final class EventBus {
@SuppressWarnings("unchecked")
public <T extends Event> EventManager<T> channel(Class<T> clazz) {
events.putIfAbsent(clazz, new EventManager<>());
return (EventManager<T>) events.get(clazz);
return (EventManager<T>) events.computeIfAbsent(clazz, ignored -> new EventManager<>());
}
@SuppressWarnings("unchecked")
@@ -23,10 +23,10 @@ import org.jackhuang.hmcl.util.platform.Platform;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.Set;
/**
* Supports operations on versioning.
@@ -250,8 +250,8 @@ public interface GameRepository extends VersionProvider {
*/
Path getLoggingObject(String version, String assetId, LoggingInfo loggingInfo);
default List<String> getClasspath(Version version) {
List<String> classpath = new ArrayList<>();
default Set<String> getClasspath(Version version) {
Set<String> classpath = new LinkedHashSet<>();
for (Library library : version.getLibraries())
if (library.appliesToCurrentEnvironment() && !library.isNative()) {
File f = getLibraryFile(version, library);
@@ -23,9 +23,7 @@ import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.Serializable;
import java.net.Proxy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.*;
/**
*
@@ -42,6 +40,7 @@ public class LaunchOptions implements Serializable {
private final List<String> overrideJavaArguments = new ArrayList<>();
private final List<String> javaArguments = new ArrayList<>();
private final List<String> javaAgents = new ArrayList<>(0);
private final Map<String, String> environmentVariables = new LinkedHashMap<>();
private Integer minMemory;
private Integer maxMemory;
private Integer metaspace;
@@ -59,7 +58,7 @@ public class LaunchOptions implements Serializable {
private NativesDirectoryType nativesDirType;
private String nativesDir;
private ProcessPriority processPriority = ProcessPriority.NORMAL;
private boolean useSoftwareRenderer;
private Renderer renderer = Renderer.DEFAULT;
private boolean useNativeGLFW;
private boolean useNativeOpenAL;
private boolean daemon;
@@ -130,6 +129,10 @@ public class LaunchOptions implements Serializable {
return Collections.unmodifiableList(javaAgents);
}
public Map<String, String> getEnvironmentVariables() {
return environmentVariables;
}
/**
* The minimum memory that the JVM can allocate.
*/
@@ -252,8 +255,8 @@ public class LaunchOptions implements Serializable {
return processPriority;
}
public boolean isUseSoftwareRenderer() {
return useSoftwareRenderer;
public Renderer getRenderer() {
return renderer;
}
public boolean isUseNativeGLFW() {
@@ -279,43 +282,6 @@ public class LaunchOptions implements Serializable {
return options;
}
/**
* The game directory
*/
public File getGameDir() {
return options.gameDir;
}
/**
* The Java Environment that Minecraft runs on.
*/
public JavaVersion getJava() {
return options.java;
}
/**
* Will shown in the left bottom corner of the main menu of Minecraft.
* null if use the id of launch version.
*/
public String getVersionName() {
return options.versionName;
}
/**
* Will shown in the left bottom corner of the main menu of Minecraft.
* null if use Version.versionType.
*/
public String getVersionType() {
return options.versionType;
}
/**
* Don't know what the hell this is.
*/
public String getProfileName() {
return options.profileName;
}
/**
* User custom additional minecraft command line arguments.
*/
@@ -341,123 +307,6 @@ public class LaunchOptions implements Serializable {
return options.javaAgents;
}
/**
* The minimum memory that the JVM can allocate.
*/
public Integer getMinMemory() {
return options.minMemory;
}
/**
* The maximum memory that the JVM can allocate.
*/
public Integer getMaxMemory() {
return options.maxMemory;
}
/**
* The maximum metaspace memory that the JVM can allocate.
* For Java 7 -XX:PermSize and Java 8 -XX:MetaspaceSize
* Containing class instances.
*/
public Integer getMetaspace() {
return options.metaspace;
}
/**
* The initial game window width
*/
public Integer getWidth() {
return options.width;
}
/**
* The initial game window height
*/
public Integer getHeight() {
return options.height;
}
/**
* Is inital game window fullscreen.
*/
public boolean isFullscreen() {
return options.fullscreen;
}
/**
* The server ip that will connect to when enter game main menu.
*/
public String getServerIp() {
return options.serverIp;
}
/**
* i.e. optirun
*/
public String getWrapper() {
return options.wrapper;
}
/**
* Proxy settings
*/
public Proxy getProxy() {
return options.proxy;
}
/**
* The user name of the proxy, optional.
*/
public String getProxyUser() {
return options.proxyUser;
}
/**
* The password of the proxy, optional
*/
public String getProxyPass() {
return options.proxyPass;
}
/**
* Prevent game launcher from generating default JVM arguments like max memory.
*/
public boolean isNoGeneratedJVMArgs() {
return options.noGeneratedJVMArgs;
}
/**
* Called command line before launching the game.
*/
public String getPreLaunchCommand() {
return options.preLaunchCommand;
}
public NativesDirectoryType getNativesDirType() {
return options.nativesDirType;
}
public String getNativesDir() {
return options.nativesDir;
}
public boolean isUseSoftwareRenderer() {
return options.useSoftwareRenderer;
}
public boolean isUseNativeGLFW() {
return options.useNativeGLFW;
}
public boolean isUseNativeOpenAL() {
return options.useNativeOpenAL;
}
public boolean isDaemon() {
return options.daemon;
}
public Builder setGameDir(File gameDir) {
options.gameDir = gameDir;
return this;
@@ -507,6 +356,12 @@ public class LaunchOptions implements Serializable {
return this;
}
public Builder setEnvironmentVariables(Map<String, String> env) {
options.environmentVariables.clear();
options.environmentVariables.putAll(env);
return this;
}
public Builder setMinMemory(Integer minMemory) {
options.minMemory = minMemory;
return this;
@@ -592,8 +447,8 @@ public class LaunchOptions implements Serializable {
return this;
}
public Builder setUseSoftwareRenderer(boolean useSoftwareRenderer) {
options.useSoftwareRenderer = useSoftwareRenderer;
public Builder setRenderer(@NotNull Renderer renderer) {
options.renderer = renderer;
return this;
}
@@ -0,0 +1,8 @@
package org.jackhuang.hmcl.game;
public enum Renderer {
DEFAULT,
ZINK,
LLVMPIPE,
D3D12
}
@@ -207,7 +207,7 @@ public class DefaultLauncher extends Launcher {
res.addDefault("-Dfml.ignorePatchDiscrepancies=", "true");
}
List<String> classpath = repository.getClasspath(version);
Set<String> classpath = repository.getClasspath(version);
File jar = repository.getVersionJar(version);
if (!jar.exists() || !jar.isFile())
@@ -462,16 +462,29 @@ public class DefaultLauncher extends Launcher {
private Map<String, String> getEnvVars() {
String versionName = Optional.ofNullable(options.getVersionName()).orElse(version.getId());
Map<String, String> env = new HashMap<>();
Map<String, String> env = new LinkedHashMap<>();
env.put("INST_NAME", versionName);
env.put("INST_ID", versionName);
env.put("INST_DIR", repository.getVersionRoot(version.getId()).getAbsolutePath());
env.put("INST_MC_DIR", repository.getRunDirectory(version.getId()).getAbsolutePath());
env.put("INST_JAVA", options.getJava().getBinary().toString());
if (options.isUseSoftwareRenderer() && OperatingSystem.CURRENT_OS == OperatingSystem.LINUX) {
env.put("LIBGL_ALWAYS_SOFTWARE", "1");
env.put("__GLX_VENDOR_LIBRARY_NAME", "mesa");
Renderer renderer = options.getRenderer();
if (renderer != Renderer.DEFAULT) {
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
if (renderer != Renderer.LLVMPIPE)
env.put("GALLIUM_DRIVER", renderer.name().toLowerCase(Locale.ROOT));
} else if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX) {
env.put("__GLX_VENDOR_LIBRARY_NAME", "mesa");
switch (renderer) {
case LLVMPIPE:
env.put("LIBGL_ALWAYS_SOFTWARE", "1");
break;
case ZINK:
env.put("MESA_LOADER_DRIVER_OVERRIDE", "zink");
break;
}
}
}
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version);
@@ -490,6 +503,9 @@ public class DefaultLauncher extends Launcher {
if (analyzer.has(LibraryAnalyzer.LibraryType.QUILT)) {
env.put("INST_QUILT", "1");
}
env.putAll(options.getEnvironmentVariables());
return env;
}
@@ -25,10 +25,8 @@ import java.io.UnsupportedEncodingException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.*;
/**
@@ -41,20 +39,20 @@ public final class Logging {
public static final Logger LOG = Logger.getLogger("HMCL");
private static final ByteArrayOutputStream storedLogs = new ByteArrayOutputStream(IOUtils.DEFAULT_BUFFER_SIZE);
private static final ConcurrentMap<String, String> forbiddenTokens = new ConcurrentHashMap<>();
private static volatile String[] accessTokens = new String[0];
public static void registerForbiddenToken(String token, String replacement) {
forbiddenTokens.put(token, replacement);
}
public static synchronized void registerAccessToken(String token) {
final String[] oldAccessTokens = accessTokens;
final String[] newAccessTokens = Arrays.copyOf(oldAccessTokens, oldAccessTokens.length + 1);
public static void registerAccessToken(String accessToken) {
registerForbiddenToken(accessToken, "<access token>");
newAccessTokens[oldAccessTokens.length] = token;
accessTokens = newAccessTokens;
}
public static String filterForbiddenToken(String message) {
for (Map.Entry<String, String> entry : forbiddenTokens.entrySet()) {
message = message.replace(entry.getKey(), entry.getValue());
}
for (String token : accessTokens)
message = message.replace(token, "<access token>");
return message;
}
@@ -17,9 +17,6 @@
*/
package org.jackhuang.hmcl.util;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Predicate;
/**
@@ -30,20 +27,6 @@ public final class ReflectionHelper {
private ReflectionHelper() {
}
private static Method accessible0;
static {
try {
accessible0 = AccessibleObject.class.getDeclaredMethod("setAccessible0", boolean.class);
accessible0.setAccessible(true);
} catch (Throwable ignored) {
}
}
public static void setAccessible(AccessibleObject obj) throws InvocationTargetException, IllegalAccessException {
accessible0.invoke(obj, true);
}
/**
* Get caller, this method is caller sensitive.
*
@@ -1,67 +0,0 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
*
* 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 <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.util;
import java.util.Objects;
public abstract class Result<T> {
public T get() {
throw new IllegalStateException("TriState not ok");
}
public boolean isOK() {
return false;
}
public boolean isError() {
return false;
}
public static <T> Result<T> ok(T result) {
return new OK<>(Objects.requireNonNull(result));
}
@SuppressWarnings("unchecked")
public static <T> Result<T> error() {
return (Result<T>) Error.INSTANCE;
}
private static class OK<T> extends Result<T> {
private final T result;
public OK(T result) {
this.result = result;
}
@Override
public T get() {
return result;
}
}
private static class Error<T> extends Result<T> {
public static final Error<Void> INSTANCE = new Error<>();
@Override
public boolean isError() {
return true;
}
}
}
@@ -1,32 +0,0 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
*
* 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 <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.util.gson;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializer;
public abstract class GsonSerializerHelper<T> implements JsonSerializer<T>, JsonDeserializer<T> {
protected static void add(JsonObject object, String property, JsonElement value) {
if (value == null) return;
object.add(property, value);
}
}
@@ -24,6 +24,7 @@ import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.UUID;
import java.util.regex.Pattern;
/**
*
@@ -51,8 +52,10 @@ public final class UUIDTypeAdapter extends TypeAdapter<UUID> {
return value.toString().replace("-", "");
}
private static final Pattern regex = Pattern.compile("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})");
public static UUID fromString(String input) {
return UUID.fromString(input.replaceFirst("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5"));
return UUID.fromString(regex.matcher(input).replaceFirst("$1-$2-$3-$4-$5"));
}
}
@@ -46,8 +46,8 @@ public enum Architecture {
PPC64LE(BIT_64, "PowerPC-64 (Little-Endian)"),
S390(BIT_32),
S390X(BIT_64, "S390x"),
RISCV32(BIT_32, "RISC-V (32 Bit)"),
RISCV64(BIT_64, "RISC-V"),
RISCV32(BIT_32, "RISC-V 32"),
RISCV64(BIT_64, "RISC-V 64"),
LOONGARCH32(BIT_32, "LoongArch32"),
LOONGARCH64_OW(BIT_64, "LoongArch64 (old world)"),
LOONGARCH64(BIT_64, "LoongArch64"),
@@ -69,12 +69,6 @@ public enum Architecture {
this.bits = bits;
}
Architecture(Bits bits, String displayName, String identifier) {
this.checkedName = identifier;
this.displayName = displayName;
this.bits = bits;
}
public Bits getBits() {
return bits;
}
@@ -17,8 +17,6 @@
*/
package org.jackhuang.hmcl.util.versioning;
import org.jackhuang.hmcl.util.StringUtils;
import java.math.BigInteger;
import java.util.*;
@@ -29,7 +27,9 @@ import java.util.*;
* Maybe we can migrate to org.jenkins-ci:version-number:1.7?
* @see <a href="http://maven.apache.org/pom.html#Version_Order_Specification">Specification</a>
*/
public class VersionNumber implements Comparable<VersionNumber> {
public final class VersionNumber implements Comparable<VersionNumber> {
public static final Comparator<String> VERSION_COMPARATOR = Comparator.comparing(VersionNumber::asVersion);
public static VersionNumber asVersion(String version) {
Objects.requireNonNull(version);
@@ -41,68 +41,85 @@ public class VersionNumber implements Comparable<VersionNumber> {
}
public static boolean isIntVersionNumber(String version) {
if (version.chars().noneMatch(ch -> ch != '.' && (ch < '0' || ch > '9'))
&& !version.contains("..") && StringUtils.isNotBlank(version)) {
String[] arr = version.split("\\.");
for (String str : arr)
if (str.length() > 9)
// Numbers which are larger than 1e9 cannot be stored as integer.
return false;
return true;
} else {
if (version.isEmpty()) {
return false;
}
int idx = 0;
boolean cont = true;
do {
int dotIndex = version.indexOf('.', idx);
if (dotIndex == idx || dotIndex == version.length() - 1) {
return false;
}
int endIndex;
if (dotIndex < 0) {
cont = false;
endIndex = version.length();
} else {
endIndex = dotIndex;
}
if (endIndex - idx > 9)
// Numbers which are larger than 10^10 cannot be stored as integer
return false;
for (int i = idx; i < endIndex; i++) {
char ch = version.charAt(i);
if (ch < '0' || ch > '9')
return false;
}
idx = endIndex + 1;
} while (cont);
return true;
}
private String value;
private String canonical;
private ListItem items;
private interface Item {
int INTEGER_ITEM = 0;
int STRING_ITEM = 1;
int LIST_ITEM = 2;
int LONG_ITEM = 0;
int BIGINTEGER_ITEM = 1;
int STRING_ITEM = 2;
int LIST_ITEM = 3;
int compareTo(Item item);
int getType();
boolean isNull();
void appendTo(StringBuilder buffer);
}
/**
* Represents a numeric item in the version item list.
*/
private static class IntegerItem
implements Item {
private final BigInteger value;
private static final class LongItem implements Item {
private final long value;
public static final IntegerItem ZERO = new IntegerItem();
public static final LongItem ZERO = new LongItem(0L);
private IntegerItem() {
this.value = BigInteger.ZERO;
}
IntegerItem(String str) {
this.value = new BigInteger(str);
LongItem(long value) {
this.value = value;
}
public int getType() {
return INTEGER_ITEM;
return LONG_ITEM;
}
public boolean isNull() {
return BigInteger.ZERO.equals(value);
return value == 0L;
}
public int compareTo(Item item) {
if (item == null) {
return BigInteger.ZERO.equals(value) ? 0 : 1; // 1.0 == 1, 1.1 > 1
return value == 0L ? 0 : 1; // 1.0 == 1, 1.1 > 1
}
switch (item.getType()) {
case INTEGER_ITEM:
return value.compareTo(((IntegerItem) item).value);
case LONG_ITEM:
long itemValue = ((LongItem) item).value;
return Long.compare(value, itemValue);
case BIGINTEGER_ITEM:
return -1;
case STRING_ITEM:
return 1; // 1.1 > 1-sp
@@ -111,10 +128,68 @@ public class VersionNumber implements Comparable<VersionNumber> {
return 1; // 1.1 > 1-1
default:
throw new RuntimeException("invalid item: " + item.getClass());
throw new AssertionError("invalid item: " + item.getClass());
}
}
@Override
public void appendTo(StringBuilder buffer) {
buffer.append(value);
}
public String toString() {
return Long.toString(value);
}
}
/**
* Represents a numeric item in the version item list.
*/
private static final class BigIntegerItem implements Item {
private final BigInteger value;
BigIntegerItem(String str) {
this.value = new BigInteger(str);
}
public int getType() {
return BIGINTEGER_ITEM;
}
public boolean isNull() {
// Never be 0
// return BigInteger.ZERO.equals(value);
return false;
}
public int compareTo(Item item) {
if (item == null) {
// return BigInteger.ZERO.equals(value) ? 0 : 1; // 1.0 == 1, 1.1 > 1
return 1;
}
switch (item.getType()) {
case LONG_ITEM:
return 1;
case BIGINTEGER_ITEM:
return value.compareTo(((BigIntegerItem) item).value);
case STRING_ITEM:
return 1; // 1.1 > 1-sp
case LIST_ITEM:
return 1; // 1.1 > 1-1
default:
throw new AssertionError("invalid item: " + item.getClass());
}
}
@Override
public void appendTo(StringBuilder buffer) {
buffer.append(value);
}
public String toString() {
return value.toString();
}
@@ -123,9 +198,8 @@ public class VersionNumber implements Comparable<VersionNumber> {
/**
* Represents a string in the version item list, usually a qualifier.
*/
private static class StringItem
implements Item {
private String value;
private static final class StringItem implements Item {
private final String value;
StringItem(String value) {
this.value = value;
@@ -145,7 +219,8 @@ public class VersionNumber implements Comparable<VersionNumber> {
return 1;
}
switch (item.getType()) {
case INTEGER_ITEM:
case LONG_ITEM:
case BIGINTEGER_ITEM:
return -1; // 1.any < 1.1 ?
case STRING_ITEM:
@@ -155,10 +230,15 @@ public class VersionNumber implements Comparable<VersionNumber> {
return -1; // 1.any < 1-1
default:
throw new RuntimeException("invalid item: " + item.getClass());
throw new AssertionError("invalid item: " + item.getClass());
}
}
@Override
public void appendTo(StringBuilder buffer) {
buffer.append(value);
}
public String toString() {
return value;
}
@@ -168,14 +248,14 @@ public class VersionNumber implements Comparable<VersionNumber> {
* Represents a version list item. This class is used both for the global item list and for sub-lists (which start
* with '-(number)' in the version specification).
*/
private static class ListItem
extends ArrayList<Item>
implements Item {
Character separator;
private static final class ListItem extends ArrayList<Item> implements Item {
private final Character separator;
public ListItem() {}
ListItem() {
this.separator = null;
}
public ListItem(char separator) {
ListItem(char separator) {
this.separator = separator;
}
@@ -184,7 +264,7 @@ public class VersionNumber implements Comparable<VersionNumber> {
}
public boolean isNull() {
return (size() == 0);
return size() == 0;
}
void normalize() {
@@ -209,7 +289,8 @@ public class VersionNumber implements Comparable<VersionNumber> {
return first.compareTo(null);
}
switch (item.getType()) {
case INTEGER_ITEM:
case LONG_ITEM:
case BIGINTEGER_ITEM:
return -1; // 1-1 < 1.0.x
case STRING_ITEM:
@@ -234,36 +315,46 @@ public class VersionNumber implements Comparable<VersionNumber> {
return 0;
default:
throw new RuntimeException("invalid item: " + item.getClass());
throw new AssertionError("invalid item: " + item.getClass());
}
}
@Override
public void appendTo(StringBuilder buffer) {
if (separator != null) {
buffer.append((char) separator);
}
final int initLength = buffer.length();
for (Item item : this) {
if (buffer.length() > initLength) {
if (!(item instanceof ListItem))
buffer.append('.');
}
item.appendTo(buffer);
}
}
public String toString() {
StringBuilder buffer = new StringBuilder();
for (Item item : this) {
if (buffer.length() > 0) {
if (!(item instanceof ListItem))
buffer.append('.');
}
buffer.append(item);
}
if (separator != null)
return separator + buffer.toString();
else
return buffer.toString();
appendTo(buffer);
return buffer.toString();
}
}
public VersionNumber(String version) {
parseVersion(version);
}
private static final int MAX_LONGITEM_LENGTH = 18;
private void parseVersion(String version) {
private final String value;
public final ListItem items;
private final String canonical;
private VersionNumber(String version) {
this.value = version;
ListItem list = items = new ListItem();
ListItem list = this.items = new ListItem();
Stack<Item> stack = new Stack<>();
Deque<Item> stack = new ArrayDeque<>();
stack.push(list);
boolean isDigit = false;
@@ -275,14 +366,14 @@ public class VersionNumber implements Comparable<VersionNumber> {
if (c == '.') {
if (i == startIndex) {
list.add(IntegerItem.ZERO);
list.add(LongItem.ZERO);
} else {
list.add(parseItem(version.substring(startIndex, i)));
}
startIndex = i + 1;
} else if ("!\"#$%&'()*+,-/:;<=>?@[\\]^_`{|}~".indexOf(c) != -1) {
if (i == startIndex) {
list.add(IntegerItem.ZERO);
list.add(LongItem.ZERO);
} else {
list.add(parseItem(version.substring(startIndex, i)));
}
@@ -290,7 +381,7 @@ public class VersionNumber implements Comparable<VersionNumber> {
list.add(list = new ListItem(c));
stack.push(list);
} else if (Character.isDigit(c)) {
} else if (c >= '0' && c <= '9') {
if (!isDigit && i > startIndex) {
list.add(parseItem(version.substring(startIndex, i)));
startIndex = i;
@@ -322,11 +413,42 @@ public class VersionNumber implements Comparable<VersionNumber> {
list.normalize();
}
canonical = items.toString();
this.canonical = items.toString();
}
// For simple version
private VersionNumber(String version, ListItem items) {
this.value = version;
this.items = items;
this.canonical = version;
}
private static Item parseItem(String buf) {
return buf.chars().allMatch(Character::isDigit) ? new IntegerItem(buf) : new StringItem(buf);
int numberLength = 0;
boolean leadingZero = true;
for (int i = 0; i < buf.length(); i++) {
char ch = buf.charAt(i);
if (ch >= '0' && ch <= '9') {
if (ch != '0') {
leadingZero = false;
}
if (!leadingZero) {
numberLength++;
}
} else {
return new StringItem(buf);
}
}
if (numberLength == 0) {
return LongItem.ZERO;
} else if (numberLength <= MAX_LONGITEM_LENGTH) {
// Numbers which are larger than 10^19 cannot be stored as long
return new LongItem(Long.parseLong(buf));
} else {
return new BigIntegerItem(buf);
}
}
public int compareTo(String o) {
@@ -364,6 +486,4 @@ public class VersionNumber implements Comparable<VersionNumber> {
public int hashCode() {
return canonical.hashCode();
}
public static final Comparator<String> VERSION_COMPARATOR = Comparator.comparing(VersionNumber::asVersion);
}
@@ -19,105 +19,135 @@ package org.jackhuang.hmcl.util.versioning;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.*;
import java.util.function.Supplier;
import static org.jackhuang.hmcl.util.versioning.VersionNumber.isIntVersionNumber;
import static org.jackhuang.hmcl.util.versioning.VersionNumber.normalize;
import static org.junit.jupiter.api.Assertions.*;
public class VersionNumberTest {
@Test
public void testCanonical() {
VersionNumber u, v;
assertEquals("3.2", normalize("3.2.0.0"));
assertEquals("3.2-5", normalize("3.2.0.0-5"));
assertEquals("3.2", normalize("3.2.0.0-0"));
assertEquals("3.2", normalize("3.2--------"));
assertEquals("3.2", normalize("3.0002"));
assertEquals("1.7.2$%%^@&snapshot-3.1.1", normalize("1.7.2$%%^@&snapshot-3.1.1"));
assertEquals("1.99999999999999999999", normalize("1.99999999999999999999"));
assertEquals("1.99999999999999999999", normalize("1.0099999999999999999999"));
assertEquals("1.99999999999999999999", normalize("1.99999999999999999999.0"));
assertEquals("1.99999999999999999999", normalize("1.99999999999999999999--------"));
}
v = VersionNumber.asVersion("3.2.0.0");
assertEquals("3.2", v.getCanonical());
@Test
public void testIsIntVersion() {
assertFalse(isIntVersionNumber(""));
assertFalse(isIntVersionNumber(" "));
assertFalse(isIntVersionNumber("."));
assertFalse(isIntVersionNumber("1."));
assertFalse(isIntVersionNumber(".1"));
assertFalse(isIntVersionNumber(".1."));
assertFalse(isIntVersionNumber("1..8"));
assertFalse(isIntVersionNumber("1.8."));
assertFalse(isIntVersionNumber(".1.8"));
assertFalse(isIntVersionNumber("1.7.10forge1614_FTBInfinity"));
assertFalse(isIntVersionNumber("3.2-5"));
assertFalse(isIntVersionNumber("1.9999999999"));
v = VersionNumber.asVersion("3.2.0.0-5");
assertEquals("3.2-5", v.getCanonical());
assertTrue(isIntVersionNumber("0"));
assertTrue(isIntVersionNumber("1"));
assertTrue(isIntVersionNumber("0.1"));
assertTrue(isIntVersionNumber("0.1.0"));
assertTrue(isIntVersionNumber("1.8"));
assertTrue(isIntVersionNumber("1.12.2"));
assertTrue(isIntVersionNumber("1.13.1"));
assertTrue(isIntVersionNumber("1.999999999"));
assertTrue(isIntVersionNumber("999999999.0"));
}
v = VersionNumber.asVersion("3.2.0.0-0");
assertEquals("3.2", v.getCanonical());
private static void assertLessThan(String s1, String s2) {
Supplier<String> messageSupplier = () -> String.format("%s should be less than %s", s1, s2);
v = VersionNumber.asVersion("3.2--------");
assertEquals("3.2", v.getCanonical());
VersionNumber v1 = VersionNumber.asVersion(s1);
VersionNumber v2 = VersionNumber.asVersion(s2);
v = VersionNumber.asVersion("1.7.2$%%^@&snapshot-3.1.1");
assertEquals("1.7.2$%%^@&snapshot-3.1.1", v.getCanonical());
assertTrue(v1.compareTo(v2) < 0, messageSupplier);
assertTrue(v2.compareTo(v1) > 0, messageSupplier);
}
@Test
public void testComparator() {
VersionNumber u, v;
u = VersionNumber.asVersion("1.7.10forge1614_FTBInfinity");
v = VersionNumber.asVersion("1.12.2");
assertTrue(u.compareTo(v) < 0);
u = VersionNumber.asVersion("1.8.0_51");
v = VersionNumber.asVersion("1.8.0.51");
assertTrue(u.compareTo(v) < 0);
u = VersionNumber.asVersion("1.8.0_151");
v = VersionNumber.asVersion("1.8.0_77");
assertTrue(u.compareTo(v) > 0);
u = VersionNumber.asVersion("1.6.0_22");
v = VersionNumber.asVersion("1.8.0_11");
assertTrue(u.compareTo(v) < 0);
u = VersionNumber.asVersion("1.7.0_22");
v = VersionNumber.asVersion("1.7.99");
assertTrue(u.compareTo(v) < 0);
u = VersionNumber.asVersion("1.12.2-14.23.5.2760");
v = VersionNumber.asVersion("1.12.2-14.23.4.2739");
assertTrue(u.compareTo(v) > 0);
assertLessThan("1.7.10forge1614_FTBInfinity", "1.12.2");
assertLessThan("1.8.0_51", "1.8.0.51");
assertLessThan("1.8.0_77", "1.8.0_151");
assertLessThan("1.6.0_22", "1.8.0_11");
assertLessThan("1.7.0_22", "1.7.99");
assertLessThan("1.12.2-14.23.4.2739", "1.12.2-14.23.5.2760");
assertLessThan("1.9", "1.99999999999999999999");
assertLessThan("1.99999999999999999999", "1.199999999999999999999");
assertLessThan("1.99999999999999999999", "2");
assertLessThan("1.99999999999999999999", "2.0");
}
@Test
public void testSorting() {
List<String> input = Arrays.asList(
"1.10",
"1.10.2",
"1.10.2-All the Mods",
"1.10.2-AOE",
"1.10.2-AOE-1.1.5",
"1.10.2-forge2511-Age_of_Progression",
"1.10.2-forge2511-AOE-1.1.2",
"1.10.2-forge2511-ATM-E",
"1.10.2-forge2511-simple_life_2",
"1.10.2-forge2511_bxztest",
"1.10.2-forge2511_Farming_Valley",
"1.10.2-forge2511中文",
"1.10.2-FTB_Beyond",
"1.10.2-LiteLoader1.10.2",
"1.12.2",
"1.12.2_Modern_Skyblock-3.4.2",
"1.13.1",
final Comparator<String> comparator = VersionNumber.VERSION_COMPARATOR.thenComparing(String::compareTo);
final List<String> input = Collections.unmodifiableList(Arrays.asList(
"0",
"0.10.0",
"1.6.4",
"1.6.4-Forge9.11.1.1345",
"1.7.10",
"1.7.10-1614",
"1.7.10-1614-test",
"1.7.10Agrarian_Skies_2",
"1.7.10-F1614-L",
"1.7.10-FL1614_04",
"1.7.10-Forge10.13.4.1614-1.7.10",
"1.7.10-Forge1614",
"1.7.10-Forge1614.1",
"1.7.10Agrarian_Skies_2",
"1.7.10forge1614test",
"1.7.10forge1614_ATlauncher",
"1.7.10forge1614_FTBInfinity",
"1.7.10Forge1614_FTBInfinity-2.6.0",
"1.7.10Forge1614_FTBInfinity-3.0.1",
"1.7.10-Forge1614.1",
"1.7.10forge1614_ATlauncher",
"1.7.10forge1614_FTBInfinity",
"1.7.10forge1614_FTBInfinity_server",
"1.7.10forge1614test",
"1.7.10-1614",
"1.7.10-1614-test",
"1.8",
"1.8-forge1577",
"1.8.9",
"1.8.9-forge1902",
"1.9");
input.sort(Comparator.comparing(VersionNumber::asVersion));
"1.9",
"1.10",
"1.10.2",
"1.10.2-AOE",
"1.10.2-AOE-1.1.5",
"1.10.2-All the Mods",
"1.10.2-FTB_Beyond",
"1.10.2-LiteLoader1.10.2",
"1.10.2-forge2511-AOE-1.1.2",
"1.10.2-forge2511-ATM-E",
"1.10.2-forge2511-Age_of_Progression",
"1.10.2-forge2511_Farming_Valley",
"1.10.2-forge2511_bxztest",
"1.10.2-forge2511-simple_life_2",
"1.10.2-forge2511中文",
"1.12.2",
"1.12.2_Modern_Skyblock-3.4.2",
"1.13.1",
"1.99999999999999999999",
"2",
"2.0",
"2.1"));
List<String> output = new ArrayList<>(input);
output.sort(comparator);
assertIterableEquals(input, output);
Collections.shuffle(output, new Random(0));
output.sort(comparator);
assertIterableEquals(input, output);
}
}
+1 -1
View File
@@ -7,10 +7,10 @@
| ARM64 | 👌 (1.8+)<br/>✅ (use x86 emulation) | 👌 | 👌 (1.6~1.18.2)<br/>✅ (1.19+)<br/>✅ (use Rosetta 2) | ❔ |
| ARM32 | /️ | 👌 | / | / |
| MIPS64el | / | 👌 | / | / |
| RISC-V 64 | / | 👌 (1.13+) | / | / |
| LoongArch64 | / | 👌 (for Old World)<br/>❔ (for New World) | / | / |
| PowerPC-64 (Little-Endian) | / | ❔ | / | / |
| S390x | / | ❔ | / | / |
| RISC-V | / | ❔ | / | / |
Legend:
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+2 -2
View File
@@ -144,7 +144,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -152,7 +152,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
+1
View File
@@ -41,6 +41,7 @@ val jfxPlatforms = listOf(
Platform("linux-arm32", "linux-arm32-monocle", unsupportedModules = listOf("media", "web")),
Platform("linux-arm64", "linux-aarch64"),
Platform("linux-loongarch64_ow", "linux", groupId = "org.glavo.hmcl.openjfx", version = "19-ea+10-loongson64", unsupportedModules = listOf("media", "web")),
Platform("linux-riscv64", "linux", groupId = "org.glavo.hmcl.openjfx", version = "19.0.2.1-riscv64", unsupportedModules = listOf("media", "web")),
)
val jfxInClasspath =