Compare commits

...
60 Commits
Author SHA1 Message Date
zkitefly 6133e7e730 Update PURCHASE_URL and MIGRATION_FAQ_URL (#2229) 2023-06-16 08:39:16 +08:00
zkitefly c5cbafea60 添加 remove_assets 按钮 (#2257) 2023-06-16 08:38:27 +08:00
zkitefly 167e25e859 添加更多日志分析规则 (#2192)
* 添加“Forge 安装不完整”的检查

* fix

* fix

* add modName

* 更新 CrashReportAnalyzer.java

* 更新 CrashReportAnalyzerTest.java
2023-06-16 08:36:03 +08:00
zkitefly f47bd8a407 更新 CrashReportAnalyzer.java (#2175) 2023-06-16 08:31:44 +08:00
zkitefly 464be411d1 Updata account.failed.server_disconnected (#2247) 2023-06-16 08:23:11 +08:00
zkitefly d3b6eb36d9 Update DefaultGameRepository.java (#2273) 2023-06-16 08:21:27 +08:00
Glavo 71458f5167 启动时检测 Fractureiser 病毒 (#2270) 2023-06-16 08:21:06 +08:00
zkitefly 597730389f Close #1367 (#2232) 2023-06-16 08:18:41 +08:00
zkitefly 56b227f467 添加新显示项目:物理内存大小 (#2279) 2023-06-16 08:17:30 +08:00
Glavo 213b03ea56 Print warning message when signature key is missing (#2283) 2023-06-16 08:13:05 +08:00
Glavo 959c4d0698 检测 macOS 上的 App Translocation (#2252)
* check app translocation on macOS

* update

* update
2023-06-16 08:12:41 +08:00
zkitefly 57b3bd9ff0 修复分类翻译键问题 (#2277)
* 修复分类翻译键问题

* 更新搜索版本号
2023-06-16 08:11:09 +08:00
zkitefly 68f4038879 更新 Issue 模板 (#2278)
* Create feature.yml

* Update config.yml

* Update config.yml

* Update feature.yml
2023-06-16 08:10:44 +08:00
Glavo 6010008409 使用 ObservableMap 记录已显示的提示 (#2271) 2023-06-13 10:21:03 +08:00
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
Glavo 5fa066de6b fix crash (#2124) 2023-02-15 23:27:59 +08:00
zkitefly 1454ea8358 添加提示 (#2108)
* 添加提示

* Update I18N_zh_CN.properties
2023-02-15 17:45:25 +08:00
Glavo ab264955f5 添加动画开关 (#2111) 2023-02-14 19:12:30 +08:00
Glavo 389d430dbd fix #2094 (#2096) 2023-02-14 19:08:26 +08:00
Glavo 8b98932ae8 fix crash (#2115) 2023-02-13 19:59:24 +08:00
Glavo 62f39a3280 更新 I18N (#2083)
* Remove multiplayer messages

* update

* update

* update

* update

* update

* update

* update

* update
2023-02-12 09:33:52 +08:00
Glavo b116186f42 fix export batch script (#2107) 2023-02-11 23:43:13 +08:00
Glavo 40eeee7137 fix #2105: catch NPE in Accounts (#2106) 2023-02-11 22:53:58 +08:00
zkitefly 54eab87356 初始化内容修改为“热度” (#2099)
* 将初始化内容设定为“名称”排序

* 更新 RemoteModRepository.java

* 初始化内容修改为“下载量”

搜索时排序依然是“名称”

* 还是默认“热度”吧

因为一般搜模组都是搜热度比较高的,搜出的结果就较为准确

* Update DownloadListPage.java
2023-02-11 13:51:32 +08:00
zkitefly d828f7bc7b 初始化排序修改为“下载量” (#2097)
* 将初始化内容设定为“名称”排序

* 更新 RemoteModRepository.java

* 初始化内容修改为“下载量”

搜索时排序依然是“名称”
2023-02-11 11:47:26 +08:00
Glavo ae3e135e06 FileSelector needs to update the value after selecting a file (#2095)
* FileSelector needs to update the value after selecting a file

* update
2023-02-11 00:14:51 +08:00
Glavo 5ba8bdad10 Catch i18n exception (#2093)
* Catch i18n exception

* update

* update
2023-02-10 11:01:34 +08:00
Glavo bffbfd7af9 Fix the problem of adding redundant G1 parameters (#2092) 2023-02-09 22:18:01 +08:00
Glavo a8c1e8d2cb Do not save the default number of log lines to the configuration (#2086) 2023-02-09 00:34:17 +08:00
Glavo 230d6b20ab 修复下载源选择 ComboBox 过长的问题 (#2085) 2023-02-09 00:33:47 +08:00
Glavo 109a14dd74 Remove unused files 2023-02-08 23:17:21 +08:00
Glavo 1ac1ff3c95 fix crash 2023-02-08 21:16:24 +08:00
Glavo 7e522ff136 弃用 commons-lang (#2061)
* Remove ArrayUtils

* Remove MutableObject

* VersionRange

* Replace Range with VersionRange

* fix checkstyle

* Update

* update

* update

* fix checkstyle

* fix checkstyle

* update
2023-02-08 19:57:52 +08:00
zkiteflyandYuhui Huang 90c261ddd1 Quilt 相关功能完善 (#2035)
* Quilt 相关功能完善

- Mcbbs 整合包适配 Quilt
- 自定义命令支持 Quilt 环境变量
- 顺便把语言文件内的机翻错误“\:”修复

* Fix

---------

Co-authored-by: Yuhui Huang <i@huangyuhui.net>
2023-02-08 19:54:54 +08:00
95 changed files with 2516 additions and 1804 deletions

No files matched your search

+2 -2
View File
@@ -10,5 +10,5 @@ contact_links:
url: https://afdian.net/@huanghongxun
about: 前往爱发电赞助 HMCL。赞助后可以申请加入官方 QQ 群反馈问题。
- name: 其他反馈
url: https://github.com/huanghongxun/HMCL/issues/new
about: 通过 Issue 反馈其他问题。
url: https://github.com/huanghongxun/HMCL/discussions/new/choose
about: 通过 Discussions 反馈其他问题。
+21
View File
@@ -0,0 +1,21 @@
name: "Feature Request"
description: "为 HMCL 提出新功能"
labels: feature request
body:
- type: markdown
attributes:
value: "提交前请确保查看 issues 列表是否有重复的项目!"
- type: textarea
id: "yml-2"
attributes:
label: 描述 Description
description: "详细描述你想加入的新功能"
validations:
required: true
- type: textarea
id: "yml-3"
attributes:
label: 原因 Why
description: "描述该功能带来的好处及原因"
validations:
required: true
-14
View File
@@ -1,14 +0,0 @@
language: java
jdk:
- oraclejdk8
branches:
only:
- master
before_install:
- chmod +x gradlew
script: "bash ./gradlew clean build --stacktrace"
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
cache:
directories:
- "$HOME/.gradle/caches/"
+6 -1
View File
@@ -56,7 +56,12 @@ fun createChecksum(file: File) {
}
fun attachSignature(jar: File) {
val keyLocation = System.getenv("HMCL_SIGNATURE_KEY") ?: return
val keyLocation = System.getenv("HMCL_SIGNATURE_KEY")
if (keyLocation == null) {
logger.warn("Missing signature key")
return
}
val privatekey = KeyFactory.getInstance("RSA").generatePrivate(PKCS8EncodedKeySpec(File(keyLocation).readBytes()))
val signer = Signature.getInstance("SHA512withRSA")
signer.initSign(privatekey)
@@ -4,8 +4,6 @@ import javafx.scene.shape.Mesh;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh;
import org.apache.commons.lang3.ArrayUtils;
public class SkinCube extends MeshView {
public static class Model extends TriangleMesh {
@@ -77,13 +75,23 @@ public class SkinCube extends MeshView {
};
int[] copy = faces.clone();
ArrayUtils.reverse(copy);
for (int i = 0, mid = copy.length >> 1, j = copy.length - 1; i < mid; i++, j--) {
int tmp = copy[i];
copy[i] = copy[j];
copy[j] = tmp;
}
for (int i = 0; i < copy.length; i += 2) {
int tmp = copy[i];
copy[i] = copy[i + 1];
copy[i + 1] = tmp;
}
return ArrayUtils.addAll(faces, copy);
int[] result = new int[faces.length + copy.length];
System.arraycopy(faces, 0, result, 0, faces.length);
System.arraycopy(copy, 0, result, faces.length, copy.length);
return result;
}
}
@@ -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;
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ButtonType;
import javafx.scene.input.Clipboard;
import javafx.scene.input.DataFormat;
@@ -85,12 +86,18 @@ public final class Launcher extends Application {
Main.showErrorAndExit(i18n("fatal.config_loading_failure", ConfigHolder.configLocation().getParent()));
}
checkConfigInTempDir();
// https://lapcatsoftware.com/articles/app-translocation.html
if (OperatingSystem.CURRENT_OS == OperatingSystem.OSX
&& ConfigHolder.isNewlyCreated()
&& System.getProperty("user.dir").startsWith("/private/var/folders/")) {
if (showAlert(AlertType.WARNING, i18n("fatal.mac_app_translocation"), ButtonType.YES, ButtonType.NO) == ButtonType.NO)
return;
} else {
checkConfigInTempDir();
}
if (ConfigHolder.isOwnerChanged()) {
ButtonType res = new Alert(Alert.AlertType.WARNING, i18n("fatal.config_change_owner_root"), ButtonType.YES, ButtonType.NO)
.showAndWait()
.orElse(null);
if (res == ButtonType.NO)
if (showAlert(AlertType.WARNING, i18n("fatal.config_change_owner_root"), ButtonType.YES, ButtonType.NO) == ButtonType.NO)
return;
}
@@ -114,6 +121,10 @@ public final class Launcher extends Application {
}
}
private static ButtonType showAlert(AlertType alertType, String contentText, ButtonType... buttons) {
return new Alert(alertType, contentText, buttons).showAndWait().orElse(null);
}
private static boolean isConfigInTempDir() {
String configPath = ConfigHolder.configLocation().toString();
@@ -152,13 +163,9 @@ public final class Launcher extends Application {
}
private static void checkConfigInTempDir() {
if (ConfigHolder.isNewlyCreated() && isConfigInTempDir()) {
ButtonType res = new Alert(Alert.AlertType.WARNING, i18n("fatal.config_in_temp_dir"), ButtonType.YES, ButtonType.NO)
.showAndWait()
.orElse(null);
if (res == ButtonType.NO) {
System.exit(0);
}
if (ConfigHolder.isNewlyCreated() && isConfigInTempDir()
&& showAlert(AlertType.WARNING, i18n("fatal.config_in_temp_dir"), ButtonType.YES, ButtonType.NO) == ButtonType.NO) {
System.exit(0);
}
}
@@ -190,10 +197,9 @@ public final class Launcher extends Application {
String command = new CommandBuilder().add("sudo", "chown", "-R", userName).addAll(files).toString();
ButtonType copyAndExit = new ButtonType(i18n("button.copy_and_exit"));
ButtonType res = new Alert(Alert.AlertType.ERROR, i18n("fatal.config_loading_failure.unix", owner, command), copyAndExit, ButtonType.CLOSE)
.showAndWait()
.orElse(null);
if (res == copyAndExit) {
if (showAlert(AlertType.ERROR,
i18n("fatal.config_loading_failure.unix", owner, command),
copyAndExit, ButtonType.CLOSE) == copyAndExit) {
Clipboard.getSystemClipboard()
.setContent(Collections.singletonMap(DataFormat.PLAIN_TEXT, command));
}
@@ -222,7 +228,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 +237,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.
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl;
import javafx.application.Platform;
import javafx.scene.control.Alert;
import org.jackhuang.hmcl.ui.AwtUtils;
import org.jackhuang.hmcl.util.FractureiserDetector;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.SelfDependencyPatcher;
import org.jackhuang.hmcl.ui.SwingUtils;
@@ -71,6 +72,7 @@ public final class Main {
Logging.start(Metadata.HMCL_DIRECTORY.resolve("logs"));
checkJavaFX();
detectFractureiser();
Launcher.main(args);
}
@@ -89,6 +91,13 @@ public final class Main {
}
}
private static void detectFractureiser() {
if (FractureiserDetector.detect()) {
LOG.log(Level.SEVERE, "Detected that this computer is infected by fractureiser");
showErrorAndExit(i18n("fatal.fractureiser"));
}
}
private static void checkJavaFX() {
try {
SelfDependencyPatcher.patch();
@@ -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;
@@ -182,7 +183,7 @@ public final class Accounts {
globalAccountStorages.setAll((List<Map<Object, Object>>)
Config.CONFIG_GSON.fromJson(reader, new TypeToken<List<Map<Object, Object>>>() {
}.getType()));
} catch (IOException e) {
} catch (Throwable e) {
LOG.log(Level.WARNING, "Failed to load global accounts", e);
}
}
@@ -325,7 +326,7 @@ public final class Accounts {
Schedulers.io().execute(() -> {
try {
finalSelected.logIn();
} catch (AuthenticationException e) {
} catch (Throwable e) {
LOG.log(Level.WARNING, "Failed to log " + finalSelected + " in", e);
}
});
@@ -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) {
@@ -191,6 +191,12 @@ public final class Config implements Cloneable, Observable {
@SerializedName("preferredLoginType")
private StringProperty preferredLoginType = new SimpleStringProperty();
@SerializedName("animationDisabled")
private BooleanProperty animationDisabled = new SimpleBooleanProperty();
@SerializedName("shownTips")
private ObservableMap<String, Object> shownTips = FXCollections.observableHashMap();
private transient ObservableHelper helper = new ObservableHelper(this);
public Config() {
@@ -601,6 +607,18 @@ public final class Config implements Cloneable, Observable {
return preferredLoginType;
}
public boolean isAnimationDisabled() {
return animationDisabled.get();
}
public BooleanProperty animationDisabledProperty() {
return animationDisabled;
}
public void setAnimationDisabled(boolean animationDisabled) {
this.animationDisabled.set(animationDisabled);
}
public boolean isTitleTransparent() {
return titleTransparent.get();
}
@@ -624,4 +642,8 @@ public final class Config implements Cloneable, Observable {
public void setPromptedVersion(String promptedVersion) {
this.promptedVersion.set(promptedVersion);
}
public ObservableMap<String, Object> getShownTips() {
return shownTips;
}
}
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.setting;
import javafx.beans.binding.Bindings;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.game.HMCLCacheRepository;
import org.jackhuang.hmcl.ui.animation.AnimationUtils;
import org.jackhuang.hmcl.util.CacheRepository;
import org.jackhuang.hmcl.util.io.FileUtils;
@@ -54,6 +55,7 @@ public final class Settings {
Accounts.init();
Profiles.init();
AuthlibInjectorServers.init();
AnimationUtils.init();
CacheRepository.setInstance(HMCLCacheRepository.REPOSITORY);
HMCLCacheRepository.REPOSITORY.directoryProperty().bind(Bindings.createStringBinding(() -> {
@@ -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);
@@ -46,9 +46,10 @@ import javafx.scene.text.TextFlow;
import javafx.util.Callback;
import javafx.util.Duration;
import javafx.util.StringConverter;
import org.apache.commons.lang3.mutable.MutableObject;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.ui.animation.AnimationUtils;
import org.jackhuang.hmcl.ui.construct.JFXHyperlink;
import org.jackhuang.hmcl.util.Holder;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.ResourceNotFoundError;
import org.jackhuang.hmcl.util.io.FileUtils;
@@ -275,7 +276,8 @@ public final class FXUtils {
}
public static void smoothScrolling(ScrollPane scrollPane) {
ScrollUtils.addSmoothScrolling(scrollPane);
if (AnimationUtils.isAnimationEnabled())
ScrollUtils.addSmoothScrolling(scrollPane);
}
public static void installFastTooltip(Node node, Tooltip tooltip) {
@@ -623,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);
}
@@ -643,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);
}
/**
@@ -721,16 +722,16 @@ public final class FXUtils {
}
public static <T> Callback<ListView<T>, ListCell<T>> jfxListCellFactory(Function<T, Node> graphicBuilder) {
MutableObject<Object> lastCell = new MutableObject<>();
Holder<Object> lastCell = new Holder<>();
return view -> new JFXListCell<T>() {
@Override
public void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
// https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html
if (this == lastCell.getValue() && !isVisible())
if (this == lastCell.value && !isVisible())
return;
lastCell.setValue(this);
lastCell.value = this;
if (!empty) {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
@@ -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;
@@ -72,6 +73,7 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class GameCrashWindow extends Stage {
private final Version version;
private final String memory;
private final String total_memory;
private final String java;
private final LibraryAnalyzer analyzer;
private final StringProperty os = new SimpleStringProperty(OperatingSystem.SYSTEM_NAME);
@@ -99,6 +101,8 @@ public class GameCrashWindow extends Stage {
memory = Optional.ofNullable(launchOptions.getMaxMemory()).map(i -> i + " MB").orElse("-");
total_memory = Optional.ofNullable(OperatingSystem.TOTAL_MEMORY).map(i -> i + " MB").orElse("-");
this.java = launchOptions.getJava().getArchitecture() == Architecture.SYSTEM_ARCH
? launchOptions.getJava().getVersion()
: launchOptions.getJava().getVersion() + " (" + launchOptions.getJava().getArchitecture().getDisplayName() + ")";
@@ -107,7 +111,7 @@ public class GameCrashWindow extends Stage {
this.feedbackTextFlow.getChildren().addAll(FXUtils.parseSegment(i18n("game.crash.feedback"), Controllers::onHyperlinkAction));
setScene(new Scene(view, 854, 700));
setScene(new Scene(view, 800, 480));
getScene().getStylesheets().addAll(Theme.getTheme().getStylesheets(config().getLauncherFontFamily()));
setTitle(i18n("game.crash.title"));
getIcons().add(newImage("/assets/img/icon.png"));
@@ -291,6 +295,11 @@ public class GameCrashWindow extends Stage {
version.setTitle(i18n("archive.game_version"));
version.setSubtitle(GameCrashWindow.this.version.getId());
TwoLineListItem total_memory = new TwoLineListItem();
total_memory.getStyleClass().setAll("two-line-item-second-large");
total_memory.setTitle(i18n("settings.physical_memory"));
total_memory.setSubtitle(GameCrashWindow.this.total_memory);
TwoLineListItem memory = new TwoLineListItem();
memory.getStyleClass().setAll("two-line-item-second-large");
memory.setTitle(i18n("settings.memory"));
@@ -311,7 +320,7 @@ public class GameCrashWindow extends Stage {
arch.setTitle(i18n("system.architecture"));
arch.subtitleProperty().bind(GameCrashWindow.this.arch);
infoPane.getChildren().setAll(launcher, version, memory, java, os, arch);
infoPane.getChildren().setAll(launcher, version, total_memory, memory, java, os, arch);
}
HBox moddedPane = new HBox(8);
@@ -347,9 +356,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();
@@ -35,9 +35,9 @@ import javafx.scene.control.Label;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import org.apache.commons.lang3.mutable.MutableObject;
import org.jackhuang.hmcl.game.LauncherHelper;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.util.Holder;
import org.jackhuang.hmcl.util.CircularArrayList;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.Log4jLevel;
@@ -90,7 +90,7 @@ public final class LogWindow extends Stage {
private boolean stopCheckLogCount = false;
public LogWindow() {
setScene(new Scene(impl, 854, 480));
setScene(new Scene(impl, 800, 480));
getScene().getStylesheets().addAll(Theme.getTheme().getStylesheets(config().getLauncherFontFamily()));
setTitle(i18n("logwindow.title"));
getIcons().add(newImage("/assets/img/icon.png"));
@@ -166,20 +166,20 @@ public final class LogWindow extends Stage {
listView.setItems(FXCollections.observableList(new CircularArrayList<>(config().getLogLines() + 1)));
boolean flag = false;
cboLines.getItems().setAll("10000", "5000", "2000", "500");
cboLines.getItems().setAll("500", "2000", "5000", "10000");
for (String i : cboLines.getItems())
if (Integer.toString(config().getLogLines()).equals(i)) {
cboLines.getSelectionModel().select(i);
flag = true;
}
if (!flag)
cboLines.getSelectionModel().select(2);
cboLines.getSelectionModel().selectedItemProperty().addListener((a, b, newValue) -> {
config().setLogLines(newValue == null ? 100 : Integer.parseInt(newValue));
});
if (!flag)
cboLines.getSelectionModel().select(0);
Log4jLevel[] levels = new Log4jLevel[]{Log4jLevel.FATAL, Log4jLevel.ERROR, Log4jLevel.WARN, Log4jLevel.INFO, Log4jLevel.DEBUG};
String[] suffix = new String[]{"fatals", "errors", "warns", "infos", "debugs"};
for (int i = 0; i < 5; ++i) {
@@ -294,7 +294,7 @@ public final class LogWindow extends Stage {
listView.setStyle("-fx-font-family: " + Lang.requireNonNullElse(config().getFontFamily(), FXUtils.DEFAULT_MONOSPACE_FONT)
+ "; -fx-font-size: " + config().getFontSize() + "px;");
MutableObject<Object> lastCell = new MutableObject<>();
Holder<Object> lastCell = new Holder<>();
listView.setCellFactory(x -> new ListCell<Log>() {
{
getStyleClass().add("log-window-list-cell");
@@ -313,9 +313,9 @@ public final class LogWindow extends Stage {
super.updateItem(item, empty);
// https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html
if (this == lastCell.getValue() && !isVisible())
if (this == lastCell.value && !isVisible())
return;
lastCell.setValue(this);
lastCell.value = this;
pseudoClassStateChanged(EMPTY, empty);
pseudoClassStateChanged(FATAL, !empty && item.level == Log4jLevel.FATAL);
@@ -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);
@@ -48,9 +48,15 @@ public class OAuthAccountLoginDialog extends DialogPane {
FXUtils.onChangeAndOperate(deviceCode, deviceCode -> {
if (deviceCode != null) {
FXUtils.copyText(deviceCode.getUserCode());
hintPane.setSegment(i18n("account.login.refresh.microsoft.manual", deviceCode.getUserCode(), deviceCode.getVerificationUri()));
hintPane.setSegment(
"<b>" + i18n("account.login.refresh.microsoft.hint") + "</b>\n"
+ i18n("account.methods.microsoft.manual", deviceCode.getUserCode(), deviceCode.getVerificationUri())
);
} else {
hintPane.setSegment(i18n("account.login.refresh.microsoft.hint"));
hintPane.setSegment(
"<b>" + i18n("account.login.refresh.microsoft.hint") + "</b>\n"
+ i18n("account.methods.microsoft.hint")
);
}
});
hintPane.setOnMouseClicked(e -> {
@@ -0,0 +1,23 @@
package org.jackhuang.hmcl.ui.animation;
import org.jackhuang.hmcl.setting.ConfigHolder;
public final class AnimationUtils {
private AnimationUtils() {
}
/**
* Trigger initialization of this class.
* Should be called from {@link org.jackhuang.hmcl.setting.Settings#init()}.
*/
@SuppressWarnings("JavadocReference")
public static void init() {
}
private static final boolean enabled = !ConfigHolder.config().isAnimationDisabled();
public static boolean isAnimationEnabled() {
return enabled;
}
}
@@ -69,18 +69,23 @@ public class TransitionPane extends StackPane implements AnimationHandler {
return;
}
transition.init(this);
if (AnimationUtils.isAnimationEnabled()) {
transition.init(this);
// runLater or "init" will not work
Platform.runLater(() -> {
Timeline newAnimation = new Timeline();
newAnimation.getKeyFrames().addAll(transition.animate(this));
newAnimation.getKeyFrames().add(new KeyFrame(duration, e -> {
setMouseTransparent(false);
getChildren().remove(previousNode);
}));
FXUtils.playAnimation(this, "transition_pane", newAnimation);
});
// runLater or "init" will not work
Platform.runLater(() -> {
Timeline newAnimation = new Timeline();
newAnimation.getKeyFrames().addAll(transition.animate(this));
newAnimation.getKeyFrames().add(new KeyFrame(duration, e -> {
setMouseTransparent(false);
getChildren().remove(previousNode);
}));
FXUtils.playAnimation(this, "transition_pane", newAnimation);
});
} else {
setMouseTransparent(false);
getChildren().remove(previousNode);
}
}
private void updateContent(Node newView) {
@@ -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 -> {
@@ -25,6 +25,9 @@ import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
@@ -35,11 +38,12 @@ import javafx.util.Duration;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
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;
@@ -70,6 +74,7 @@ class ComponentListCell extends StackPane {
}
}
@SuppressWarnings("unchecked")
private void updateLayout() {
if (content instanceof ComponentList) {
ComponentList list = (ComponentList) content;
@@ -134,45 +139,53 @@ class ComponentListCell extends StackPane {
container.getChildren().setAll(content);
groupNode.getChildren().add(container);
Runnable onExpand = () -> {
EventHandler<Event> onExpand = e -> {
if (expandAnimation != null && expandAnimation.getStatus() == Animation.Status.RUNNING) {
expandAnimation.stop();
}
setExpanded(!isExpanded());
if (isExpanded()) {
list.onExpand();
boolean expanded = !isExpanded();
setExpanded(expanded);
if (expanded) {
list.doLazyInit();
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;
double newAnimatedHeight = (list.prefHeight(-1) + 8 + 10) * (expanded ? 1 : -1);
double newHeight = expanded ? getHeight() + newAnimatedHeight : prefHeight(-1);
double contentHeight = expanded ? newAnimatedHeight : 0;
if (isExpanded()) {
if (expanded) {
updateClip(newHeight);
}
expandAnimation = new Timeline(new KeyFrame(new Duration(320.0),
new KeyValue(container.minHeightProperty(), contentHeight, FXUtils.SINE),
new KeyValue(container.maxHeightProperty(), contentHeight, FXUtils.SINE)
));
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()) {
expandAnimation.setOnFinished(e2 -> updateClip(newHeight));
if (!expanded) {
expandAnimation.setOnFinished(e2 -> updateClip(newHeight));
}
expandAnimation.play();
} else {
container.setMinHeight(contentHeight);
container.setMaxHeight(contentHeight);
if (!expanded) {
updateClip(newHeight);
}
}
expandAnimation.play();
});
};
headerRippler.setOnMouseClicked(e -> onExpand.run());
expandButton.setOnMouseClicked(e -> onExpand.run());
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 {
@@ -37,7 +37,7 @@ import java.io.File;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class FileSelector extends HBox {
private StringProperty value = new SimpleStringProperty();
private final StringProperty value = new SimpleStringProperty();
private String chooserTitle = i18n("selector.choose_file");
private boolean directory = false;
private final ObservableList<FileChooser.ExtensionFilter> extensionFilters = FXCollections.observableArrayList();
@@ -87,15 +87,21 @@ public class FileSelector extends HBox {
DirectoryChooser chooser = new DirectoryChooser();
chooser.setTitle(chooserTitle);
File dir = chooser.showDialog(Controllers.getStage());
if (dir != null)
customField.setText(dir.getAbsolutePath());
if (dir != null) {
String path = dir.getAbsolutePath();
customField.setText(path);
value.setValue(path);
}
} else {
FileChooser chooser = new FileChooser();
chooser.getExtensionFilters().addAll(getExtensionFilters());
chooser.setTitle(chooserTitle);
File file = chooser.showOpenDialog(Controllers.getStage());
if (file != null)
customField.setText(file.getAbsolutePath());
if (file != null) {
String path = file.getAbsolutePath();
customField.setText(path);
value.setValue(path);
}
}
});
@@ -22,17 +22,17 @@ import javafx.css.PseudoClass;
import javafx.scene.control.ListCell;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import org.apache.commons.lang3.mutable.MutableObject;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.util.Holder;
public abstract class MDListCell<T> extends ListCell<T> {
private final PseudoClass SELECTED = PseudoClass.getPseudoClass("selected");
private final StackPane container = new StackPane();
private final StackPane root = new StackPane();
private final MutableObject<Object> lastCell;
private final Holder<Object> lastCell;
public MDListCell(JFXListView<T> listView, MutableObject<Object> lastCell) {
public MDListCell(JFXListView<T> listView, Holder<Object> lastCell) {
this.lastCell = lastCell;
setText(null);
@@ -57,9 +57,9 @@ public abstract class MDListCell<T> extends ListCell<T> {
// https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html
if (lastCell != null) {
if (this == lastCell.getValue() && !isVisible())
if (this == lastCell.value && !isVisible())
return;
lastCell.setValue(this);
lastCell.value = this;
}
updateControl(item, empty);
@@ -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 -> {
@@ -20,7 +20,8 @@ package org.jackhuang.hmcl.ui.decorator;
import com.jfoenix.controls.JFXDialog;
import com.jfoenix.controls.JFXSnackbar;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.InvalidationListener;
import javafx.beans.WeakInvalidationListener;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
@@ -35,6 +36,7 @@ import javafx.stage.Stage;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorDnD;
import org.jackhuang.hmcl.setting.EnumBackgroundImage;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.account.AddAuthlibInjectorServerPane;
@@ -55,6 +57,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import java.util.stream.Stream;
@@ -89,10 +92,23 @@ public class DecoratorController {
decorator.onBackNavButtonActionProperty().set(e -> back());
decorator.onRefreshNavButtonActionProperty().set(e -> refresh());
setupBackground();
setupAuthlibInjectorDnD();
// Setup background
decorator.setContentBackground(getBackground());
changeBackgroundListener = o -> {
final int currentCount = ++this.changeBackgroundCount;
CompletableFuture.supplyAsync(this::getBackground, Schedulers.io())
.thenAcceptAsync(background -> {
if (this.changeBackgroundCount == currentCount)
decorator.setContentBackground(background);
}, Schedulers.javafx());
};
WeakInvalidationListener weakListener = new WeakInvalidationListener(changeBackgroundListener);
config().backgroundImageTypeProperty().addListener(weakListener);
config().backgroundImageProperty().addListener(weakListener);
config().backgroundImageUrlProperty().addListener(weakListener);
// pass key events to current dialog / current page
decorator.addEventFilter(KeyEvent.ANY, e -> {
if (!(e.getTarget() instanceof Node)) {
@@ -134,37 +150,40 @@ public class DecoratorController {
// ==== Background ====
private void setupBackground() {
decorator.contentBackgroundProperty().bind(
Bindings.createObjectBinding(
() -> {
Image image = null;
if (config().getBackgroundImageType() == EnumBackgroundImage.CUSTOM && config().getBackgroundImage() != null) {
image = tryLoadImage(Paths.get(config().getBackgroundImage()))
.orElse(null);
}
if (config().getBackgroundImageType() == EnumBackgroundImage.NETWORK) {
if (!NetworkUtils.isURL(config().getBackgroundImageUrl())) {
image = loadDefaultBackgroundImage();
} else {
image = new Image(config().getBackgroundImageUrl(), true);
}
} else if (config().getBackgroundImageType() == EnumBackgroundImage.CLASSIC) {
image = newImage("/assets/img/background-classic.jpg");
} else if (config().getBackgroundImageType() == EnumBackgroundImage.TRANSLUCENT) {
return new Background(new BackgroundFill(new Color(1, 1, 1, 0.5), CornerRadii.EMPTY, Insets.EMPTY));
}
if (image == null) {
image = loadDefaultBackgroundImage();
}
return new Background(new BackgroundImage(image, BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.DEFAULT, new BackgroundSize(800, 480, false, false, true, true)));
},
config().backgroundImageTypeProperty(),
config().backgroundImageProperty(),
config().backgroundImageUrlProperty()));
//FXThread
private int changeBackgroundCount = 0;
@SuppressWarnings("FieldCanBeLocal") // Strong reference
private final InvalidationListener changeBackgroundListener;
private Background getBackground() {
EnumBackgroundImage imageType = config().getBackgroundImageType();
Image image = null;
switch (imageType) {
case CUSTOM:
String backgroundImage = config().getBackgroundImage();
if (backgroundImage != null)
image = tryLoadImage(Paths.get(backgroundImage)).orElse(null);
break;
case NETWORK:
String backgroundImageUrl = config().getBackgroundImageUrl();
if (backgroundImageUrl != null && NetworkUtils.isURL(backgroundImageUrl))
image = tryLoadImage(backgroundImageUrl).orElse(null);
break;
case CLASSIC:
image = newImage("/assets/img/background-classic.jpg");
break;
case TRANSLUCENT:
return new Background(new BackgroundFill(new Color(1, 1, 1, 0.5), CornerRadii.EMPTY, Insets.EMPTY));
}
if (image == null) {
image = loadDefaultBackgroundImage();
}
return new Background(new BackgroundImage(image, BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.DEFAULT, new BackgroundSize(800, 480, false, false, true, true)));
}
private Image defaultBackground;
private volatile Image defaultBackground;
/**
* Load background image from bg/, background.png, background.jpg, background.gif
@@ -196,7 +215,7 @@ public class DecoratorController {
List<Path> candidates;
try (Stream<Path> stream = Files.list(imageDir)) {
candidates = stream
.filter(Files::isReadable)
.filter(Files::isReadable)
.filter(it -> {
String ext = getExtension(it).toLowerCase(Locale.ROOT);
return ext.equals("png") || ext.equals("jpg") || ext.equals("gif");
@@ -224,9 +243,13 @@ public class DecoratorController {
if (!Files.isReadable(path))
return Optional.empty();
return tryLoadImage(path.toAbsolutePath().toUri().toString());
}
private Optional<Image> tryLoadImage(String url) {
Image img;
try {
img = new Image(path.toAbsolutePath().toUri().toString());
img = new Image(url);
} catch (IllegalArgumentException e) {
LOG.log(WARNING, "Couldn't load background image", e);
return Optional.empty();
@@ -28,7 +28,6 @@ import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.image.Image;
import javafx.scene.layout.*;
import org.apache.commons.lang3.mutable.MutableObject;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList;
@@ -53,6 +52,7 @@ import org.jackhuang.hmcl.ui.wizard.Navigation;
import org.jackhuang.hmcl.ui.wizard.Refreshable;
import org.jackhuang.hmcl.ui.wizard.WizardPage;
import org.jackhuang.hmcl.util.HMCLService;
import org.jackhuang.hmcl.util.Holder;
import org.jackhuang.hmcl.util.i18n.Locales;
import java.util.EnumMap;
@@ -179,7 +179,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
btnRefresh.setGraphic(wrap(SVG.refresh(Theme.blackFillBinding(), -1, -1)));
MutableObject<RemoteVersionListCell> lastCell = new MutableObject<>();
Holder<RemoteVersionListCell> lastCell = new Holder<>();
EnumMap<VersionIconType, Image> icons = new EnumMap<>(VersionIconType.class);
list.setCellFactory(listView -> new RemoteVersionListCell(lastCell, icons));
@@ -273,10 +273,10 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
final RipplerContainer ripplerContainer = new RipplerContainer(content);
final StackPane pane = new StackPane();
private final MutableObject<RemoteVersionListCell> lastCell;
private final Holder<RemoteVersionListCell> lastCell;
private final EnumMap<VersionIconType, Image> icons;
RemoteVersionListCell(MutableObject<RemoteVersionListCell> lastCell, EnumMap<VersionIconType, Image> icons) {
RemoteVersionListCell(Holder<RemoteVersionListCell> lastCell, EnumMap<VersionIconType, Image> icons) {
this.lastCell = lastCell;
this.icons = icons;
@@ -294,9 +294,9 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
super.updateItem(remoteVersion, empty);
// https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html
if (this == lastCell.getValue() && !isVisible())
if (this == lastCell.value && !isVisible())
return;
lastCell.setValue(this);
lastCell.value = this;
if (empty) {
setGraphic(null);
@@ -90,7 +90,7 @@ public class DownloadSettingsPage extends StackPane {
JFXComboBox<String> cboDownloadSource = new JFXComboBox<>();
cboDownloadSource.setConverter(stringConverter(key -> i18n("download.provider." + key)));
downloadSourcePane.setCenter(cboDownloadSource);
downloadSourcePane.setRight(cboDownloadSource);
FXUtils.setLimitWidth(cboDownloadSource, 420);
cboDownloadSource.getItems().setAll(DownloadProviders.rawProviders.keySet());
@@ -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;
@@ -45,6 +46,7 @@ import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.animation.AnimationUtils;
import org.jackhuang.hmcl.ui.construct.AnnouncementCard;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
import org.jackhuang.hmcl.ui.construct.PopupMenu;
@@ -70,6 +72,8 @@ import static org.jackhuang.hmcl.ui.FXUtils.SINE;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class MainPage extends StackPane implements DecoratorPage {
private static final String ANNOUNCEMENT = "announcement";
private final ReadOnlyObjectWrapper<State> state = new ReadOnlyObjectWrapper<>();
private final PopupMenu menu = new PopupMenu();
@@ -82,7 +86,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;
@@ -111,7 +115,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().getShownTips().get(ANNOUNCEMENT)))) {
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);
@@ -220,7 +232,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);
@@ -234,14 +246,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);
@@ -254,18 +258,22 @@ public final class MainPage extends StackPane implements DecoratorPage {
}
private void doAnimation(boolean show) {
Duration duration = Duration.millis(320);
Timeline nowAnimation = new Timeline();
nowAnimation.getKeyFrames().addAll(
new KeyFrame(Duration.ZERO,
new KeyValue(updatePane.translateXProperty(), show ? 260 : 0, SINE)),
new KeyFrame(duration,
new KeyValue(updatePane.translateXProperty(), show ? 0 : 260, SINE)));
if (show) nowAnimation.getKeyFrames().add(
new KeyFrame(Duration.ZERO, e -> updatePane.setVisible(true)));
else nowAnimation.getKeyFrames().add(
new KeyFrame(duration, e -> updatePane.setVisible(false)));
nowAnimation.play();
if (AnimationUtils.isAnimationEnabled()) {
Duration duration = Duration.millis(320);
Timeline nowAnimation = new Timeline();
nowAnimation.getKeyFrames().addAll(
new KeyFrame(Duration.ZERO,
new KeyValue(updatePane.translateXProperty(), show ? 260 : 0, SINE)),
new KeyFrame(duration,
new KeyValue(updatePane.translateXProperty(), show ? 0 : 260, SINE)));
if (show) nowAnimation.getKeyFrames().add(
new KeyFrame(Duration.ZERO, e -> updatePane.setVisible(true)));
else nowAnimation.getKeyFrames().add(
new KeyFrame(duration, e -> updatePane.setVisible(false)));
nowAnimation.play();
} else {
updatePane.setVisible(show);
}
}
private void launch() {
@@ -289,6 +297,16 @@ public final class MainPage extends StackPane implements DecoratorPage {
showUpdate.set(false);
}
public void hideAnnouncementPane() {
if (announcementPane != null) {
config().getShownTips().put(ANNOUNCEMENT, Metadata.VERSION);
Pane parent = (Pane) announcementPane.getParent();
if (parent != null)
parent.getChildren().remove(announcementPane);
announcementPane = null;
}
}
@Override
public ReadOnlyObjectWrapper<State> stateProperty() {
return state;
@@ -88,6 +88,12 @@ public class PersonalizationPage extends StackPane {
titleTransparentButton.selectedProperty().bindBidirectional(config().titleTransparentProperty());
titleTransparentButton.setTitle(i18n("settings.launcher.title_transparent"));
}
{
OptionToggleButton animationButton = new OptionToggleButton();
themeList.getContent().add(animationButton);
animationButton.selectedProperty().bindBidirectional(config().animationDisabledProperty());
animationButton.setTitle(i18n("settings.launcher.turn_off_animations"));
}
content.getChildren().addAll(ComponentList.createComponentListTitle(i18n("settings.launcher.appearance")), themeList);
{
@@ -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)
@@ -27,10 +27,10 @@ import javafx.scene.Cursor;
import javafx.scene.control.Label;
import javafx.scene.layout.*;
import javafx.scene.text.TextAlignment;
import org.apache.commons.lang3.mutable.MutableObject;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.util.Holder;
import org.jackhuang.hmcl.util.io.HttpRequest;
import java.math.BigDecimal;
@@ -85,16 +85,16 @@ public class SponsorPage extends StackPane {
StackPane pane = new StackPane();
pane.getStyleClass().add("card");
listView = new JFXListView<>();
MutableObject<Object> lastCell = new MutableObject<>();
Holder<Object> lastCell = new Holder<>();
listView.setCellFactory((listView) -> new JFXListCell<Sponsor>() {
@Override
public void updateItem(Sponsor item, boolean empty) {
super.updateItem(item, empty);
// https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html
if (this == lastCell.getValue() && !isVisible())
if (this == lastCell.value && !isVisible())
return;
lastCell.setValue(this);
lastCell.value = this;
if (!empty) {
setText(item.getName());
@@ -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;
}
}
@@ -118,7 +118,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
if (!searchInitialized) {
searchInitialized = true;
search("", null, 0, "", RemoteModRepository.SortType.NAME);
search("", null, 0, "", RemoteModRepository.SortType.POPULARITY);
}
if (versionSelection) {
@@ -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);
}
}
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hmcl.ui.versions;
import com.google.gson.JsonParseException;
import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
@@ -82,8 +83,8 @@ public class GameItem extends Control {
ModpackConfiguration<?> config = profile.getRepository().readModpackConfiguration(version);
if (config == null) return;
tag.set(config.getVersion());
} catch (IOException e) {
LOG.log(Level.WARNING, "Failed to read modpack configuration from ", e);
} catch (IOException | JsonParseException e) {
LOG.log(Level.WARNING, "Failed to read modpack configuration from " + version, e);
}
}, Platform::runLater)
.exceptionally(handleUncaught);
@@ -32,7 +32,6 @@ import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import org.apache.commons.lang3.mutable.MutableObject;
import org.jackhuang.hmcl.mod.LocalModFile;
import org.jackhuang.hmcl.mod.ModManager;
import org.jackhuang.hmcl.setting.Theme;
@@ -44,6 +43,7 @@ import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
import org.jackhuang.hmcl.ui.animation.TransitionPane;
import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.util.Holder;
import org.jackhuang.hmcl.util.Lazy;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.i18n.I18n;
@@ -164,7 +164,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
center.getStyleClass().add("large-spinner-pane");
center.loadingProperty().bind(skinnable.loadingProperty());
MutableObject<Object> lastCell = new MutableObject<>();
Holder<Object> lastCell = new Holder<>();
listView.setCellFactory(x -> new ModInfoListCell(listView, lastCell));
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
Bindings.bindContent(listView.getItems(), skinnable.getItems());
@@ -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()));
@@ -371,7 +367,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
JFXButton revealButton = new JFXButton();
BooleanProperty booleanProperty;
ModInfoListCell(JFXListView<ModInfoObject> listView, MutableObject<Object> lastCell) {
ModInfoListCell(JFXListView<ModInfoObject> listView, Holder<Object> lastCell) {
super(listView, lastCell);
HBox container = new HBox(8);
@@ -163,6 +163,10 @@ public class VersionPage extends DecoratorAnimatedPage implements DecoratorPage
FileUtils.deleteDirectoryQuietly(new File(getProfile().getRepository().getBaseDirectory(), "libraries"));
}
private void clearAssets() {
FileUtils.deleteDirectoryQuietly(new File(getProfile().getRepository().getBaseDirectory(), "assets"));
}
private void clearJunkFiles() {
Versions.cleanVersion(getProfile(), getVersion());
}
@@ -291,6 +295,7 @@ public class VersionPage extends DecoratorAnimatedPage implements DecoratorPage
new IconedMenuItem(FXUtils.limitingSize(SVG.export(Theme.blackFillBinding(), 14, 14), 14, 14), i18n("modpack.export"), FXUtils.withJFXPopupClosing(control::export, managementPopup)),
new MenuSeparator(),
new IconedMenuItem(null, i18n("version.manage.redownload_assets_index"), FXUtils.withJFXPopupClosing(control::redownloadAssetIndex, managementPopup)),
new IconedMenuItem(null, i18n("version.manage.remove_assets"), FXUtils.withJFXPopupClosing(control::clearAssets, managementPopup)),
new IconedMenuItem(null, i18n("version.manage.remove_libraries"), FXUtils.withJFXPopupClosing(control::clearLibraries, managementPopup)),
new IconedMenuItem(null, i18n("version.manage.clean"), FXUtils.withJFXPopupClosing(control::clearJunkFiles, managementPopup)).addTooltip(i18n("version.manage.clean.tooltip"))
);
@@ -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();
@@ -0,0 +1,55 @@
package org.jackhuang.hmcl.util;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
/**
* @see <a href="https://github.com/fractureiser-investigation/fractureiser">fractureiser-investigation/fractureiser</a>
* @see <a href="https://prismlauncher.org/news/cf-compromised-alert/#automated-script">[MALWARE WARNING] "fractureiser" malware in many popular Minecraft mods and modpacks</a>
*/
public final class FractureiserDetector {
private FractureiserDetector() {
}
public static boolean detect() {
try {
ArrayList<Path> badPaths = new ArrayList<>();
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
Path appdata = Paths.get(System.getProperty("user.home"), "AppData");
if (Files.isDirectory(appdata)) {
badPaths.add(appdata.resolve("Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\run.bat"));
Path falseEdgePath = appdata.resolve("Local\\Microsoft Edge");
if (Files.exists(falseEdgePath)) {
badPaths.add(falseEdgePath.resolve(".ref"));
badPaths.add(falseEdgePath.resolve("client.jar"));
badPaths.add(falseEdgePath.resolve("lib.dll"));
badPaths.add(falseEdgePath.resolve("libWebGL64.jar"));
badPaths.add(falseEdgePath.resolve("run.bat"));
}
}
} else if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX) {
Path dataDir = Paths.get(System.getProperty("user.home"), ".config", ".data");
if (Files.exists(dataDir)) {
badPaths.add(dataDir.resolve(".ref"));
badPaths.add(dataDir.resolve("client.jar"));
badPaths.add(dataDir.resolve("lib.jar"));
}
}
for (Path badPath : badPaths) {
if (Files.exists(badPath)) {
return true;
}
}
} catch (Throwable ignored) {
}
return false;
}
}
@@ -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");
}
}
@@ -20,6 +20,8 @@ package org.jackhuang.hmcl.util.i18n;
import org.jackhuang.hmcl.setting.ConfigHolder;
import org.jackhuang.hmcl.util.i18n.Locales.SupportedLocale;
import java.util.Arrays;
import java.util.IllegalFormatException;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.logging.Level;
@@ -28,7 +30,8 @@ import static org.jackhuang.hmcl.util.Logging.LOG;
public final class I18n {
private I18n() {}
private I18n() {
}
public static SupportedLocale getCurrentLocale() {
try {
@@ -45,7 +48,15 @@ public final class I18n {
}
public static String i18n(String key, Object... formatArgs) {
return String.format(i18n(key), formatArgs);
try {
return String.format(getResourceBundle().getString(key), formatArgs);
} catch (MissingResourceException e) {
LOG.log(Level.SEVERE, "Cannot find key " + key + " in resource bundle", e);
} catch (IllegalFormatException e) {
LOG.log(Level.SEVERE, "Illegal format string, key=" + key + ", args=" + Arrays.toString(formatArgs), e);
}
return key + Arrays.toString(formatArgs);
}
public static String i18n(String key) {
@@ -58,11 +69,6 @@ public final class I18n {
}
public static boolean hasKey(String key) {
try {
getResourceBundle().getString(key);
return true;
} catch (MissingResourceException e) {
return false;
}
return getResourceBundle().containsKey(key);
}
}
@@ -65,14 +65,16 @@ account.failed.invalid_password=Invalid password
account.failed.invalid_token=Please try to re-login again.
account.failed.migration=Your account needs to be migrated to a Microsoft account. If you already did, you should re-login to your migrated Microsoft account instead.
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_disconnected=Cannot access authentication server. You can log in offline or try to re-login.\n\
If you try multiple times and still fail, please try adding the account again.
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.
account.injector.add=New Auth Server
account.injector.empty=None (You can click on the plus button on the right to add one)
account.injector.http=Warning\: This server uses the unsafe HTTP protocol, anyone between your connection will be able to see your credentials in cleartext.
account.injector.http=Warning: This server uses the unsafe HTTP protocol, anyone between your connection will be able to see your credentials in cleartext.
account.injector.link.homepage=Homepage
account.injector.link.register=Register
account.injector.server=Authentication Server
@@ -83,14 +85,12 @@ account.login.hint=We will not store your password.
account.login.skip=Login offline
account.login.retry=Retry
account.login.refresh=Re-login
account.login.refresh.microsoft.hint=<b>Because the account authorization is invalid, you need to re-add your Microsoft account</b>\nPlease click on the "OK" button, and copy the code shown here later to finish the login process in the opened browser window.
account.login.refresh.microsoft.manual=<b>Because the account authorization is invalid, you need to re-add your Microsoft account</b>\nYour device code is <b>%1$s</b>, please click here to copy. After clicking on the "OK" button, you should finish the login process in the opened browser window. If it did not show, you can go to %2$s manually.
account.login.refresh.microsoft.hint=Because the account authorization is invalid, you need to re-add your Microsoft account
account.logout=Logout
account.register=Register
account.manage=Account List
account.methods=Login Type
account.methods.authlib_injector=authlib-injector
account.methods.little_skin=Little Skin
account.methods.microsoft=Microsoft Account
account.methods.microsoft.birth=How to update your account birthday
account.methods.microsoft.close_page=Microsoft account authorization is now completed.\n\
@@ -102,7 +102,7 @@ account.methods.microsoft.error.add_family_probably=Please check if the age indi
account.methods.microsoft.error.country_unavailable=Xbox Live is not available in your current country/region.
account.methods.microsoft.error.missing_xbox_account=Your Microsoft account does not have a linked Xbox account yet. Please create one before continuing.
account.methods.microsoft.error.no_character=Your account does not own the Minecraft Java Edition.\nThe game profile may not have been created,\nplease click the link above to create it.
account.methods.microsoft.error.unknown=Failed to log in, error\: %d.
account.methods.microsoft.error.unknown=Failed to log in, error: %d.
account.methods.microsoft.logging_in=Logging in...
account.methods.microsoft.hint=Please click on the "login" button, and copy the code shown here later to finish the login process in the opened browser window.\n\
\n\
@@ -110,11 +110,10 @@ If the token used to log in to the Microsoft account is leaked, you can click on
account.methods.microsoft.manual=Your device code is <b>%1$s</b>, please click here to copy. After clicking on the "Login" button, you should finish the login process in the opened browser window. If it did not show, you can go to %2$s manually.\n\
\n\
If the token used to log in to the Microsoft account is leaked, you can click on "Unauthorize the account" to deauthorize it.
account.methods.microsoft.makegameidsettings=Create Profile
account.methods.microsoft.makegameidsettings=Create Profile / Edit Profile Name
account.methods.microsoft.profile=Account Profile
account.methods.microsoft.snapshot=You are using an unofficial build of HMCL. Please download the official build for login.
account.methods.microsoft.snapshot.website=Official Website
account.methods.microsoft.waiting_browser=Waiting for login to complete in the opened window...
account.methods.offline=Offline
account.methods.offline.name.special_characters=Recommended to use letters, numbers and underscores for naming
account.methods.offline.uuid=UUID
@@ -198,7 +197,7 @@ curse.category.4477=Mini Game
curse.category.4478=Quests
curse.category.4484=Multiplayer
curse.category.4476=Exploration
curse.category.4736=Skyblock
curse.category.6145=Skyblock
curse.category.4475=Adventure and RPG
curse.category.4487=FTB
curse.category.4480=Map Based
@@ -307,13 +306,13 @@ curse.sort.popularity=Popularity
curse.sort.total_downloads=Total Downloads
download=Download
download.code.404=File not found on the remote server\: %s
download.code.404=File not found on the remote server: %s
download.content=Addons
download.curseforge.customization=Light and shadow, and game customization
download.curseforge.unavailable=HMCL nightly build does not support access to CurseForge, please use stable version or beta version to download.
download.existing=The file cannot be saved because it already exists. You can use 'Save As' to save the file elsewhere.
download.external_link=Open Website
download.failed=Failed to download %1$s, response code\: %2$d
download.failed=Failed to download %1$s, response code: %2$d
download.failed.empty=No versions are available, please click here to go back.
download.failed.refresh=Unable to fetch version list. Please click here to retry.
download.game=Game
@@ -328,14 +327,14 @@ download.javafx=Downloading dependencies for launcher...
download.javafx.notes=We are currently downloading dependencies for HMCL from the Internet.\n\
\n\
You can click on 'Change Download Mirror' to select the download mirror or click on "Cancel" to stop and exit.\n\
Note\: If your download speed is too slow, you can try to switch to another mirror.
Note: If your download speed is too slow, you can try to switch to another mirror.
download.javafx.component=Downloading module %s
download.javafx.prepare=Preparing to download
exception.access_denied=HMCL is unable to access the file %s, it may be locked by another process.\n\
\n\
For Windows users, you can open up 'Resource Monitor' to check if another process is currently using it. If so, you can try again after closing that process.\n\
If not, please check if your account has enough privileges to access it.
If not, please check if your account has enough privileges to access it.
exception.artifact_malformed=Cannot verify the integrity of the downloaded files .
exception.ssl_handshake=Unable to establish SSL connection due to missing SSL certificates in current Java installation. You can try to start HMCL in another Java version, then try again.
@@ -345,20 +344,16 @@ extension.png=Image File
extension.ps1=Windows PowerShell Cmdlet File
extension.sh=Bash Shell Script
fatal.fractureiser=Hello Minecraft! Launcher has detected that your computer has been infected with the Fraureiser, which poses a serious security issue.\n\
Please use antivirus software to perform a full scan immediately, and then change the passwords of all accounts you have logged in on this computer.
fatal.javafx.incompatible=Missing JavaFX environment.\n\
HMCL cannot automatically install JavaFX under Java versions below 11.\n\
Please try to switch to Java versions like Oracle Java 8 which has JavaFX built-in, or update to version 11 or higher.
Please update your Java to version 11 or higher.
fatal.javafx.incompatible.loongson=Missing JavaFX environment.\n\
Please try to switch to Loongson JDK 8 (http://www.loongnix.cn/zh/api/java/downloads-jdk8/index.html) to start HMCL.
fatal.javafx.missing=Missing JavaFX environment.\n\
If you are using Java 11 or higher, please downgrade it to Oracle JRE 8 (java.com), or install BellSoft Liberica Full JRE (bell-sw.com/pages/downloads/?package\=jre-full).\n\
Or, if you are using OpenJDK distributions, please make sure it has OpenJFX included.
fatal.missing_dst_root_ca_x3=The DST Root CA X3 certificate is missing from the current Java distribution.\n\
\n\
You can still use Hello Minecraft! Launcher, but it will be unable to connect to some websites (such as sites that use certificates issued by Let's Encrypt), which may cause the launcher to work improperly.\n\
\n\
Please update your Java version to at least 8u101 to fix this issue.
# '
fatal.config_change_owner_root=You are using the root account to start Hello Minecraft! Launcher, this may cause you to fail to start Hello Minecraft! Launcher with other account in the future.\n\
Do you still want to continue?
fatal.config_in_temp_dir=You are start Hello Minecraft! Launcher in a temporary directory, your settings and game data may be lost.\n\
@@ -368,6 +363,9 @@ fatal.config_loading_failure=Cannot load configuration files.\n\
Please make sure that "Hello Minecraft! Launcher" has read and write access to "%s" and the files in it.
fatal.config_loading_failure.unix=Hello Minecraft! Launcher could not load the profile because the profile was created by user %1$s.\n\
Please start HMCL with sudo (not recommended), or execute the following command in the terminal to change the ownership of the configuration file to the current user:\n%2$s
fatal.mac_app_translocation=Due to the security mechanism of macOS, Hello Minecraft! Launcher is quarantined by the system to the temporary folder.\n\
Please move Hello Minecraft! Launcher to a different folder before attempting to start, otherwise your settings and game data may be lost after restarting.\n\
Do you still want to continue?
fatal.migration_requires_manual_reboot=Hello Minecraft! Launcher has been upgraded. Please reopen the launcher.
fatal.apply_update_failure=We are sorry, but Hello Minecraft! Launcher is unable to update.\n\
\n\
@@ -380,8 +378,8 @@ fatal.unsupported_platform=Minecraft is not yet fully supported for your platfor
If you can't start Minecraft 1.17 and above, you can try turning on the "Use OpenGL software renderer" option\n\
in instance settings to use CPU rendering for better compatibility.
# '
fatal.unsupported_platform.osx_arm64=HMCL has provided support for Apple Silicon platform, using native ARM java to launch games to get a smoother game experience.\nIf you encounter problems in the game, starting the game with Java based on x86-64 architecture may have better compatibility.
fatal.unsupported_platform.windows_arm64=HMCL has provided native support for the Windows on ARM platform. If you encounter problems when playing game, please try to start the game with Java based on x86 architecture.\n\nIf you are using the <b>Qualcomm</b> platform, you may need to install the <a href="ms-windows-store://pdp/?productid=9NQPSL29BFFF">OpenGL Compatibility Pack</a> before playing games.\nClick the link to go to the Microsoft Store and install the compatibility pack.
fatal.unsupported_platform.osx_arm64=Hello Minecraft! Launcher has provided support for Apple Silicon platform, using native ARM java to launch games to get a smoother game experience.\nIf you encounter problems in the game, starting the game with Java based on x86-64 architecture may have better compatibility.
fatal.unsupported_platform.windows_arm64=Hello Minecraft! Launcher has provided native support for the Windows on ARM platform. If you encounter problems when playing game, please try to start the game with Java based on x86 architecture.\n\nIf you are using the <b>Qualcomm</b> platform, you may need to install the <a href="ms-windows-store://pdp/?productid=9NQPSL29BFFF">OpenGL Compatibility Pack</a> before playing games.\nClick the link to go to the Microsoft Store and install the compatibility pack.
feedback=Feedback
feedback.channel=Feedback channel
@@ -412,8 +410,8 @@ game.crash.reason.block=The game crashed due to a block.\n\
\n\
You can try to remove this block using MCEdit or delete that mod that added it.\n\
\n\
Block Type\: %1$s\n\
Location\: %2$s
Block Type: %1$s\n\
Location: %2$s
game.crash.reason.bootstrap_failed=The game crashed due to mod %1$s.\n\
\n\
You can try to delete or update it.
@@ -421,7 +419,7 @@ game.crash.reason.config=The game crashed because a mod %1$s cannot parse its co
game.crash.reason.debug_crash=The game crashed because you manually triggered it.\n\
\n\
So, you probably know why.
game.crash.reason.duplicated_mod=The game cannot launch due to duplicate mods\: %1$s.\n\
game.crash.reason.duplicated_mod=The game cannot launch due to duplicate mods: %1$s.\n\
\n\
%2$s\n\
\n\
@@ -440,6 +438,8 @@ game.crash.reason.mod_resolution0=The current game cannot continue to run becaus
game.crash.reason.mod_profile_causes_game_crash=The current game cannot continue to run because of a problem with the Mod configuration file.\nYou can check the log to find the error module and its configuration file.
game.crash.reason.fabric_reports_an_error_and_gives_a_solution=Forge may have provided error information.\nYou can view the log and make corresponding processing according to the log information in the error report.\nIf you do not see the error message, you can view the error report to understand how the error occurred.
game.crash.reason.java_version_is_too_high=The current game crashed because the Java version used is too high to continue running.\nPlease use a lower version of Java in the Java path tab of global game settings or game specific settings, and then start the game.\nIf not, you can download it from <a href="https://www.java.com/download/">java.com (Java8)</a> or <a href="https://bell-sw.com/pages/downloads">BellSoft Liberica Full JRE (Java17)</a> and other platforms to download and install one (restart the launcher after installation).
game.crash.reason.mod_name=The current game cannot continue to run because of Mod file name problems. \nMod file names should use only upper and lower case letters (Aa~Zz), numbers (0~9), horizontal lines (-), underscores (_), and dots (.) in full half-angle English . \nPlease go to the Mod folder and add all non-compliant Mod file names with one of the above compliant characters.
game.crash.reason.incomplete_forge_installation=The current game cannot continue due to an incomplete installation of Forge. \nPlease uninstall and reinstall Forge in Version Settings - Automatic Installation.
game.crash.reason.fabric_version_0_12=Fabric 0.12 or above are incompatible with currently installed mods. You need to downgrade it to 0.11.7.
game.crash.reason.fabric_warnings=The Fabric modloader warned\:\n\
%1$s
@@ -510,7 +510,7 @@ game.crash.reason.mod_resolution_conflict=The game crashed because of conflictin
%1$s is incompatible with %2$s.
game.crash.reason.mod_resolution_missing=The game crashed because some dependency mods are not installed.\n\
\n\
%1$s requires mod\: %2$s.\n\
%1$s requires mod: %2$s.\n\
\n\
This means that you have to download and install %2$s first to continue playing.
game.crash.reason.mod_resolution_missing_minecraft=The game crashed because a mod is incompatible with the current Minecraft version.\n\
@@ -519,18 +519,9 @@ game.crash.reason.mod_resolution_missing_minecraft=The game crashed because a mo
\n\
If you want to play with this version of the mod installed, you should change the instance version.\n\
Otherwise, you should install a version that is compatible with this Minecraft version.
game.crash.reason.mod_resolution_mod_version=%1$s (Version\: %2$s)
game.crash.reason.mod_resolution_mod_version=%1$s (Version: %2$s)
game.crash.reason.mod_resolution_mod_version.any=%1$s (Any Version)
game.crash.reason.modlauncher_8=The game crashed because your current Forge version is not compatible with your Java installation. Please try to update Forge, or try to use Java 8u312/11.0.13/17.0.1 or earlier versions.\n\
\n\
8u312 and earlier\:\n\
https://oracle.com/java/technologies/javase/javase8u211-later-archive-downloads.html\n\
\n\
11.0.13 and earlier\:\n\
https://oracle.com/java/technologies/javase/jdk11-archive-downloads.html\n\
\n\
17.0.1 and earlier\:\n\
https://oracle.com/java/technologies/javase/jdk17-archive-downloads.html
game.crash.reason.modlauncher_8=The game crashed because your current Forge version is not compatible with your Java installation, please try to update Forge.
game.crash.reason.no_class_def_found_error=The game cannot run because of incomplete code.\n\
\n\
Your game instance is missing %1$s, this might be due to a mod missing, an incompatible mod installed, or some files might be corrupted.\n\
@@ -569,11 +560,11 @@ You need to switch to a newer version (%1$s) of Java in the game settings and th
game.crash.reason.unknown=We are not able to figure out why the game crashed, please refer to the game logs.\n\
\n\
<b>When asking someone else for help, please share the full game log and related crash report file with them\!</b>\n
game.crash.reason.unsatisfied_link_error=Unable to launch Minecraft due to missing libraries\: %1$s.\n\
game.crash.reason.unsatisfied_link_error=Unable to launch Minecraft due to missing libraries: %1$s.\n\
\n\
If you have modified native library settings, please make sure these libraries do exist. Or, please try to launch again after reverting it back to default.\n\
If you did not, please check if you have missing dependency mods.\n\
Otherwise, if you believe this is caused by HMCL, please feedback to us.
Otherwise, if you believe this is caused by HMCL, please feed back to us.
game.crash.title=Game Crashed
game.directory=Game Path
game.version=Game Version
@@ -591,9 +582,9 @@ install=New Instance
install.change_version=Change Version
install.change_version.confirm=Are you sure you want to switch %s from version %s to %s?
install.failed=Installation Failed
install.failed.downloading=We are unable to download some of the required files.
install.failed.downloading.detail=Unable to download file\: %s
install.failed.downloading.timeout=Download timeout when fetching\: %s
install.failed.downloading=We are unable to download some required files.
install.failed.downloading.detail=Unable to download file: %s
install.failed.downloading.timeout=Download timeout when fetching: %s
install.failed.install_online=Unable to identify the provided file. If you are installing a mod, go to the "Mods" page.
install.failed.malformed=The downloaded files are corrupted. You can try to fix this issue by switching to another download mirror.
install.failed.optifine_conflict=Cannot install both Fabric, OptiFine, and Forge on Minecraft 1.13 or above.
@@ -604,7 +595,7 @@ install.installer.choose=Choose Your %s Version
install.installer.depend=Requires %s
install.installer.fabric=Fabric
install.installer.fabric-api=Fabric API
install.installer.fabric-api.warning=Warning\: Fabric API is a mod, and will be installed into the mod directory of the game instance. Please do not change the working directory of the game, or the Fabric API will not work. If you do want to change these settings, you should reinstall it.
install.installer.fabric-api.warning=Warning: Fabric API is a mod, and will be installed into the mod directory of the game instance. Please do not change the working directory of the game, or the Fabric API will not work. If you do want to change these settings, you should reinstall it.
install.installer.forge=Forge
install.installer.game=Minecraft
install.installer.incompatible=Incompatible with %s
@@ -766,12 +757,12 @@ modpack.origin.mcbbs.prompt=Post ID
modpack.scan=Parsing Modpack Index
modpack.task.install=Install Modpack
modpack.task.install.error=Unable to identify this modpack. We currently only support Curse, Modrinth, MultiMC, and MCBBS modpacks.
modpack.task.install.will=You are going to install modpack\:
modpack.task.install.will=You are going to install modpack:
modpack.type.curse=Curse
modpack.type.curse.tolerable_error=Unable to download dependencies, you can try to continue downloading by launching this game instance.
modpack.type.curse.error=Unable to download dependencies from CurseForge, please try again or use a proxy connection.
modpack.type.curse.not_found=Some dependencies are no longer available, please try to install a newer version of the modpack.
modpack.type.manual.warning=This archive contains a full copy of a Minecraft instance. Most of the time, you just only need to unzip it and launch the game using its built-in launcher. But, HMCL can still import it, with no guarantee of its usability, still continue?
modpack.type.manual.warning=The modpack is manually packaged by the publisher, which may already contain a launcher. It is recommended to try to decompress and run the game with its own launcher. HMCL can still import it, with no guarantee of its usability, still continue?
modpack.type.mcbbs=MCBBS Type
modpack.type.mcbbs.export=Can be imported by Hello Minecraft! Launcher and MultiMC
modpack.type.modrinth=Modrinth
@@ -779,8 +770,8 @@ modpack.type.multimc=MultiMC
modpack.type.multimc.export=Can be imported by Hello Minecraft! Launcher and MultiMC
modpack.type.server=Auto-Update Modpack from Server
modpack.type.server.export=Allows server owner to update the game instance remotely
modpack.type.server.malformed=Invalid modpack manifest, please refer to the server modpack maker to fix this issue.
modpack.unsupported=Unsupported format, we currently only support MCBBS, Modrinth, MultiMC, and CurseForge formats.
modpack.type.server.malformed=Invalid modpack manifest, please refer to the server modpack maker to fix this issue.
modpack.unsupported=Unsupported modpack format
modpack.update=Updating modpack
modpack.wizard=Modpack Export Wizard
modpack.wizard.step.1=Basic Settings
@@ -796,7 +787,7 @@ modpack.wizard.step.initialization.include_launcher=Include the launcher
modpack.wizard.step.initialization.save=Export to...
modpack.wizard.step.initialization.warning=Before creating a modpack, please make sure the game launches, and it is a release version instead of a snapshot version. The launcher will save your download settings.\n\
\n\
Keep in mind that you are not allowed to add mods and resource packs that are explicitly said not to be distributed or put in a modpack.
Keep in mind that you are not allowed to add mods and resource packs that are explicitly said not to be distributed or put in a modpack.
modpack.wizard.step.initialization.server=Click here for more tutorials for making a server modpack that can be automatically updated.
modrinth.category.adventure=Adventure
@@ -873,67 +864,8 @@ mods.url=Official Page
mods.update_modpack_mod.warning=Updating mods in a modpack can lead to irreparable results, possibly corrupting the modpack so that it cannot start. Are you sure you want to update?
multiplayer=Multiplayer online
multiplayer.agreement.prompt=Before using the functionality of multiplayer, you must agree to the user agreement and disclaimer of the multiplayer service provider mcer.cn.\nEspecially, you agree that any problems and disputes you encounter while using the multiplayer functionality (including its paid services) have nothing to do with HMCL and should be resolved through consultation with mcer.cn
multiplayer.download=Downloading dependencies
multiplayer.download.failed=Initialization failed, some files failed to download
multiplayer.download.success=Multiplayer online initialization completed
multiplayer.download.unsupported=Multiplayer dependencies are not supported on current system or platform
multiplayer.error.failed_get_device=HiPer could not create a network device, maybe HiPer has already started or lacks administrator privileges.
multiplayer.error.failed_load_config=HiPer initialization failed, maybe there is a problem with the configuration file or the certificate is invalid.
multiplayer.error.failed_sudo.linux=HiPer requires administrator privileges. You can configure how HiPer applies for privileges by setting the environment variable HMCL_ASKPASS.\nIf HMCL_ASKPASS is set to 'false', please configure sudoers file to allow '%s' to obtain root privileges without password, then restart the computer and start HMCL again;\nIf HMCL_ASKPASS is set to 'user', please set the environment variable SUDO_ASKPASS first, then HMCL will start HiPer with 'sudo --askpass' (see man sudo for details).
multiplayer.error.failed_sudo.mac=HiPer requires administrator privileges. Do you want to grant HiPer administrator rights?\nClick "Yes" to grant HiPer administrator permission, and then restart your computer and open HMCL again.
multiplayer.error.failed_sudo.windows=HiPer requires administrator privileges, please restart HMCL with administrator privileges. Would you like to see the tutorial (Simplified Chinese only)?
multiplayer.error.file_not_found=An update to HiPer has been found. Please re-enter the multiplayer online page to update HiPer.
multiplayer.error.session_expired=Current multiplayer session has been expired. Please fetch a new multiplayer token.
multiplayer.exit=HiPer exited unexpectedly with exit code %d
multiplayer.help.1=管理员权限打开
multiplayer.help.2=多人联机教程
multiplayer.help.3=创建方帮助
multiplayer.help.4=参与者帮助
multiplayer.help.text=测试能否联机
multiplayer.hint=Multiplayer online function is under maintenance.
multiplayer.hint.details=View details
multiplayer.persistence=License Management
multiplayer.persistence.export=Export license to file
multiplayer.persistence.export.button=Export license file
multiplayer.persistence.export.failed=Failed to export the license file, please check the save path you selected.
multiplayer.persistence.export.file_not_exists=Failed to obtain license file, please check whether the token is entered correctly.
multiplayer.persistence.export.title=Select the path to save the license file
multiplayer.persistence.hint=This feature is available for long-term license (valid for more than 24 hours) users, if you are using short-term license (valid for less than 24 hours), you do not need to use this feature.\nWhen you use a long-term license, please use the token to start HiPer within 24 hours after obtaining the license, otherwise HiPer will not be able to obtain the license information.\nAfterwards, you can save the license file through the "Export License File" function below.\nPlease keep the license file in a safe place. After 24 hours of authorization, this file is the only license for your. If you lose this file, you will not be able to play multiplayer games through the token.
multiplayer.persistence.import=Import license from file
multiplayer.persistence.import.button=Import license file
multiplayer.persistence.import.failed=Failed to import license file.
multiplayer.persistence.import.file_already_exists=The license file already exists.
multiplayer.persistence.import.title=Choose license file
multiplayer.persistence.import.token_not_match=The license file you selected may not match the token.\nDo you still want to continue?
multiplayer.persistence.license_file=License file
multiplayer.powered_by=This service is provided under the license of (<a href="https://mcer.cn">Matrix Lab</a>)<a href="https://docs.hmcl.net/multiplayer/#%E4%BD%BF%E7%94%A8%E6%8E%88%E6%9D%83" >License Agreement</a>
multiplayer.report=Illegal and Violation Report
multiplayer.session.name.motd=HMCL Multiplayer Session
multiplayer.token=Token
multiplayer.token.apply=Apply for a token
multiplayer.token.expired=HiPer token expired, please restart HMCL and try again.
multiplayer.token.format_invalid=Invalid token format
multiplayer.token.invalid=Invalid token.
multiplayer.token.malformed=HiPer configuration file could not be parsed, please restart HMCL and try again.
multiplayer.token.prompt=You need a token to use the multiplayer service. Click on the "Application Credentials" next to it to view the details
multiplayer.off.hint=Due to the need to register system network service permissions, administrator rights will be requested when starting HiPer. If prompted for authentication, please allow to start HiPer.
multiplayer.off.start=Start HiPer
multiplayer.master=Prompt for creator
multiplayer.master.hint=Multiplayer online requires one player to start the game first, select the single player mode to enter a save, and select the "Open to LAN" option in the game options menu. After that you can see the port number (usually 5 digits) indicated by the game in the game chat box. Click the Generate Server Address button below, enter the port number, and you can get your server address. This address needs to be provided to other players who need to join the server to add the server.
multiplayer.master.server_address=Generate server address
multiplayer.master.server_address.copy=Copy
multiplayer.master.video_tutorial=Video Tutorial
multiplayer.master.port=Port number
multiplayer.master.port.validate=The port number (0~65535) displayed in the game chat box,when you open the game in LAN.
multiplayer.slave=Participant Prompt
multiplayer.slave.hint=1.Ask the creator to follow the instructions above\n2.Start the game\n3.Choose multiplayer mode and add a server\n4.The game will require you to enter the server address. You only need to ask the creator for the server address and enter it into the server.
multiplayer.slave.hint2=1. In general, the participant's game account must be a Microsoft account or an external login account (such as Little Skin), otherwise you need to enter the server address into the input box below and click "Join", select the multiplayer mode in the game, enter the LAN world before you can join\n2. In general, the participant's game version and module must be the same as the creator, otherwise join fails.
multiplayer.slave.server_address=Creator server address
multiplayer.slave.server_address.start=Join
multiplayer.slave.server_address.stop=Exit
multiplayer.slave.video_tutorial=Video Tutorial
multiplayer.session.expiration=Token Expiration Time
datapack=Datapacks
datapack.add=Install datapack
@@ -945,7 +877,7 @@ world=Worlds
world.add=Add a World (.zip)
world.datapack=Manage Datapacks
world.datapack.1_13=Only Minecraft 1.13 or newer supports datapacks.
world.description=%s. Last played on %s. Game Version\: %s.
world.description=%s. Last played on %s. Game Version: %s.
world.download=Download a World
world.export=Export World
world.export.title=Select a directory for this exported world
@@ -955,7 +887,7 @@ world.extension=World Archive
world.game_version=Game Version
world.import.already_exists=This world already exists.
world.import.choose=Select the save archive you want to import
world.import.failed=Unable to import this world\: %s
world.import.failed=Unable to import this world: %s
world.import.invalid=Unable to parse the save.
world.info.title=World %s - Information
world.info.basic=Basic Information
@@ -1029,20 +961,24 @@ 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\
- $INST_ID\: version id\n\
- $INST_DIR\: absolute path of the version\n\
- $INST_MC_DIR\: absolute path of minecraft\n\
- $INST_JAVA\: java binary used for launch\n\
- $INST_FORGE\: set if Forge installed\n\
- $INST_LITELOADER\: set if LiteLoader installed\n\
- $INST_OPTIFINE\: set if OptiFine installed\n\
- $INST_FABRIC\: set if Fabric installed
- $INST_NAME: version name\n\
- $INST_ID: version id\n\
- $INST_DIR: absolute path of the version\n\
- $INST_MC_DIR: absolute path of minecraft\n\
- $INST_JAVA: java binary used for launch\n\
- $INST_FORGE: set if Forge installed\n\
- $INST_LITELOADER: set if LiteLoader installed\n\
- $INST_OPTIFINE: set if OptiFine installed\n\
- $INST_FABRIC: set if Fabric installed\n\
- $INST_QUILT: set if Quilt installed
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
@@ -1077,11 +1013,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
@@ -1142,6 +1082,7 @@ settings.launcher.proxy.socks=SOCKS
settings.launcher.proxy.username=Username
settings.launcher.theme=Theme
settings.launcher.title_transparent=Transparent titlebar
settings.launcher.turn_off_animations=Turn off animation (applies after restart)
settings.launcher.version_list_source=Version List
settings.memory=Memory
@@ -1182,23 +1123,21 @@ 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
update.failed=Unable to update
update.found=Update Available\!
update.newest_version=Latest version\: %s
update.bubble.title=Update Available\: %s
update.newest_version=Latest version: %s
update.bubble.title=Update Available: %s
update.bubble.subtitle=Click here to update
update.note=Warning\: Beta versions and nightly versions may have more features or fixes, but they also come with more potential issues.
update.note=Warning: Beta versions and nightly versions may have more features or fixes, but they also come with more potential issues.
update.latest=This is the latest version.
update.no_browser=Cannot open in the system browser. But, we copied the link to your clipboard and you can open it manually.
update.tooltip=Update
@@ -1234,6 +1173,7 @@ version.manage.remove=Delete Instance
version.manage.remove.confirm=Are you sure you want to permanently remove the version %s? This action cannot be undone\!
version.manage.remove.confirm.trash=Are you sure you want to remove the version %s? You can still find its files in your recycle bin by the name of %s.
version.manage.remove.confirm.independent=Since this instance is stored in an isolated directory, deleting it will also delete its saves and other data. Do you still want to delete instance %s?
version.manage.remove_assets=Delete All Assets
version.manage.remove_libraries=Delete All Libraries
version.manage.rename=Rename Instance
version.manage.rename.message=Please enter the new name for this instance
@@ -78,20 +78,18 @@ account.injector.server_name=Nombre del servidor
account.login=Acceder
account.login.hint=No almacenaremos su contraseña.
account.login.refresh=Reiniciar sesión
account.login.refresh.microsoft.hint=<b>Because the account authorization is invalid, you need to re-add your Microsoft account</b>\nPor favor, haz clic en el botón "Acceder", y copia el código que se muestra aquí para terminar el proceso de acceso en la ventana abierta del navegador.
account.login.refresh.microsoft.manual=<b>Because the account authorization is invalid, you need to re-add your Microsoft account</b>\nEl código de su dispositivo es <b>%1$s</b>, por favor, haga clic aquí para copiar. Después de hacer clic en el botón "Iniciar sesión", debería terminar el proceso de inicio de sesión en la ventana abierta del navegador. Si no aparece, puede ir a %2$s manualmente.
account.login.refresh.microsoft.hint=Because the account authorization is invalid, you need to re-add your Microsoft account
account.logout=Salir
account.register=Registrarse
account.manage=Lista de cuentas
account.methods=Tipo de inicio de sesión
account.methods.authlib_injector=authlib-injector
account.methods.little_skin=Little Skin
account.methods.microsoft=Cuenta Microsoft
account.methods.microsoft.birth=Cómo actualizar la fecha de nacimiento de tu cuenta
account.methods.microsoft.close_page=La autorización de la cuenta de Microsoft ha finalizado. \n\
\n\
Hay algunos trabajos extra para nosotros, pero puedes cerrar esta pestaña con seguridad por ahora.
account.methods.microsoft.makegameidsettings=Create Profile
account.methods.microsoft.makegameidsettings=Create Profile / Editar el nombre de perfil
account.methods.microsoft.deauthorize=Desautorizar
account.methods.microsoft.error.add_family=Como todavía no tienes 18 años, un adulto debe añadirte a una familia para que puedas jugar a Minecraft.
account.methods.microsoft.error.add_family_probably=Por favor, comprueba si la edad indicada en la configuración de tu cuenta es de al menos 18 años. Si no es así y crees que se trata de un error, puedes hacer clic en el enlace anterior para cambiarlo.
@@ -108,7 +106,6 @@ account.methods.microsoft.manual=El código de su dispositivo es <b>%1$s</b>, po
Si el token utilizado para acceder a la cuenta de Microsoft se ha filtrado, puedes hacer clic en "Desautorizar la cuenta" para desautorizarla.
account.methods.microsoft.profile=Perfil de la cuenta
account.methods.microsoft.snapshot=Estás usando una construcción no oficial de hmcls, por favor descargue la construcción oficial para iniciar sesión en microsoft.
account.methods.microsoft.waiting_browser=Esperando a que se complete el inicio de sesión en la ventana abierta...
account.methods.offline=Sin conexión
account.methods.offline.uuid=UUID
account.methods.offline.uuid.hint=UUID es el identificador único del personaje del juego en Minecraft. La forma en que se genera puede variar entre diferentes launcheres. Cambiarlo por el generado por otros launchers te permite mantener tus objetos en el inventario de tu cuenta offline. \n\
@@ -186,7 +183,7 @@ curse.category.4477=Mini Juego
curse.category.4478=Quests
curse.category.4484=Multijugador
curse.category.4476=Exploración
curse.category.4736=Skyblock
curse.category.6145=Skyblock
curse.category.4475=Aventura y RPG
curse.category.4487=FTB
curse.category.4480=Basado en mapa
@@ -294,12 +291,12 @@ curse.sort.popularity=Popularidad
curse.sort.total_downloads=Descargas totales
download=Descargar
download.code.404=Archivo no encontrado en el servidor remoto\: %s
download.code.404=Archivo no encontrado en el servidor remoto: %s
download.content=Complementos
download.curseforge.customization=Luz y sombras, y personalización del juego
download.existing=El archivo no se puede guardar porque ya existe. Puedes usar 'Guardar como' para guardar el archivo en otro lugar.
download.external_link=Abrir sitio web
download.failed=Falló la descarga de %1$s, código de respuesta\: %2$d
download.failed=Falló la descarga de %1$s, código de respuesta: %2$d
download.failed.empty=No hay versiones disponibles, por favor haga clic aquí para volver.
download.failed.refresh=No se ha podido obtener la lista de versiones. Por favor, haga clic aquí para volver a intentarlo.
download.game=Juego
@@ -313,7 +310,7 @@ download.javafx=Descargar las dependencias para el launcher...
download.javafx.notes=Estamos descargando dependencias para HMCL desde Internet.\n\
\n\
Puede hacer clic en "Cambiar el espejo de descarga" para seleccionar el espejo de descarga o hacer clic en "Cancelar" para detener y salir.\n\
Nota\: Si su velocidad de descarga es demasiado lenta, puede intentar cambiar a otro espejo.
Nota: Si su velocidad de descarga es demasiado lenta, puede intentar cambiar a otro espejo.
download.javafx.component=Descargando módulo %s
download.javafx.prepare=Preparando la descarga
@@ -336,12 +333,6 @@ Por favor, intente cambiar a versiones de Java como Oracle Java 8 que tiene Java
fatal.javafx.missing=No se encontró un entorno JavaFX.\n\
Si está utilizando Java 11 o superior, por favor, descargue Oracle JRE 8 (java.com), o instale BellSoft Liberica Full JRE (bell-sw.com/pages/downloads/?package\=jre-full).\n\
O, si está utilizando distribuciones de OpenJDK, asegúrese de que tiene incluido OpenJFX.
fatal.missing_dst_root_ca_x3=El certificado DST Root CA X3 falta en la distribución Java actual.\n\
\n\
¡Usted todavía puede utilizar Hello Minecraft! Launcher, pero no podrá conectarse a algunos sitios web (como los sitios que utilizan certificados emitidos por Let's Encrypt), lo que puede hacer que el launcher no funcione correctamente.\n\
\n\
Por favor, actualice su versión de Java al menos a la 8u101 para solucionar este problema.
# '
fatal.config_loading_failure=No se pueden cargar los archivos de configuración.
¡Por favor, asegúrese de que "Hello Minecraft! Launcher" tiene acceso de lectura y escritura a "%s" y a los archivos que contiene.
fatal.migration_requires_manual_reboot=Hello Minecraft! Launcher ha sido actualizado. Por favor, reinicie el launcher.
@@ -378,7 +369,7 @@ game.crash.reason.block=El juego se ha bloqueado debido a un error.\
Puede tratar de eliminar este bloque utilizando MCEdit o eliminar el mod que lo ha añadido.\n\
\n\
Tipo de bloque: %1$s\n\
Ubicación\: %2$s
Ubicación: %2$s
game.crash.reason.bootstrap_failed=El juego se ha bloqueado debido al mod %1$s.\n\
\n\
Puedes intentar borrarlo o actualizarlo.
@@ -396,7 +387,7 @@ game.crash.reason.entity=El juego se ha bloqueado debido a una entidad.\n\
Puede intentar eliminar esta entidad utilizando MCEdit o eliminar el mod que la ha añadido.\n\
\n\
Tipo de bloque: %1$s\n\
Ubicación\: %2$s
Ubicación: %2$s
game.crash.reason.fabric_version_0_12=La versión de Fabric 0.12 o superior es incompatible con los mods instalados actualmente. Necesitas bajarlo a la versión 0.11.7.
game.crash.reason.fabric_warnings=El cargador de mods de Fabric ha advertido:\n\
%1$s
@@ -420,7 +411,7 @@ Si utiliza su tarjeta gráfica integrada, tenga en cuenta que Minecraft 1.16.5 o
Puedes ir a <a href="https://hmcl.huangyuhui.net/help/game/graphics-card">este sitio web (en chino)</a> para más información.
game.crash.reason.illegal_access_error=El juego se bloquea por culpa de algún(os) mod(s).\n\
\n\
Si sabes\: %1$s, puedes actualizar o eliminar los mods y volver a intentarlo.
Si sabes: %1$s, puedes actualizar o eliminar los mods y volver a intentarlo.
game.crash.reason.jdk_9=El juego no puede ejecutarse porque la versión de Java es demasiado nueva para esta instancia.\n\
\n\
Es necesario descargar e instalar Java 8 y seleccionarlo en la configuración de la instancia.
@@ -465,7 +456,7 @@ game.crash.reason.mod_resolution_missing_minecraft=El juego se ha bloqueado porq
\n\
Si quieres jugar con esta versión del mod instalada, debes cambiar la versión de la instancia.\n\
En caso contrario, debes instalar una versión compatible con esta versión de Minecraft.
game.crash.reason.mod_resolution_mod_version=%1$s (Versión\: %2$s)
game.crash.reason.mod_resolution_mod_version=%1$s (Versión: %2$s)
game.crash.reason.mod_resolution_mod_version.any=%1$s (Cualquier versión)
game.crash.reason.forge_repeat_installation=El juego actual no puede continuar ejecutándose debido a una instalación duplicada de Forge. <a href="https://github.com/huanghongxun/HMCL/issues/1880">Este es un problema conocido</a>\nSe recomienda cargar los comentarios del registro en Github para que podamos encontrar más pistas y arreglar esta pregunta. \nActualmente puede ir a la instalación automática para desinstalar Forge y volver a instalarlo.
game.crash.reason.optifine_repeat_installation=El juego actual no puede seguir ejecutándose debido a la instalación repetida de Optifine. \nElimine Optifine de la carpeta Mod o vaya a Gestión de juegos-Instalación automática para desinstalar Optifine que se instala automáticamente.
@@ -477,6 +468,8 @@ game.crash.reason.mod_solution0=El juego actual no puede seguir ejecutándose de
game.crash.reason.mod_profile_causes_game_crash=El juego actual no puede seguir ejecutándose debido a un problema con el perfil de mod. \nPuede consultar el registro del mod defectuoso y su archivo de configuración.
game.crash.reason.fabric_reports_an_error_and_gives_a_solution=Forge puede haber dado un mensaje de error. \nPuede ver el registro y realizar las acciones correspondientes de acuerdo con la información de registro en el informe de errores. \nSi no ve un mensaje de error, puede consultar el informe de errores para saber cómo ocurrió el error.
game.crash.reason.java_version_is_too_high=El juego actual se bloqueó porque la versión de Java es demasiado alta y no puede seguir ejecutándose. \nCambie a una versión anterior de Java en la pestaña Java Path de Configuración global del juego o Configuración específica del juego antes de iniciar el juego. \nSi no es así, puede descargarlo desde <a href="https://www.java.com/download/">java.com (Java8)</a> o <a href="https://bell-sw.com/pages/downloads">BellSoft Liberica Full JRE (Java17)</a> y otras plataformas para descargar e instalar una (reinicie el iniciador después de la instalación).
game.crash.reason.mod_name=El juego actual no puede continuar ejecutándose debido a un problema con el nombre del archivo mod. El nombre del archivo de \nMod solo debe usar mayúsculas y minúsculas (Aa ~ Zz), números (0 ~ 9), líneas horizontales (-), subrayado (_) y puntos (.) en toda la mitad del inglés. \n Por favor, vaya a la carpeta mod para agregar un carácter de Cumplimiento anterior a todos los nombres de archivo mod que no cumplan.
game.crash.reason.incomplete_forge_installation=O jogo atual não pode continuar devido a uma instalação incompleta do Forge. \nDesinstale e reinstale o Forge em Configurações de versão - Instalação automática.
game.crash.reason.modlauncher_8=El juego se ha bloqueado porque tu versión actual de Forge no es compatible con tu instalación de Java. Por favor, intente actualizar Forge, o intente utilizar Java 8u312/11.0.13/17.0.1 o versiones anteriores.\n\
\n\
8u312 y anteriores\:\n\
@@ -524,7 +517,7 @@ game.crash.reason.unknown=No somos capaces de averiguar por qué el juego se est
<b>Cuando pidas ayuda a otra persona, por favor, comparte con ella el registro completo del juego y el archivo de informe de fallos relacionado.</b>\n\
\n\
¿Esta caída es causada por un problema común que casi todo el mundo tiene? Puedes proporcionar tus registros en un nuevo tema de GitHub para mejorar el launcher.
game.crash.reason.unsatisfied_link_error=No se puede iniciar Minecraft porque faltan bibliotecas\: %1$s.\n\
game.crash.reason.unsatisfied_link_error=No se puede iniciar Minecraft porque faltan bibliotecas: %1$s.\n\
\n\
Si ha modificado la configuración de la biblioteca nativa, por favor, asegúrese de que estas bibliotecas existen. O, por favor, intente iniciar de nuevo después de revertir a los valores predeterminados.\n\
Si no lo ha hecho, por favor, compruebe si le faltan mods de dependencia.\n\
@@ -547,7 +540,7 @@ install.change_version=Cambiar versión
install.change_version.confirm=¿Está seguro de querer cambiar %s de la versión %s a %s?
install.failed=Fallo en la instalación
install.failed.downloading=No podemos descargar algunos de los archivos necesarios.
install.failed.downloading.detail=No se ha podido descargar el archivo\: %s
install.failed.downloading.detail=No se ha podido descargar el archivo: %s
install.failed.downloading.timeout=Tiempo de descarga agotado en la búsqueda: %s
install.failed.install_online=No se ha podido identificar el archivo proporcionado. Si está instalando un mod, vaya a la página "Mods".
install.failed.malformed=Los archivos descargados están dañados. Puedes intentar solucionar este problema cambiando a otra espejo de descarga.
@@ -559,7 +552,7 @@ install.installer.choose=Elija su versión %s
install.installer.depend=Requiere %s
install.installer.fabric=Fabric
install.installer.fabric-api=Fabric API
install.installer.fabric-api.warning=Atención\: Fabric API es un mod, y se instalará en el directorio de mods de la instancia del juego. Por favor, no cambies el directorio de trabajo del juego, o el Fabric API no funcionará. Si quieres cambiar esta configuración, debes reinstalarlo.
install.installer.fabric-api.warning=Atención: Fabric API es un mod, y se instalará en el directorio de mods de la instancia del juego. Por favor, no cambies el directorio de trabajo del juego, o el Fabric API no funcionará. Si quieres cambiar esta configuración, debes reinstalarlo.
install.installer.forge=Forge
install.installer.game=Minecraft
install.installer.incompatible=Incompatible con %s
@@ -721,7 +714,7 @@ modpack.origin.mcbbs.prompt=ID de publicación
modpack.scan=Análisis del índice de modpacks
modpack.task.install=Instalar Modpack
modpack.task.install.error=No se ha podido identificar este modpack. Actualmente sólo soportamos los modpacks Curse, Modrinth, MultiMC y MCBBS.
modpack.task.install.will=Vas a instalar el modpack\:
modpack.task.install.will=Vas a instalar el modpack:
modpack.type.curse=Curse
modpack.type.curse.tolerable_error=No se han podido descargar las dependencias, puedes intentar continuar la descarga ejecutando esta instancia del juego.
modpack.type.curse.error=No se han podido descargar las dependencias de CurseForge, inténtalo de nuevo o utiliza una conexión proxy.
@@ -734,8 +727,8 @@ modpack.type.multimc=MultiMC
¡modpack.type.multimc.export=Puede ser importado por Hello Minecraft! Launcher y MultiMC
modpack.type.server=Actualización automática del modpack desde el servidor
modpack.type.server.export=Permite al propietario del servidor actualizar la instancia del juego de forma remota
modpack.type.server.malformed=Manifiesto de modpack inválido, por favor, consulte al creador del modpack del servidor para solucionar este problema.
modpack.unsupported=Formato no soportado, actualmente sólo soportamos los formatos MCBBS, Modrinth, MultiMC y CurseForge.
modpack.type.server.malformed=Manifiesto de modpack inválido, por favor, consulte al creador del modpack del servidor para solucionar este problema.
modpack.unsupported=Hello Minecraft! Launcher no admite el formato del paquete de integración
modpack.update=Actualización del modpack
modpack.wizard=Asistente de exportación de modpack
modpack.wizard.step.1=Configuración básica
@@ -750,7 +743,7 @@ modpack.wizard.step.initialization.include_launcher=Incluir el launcher
modpack.wizard.step.initialization.save=Exportar a...
modpack.wizard.step.initialization.warning=Antes de crear un modpack, por favor asegúrate de que el juego se ejecuta, y que es una versión de lanzamiento en lugar de una versión instantánea. El launcher guardará su configuración de descarga.
\n\
Ten en cuenta que no se te permite añadir mods y resource packs que se digan explícitamente que no se pueden distribuir o poner en un modpack.
Ten en cuenta que no se te permite añadir mods y resource packs que se digan explícitamente que no se pueden distribuir o poner en un modpack.
modpack.wizard.step.initialization.server=Haga clic aquí para ver más tutoriales para hacer un modpack de servidor que se pueda actualizar automáticamente.
modrinth.category.adventure=Aventura
@@ -811,96 +804,6 @@ mods.url=Página oficial
mods.update_modpack_mod.warning=Actualizar mods en un modpack puede generar resultados irreparables, posiblemente corrompiendo el modpack para que no pueda iniciarse. ¿Seguro que quieres actualizar?
multiplayer=Multijugador
multiplayer.agreement.prompt=Antes de utilizar el servicio multijugador, debe aceptar las condiciones de servicio del proveedor del servicio, ioi, en primer lugar.
Al utilizar este servicio, usted acepta estas condiciones de servicio.
multiplayer.download=Descargando dependencias
multiplayer.download.failed=Fallo en la inicialización de la función multijugador, algunos archivos no se pueden descargar.
multiplayer.download.success=Inicialización de dependencias completada
multiplayer.download.unsupported=El sistema operativo o la arquitectura actuales no son compatibles.
multiplayer.exit.after_ready=La sesión multijugador ha terminado de forma anormal, cato exitcode %d
multiplayer.exit.before_ready=No se ha podido crear una sesión multijugador, cato exitcode %d
multiplayer.exit.timeout.static_token=Fallo en la conexión con el proveedor de multijugador. Es posible que tu token no funcione, puedes intentar borrarlo y volver a intentarlo.\n\
O bien, puedes esperar unos minutos. Si la función sigue sin funcionar, consulta a los administradores para obtener más ayuda.
multiplayer.exit.timeout.dynamic_token=No se ha podido conectar al servicio multijugador, los servidores de autenticación pueden tener demasiadas peticiones en ese momento. Por favor, espere hasta el próximo medio tiempo para permitir que los servidores despejen algunas sesiones.\n\
\n\
También puedes comprar algunos tokens multijugador para conseguir una experiencia multijugador estable.
multiplayer.nat=Detección de red
multiplayer.nat.failed=No se ha podido detectar el tipo de red.
multiplayer.nat.hint=La detección del tipo de red comprobará si su entorno de red es compatible con la funcionalidad multijugador.\n\
\n\
Si el resultado es 'Malo' o 'Fallido', es posible que no pueda utilizar esta función.\n\
multiplayer.nat.latency=Latencia
multiplayer.nat.not_yet_tested=No probado aún
multiplayer.nat.packet_loss_ratio=Tasa de pérdida de paquetes
multiplayer.nat.testing=Prueba
multiplayer.nat.type=Tipo de NAT
multiplayer.nat.type.blocked_udp=Muy malo (UDP está bloqueado)
multiplayer.nat.type.full_cone=Mejor (Cono completo)
multiplayer.nat.type.open_access=Mejor (Acceso a la red pública)
multiplayer.nat.type.port_restricted_cone=Bueno (Cono de puerto restringido)
multiplayer.nat.type.restricted_cone=Bueno (Cono Restringido)
multiplayer.nat.type.symmetric=Malo (Simétrico)
multiplayer.nat.type.symmetric_udp_firewall=Malo (Simétrico con cortafuegos UDP)
multiplayer.nat.type.unknown=Desconocido
multiplayer.powered_by=El servicio multijugador es proporcionado por <a href="https://mcer.cn">mcer.cn</a>. <a href="https://hmcl.huangyuhui.net/api/redirect/multiplayer-agreement">Licence</a>
multiplayer.relay=Modo de retransmisión
multiplayer.relay.hint=El modo de retransmisión proporciona una experiencia multijugador sin problemas para los usuarios con una mala conexión a Internet.\n\
\n\
Puedes activar esta opción después de conseguir algunos tokens en mcer.cn.
multiplayer.report=Reportar
multiplayer.session=Sesión
multiplayer.session.name.format=%1$s's Sesión
# '
multiplayer.session.name.motd=Sesión Multijugador HMCL - %s
multiplayer.session.close=Terminar sesión
multiplayer.session.close.warning=Todos los demás jugadores de esta sesión serán desconectados. ¿Continuar?
multiplayer.session.copy_room_code=Copiar código de invitación
multiplayer.session.create=Crear sesión
multiplayer.session.create.error.dynamic_token=No se ha podido crear una sesión multijugador. Por favor, inténtelo de nuevo más tarde.
multiplayer.session.create.error.static_token=No se ha podido crear una sesión multijugador. Puedes borrar tu token y volver a intentarlo más tarde.
multiplayer.session.create.hint=Antes de crear una sesión multijugador, debes hacer clic en "Abrir a LAN" primero en el menú de pausa del juego. A continuación, introduce el número de puerto que aparece a continuación.
multiplayer.session.create.join=Solicitud de unión
multiplayer.session.create.join.allow=Acepta automáticamente todas las solicitudes de conexión
multiplayer.session.create.join.prompt=El jugador %s solicita unirse a la sesión multijugador. ¿Desea aceptarla?
multiplayer.session.create.members=Jugadores
multiplayer.session.create.members.kick=Ban
multiplayer.session.create.members.kick.prompt=¿Quieres prohibir a este jugador?
multiplayer.session.create.name=Nombre
multiplayer.session.create.port=Puerto
multiplayer.session.create.port.error=No se ha podido detectar el puerto del juego, debes hacer clic en "Abrir en LAN" en el menú de pausa del juego para permitir que otros se unan a tu mundo.
multiplayer.session.create.token=Token
multiplayer.session.create.token.apply=Solicitar un token estático
multiplayer.session.create.token.prompt=Token aleatorio por defecto, puedes hacer clic en 'Aplicar para un token estático' para más información.
multiplayer.session.error.already_started=El servicio cato ya se está ejecutando, por favor compruebe si hay otras instancias HMCL en ejecución, o finalice el proceso cato antes de continuar.
multiplayer.session.error.file_not_found=No se ha podido encontrar el ejecutable de cato. Este programa ya debería estar descargado después de entrar en esta página, por favor reabre el launcher e inténtalo de nuevo.
multiplayer.session.expired=La sesión multijugador ha superado la duración máxima de 3 horas. Por favor, vuelva a crear o a unirse a una sesión.
multiplayer.session.join=Unirse
multiplayer.session.join.error=No se ha podido unir a la sesión multijugador. Puedes intentar utilizar el modo de retransmisión en su lugar.
multiplayer.session.join.error.connection=No es posible unirse a la sesión multijugador. Asegúrate de que estás utilizando la misma versión de HMCL que el anfitrión de la sesión.
multiplayer.session.join.hint=Debes obtener primero el código de la sesión del anfitrión para unirte a ella.
multiplayer.session.join.invitation_code=Código de invitación
multiplayer.session.join.invitation_code.error=Código de invitación no válido. Obtén uno del anfitrión de la sesión multijugador y asegúrate de que estás ejecutando la misma versión de HMCL.
multiplayer.session.join.kicked=Perdió la conexión con la sesión: %s.
multiplayer.session.join.kicked.join_acceptance_timeout=El host no aceptó tu solicitud a tiempo.
multiplayer.session.join.kicked.kicked=El anfitrión te ha expulsado.
multiplayer.session.join.kicked.version_not_matched=Se han detectado versiones inconsistentes de las bibliotecas multijugador, por favor, asegúrate de que estás utilizando las mismas que el anfitrión de la sesión.
multiplayer.session.join.lost_connection=Conexión perdida con la sesión multijugador. Puede que el host la haya finalizado, o que el servidor la haya cerrado por exceder el límite de tiempo, o que haya algún problema con tu conexión a Internet.
multiplayer.session.join.port.error=No se ha podido vincular un puerto de red local disponible para la escucha. Asegúrate de que HMCL tiene privilegios para escuchar en un puerto.
multiplayer.session.join.rejected=El host ha rechazado tu solicitud de conexión.
multiplayer.session.join.wait=Esperando a que el host acepte...
multiplayer.session.members=Miembros
multiplayer.session.quit=Abandonar
multiplayer.session.quit.warning=Se desconectará del servidor si abandona, ¿continúa?
multiplayer.session.username=Nombre de usuario
multiplayer.state.connecting=Conectando
multiplayer.state.disconnected=No está en una sesión
multiplayer.state.disconnected.hint=El anfitrión debe crear una sesión primero antes de que otros puedan unirse a su mundo.
multiplayer.state.master=Sesión %1$s creada, puerto\: %2$d
multiplayer.state.master.hint=Después de crear una sesión, puedes compartir tu código de invitación con tus compañeros. Ahora pueden unirse a tu sesión tras introducir el código en la página multijugador de HMCL.
multiplayer.state.slave=Sesión iniciada %1$s, dirección\: %2$s
multiplayer.state.slave.copy=Copiar dirección
multiplayer.state.slave.hint=Después de unirte a una sesión multijugador, puedes unirte al multijugador entrando en el servidor "Sesión Multijugador HMCL" que se muestra en la lista de servidores multijugador de Minecraft o conectarte manualmente a la siguiente dirección.
datapack=Datapacks
datapack.add=Instalar datapacks
@@ -912,7 +815,7 @@ world=Mundos
world.add=Añadir un mundo (.zip)
world.datapack=Gestionar datapacks
world.datapack.1_13=Sólo Minecraft 1.13 o superior soporta datapacks.
world.description=%s. Jugado por última vez en %s. Versión del juego\: %s.
world.description=%s. Jugado por última vez en %s. Versión del juego: %s.
world.download=Descargar un mundo
world.export=Exportar un mundo
world.export.title=Selecciona un directorio para este mundo exportado
@@ -922,7 +825,7 @@ world.extension=Archivo del mundo
world.game_version=Versión del juego
world.import.already_exists=Este mundo ya existe.
world.import.choose=Selecciona el archivo de guardado que quieres importar
world.import.failed=No se ha podido importar este mundo\: %s
world.import.failed=No se ha podido importar este mundo: %s
world.import.invalid=No se ha podido analizar el archivo guardado.
world.manage=Mundos / Datapacks
world.name=Nombre del mundo
@@ -968,17 +871,19 @@ 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\
- $INST_ID\: id de la versión\n\
- $INST_DIR\: ruta absoluta de la versión\n\
- $INST_MC_DIR\: ruta absoluta de minecraft\n\
- $INST_JAVA\: binario de java utilizado para la ejecución\n\
- $INST_FORGE\: se establece si Forge está instalado\n\
- $INST_NAME: nombre de la versión\n\
- $INST_ID: id de la versión\n\
- $INST_DIR: ruta absoluta de la versión\n\
- $INST_MC_DIR: ruta absoluta de minecraft\n\
- $INST_JAVA: binario de java utilizado para la ejecución\n\
- $INST_FORGE: se establece si Forge está instalado\n\
- $INST_LITELOADER: se establece si LiteLoader está instalado\n\
- $INST_OPTIFINE: establecer si se ha instalado OptiFine\n\
- $INST_FABRIC\: establecer si está instalado Fabric
- $INST_FABRIC: establecer si está instalado Fabric\n\
- $INST_QUILT: establecer si está instalado Quilt
settings.advanced.dont_check_game_completeness=No chequear integridad del juego
settings.advanced.dont_check_jvm_validity=No comprobar la compatibilidad de la JVM
settings.advanced.game_dir.default=Por defecto (.minecraft/)
@@ -1114,23 +1019,21 @@ 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
update.failed=No se puede actualizar
update.found=¡Actualización disponible\!
update.newest_version=Última versión\: %s
update.bubble.title=Actualización disponible\: %s
update.newest_version=Última versión: %s
update.bubble.title=Actualización disponible: %s
update.bubble.subtitle=Haga clic aquí para actualizar
update.note=Advertencia\: Las versiones beta y las versiones nocturnas pueden tener más funciones o correcciones, pero también tienen más problemas potenciales.
update.note=Advertencia: Las versiones beta y las versiones nocturnas pueden tener más funciones o correcciones, pero también tienen más problemas potenciales.
update.latest=Esta es la última versión.
update.no_browser=No se puede abrir en el navegador del sistema. Pero, hemos copiado el enlace a su portapapeles y puede abrirlo manualmente.
update.tooltip=Actualización
@@ -1166,6 +1069,7 @@ version.manage.remove=Borrar instancia
version.manage.remove.confirm=¿Está seguro de que quiere eliminar permanentemente la versión %s? ¡Esta acción no se puede deshacer\!
version.manage.remove.confirm.trash=¿Estás seguro de que quieres eliminar la versión %s? Todavía puedes encontrar sus archivos en tu papelera de reciclaje con el nombre de %s.
version.manage.remove.confirm.independent=Dado que esta instancia está almacenada en un directorio aislado, al eliminarla también se eliminarán sus guardados y otros datos. ¿Aún quieres borrar la instancia %s?
version.manage.remove_assets=Borrar todas las activos del juego
version.manage.remove_libraries=Borrar todas las bibliotecas
version.manage.rename=Renombrar instancia
version.manage.rename.message=Por favor, introduzca el nuevo nombre de esta instancia
@@ -76,14 +76,12 @@ account.injector.server_name=サーバー名
account.login=ログイン
account.login.hint=パスワードは保存されません。
account.login.refresh=再ログイン
account.login.refresh.microsoft.hint=<b>アカウント認証が無効なため、Microsoft アカウントを再度追加する必要があります</b>\n「OK」ボタンをクリックして、新しく開いたブラウザウィンドウでログインプロセスを続行する必要があります。
account.login.refresh.microsoft.manual=<b>アカウント認証が無効なため、Microsoft アカウントを再度追加する必要があります</b>\n「OK」ボタンをクリックした後、新しく開いたブラウザウィンドウで認証を完了する必要があります。ブラウザウィンドウが表示されない場合は、ここをクリックしてURLをコピーし、ブラウザで手動で開くことができます。
account.login.refresh.microsoft.hint=アカウント認証が無効なため、Microsoft アカウントを再度追加する必要があります
account.logout=ログアウト
account.register=登録
account.manage=アカウントリスト
account.methods=ログインタイプ
account.methods.authlib_injector=authlib-インジェクター
account.methods.little_skin=Little Skin
account.methods.microsoft=Microsoft Account
account.methods.microsoft.birth=誕生日の設定を編集する方法...
account.methods.microsoft.deauthorize=アカウントのバインドを解除
@@ -96,12 +94,11 @@ account.methods.microsoft.error.no_character=アカウントにMinecraftJavaプ
account.methods.microsoft.error.unknown=ログインに失敗しました。Microsoftはエラーコード %d で応答します。
account.methods.microsoft.logging_in=ログイン...
account.methods.forgot_password=パスワードをお忘れの方
account.methods.microsoft.makegameidsettings=プロファイルを作成
account.methods.microsoft.makegameidsettings=プロファイルを作成/プロフィール名を編集する
account.methods.microsoft.hint=「ログイン」ボタンをクリックして、新しく開いたブラウザウィンドウでログインプロセスを続行する必要があります。\nMicrosoftアカウントへのログインに使用されたトークンが誤って漏洩した場合は、下の[アカウントのバインドを解除]をクリックして、ログイン認証をキャンセルできます。
account.methods.microsoft.manual=「ログイン」ボタンをクリックした後、新しく開いたブラウザウィンドウで認証を完了する必要があります。ブラウザウィンドウが表示されない場合は、ここをクリックしてURLをコピーし、ブラウザで手動で開くことができます。\nMicrosoftアカウントへのログインに使用されたトークンが誤って漏洩した場合は、下の[アカウントのバインドを解除]をクリックして、ログイン認証をキャンセルできます。
account.methods.microsoft.profile=アカウントプロファイル..
account.methods.microsoft.snapshot=非公式構築 HMCL を使用しているので、公式構築をダウンロードしてマイクロソフトにログインしてください。
account.methods.microsoft.waiting_browser=開いているブラウザウィンドウで認証を待機しています...
account.methods.offline=オフライン
account.methods.offline.uuid=UUID
account.methods.offline.uuid.hint=UUIDは、Minecraftのゲームキャラクターの一意の識別子です。 UUIDの生成方法は、ゲームランチャーによって異なります。 UUIDを他のランチャーによって生成されたものに変更すると、オフラインアカウントのバックパック内のゲームブロック/アイテムが残ることが約束されます。このオプションは専門家向けです。何をしているのかわからない限り、このオプションを変更することはお勧めしません。
@@ -173,7 +170,7 @@ curse.category.4477=ミニゲーム
curse.category.4478=クエスト
curse.category.4484=マルチプレイヤー
curse.category.4476=探索
curse.category.4736=Skyblock
curse.category.6145=Skyblock
curse.category.4475=アドベンチャーとRPG
curse.category.4487=FTB
curse.category.4480=マップベース
@@ -310,7 +307,6 @@ extension.sh=Bashスクリプト
fatal.javafx.incompatible=アプリケーションは11未満の現在のJava環境でJavaFXにパッチを適用できません。\nこのアプリは、JDK11以降またはJavaFXがバンドルされたJDKを使用して実行してください。
fatal.javafx.missing=JavaFXがありません。\nJava11以降を使用している場合は、Oracle JRE 8にダウングレードするか、BellSoft Liberica FullJREをインストールしてください。\n他のOpenJDKビルドを使用している場合は、OpenJFXが含まれていることを確認してください。
fatal.missing_dst_root_ca_x3=現在のJavaプラットフォームにDSTルートCAX3証明書がありません。\n引き続きHelloMinecraftを使用できます。ランチャーですが、一部のサイト(Let us Encryptによって発行された証明書を使用するサイトなど)に接続できず、ランチャーが正しく機能しない可能性があります。\n問題を解決するには、Javaランタイムを8u101以降に更新してください。
fatal.config_loading_failure=構成にアクセスできません。\nHelloMinecraftを確認してください。 Launcherには、「%s」とその中のファイルへの読み取りおよび書き込みアクセス権があります。
fatal.migration_requires_manual_reboot=更新が完了しました。 Hello Minecraftを再開してください!ランチャー。
fatal.apply_update_failure=ごめんなさい、Hello Minecraft! Launcher 何か問題が発生したため、ランチャーは更新を完了できませんでした。\nただし、Hello Minecraftをダウンロードすることで、手動で更新を終了できます。 %s からのランチャー。\nこの問題を報告することを検討してください。
@@ -353,6 +349,8 @@ game.crash.reason.mod_resolution0==mod の問題により、現在のゲーム
game.crash.reason.mod_profile_causes_game_crash==mod プロファイルに問題があるため、現在のゲームを続行できません。 \n障害のある mod とその構成ファイルのログを確認できます。
game.crash.reason.fabric_reports_an_error_and_gives_a_solution==Forge がエラー メッセージを表示した可能性があります。 \nログを表示し、エラー レポートのログ情報に従って対応するアクションを実行できます。 \nエラー メッセージが表示されない場合は、エラー レポートをチェックして、エラーがどのように発生したかを知ることができます。
game.crash.reason.java_version_is_too_high=Java のバージョンが高すぎて実行を継続できないため、現在のゲームがクラッシュしました。 \nゲームを起動する前に、グローバル ゲーム設定またはゲーム固有の設定の Java パス タブで Java の以前のバージョンに切り替えてください。 \nそうでない場合は、<a href="https://www.java.com/download/">java.com (Java8)</a> または <a href="https://bell-sw.com/pages/downloads">BellSoft Liberica Full JRE (Java17)</a> およびその他のプラットフォームをダウンロードしてインストールします (インストール後にランチャーを再起動します)。
game.crash.reason.mod_name=現在のゲームはModファイル名の問題で続行できません。\nModファイル名は、英文の全半角の大文字と小文字(Aa ~ Zz)、数字(0 ~ 9)、横線(-)、アンダースコア(_)、点(.)のみを使用してください。\n上記のコンプライアンス文字をModフォルダに追加してください。
game.crash.reason.incomplete_forge_installation=Forge のインストールが不完全なため、現在のゲームを続行できません。 \nバージョン設定 - 自動インストールで Forge をアンインストールしてから再インストールしてください。
game.crash.reason.file_already_exists=ファイル %1$s が既に存在するので、現在のゲームは続行できません。 \このファイルを削除してもよいと思われる場合は、このファイルをバックアップして、ゲームを再起動してから削除してみてください。
game.crash.reason.file_changed=ファイルのチェックサムに失敗したため、現在のゲームを続行できません。 \nMinecraft.jar ファイルを手動で変更した場合、変更をロールバックするか、ゲームを再度ダウンロードする必要があります。
game.crash.reason.gl_operation_failure=一部のモッド、シェーダーパック、テクスチャパックが原因でゲームがクラッシュしました。\n使用しているモッド/シェーダーパック/テクスチャパックを無効にして、再試行してください。
@@ -581,7 +579,7 @@ modpack.type.multimc.export=Hello Minecraftでインポートできます!ラ
modpack.type.server=サーバー自動更新Modpack
modpack.type.server.export=サーバーマネージャーがゲームクライアントをリモートで更新できるようにする
modpack.type.server.malformed=不正なサーバーのmodpackマニフェスト
modpack.unsupported=サポートされていないmodpack、Curse, Modrinth, MultiMC, MCBBSおよびMultiMCのみがサポートされています。
modpack.unsupported=Hello Minecraft! Launcher はこのmodpackフォーマットをサポートしていません
modpack.update=ゲームアップデート
modpack.wizard=modpackウィザードのエクスポート
modpack.wizard.step.1=基本オプション
@@ -644,86 +642,6 @@ mods.url=公式ページ
mods.update_modpack_mod.warning=modpack 内の mod を更新すると、修復不可能な結果につながる可能性があり、modpack が破損して起動できなくなる可能性があります。 更新してもよろしいですか?
multiplayer=マルチプレイ
multiplayer.agreement.prompt=マルチプレイヤーサービスの使用を開始する前に、まずEULAに同意する必要があります。
multiplayer.download=マルチプレイヤーの依存関係をダウンロードする
multiplayer.download.failed=マルチプレイヤーの初期化に失敗しました。一部のファイルはダウンロードできません
multiplayer.download.success=依存関係の初期化に成功しました
multiplayer.download.unsupported=現在のオペレーティングシステムまたはアーキテクチャはサポートされていません。
multiplayer.exit.after_ready=マルチプレイヤーセッションが壊れています。 cato exitcode %d
multiplayer.exit.before_ready=マルチプレイヤーセッションの作成に失敗しました。 cato exitcode %d
multiplayer.exit.timeout=マルチプレイヤーサーバーへの接続に失敗しました。
multiplayer.exit.timeout.dynamic_token=マルチプレイヤーサーバーへの接続に失敗しました。
multiplayer.nat=ネットワークタイプの検出
multiplayer.nat.failed=ネットワークタイプの検出に失敗しました。
multiplayer.nat.hint=ネットワークタイプの検出により、ネットワークがマルチプレイヤーモードの要件を満たしているかどうかが明確になります。
multiplayer.nat.latency=レイテンシー
multiplayer.nat.not_yet_tested=まだテストされていません
multiplayer.nat.packet_loss_ratio=パケット損失率
multiplayer.nat.testing=テスト
multiplayer.nat.type=NATタイプ
multiplayer.nat.type.blocked_udp=非常に悪い(ブロックされたUDP)
multiplayer.nat.type.full_cone=Good(フルコーン)
multiplayer.nat.type.open_access=Good(オープンアクセス)
multiplayer.nat.type.port_restricted_cone=Medium(ポート制限付きコーン)
multiplayer.nat.type.restricted_cone=Medium(制限付きコーン)
multiplayer.nat.type.symmetric=Bad(対称)
multiplayer.nat.type.symmetric_udp_firewall=Bad(UDPファイアウォールと対称)
multiplayer.nat.type.unknown=不明`
multiplayer.powered_by=マルチプレイヤーサービスは<a href="https://mcer.cn">mcer.cn</a>によって提供されます。 <a href\="https://docs.hmcl.net/multiplayer/#%E4%BD%BF%E7%94%A8%E6%8E%88%E6%9D%83">Licence/a>
multiplayer.report=レポート
multiplayer.relay=リレーモード
multiplayer.relay.hint=静的トークンを持ち、テストされたNATタイプが不良対称であるユーザーの場合
multiplayer.session=セッション
multiplayer.session.name.format=%1$s の部屋
multiplayer.session.name.motd=HMCLマルチプレイヤーセッション - %s
multiplayer.session.close=部屋を閉じる
multiplayer.session.close.warning=部屋を閉じた後、部屋に参加したすべてのプレイヤーは接続を失います。継続する?
multiplayer.session.copy_room_code=招待コードをコピーする
multiplayer.session.create=セッションの作成
multiplayer.session.create.error.dynamic_token=マルチプレイヤーセッションの作成に失敗しました。後でもう一度やり直してください。
multiplayer.session.create.error.static_token=マルチプレイヤーセッションの作成に失敗しました。動的トークンで再試行できます。
multiplayer.session.create.hint=マルチプレイヤーセッションを作成する前に、実行中のゲームで[LANサーバーを開く]をクリックし、ゲームに表示されているポートを下の空白に入力する必要があります。
multiplayer.session.create.join=接続リクエスト
multiplayer.session.create.join.allow=すべての参加リクエストを受け入れます(無効にした場合、攻撃を防ぐために、参加リクエストを手動で受け入れる必要があります)。
multiplayer.session.create.join.prompt=Player %s がマルチプレイヤーセッションに参加したいと考えています。承認?
multiplayer.session.create.members=メンバー
multiplayer.session.create.members.kick=キック
multiplayer.session.create.members.kick.prompt=このプレーヤーをキックした後、このプレーヤーとの確立された接続は切断されます。
multiplayer.session.create.name=セッション名
multiplayer.session.create.port=ポート
multiplayer.session.create.port.error=ゲームポートを検出できません。マルチプレイヤー機能を有効にするには、ゲームで[LANサーバーを開く]をクリックする必要があります。
multiplayer.session.create.token=トークン
multiplayer.session.create.token.apply=静的トークンを申請する
multiplayer.session.create.token.prompt=デフォルトはランダム化されています。 mcer.cn で自分のトークンを申請することができます。
multiplayer.session.error.already_started=Catoサービスはすでにローカルで開始されています。他のマルチプレイヤーサービスが提供されているかどうかを確認してください。または、タスクマネージャーでcatoプロセスを強制終了できます。
multiplayer.session.error.file_not_found=cato実行可能ファイルが見つかりません。このページに入るときに、このファイルをダウンロードする必要があります。ウイルス対策ソフトウェアがファイルを分離したかどうかを確認してください。 trueの場合、ロールバックしてください。
multiplayer.session.expired=マルチプレイヤーセッションの有効期限が切れています。続行するには、部屋を再作成または再参加する必要があります。
multiplayer.session.join=セッションに参加する
multiplayer.session.join.error=マルチプレイヤーセッションへの参加に失敗しました
multiplayer.session.join.error.connection=マルチプレイヤーセッションへの参加に失敗しました。接続を確立できません。
multiplayer.session.join.hint=マルチプレイヤーセッションをすでに作成しているゲーマーから招待コードを取得する必要があります。
multiplayer.session.join.invitation_code=招待コード
multiplayer.session.join.invitation_code.error=不正な招待コード。マルチプレイヤーセッションを作成したプレイヤーから招待コードを入手してください。
multiplayer.session.join.kicked=セッションホルダーによってキックされました。マルチプレイヤーセッションとの接続が失われます。
multiplayer.session.join.kicked.join_acceptance_timeout=相手方が即時加入の要求に応じない場合
multiplayer.session.join.kicked.kicked=オーナーに部屋から追い出される。
multiplayer.session.join.kicked.version_not_matched=マルチプレイヤーオンライン機能のバージョン番号が異なっているため、接続しているマルチプレイヤーオンライン機能のバージョン番号が同じであることを確認してください。
multiplayer.session.join.lost_connection=マルチプレイヤーセッションとの接続が失われました。セッションが作成者によって破棄されたか、このセッションとの接続を確立できない可能性があります。
multiplayer.session.join.port.error=リスニングに使用できるローカルネットワークポートが見つかりません。 HMCLがポートでリッスンする権限を持っていることを確認してください。
multiplayer.session.join.rejected=接続はセッション所有者によって拒否されます。
multiplayer.session.join.wait=受け入れを待っています。
multiplayer.session.members=ルームメンバー
multiplayer.session.quit=この部屋から出る
multiplayer.session.quit.warning=部屋を終了した後、サーバーとの接続が失われます。継続する?
multiplayer.session.username=ユーザー名
multiplayer.state.connecting=接続中
multiplayer.state.disconnected=マルチプレイヤーセッションが作成/開始されていません
multiplayer.state.disconnected.hint=誰かがマルチプレイヤーセッションを作成し、他の人がセッションに参加して一緒にゲームをプレイする必要があります。
multiplayer.state.master=作成された部屋:%1$s、ポート:%2$d
multiplayer.state.master.hint=マルチプレイヤールームを作成したら、[招待コードをコピー]ボタンをクリックして、招待したいプレイヤーに招待コードを送信します。これらのプレーヤーは、会議室に参加するときに招待コードを貼り付けることができます。
multiplayer.state.slave=参加ルーム:%1$s、アドレス:%2$s
multiplayer.state.slave.copy=コピーアドレス
multiplayer.state.slave.hint=マルチプレイヤールームに参加した後、Minecraftのマルチプレイヤーページにアクセスして「HMCLマルチプレイヤーセッション」サーバーに接続するか、以下に示すアドレスのサーバーを手動で追加する必要があります。
datapack=Datapacks
datapack.add=データパックをインストールします
@@ -787,6 +705,7 @@ selector.custom=カスタム
settings=設定
settings.advanced=詳細設定
settings.advanced.title=詳細設定 - %s
settings.advanced.custom_commands=カスタムコマンド
settings.advanced.custom_commands.hint=カスタムコマンドは、次の環境変数を使用して実行されます。\n \
\-$ INST_NAME:バージョンの名前\n \
@@ -797,7 +716,8 @@ settings.advanced.custom_commands.hint=カスタムコマンドは、次の環
\-$ INST_FORGE:Forgeがインストールされている場合に設定\n \
\-$ INST_LITELOADER:LiteLoaderがインストールされている場合に設定\n \
\-$ INST_OPTIFINE:OptiFineがインストールされている場合に設定\n \
\-$ INST_FABRIC:ファブリックがインストールされている場合に設定
\-$ INST_FABRIC:ファブリックがインストールされている場合に設定\n \
\-$ INST_QUILT:ファブリックがインストールされている場合に設定
settings.advanced.dont_check_game_completeness=ゲームファイルをスキャンしない
settings.advanced.dont_check_jvm_validity=JVMがゲームを起動できるかどうかを確認しないでください
settings.advanced.game_dir.default=標準(.minecraft /)
@@ -926,16 +846,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=更新の確認
@@ -980,6 +899,7 @@ version.manage.remove=このバージョンを削除します
version.manage.remove.confirm=このバージョン %s を削除してもよろしいですか?このバージョンを再度復元することはできません。
version.manage.remove.confirm.trash=このバージョン %s を削除してもよろしいですか?このバージョンは、システムのゴミ箱に %s という名前で復元できます。
version.manage.remove.confirm.independent=このバージョンは独立モードであるため、このバージョンを削除すると、このバージョンに属するすべての保存済みワールドも削除されます。このバージョン %s を削除しますか?
version.manage.remove_assets=すべてのゲームリソースファイルの削除
version.manage.remove_libraries=ライブラリファイルを削除します
version.manage.rename=このバージョンの名前を変更します
version.manage.rename.message=このバージョンの新しい名前を入力してください
@@ -76,14 +76,12 @@ account.injector.server_name=Имя сервера
account.login=Войти
account.login.hint=Мы не храним ваш пароль.
account.login.refresh=Повторный вход
account.login.refresh.microsoft.hint=<b>Поскольку авторизация учетной записи недействительна, вам необходимо повторно добавить свою учетную запись Microsoft</b>\nВам необходимо нажать кнопку «OK» и продолжить процесс авторизации во вновь открывшемся окне браузера.
account.login.refresh.microsoft.manual=<b>Поскольку авторизация учетной записи недействительна, вам необходимо повторно добавить свою учетную запись Microsoft</b>\nПосле нажатия кнопки «OK», вы должны завершить авторизацию во вновь открывшемся окне браузера. Если окно браузера не открылось, вы можете щёлкнуть здесь, чтобы скопировать ссылку и вручную открыть её в браузере.
account.login.refresh.microsoft.hint=Поскольку авторизация учетной записи недействительна, вам необходимо повторно добавить свою учетную запись Microsoft
account.logout=Выйти
account.register=Регистрация
account.manage=Список аккаунтов
account.methods=Метод авторизации
account.methods.authlib_injector=authlib-injector
account.methods.little_skin=Little Skin
account.methods.microsoft=Аккаунт Microsoft
account.methods.microsoft.birth=Как изменить настройки дня рождения...
account.methods.microsoft.deauthorize=Отменить авторизацию аккаунта
@@ -95,13 +93,12 @@ account.methods.microsoft.error.missing_xbox_account=Ваш аккаунт Micro
account.methods.microsoft.error.no_character=На аккаунте отсутствует профиль Minecraft Java. \nФайл игры не может быть создан, пожалуйста,\nнажмите на ссылку выше, чтобы создать его
account.methods.microsoft.error.unknown=Не удалось войти. Microsoft отвечает с кодом ошибки %d.
account.methods.microsoft.logging_in=Авторизация...
account.methods.microsoft.makegameidsettings=Создать архив
account.methods.microsoft.makegameidsettings=Создать архив / изменение имени профиля
account.methods.microsoft.hint=Вам необходимо нажать кнопку «Войти» и продолжить процесс авторизации во вновь открывшемся окне браузера.\nЕсли токен, используемый для входа в аккаунт Microsoft случайно утёк, вы можете нажать «Отменить авторизацию аккаунта» ниже, чтобы отменить авторизацию.
account.methods.microsoft.manual=После нажатия кнопки «Войти», вы должны завершить авторизацию во вновь открывшемся окне браузера. Если окно браузера не открылось, вы можете щёлкнуть здесь, чтобы скопировать ссылку и вручную открыть её в браузере.\nЕсли токен, используемый для входа в аккаунт Microsoft случайно утёк, вы можете нажать «Отменить авторизацию аккаунта» ниже, чтобы отменить авторизацию.
account.methods.microsoft.profile=Профиль аккаунта...
account.methods.forgot_password=ЗАБЫЛ ПАРОЛЬ
account.methods.microsoft.snapshot=Вы используете неофициальное построение HMCL, загрузите официальное построение для входа в Microsoft.
account.methods.microsoft.waiting_browser=Ожидание авторизации в открытом окне браузера...
account.methods.offline=Офлайн
account.methods.offline.uuid=UUID
account.methods.offline.uuid.hint=UUID - это уникальный идентификатор игрового персонажа в Minecraft. Способ генерации UUID различается в разных лаунчерах игр. Изменение UUID на тот, который генерируется другим лаунчером гарантирует, что игровые блоки/предметы в рюкзаке вашего офлайн аккаунта останутся. Этот параметр предназначен для экспертов. Если вы не знаете что делаете, мы не советуем вам изменять этот параметр.
@@ -174,7 +171,7 @@ curse.category.4477=Mini Game
curse.category.4478=Quests
curse.category.4484=Multiplayer
curse.category.4476=Exploration
curse.category.4736=Skyblock
curse.category.6145=Skyblock
curse.category.4475=Adventure and RPG
curse.category.4487=FTB
curse.category.4480=Map Based
@@ -312,8 +309,6 @@ extension.sh=Сценарий Bash
fatal.javafx.incompatible=Приложение не может установить JavaFX на текущую среду Java ниже 11.\nЗапустите это приложение, используя JDK 11 или выше или JDK с JavaFX в комплекте.
fatal.javafx.missing=JavaFX отсутствует.\nЕсли вы используете Java 11 или более позднюю версию, перейдите на Oracle JRE 8 или установите BellSoft Liberica Full JRE.\nЕсли вы используете другие сборки OpenJDK, убедитесь, что OpenJFX включен.
fatal.missing_dst_root_ca_x3=Сертификат DST Root CA X3 отсутствует на текущей платформе Java.\nВы всё ещё можете использовать лаунчер, но он не сможет подключиться к некоторым сайтам (например к сайтам, использующим сертификаты, выданные Let's Encrypt), что может привести к неправильной работе лаунчера.\nОбновите Java Runtime до версии 8u101 или более поздней, чтобы решить эту проблему.
# '
fatal.config_loading_failure=Конфигурация недоступна.\nПредоставьте лаунчеру разрешение на чтение и запись "%s" и файлам в нём.
fatal.migration_requires_manual_reboot=Обновление завершено. Перезапустите лаунчер.
fatal.apply_update_failure=Мы сожалеем, лаунчер не смог завершить обновление, потому что что-то пошло не так.\nНо вы все ещё можете вручную завершить обновление, загрузив Hлаунчер с %s.\nВы можете сообщить нам об этой проблеме.
@@ -356,6 +351,8 @@ game.crash.reason.mod_resolution0=Текущая игра не может про
game.crash.reason.mod_profile_causes_game_crash=Текущая игра не может продолжать работать из-за проблемы с профилем мода. \nВы можете проверить журнал на наличие неисправного мода и его конфигурационного файла.
game.crash.reason.fabric_reports_an_error_and_gives_a_solution=Forge мог выдать сообщение об ошибке. \nВы можете просмотреть журнал и выполнить соответствующие действия в соответствии с информацией журнала в отчете об ошибках. \nЕсли вы не видите сообщения об ошибке, вы можете просмотреть отчет об ошибке, чтобы узнать, как она возникла.
game.crash.reason.java_version_is_too_high=Текущая игра аварийно завершилась из-за того, что версия Java слишком высока и не может продолжать работать. \nПеред запуском игры переключитесь на более раннюю версию Java на вкладке "Путь к Java" в глобальных настройках игры или в настройках игры. \nЕсли нет, вы можете загрузить его с <a href="https://www.java.com/download/">java.com (Java8)</a> или <a href="https://bell- sw.com/pages/downloads">BellSoft Liberica Full JRE (Java17)</a> и другие платформы, чтобы загрузить и установить ее (перезапустите программу запуска после установки).
game.crash.reason.mod_name=Текущая игра не может продолжать работать из - за проблем с именем файла Mod. Имя файла \nMod должно быть использовано только в английском языке в полном углу с большими буквами (Aa ~ Zz), цифрами (0 ~ 9), горизонтальными линиями (-), подчеркнутыми (_) и точками (.) . \nПожалуйста, добавьте символ соответствия в папку Mod для всех несовместимых имен файлов Mod.
game.crash.reason.incomplete_forge_installation=Текущая игра не может быть продолжена из-за незавершенной установки Forge. \nУдалите и переустановите Forge в настройках версии - Автоматическая установка.
game.crash.reason.file_already_exists=Невозможно запустить игру из-за того, что файл %1$s уже существует.\nЕсли его можно удалить, создайте резервную копию и удалите его, затем попробуйте снова.
game.crash.reason.file_changed=Невозможно запустить игру из-за сбоя проверки файла.\nЕсли вы изменили первичный jar Minecraft, вам нужно откатить изменения или скачать игру заново.
game.crash.reason.gl_operation_failure=Игра вылетела из-за некоторых модов, шейдер-паков, текстур-паков.\nОтключите используемые моды или наборы шейдеров/текстур и попробуйте снова.
@@ -587,7 +584,7 @@ modpack.type.multimc.export=Может быть импортирован Hello M
modpack.type.server=Сервер автообновления модпака
modpack.type.server.export=Разрешить менеджеру сервера удаленно обновлять клиент игры
modpack.type.server.malformed=Некорректный манифест модпака сервера
modpack.unsupported=Неподдерживаемый модпак, поддерживаются только модпаки Curse, Modrinth, MultiMC, MCBBS.
modpack.unsupported=Hello Minecraft! Launcher не поддерживает этот формат пакетов
modpack.update=Обновить игру
modpack.wizard=Мастер экспорта модпака
modpack.wizard.step.1=Основные параметры
@@ -650,71 +647,6 @@ mods.url=Официальная страница
mods.update_modpack_mod.warning=Обновление модов в пакете модов может привести к непоправимым результатам, возможно, повредить пакет модов так, что он не сможет запуститься. Вы уверены, что хотите обновить?
multiplayer=Сетевая игра
multiplayer.agreement.prompt=Перед началом использования сервиса сетевой игры, необходимо сначала согласиться с EULA.
multiplayer.download=Скачивание зависимостей для сетевой игры
multiplayer.download.failed=Не удалось инициализировать сетевую игру, некоторые файлы не могут быть скачаны
multiplayer.download.success=Инициализация зависимостей прошла успешно
multiplayer.download.unsupported=Текущая операционная система или архитектура не поддерживается.
multiplayer.exit.after_ready=Сессия сетевой игры сломана. Код выхода като %d
multiplayer.exit.before_ready=Не удалось создать сессию сетевой игры. Код выхода като %d
multiplayer.exit.timeout.static_token=Не удалось подключиться к серверу сетевой игры. Переключитесь на динамический токен и попробуйте снова.
multiplayer.exit.timeout.dynamic_token=Не удалось подключиться к серверу сетевой игры.
multiplayer.powered_by=Сетевая игра предоставлена <a href="https://mcer.cn">mcer.cn</a>. <a href\="https://docs.hmcl.net/multiplayer/#%E4%BD%BF%E7%94%A8%E6%8E%88%E6%9D%83">Licence</a>
multiplayer.report=Отчёт
multiplayer.relay=Режим реле
multiplayer.relay.hint=Для пользователей, у которых есть статический токен, и чей проверенный тип NAT - плохо-семитричный
multiplayer.session=Комната
multiplayer.session.name.format=Комната %1$s
multiplayer.session.name.motd=Сетевая комната - %s
multiplayer.session.close=Закрыть комнату
multiplayer.session.close.warning=После закрытия комнаты, все игроки присоединившиеся к комнате потеряют связь. Продолжить?
multiplayer.session.copy_room_code=Скопировать код приглашения
multiplayer.session.create=Создать комнату
multiplayer.session.create.error.dynamic_token=Невозможно создать сетевую комнату. Повторите попытку позже.
multiplayer.session.create.error.static_token=Невозможно создать сетевую комнату. Вы можете повторить попытку с динамическим токеном.
multiplayer.session.create.hint=Перед созданием сетевой комнаты необходимо нажать кнопку «Открыть локальный сервер». в запущенной игре, и ввести порт отображаемый в игре, в пустом поле ниже.
multiplayer.session.create.join=Запрос на подключение
multiplayer.session.create.join.allow=Принимать все запросы на присоединение (Если этот параметр отключен, вам необходимо принимать запросы на присоединение вручную, чтобы предотвратить атаки.)
multiplayer.session.create.join.prompt=Игрок %s хочет присоединиться к сетевой комнате. Принять?
multiplayer.session.create.members=Участники
multiplayer.session.create.members.kick=Выгнать
multiplayer.session.create.members.kick.prompt=Выгнав этого игрока, установленная связь с этим игроком будет разорвана.
multiplayer.session.create.name=Название
multiplayer.session.create.port=Порт
multiplayer.session.create.port.error=Не удается обнаружить игровой порт, необходимо нажать кнопку «Открыть локальный сервер» в игре, чтобы включить сетевую игру.
multiplayer.session.create.token=Токен
multiplayer.session.create.token.apply=Подать заявку на статический токен
multiplayer.session.create.token.prompt=Случайный по умолчанию. Вы можете подать заявку на получение собственного токена на сайте mcer.cn.
multiplayer.session.error.already_started=Сервис Cato уже запущен локально. Проверьте, обслуживаются ли другие сетевые сервисы или нет. Или вы можете завершить процесс cato в диспетчере задач.
multiplayer.session.error.file_not_found=Не удается найти исполняемый файл cato. Этот файл должен быть скачан при входе на эту страницу. Проверьте, не изолировал ли ваш антивирус файл. Если это так, восстановите его.
multiplayer.session.expired=Срок действия сетевой комнаты истёк. Вы должны пересоздать или повторно присоединиться к комнате, чтобы продолжить.
multiplayer.session.hint=Вы должны нажать «Открыть локальный сервер» в игре, чтобы включить сетевую игру.
multiplayer.session.join=Подключиться
multiplayer.session.join.error=Невозможно подключиться к сетевой комнате
multiplayer.session.join.error.connection=Невозможно подключиться к сетевой комнате. Не удаётся установить соединение.
multiplayer.session.join.hint=Вы должны получить код приглашения от игрока, который уже создал сетевую комнату.
multiplayer.session.join.invitation_code=Код приглашения
multiplayer.session.join.invitation_code.error=Неверный код приглашения. Получите код от игрока, который создаёт сетевую комнату.
multiplayer.session.join.kicked=Связь потеряна: %s.
multiplayer.session.join.kicked.join_acceptance_timeout=Владелец комнаты не успел принять ваш запрос на подключение.
multiplayer.session.join.kicked.kicked=Владелец комнаты вас выгнал.
multiplayer.session.join.kicked.version_not_matched=У вас разные версии сетевой игры
multiplayer.session.join.lost_connection=Потеряно соединение с сетевой комнате. Возможно, сессия закрыта создателем или вы не можете установить с ней связь.
multiplayer.session.join.port.error=Не удается найти доступный порт локальной сети для прослушивания. Убедитесь, что лаунчер имеет разрешение на прослушивание порта.
multiplayer.session.join.rejected=Ваше соединение отклонено владельцем комнаты.
multiplayer.session.join.wait=Ожидание принятия.
multiplayer.session.members=Участники комнаты
multiplayer.session.quit=Выйти из комнаты
multiplayer.session.quit.warning=После выхода из комнаты вы потеряете связь с сервером. Продолжить?
multiplayer.session.username=Имя пользователя
multiplayer.state.connecting=Подключение
multiplayer.state.disconnected=Нет комнат
multiplayer.state.disconnected.hint=Кто-то должен создать сетевую комнату, а другие присоединяются к ней, чтобы вместе играть в игру.
multiplayer.state.master=Создана комната: %1$s, порт: %2$d
multiplayer.state.master.hint=После создания сетевой комнаты кликните «Скопировать код приглашения» и отправьте код приглашения игрокам, которых вы хотите пригласить. Эти игроки смогут вставить код приглашения при входе в комнату.
multiplayer.state.slave=Вошедших в комнату: %1$s, адрес: %2$s
multiplayer.state.slave.copy=Скопировать адрес
multiplayer.state.slave.hint=После присоединения к сетевой комнате вы должны попасть на страницу сетевой игры в Minecraft и подключиться к серверу "Сетевой сессии HMCL", или вручную добавьте сервер с адресом, показанным ниже.
datapack=Датапаки
datapack.add=Установить датапак
@@ -778,6 +710,7 @@ selector.custom=Пользовательский
settings=Настройки
settings.advanced=Расширенные настройки
settings.advanced.title=Расширенные настройки - %s
settings.advanced.custom_commands=Пользовательские команды
settings.advanced.custom_commands.hint=Пользовательские команды выполняются со следующими переменными окружения:\n\
\ - $INST_NAME: название версии\n\
@@ -788,7 +721,8 @@ settings.advanced.custom_commands.hint=Пользовательские кома
\ - $INST_FORGE: устанавливается, если установлен Forge\n\
\ - $INST_LITELOADER: устанавливается, если установлен LiteLoader\n\
\ - $INST_OPTIFINE: устанавливается, если установлен OptiFine\n\
\ - $INST_FABRIC: устанавливается, если установлена Fabric
\ - $INST_FABRIC: устанавливается, если установлена Fabric\n\
\ - $INST_QUILT: устанавливается, если установлена Quilt
settings.advanced.dont_check_game_completeness=Не сканировать игровые файлы
settings.advanced.dont_check_jvm_validity=Не проверять, может ли JVM запустить игру или нет
settings.advanced.game_dir.default=По умолчанию (.minecraft/)
@@ -921,13 +855,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=Проверка наличия обновлений
@@ -972,6 +905,7 @@ version.manage.remove=Удалить
version.manage.remove.confirm=Удалить %s? Вы не сможете восстановить эту версию снова!
version.manage.remove.confirm.trash=Удалить %s? Вы можете восстановить эту версию с именем %s из корзины системы.
version.manage.remove.confirm.independent=Поскольку эта версия находится в независимом режиме, удаление этой версии также приведёт к удалению всех сохранённых миров, принадлежащих этой версии. Удалить %s?
version.manage.remove_assets=Удалить файлы игровых активов
version.manage.remove_libraries=Удалить файлы библиотек
version.manage.rename=Переименовать
version.manage.rename.message=Введите новое название для этой версии
@@ -27,7 +27,7 @@ about.claim.statement=點擊連結以查看全文
about.dependency=相依元件
about.legal=法律聲明
about.thanks_to=鳴謝
about.thanks_to.bangbang93.statement=提供 BMCLAPI 下載地點,請贊助支持 BMCLAPI!
about.thanks_to.bangbang93.statement=提供 BMCLAPI 下載地點。請贊助支持 BMCLAPI!
about.thanks_to.contributors=所有通過 Issues、Pull Requests 等管道參與本項目的貢獻者
about.thanks_to.contributors.statement=沒有開源社區的支持,Hello Minecraft! Launcher 無法走到今天
about.thanks_to.gamerteam.statement=提供預設背景圖
@@ -53,7 +53,7 @@ account.create.yggdrasil=添加 Mojang 帳戶
account.create.offline=添加離線模式帳戶
account.create.authlibInjector=添加外置登入帳戶 (authlib-injector)
account.email=電子信箱
account.failed=帳戶刷新失敗!
account.failed=帳戶刷新失敗
account.failed.character_deleted=已刪除此角色
account.failed.connect_authentication_server=無法連線至認證伺服器,可能是網路問題,請檢查設備能否正常上網或使用代理服務
account.failed.connect_injector_server=無法連線至認證伺服器,可能是網路故障,請檢查設備能否正常上網、檢查網址是否輸入錯誤或使用代理服務
@@ -61,14 +61,16 @@ account.failed.injector_download_failure=無法下載 authlib-injector,請檢
account.failed.invalid_credentials=您的使用者名稱或密碼錯誤,或者登入次數過多被暫時禁止登入,請稍後再試
account.failed.invalid_password=密碼無效
account.failed.invalid_token=請嘗試登出並重新輸入密碼登入
account.failed.migration=你的帳號需要被遷移至微軟帳號。如果你已經遷移,你需要使用微軟登錄方式登錄遷移後的微軟帳號。
account.failed.migration=你的帳號需要被遷移至微軟帳號。如果你已經遷移,你需要使用微軟登錄方式登錄遷移後的微軟帳號
account.failed.no_character=該帳戶沒有角色
account.failed.server_disconnected=無法訪問登錄伺服器,賬戶信息刷新失敗! \n\
account.failed.server_disconnected=無法訪問登錄伺服器,賬戶信息刷新失敗。 \n\
您可以選擇“再次刷新賬戶”重新嘗試。 \n\
您也可以選擇“跳過賬戶刷新”繼續啟動遊戲,但可能會使賬戶信息不是最新的。\n\
若最近沒有刷新賬戶信息,則可能導致賬戶信息過期失效,\n\
若為 微軟賬戶 啟動遊戲,賬戶信息過期失效可能將無法進入需賬戶驗證的伺服器。
若為 微軟賬戶 啟動遊戲,賬戶信息過期失效可能將無法進入需賬戶驗證的伺服器。\n\
若嘗試多次無法成功刷新,可嘗試重新添加該賬戶嘗試解決該問題。
account.failed.server_response_malformed=無法解析認證伺服器回應,可能是伺服器故障
account.failed.ssl=連接服務器時發生了 SSL 錯誤,可能網站證書已過期或你使用的 Java 版本過低,請嘗試更新 Java
account.failed.wrong_account=登錄了錯誤的帳號
account.hmcl.hint=你需要點擊“登入”按鈕,並在打開的網頁中完成登入
account.injector.add=新增認證伺服器
@@ -81,17 +83,15 @@ 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=<b>因為賬戶授權失效,需要重新添加你的微軟賬戶</b>\n你需要按照以下步驟添加:\n \n1.點擊“確定”按鈕\n2.在網頁瀏覽器顯示的網站中輸入 HMCL 顯示的代碼(自動拷貝,直接粘貼即可),並點擊“下一步”\n3.按照網站的提示登入\n4.當網站顯示“ 是否允許此應用訪問你的信息?”的標識時,請點擊“是”\n5.當網站提示“大功告成”的標示時,及表示登入成功!然後等待賬戶完成添加即可!\n \n-若網站提示“出现错误”的標識,請嘗試重新按照以上步驟登入\n-若賬戶添加失敗,請嘗試重新按照以上步驟登入\n<b>-若設備網絡環境不佳,可能登陸網站加載很慢甚至無法加載,此時請使用 VPN 或代理伺服器並重試</b>
account.login.refresh.microsoft.manual=<b>因為賬戶授權失效,需要重新添加你的微軟賬戶</b>\n你需要按照以下步驟添加:\n \n1.點擊“確定”按鈕\n2.在網頁瀏覽器顯示的網站中輸入該代碼:<b>%1$s</b>(已自動拷貝,請點此再次拷貝),並點擊“下一步”\n3.按照網站的提示登入\n4.當網站顯示“ 是否允許此應用訪問你的信息?”的標識時,請點擊“是”\n5.當網站提示“大功告成”的標示時,及表示登入成功!然後等待賬戶完成添加即可!\n \n-若網站提示“出现错误”的標識,請嘗試重新按照以上步驟登入\n-若賬戶添加失敗,請嘗試重新按照以上步驟登入\n-若網站未能打開,請手動在網頁瀏覽器中打開:%2$s\n<b>-若設備網絡環境不佳,可能登陸網站加載很慢甚至無法加載,此時請使用 VPN 或代理伺服器並重試</b>
account.login.refresh.microsoft.hint=因為賬戶授權失效,你需要重新添加微軟賬戶
account.logout=登出
account.register=註冊
account.manage=帳戶列表
account.methods=登入方式
account.methods.authlib_injector=authlib-injector 登入
account.methods.little_skin=Little Skin
account.methods.microsoft=微軟帳戶
account.methods.microsoft.birth=如何修改帳戶出生日期
account.methods.microsoft.deauthorize=解除帳戶授權
@@ -103,16 +103,34 @@ account.methods.microsoft.error.missing_xbox_account=你的微軟帳號尚未關
account.methods.microsoft.error.no_character=該帳戶未包含 Minecraft Java 版購買記錄\n可能未創建遊戲檔案,請點擊上方鏈接創建
account.methods.microsoft.error.unknown=登入失敗,錯誤碼:%d
account.methods.microsoft.logging_in=登入中...
account.methods.microsoft.makegameidsettings=創建檔案
account.methods.microsoft.hint=你需要按照以下步驟添加:\n1.點擊“登入”按鈕\n2.在網頁瀏覽器顯示的網站中輸入 HMCL 顯示的代碼(自動拷貝,直接粘貼即可),並點擊“下一步”\n3.按照網站的提示登入\n4.當網站顯示“ 是否允許此應用訪問你的信息?”的標識時,請點擊“是”\n5.當網站提示“大功告成”的標示時,即表示登入成功!然後等待賬戶完成添加即可!\n-若網站提示“出现错误”的標識,請嘗試重新按照以上步驟登入\n-若賬戶添加失敗,請嘗試重新按照以上步驟登入\n<b>-若設備網絡環境不佳,可能登陸網站加載很慢甚至無法加載,此時請使用 VPN 或代理伺服器並重試</b>\n-若登入微軟賬號的 Token 洩露,可點擊下方“解除帳戶授權”解除登入授權,然後等待 Token 過期
account.methods.microsoft.manual=你需要按照以下步驟添加:\n1.點擊“登入”按鈕\n2.在網頁瀏覽器顯示的網站中輸入該代碼:<b>%1$s</b>(已自動拷貝,請點此再次拷貝),並點擊“下一步”\n3.按照網站的提示登入\n4.當網站顯示“ 是否允許此應用訪問你的信息?”的標識時,請點擊“是”\n5.當網站提示“大功告成”的標示時,即表示登入成功!然後等待賬戶完成添加即可\n-若網站提示“出现错误”的標識,請嘗試重新按照以上步驟登入\n-若賬戶添加失敗,請嘗試重新按照以上步驟登入\n-若網站未能打開,請手動在網頁瀏覽器中打開:%2$s\n<b>-若設備網絡環境不佳,可能登陸網站加載很慢甚至無法加載,此時請使用 VPN 或代理伺服器並重試</b>\n-若登入微軟賬號的 Token 洩露,可點擊下方“解除帳戶授權”解除登入授權,然後等待 Token 過期
account.methods.microsoft.makegameidsettings=創建檔案/編輯檔案名稱
account.methods.microsoft.hint=你需要按照以下步驟添加賬戶:\n\
1.點擊“登入”按鈕\n\
2.在網頁瀏覽器顯示的網站中輸入 HMCL 顯示的代碼(自動拷貝,直接粘貼即可),並點擊“下一步”\n\
3.按照網站的提示登入\n\
4.當網站顯示“ 是否允許此應用訪問你的信息?”的標識時,請點擊“是”\n\
5.在網站提示“大功告成”後,只需等待帳戶完成添加即可\n\
若網站提示“出现错误”的標識,請嘗試重新按照以上步驟登入\n\
-若賬戶添加失敗,請嘗試重新按照以上步驟登入\n\
<b>-若設備網絡環境不佳,可能登陸網站加載很慢甚至無法加載,此時請使用 VPN 或代理伺服器並重試</b>\n\
-若登入微軟賬號的 Token 洩露,可點擊下方“解除帳戶授權”解除登入授權,然後等待 Token 過期
account.methods.microsoft.manual=你需要按照以下步驟添加账户:\n\
1.點擊“登入”按鈕\n\
2.在網頁瀏覽器顯示的網站中輸入該代碼:<b>%1$s</b>(已自動拷貝,請點此處再次拷貝),並點擊“下一步”\n\
3.按照網站的提示登入\n\
4.當網站顯示“ 是否允許此應用訪問你的信息?”的標識時,請點擊“是”\n\
5.在網站提示“大功告成”後,只需等待帳戶完成添加即可\n\
-若網站提示“出现错误”的標識,請嘗試重新按照以上步驟登入\n\
-若賬戶添加失敗,請嘗試重新按照以上步驟登入\n\
-若網站未能打開,請手動在網頁瀏覽器中打開:%2$s\n\
<b>-若設備網絡環境不佳,可能登陸網站加載很慢甚至無法加載,此時請使用 VPN 或代理伺服器並重試</b>\n\
-若登入微軟賬號的 Token 洩露,可點擊下方“解除帳戶授權”解除登入授權,然後等待 Token 過期
account.methods.microsoft.profile=帳戶設置頁
account.methods.forgot_password=忘記密碼
account.methods.microsoft.snapshot=你正在使用非官方構建的 HMCL,請下載官方構建進行微軟登入。
account.methods.microsoft.snapshot.website=官方網站
account.methods.microsoft.waiting_browser=等待在新打開的瀏覽器窗口中完成登入...
account.methods.offline=離線模式
account.methods.offline.name.special_characters=建議使用英文字符、數字以及下劃線命名!
account.methods.offline.name.special_characters=建議使用英文字符、數字以及下劃線命名
account.methods.offline.uuid=UUID
account.methods.offline.uuid.hint=UUID 是 Minecraft 對玩家角色的唯一標識符,每個啟動器生成 UUID 的方式可能不同。通過修改 UUID 選項至原啟動器所生成的 UUID,你可以保證在切換啟動器後,遊戲還能將你的遊戲角色識別為給定 UUID 所對應的角色,從而保留原來角色的背包物品。UUID 選項為高級選項,除非你知道你在做什麼,否則你不需要調整該選項。
account.methods.offline.uuid.malformed=格式錯誤
@@ -189,7 +207,7 @@ curse.category.4477=小遊戲
curse.category.4478=任務
curse.category.4484=多人
curse.category.4476=探索
curse.category.4736=空島
curse.category.6145=空島
curse.category.4475=冒險 RPG
curse.category.4487=FTB 模組包
curse.category.4480=有特定地圖
@@ -331,21 +349,22 @@ extension.png=圖片檔案
extension.ps1=PowerShell 指令碼
extension.sh=Bash 指令碼
fatal.javafx.incompatible=缺少 JavaFX 運行環境。\nHMCL 無法在低於 Java 11 且缺少 JavaFX 的 Java 環境上自行補全 JavaFX 運行環境。\n請更換如 Oracle Java 8 等包含 JavaFX 的 Java 運行環境,或者更新到 Java 11 或更高版本。
fatal.fractureiser=Hello Minecraft! Launcher 檢測到你的電腦被 Fractureiser 病毒感染,存在嚴重安全問題。\n請立即使用殺毒軟體進行全盤查殺,隨後修改你在此電腦上登入過的所有帳號的密碼。
fatal.javafx.incompatible=缺少 JavaFX 運行環境。\nHMCL 無法在低於 Java 11 的 Java 環境上自行補全 JavaFX 運行環境,請更新到 Java 11 或更高版本。
fatal.javafx.incompatible.loongson=缺少 JavaFX 運行環境。\n請使龍芯 JDK 8 (http://www.loongnix.cn/zh/api/java/downloads-jdk8/index.html) 啟動 HMCL。
fatal.javafx.missing=找不到 JavaFX。\n如果您使用的是 Java 11 或更高版本,請降級到 Oracle JRE 8,或者安裝 BellSoft Liberica Full JRE。\n如果您使用的是其他 OpenJDK 构建,請確保其包含 OpenJFX。
fatal.missing_dst_root_ca_x3=目前的 Java 平台缺少 DST Root CA X3 憑證。\n您依然可以使用 Hello Minecraft! Launcher,但會無法連線到部分網站 (如使用 Lets Encrypt 憑證的站台),這可能會使 Hello Minecraft! Launcher 無法正常運作。\n請將您的 Java 升級到 8u101 以上來解決此問題。
fatal.javafx.missing=找不到 JavaFX,
fatal.config_change_owner_root=你正在使用 root 帳戶啟動 Hello Minecraft! Launcher,這可能導致你未來無法使用其他帳戶正常啟動 Hello Minecraft! Launcher。\n是否繼續啟動?
fatal.config_in_temp_dir=你正在臨時資料夾中啟動 Hello Minecraft! Launcher,你的設定和遊戲數據可能會遺失,建議將 HMCL 移動至其他位置再啟動。\n是否繼續啟動?
fatal.config_loading_failure=Hello Minecraft! Launcher 無法載入設定檔案。\n請確保 Hello Minecraft! Launcher 對 "%s" 目錄及該目錄下的檔案擁有讀寫權限。
fatal.config_loading_failure.unix=Hello Minecraft! Launcher 無法載入設定檔案,因為設定檔案是由用戶 %1$s 創建的。\n請使用 root 帳戶啟動 HMCL (不推薦),或在終端中執行以下命令將設定檔案的所有權變更為當前用戶: \n%2$s
fatal.mac_app_translocation=由於 macOS 的安全機制,Hello Minecraft! Launcher 被系統隔離至臨時資料夾中。\n請將Hello Minecraft! Launcher 移動到其他資料夾後再嘗試啟動,否則你的設定和遊戲數據可能會在重啟後遺失。\n是否繼續啟動?
fatal.migration_requires_manual_reboot=Hello Minecraft! Launcher 即將升級完成,請重新開啟 Hello Minecraft! Launcher。
fatal.apply_update_failure=我們很抱歉 Hello Minecraft! Launcher 無法自動完成升級程式,因為出現了一些問題。\n但你依然可以從 %s 處手動下載 Hello Minecraft! Launcher 來完成升級。\n請考慮向我們回報該問題。
fatal.samba=如果您正在通過 Samba 共亯的資料夾中運行 HMCL,HMCL 可能無法正常工作,請嘗試更新您的 Java 或在本地資料夾內運行 HMCL。
fatal.apply_update_failure=我們很抱歉 Hello Minecraft! Launcher 無法自動完成升級程式,因為出現了一些問題。\n但你依然可以從 %s 處手動下載 Hello Minecraft! Launcher 來完成升級。\n你可以訪問 https://docs.hmcl.net/help.html 網頁進行迴響。
fatal.samba=如果您正在通過 Samba 共亯的資料夾中運行 Hello Minecraft! Launcher,啟動器可能無法正常工作,請嘗試更新您的 Java 或在本地資料夾內運行 HMCL。
fatal.illegal_char=由於您的用戶資料夾路徑中存在非法字元‘=’,您將無法使用外置登入帳戶以及離線登入更換皮膚功能。
fatal.unsupported_platform=Minecraft 尚未你您的平臺提供完善支持,所以可能影響遊戲體驗或無法啟動遊戲。\n若無法啟動 Minecraft 1.17 及以上版本,可以嘗試在版本設定中打開“使用 OpenGL 軟渲染器”選項,使用 CPU 渲染以獲得更好的相容性。
fatal.unsupported_platform.osx_arm64=HMCL 已為 Apple Silicon 平臺提供支援,使用 ARM 原生 Java 啟動遊戲以獲得更流暢的遊戲體驗。\n如果你在遊戲中遭遇問題,使用 x86-64 架構的 Java 啟動遊戲可能有更好的相容性。
fatal.unsupported_platform.windows_arm64=HMCL 已為 Windows on Arm 平臺提供原生支持。如果你在遊戲中遭遇問題,請嘗試使用 x86 架構的 Java 啟動遊戲。\n\n如果你正在使用<b>高通</b>平臺,你可能需要安裝 <a href="ms-windows-store://pdp/?productid=9NQPSL29BFFF">OpenGL 相容包</a>後才能進行遊戲。點擊連結前往 Microsoft Store 安裝相容包。
fatal.unsupported_platform.osx_arm64=Hello Minecraft! Launcher 已為 Apple Silicon 平臺提供支援,使用 ARM 原生 Java 啟動遊戲以獲得更流暢的遊戲體驗。\n如果你在遊戲中遭遇問題,使用 x86-64 架構的 Java 啟動遊戲可能有更好的相容性。
fatal.unsupported_platform.windows_arm64=Hello Minecraft! Launcher 已為 Windows on Arm 平臺提供原生支持。如果你在遊戲中遭遇問題,請嘗試使用 x86 架構的 Java 啟動遊戲。\n\n如果你正在使用<b>高通</b>平臺,你可能需要安裝 <a href="ms-windows-store://pdp/?productid=9NQPSL29BFFF">OpenGL 相容包</a>後才能進行遊戲。點擊連結前往 Microsoft Store 安裝相容包。
feedback=反饋
feedback.channel=反饋渠道
@@ -368,7 +387,7 @@ folder.saves=遊戲存檔資料夾
folder.screenshots=截圖資料夾
game=遊戲
game.crash.feedback=查看了日誌發現問題很常見?在 <a href="https://discord.gg/jVvC7HfM6U">Discord</a> 或 <a href="https://kook.top/Kx7n3t">KOOK 群</a>提交回饋豐富崩潰分析系統!
game.crash.feedback=查看了日誌發現問題很常見?歡迎在 <a href="https://discord.gg/jVvC7HfM6U">Discord</a> 或 <a href="https://kook.top/Kx7n3t">KOOK 群</a>提交回饋,幫助我們豐富崩潰分析系統!
game.crash.info=遊戲訊息
game.crash.reason=崩潰原因
game.crash.reason.analyzing=分析中...
@@ -388,6 +407,8 @@ game.crash.reason.mod_resolution0=當前遊戲因為一些 Mod 出現問題,
game.crash.reason.mod_profile_causes_game_crash=當前遊戲因為 Mod 配置文件出現問題,無法繼續運行。\n你可以查看日誌尋找出錯模組及其配置文件。
game.crash.reason.fabric_reports_an_error_and_gives_a_solution=Forge 可能已經提供了錯誤信息。\n你可以查看日誌,並根據錯誤報告中的日誌信息進行對應處。\n如果沒有看到報錯信息,可以查看錯誤報告了解錯誤具體是如何發生的。
game.crash.reason.java_version_is_too_high=當前遊戲因為使用的 Java 版本過高而崩潰了,無法繼續運行。\n請在 全局遊戲設置 或 遊戲特定設置 的 Java 路徑選項卡中改用較低版本的 Java,然後再啟動遊戲。\n如果沒有,可以從 <a href="https://www.java.com/download/">java.com(Java8)</a> 或 <a href="https://bell-sw.com/pages/downloads">BellSoft Liberica Full JRE(Java17)</a> 等平台下載、安裝一個(安裝完後需重啟啟動器)。
game.crash.reason.mod_name=當前遊戲因為 Mod 檔案名稱問題,無法繼續運行。 \nMod 檔案名稱應只使用英文全半型的大小寫字母(Aa~Zz)、數位(0~9)、橫線(-)、底線(_)和點(.)。\n請到Mod資料夾中將所有不合規的Mod檔案名稱添加一個上述的合規的字元。
game.crash.reason.incomplete_forge_installation=當前遊戲因為 Forge 安裝不完整,無法繼續運行。 \n請在 版本設置 - 自動安裝 中卸載 Forge 並重新安裝。
game.crash.reason.file_already_exists=當前遊戲因為文件 %1$s 已經存在,無法繼續運行。\n如果你認為這個文件可以刪除,你可以在備份這個文件後嘗試刪除它,並重新啟動遊戲。
game.crash.reason.file_changed=當前遊戲因為檔案校驗失敗,無法繼續運行。\n如果你手動修改了 Minecraft.jar 檔案,你需要回退修改,或者重新下載遊戲。
game.crash.reason.gl_operation_failure=當前遊戲因為你使用的某些 Mod、光影包、材質包,無法繼續運行。\n請先嘗試禁用你所使用的Mod/光影包/材質包再試。
@@ -414,10 +435,10 @@ game.crash.reason.mod_resolution_mod_version.any=%1$s (任意版本)
game.crash.reason.forge_repeat_installation=當前遊戲因為 Forge 重複安裝,無法繼續運行。<a href="https://github.com/huanghongxun/HMCL/issues/1880">此為已知問題</a>\n建議將日誌上傳反饋至 Github ,以便我們找到更多線索並修復此問題。\n目前你可以到 自動安裝 裡頭卸載 Forge 並重新安裝。
game.crash.reason.optifine_repeat_installation=當前遊戲因為重複安裝 Optifine,無法繼續運行。 \n請刪除 Mod 文件夾下的 Optifine 或前往 遊戲管理-自動安裝 卸載自動安裝的 Optifine。
game.crash.reason.optifine_is_not_compatible_with_forge=當前遊戲因為OptiFine與當前版本的Forge不相容,導致了遊戲崩潰。\n請前往 <a href="https://optifine.net/downloads">OptiFine 官網</a>查看 OptiFine 所相容的 Forge 版本,並嚴格按照對應版本重新安裝遊戲或在版本設定-自動安裝中更換版本。\n經測試,Forge版本過高或過低都可能導致崩潰。
game.crash.reason.mod_files_are_decompressed=當前遊戲因為Mod檔案被解壓了,無法繼續運行。\n請直接把整個Mod檔案放進Mod資料夾中即可! \n若解壓就會導致遊戲出錯,請删除Mod資料夾中已被解壓的Mod,然後再啟動遊戲。
game.crash.reason.too_many_mods_lead_to_exceeding_the_id_limit=當前遊戲因為您所安裝的Mod過多,超出了遊戲的ID限制,無法繼續運行。\n請嘗試安裝<a href="https://www.curseforge.com/minecraft/mc-mods/jeid">JEID</a>等修復Mod,或删除部分大型Mod。
game.crash.reason.optifine_causes_the_world_to_fail_to_load=當前遊戲因為Mod檔案被解壓了,無法繼續運行。\n請直接把整個Mod檔案放進Mod資料夾中即可!\n若解壓就會導致遊戲出錯,請删除Mod資料夾中已被解壓的Mod,然後再啟動遊戲。
game.crash.reason.modlauncher_8=當前遊戲因為您所使用的 Forge 版本與當前使用的 Java 衝突崩潰。請嘗試更新 Forge,或使用 Java 8u312/11.0.13/17.0.1 及更早版本啟動。
game.crash.reason.mod_files_are_decompressed=當前遊戲因為 Mod 檔案被解壓了,無法繼續運行。\n請直接把整個 Mod 檔案放進 Mod 資料夾中即可。\n若解壓就會導致遊戲出錯,請删除Mod資料夾中已被解壓的Mod,然後再啟動遊戲。
game.crash.reason.too_many_mods_lead_to_exceeding_the_id_limit=當前遊戲因為您所安裝的 Mod 過多,超出了遊戲的ID限制,無法繼續運行。\n請嘗試安裝<a href="https://www.curseforge.com/minecraft/mc-mods/jeid">JEID</a>等修復Mod,或删除部分大型Mod。
game.crash.reason.optifine_causes_the_world_to_fail_to_load=當前遊戲因為 Mod 檔案被解壓了,無法繼續運行。\n請直接把整個Mod檔案放進Mod資料夾中即可!\n若解壓就會導致遊戲出錯,請删除Mod資料夾中已被解壓的Mod,然後再啟動遊戲。
game.crash.reason.modlauncher_8=當前遊戲因為您所使用的 Forge 版本與當前使用的 Java 衝突崩潰,請嘗試更新 Forge。
game.crash.reason.no_class_def_found_error=當前遊戲因為代碼不完整,無法繼續運行。\n你的遊戲可能缺失了某個 Mod,或者某些 Mod 檔案不完整,或者 Mod 與遊戲的版本不匹配。\n你可能需要重新安裝遊戲和 Mod,或請求他人幫助。\n缺失:%1$s
game.crash.reason.no_such_method_error=當前遊戲因為代碼不完整,無法繼續運行。\n你的遊戲可能缺失了某個 Mod,或者某些 Mod 檔案不完整,或者 Mod 與遊戲的版本不匹配。\n你可能需要重新安裝遊戲和 Mod,或請求他人幫助。
game.crash.reason.opengl_not_supported=當前遊戲因為你的顯示卡驅動存在問題,無法繼續運行。\n原因是 OpenGL 不受支援,你現在是否在遠程桌面或者串流模式下?如果是,請直接使用原電腦啟動遊戲。\n或者嘗試升級你的顯示卡驅動到最新版本後再嘗試啟動遊戲。如果你的電腦存在獨立顯示卡,你需要檢查遊戲是否使用集成/核心顯示卡啟動,如果是,請嘗試使用獨立顯示卡啟動 HMCL 與遊戲。如果仍有問題,你可能需要考慮換一個新顯示卡或新電腦。
@@ -427,7 +448,7 @@ game.crash.reason.processing_of_javaagent_failed=當前遊戲因為加載 -javaa
game.crash.reason.resolution_too_high=當前遊戲因為材質包解析度過高,無法繼續運行\n你可以更換一個解析度更低的材質,或者更換一個視訊記憶體更大的顯示卡。
game.crash.reason.stacktrace=原因未知,請點擊日誌按鈕查看詳細訊息。\n下面是一些關鍵字,其中可能包含 Mod 名稱,你可以透過搜索的方式查找有關訊息。\n%s
game.crash.reason.too_old_java=當前遊戲因為 Java 虛擬機版本過低,無法繼續運行。\n你需要在遊戲設置中更換 %1$s 或更新版本的 Java 虛擬機,並重新啟動遊戲。如果沒有下載安裝,你可以點擊 <a href="https://docs.microsoft.com/zh-TW/java/openjdk/download">此處</a> 下載微軟 JDK。
game.crash.reason.unknown=原因未知,請點擊日誌按鈕查看詳細訊息。\n<b>不要將本界面截圖給他人!</b>如果你要求助他人,請你點擊左下角導出遊戲崩潰訊息後將導出的文件發送給他人以供分析!
game.crash.reason.unknown=原因未知,請點擊日誌按鈕查看詳細訊息。\n<b>不要將本界面截圖給他人!</b>如果你要求助他人,請你點擊左下角導出遊戲崩潰訊息後將導出的文件發送給他人以供分析。
game.crash.reason.unsatisfied_link_error=當前遊戲因為缺少本地庫,無法繼續運行。\n這些本地庫缺失:%1$s。\n如果你在遊戲設置中修改了本地庫路徑選項,請你修改回預設模式。\n如果已經在預設模式下,請檢查本地庫缺失是否是 Mod 引起的,或由 HMCL 引起的。如果你確定是 HMCL 引起的,建議你向我們回饋。\n如果你確實需要自訂本地庫路徑,你需要保證其中包含缺失的本地庫。
game.crash.title=遊戲意外退出
game.directory=遊戲路徑
@@ -620,16 +641,16 @@ modpack.type.curse=Curse
modpack.type.curse.tolerable_error=但未能完成該模組包檔案的下載,您可以在啟動該遊戲版本時繼續該模組包檔案的下載。由於網路問題,您可能需要重試多次……
modpack.type.curse.error=無法完成該模組包所需的依賴下載,請多次重試或設定代理……
modpack.type.curse.not_found=部分必需檔案已經從網路中被刪除並且再也無法下載,請嘗試該模組包的最新版本或者安裝其他模組包。
modpack.type.manual.warning=您大概需要直接解压该模組包,并使用其自带的启动器即可开始游戏,而不需要导入模組包。该模組包不是由启动器导出的模組包,而是人工打包 .minecraft 资料夹而来的,这类模組包通常附带游戏启动器。HMCL 可以尝试导入该模組包,但不保证可用性,是否继续?
modpack.type.manual.warning=該模組包由發佈者手動打包,其中可能已經包含啟動器,建議嘗試解壓後使用其自帶的啟動器運行遊戲。\nHMCL 可以嘗試導入該模組包,但不保證可用性,是否繼續?
modpack.type.mcbbs=我的世界中文論壇模組包標準
modpack.type.mcbbs.export=可以被 Hello Minecraft! Launcher (HMCL) 和 MultiMC 匯入
modpack.type.mcbbs.export=可以被 Hello Minecraft! Launcher 和 MultiMC 匯入
modpack.type.modrinth=Modrinth
modpack.type.multimc=MultiMC
modpack.type.multimc.export=可以被 Hello Minecraft! Launcher (HMCL) 和 MultiMC 匯入
modpack.type.multimc.export=可以被 Hello Minecraft! Launcher 和 MultiMC 匯入
modpack.type.server=伺服器自動更新模組包
modpack.type.server.export=允許伺服器管理員遠端更新遊戲用戶端
modpack.type.server.malformed=伺服器模組包配置格式錯誤,請聯絡伺服器管理員解決此問題
modpack.unsupported=不支援該模組包。僅 Curse、Modrinth、MultiMC、MCBBS 模組包受支援。
modpack.unsupported=Hello Minecraft! Launcher 不支持該綜合包格式
modpack.update=正在升級模組包
modpack.wizard=匯出模組包引導
modpack.wizard.step.1=基本設定
@@ -719,66 +740,8 @@ mods.url=官方頁面
mods.update_modpack_mod.warning=更新模組包中的 Mod 可能導致綜合包損壞,使模組包無法正常啟動。該操作不可逆,確定要更新嗎?
multiplayer=多人聯機
multiplayer.agreement.prompt=多人聯機功能由 速聚 提供。使用前,你需要先同意多人聯機服務提供方 速聚 的用戶協議與免責聲明。\n你需要了解,HMCL 僅為 速聚 提供多人聯機服務入口,使用中遇到的任何問題由 速聚 負責處理。您在使用多人聯機服務過程中所遇到的任何問題與糾紛(包括其付費業務)均與 HMCL 無關,應與 速聚 協商解決。
multiplayer.download=正在下載相依元件
multiplayer.download.failed=初始化失敗,部分檔案未能完成下載
multiplayer.download.success=多人聯機初始化完成
multiplayer.download.unsupported=多人聯機依賴不支持當前系統或平台
multiplayer.error.failed_get_device=HiPer無法創建網路設備,可能是 HiPer 正在運行中。請關閉正在運行中的其他啟動器,或通過工作管理員終止 HiPer。
multiplayer.error.failed_load_config=HiPer 初始化失敗,可能是配置文件存在問題或證書已失效。
multiplayer.error.failed_sudo.linux=HiPer 需要管理員許可權才能運行,您可以通過設置環境變量 HMCL_ASKPASS 配置 HiPer 如何申請權限。\n若將 HMCL_ASKPASS 設置為 'false',請通過配置 sudoers 文件允許 '%s' 免密獲取 root 權限,重啟後再次啟動 HMCL 即可;\n若將 HMCL_ASKPASS 設置為 'user',請先設置環境變量 SUDO_ASKPASS,HMCL 將使用 sudo --askpass 啟動 HiPer (具體請參見 man sudo)。
multiplayer.error.failed_sudo.mac=HiPer 需要管理員許可權才能運行。是否要授予 HiPer 管理員許可權? \n點擊“是”授予 HiPer 管理員許可權,然後重啓電腦後再次打開啟動器即可聯機。
multiplayer.error.failed_sudo.windows=HiPer 需要管理員權限才能運行,請使用管理員權限重新啟動 HMCL。是否查看教程?
multiplayer.error.file_not_found=已發現 HiPer 有可用的更新,請重新進入多人聯機頁面以更新 HiPer。
multiplayer.error.session_expired=本次使用時間已結束,若要繼續使用,請更新索引碼
multiplayer.exit=HiPer 意外退出,退出碼 %d
multiplayer.help.1=管理員權限打開
multiplayer.help.2=多人聯機教程
multiplayer.help.3=創建方幫助
multiplayer.help.4=參與者幫助
multiplayer.help.text=測試能否聯機
multiplayer.hint=多人聯機服務正在維護中。
multiplayer.hint.details=查看詳情
multiplayer.persistence=授權信息管理
multiplayer.persistence.export=將授權信息導出至文件
multiplayer.persistence.export.button=導出授權文件
multiplayer.persistence.export.failed=導出授權文件失敗,請檢查您所選擇的保存路徑。
multiplayer.persistence.export.file_not_exists=獲取授權文件失敗,請檢查索引碼是否輸入正確。
multiplayer.persistence.export.title=選擇授權文件保存路徑
multiplayer.persistence.hint=此功能為長期授權 (有效期超過 24 小時) 用戶提供,如果你正在使用短期授權 (有效期不超過 24 小時),則無需使用本功能。\n當你使用長期授權時,請在獲取授權後的 24 小時內使用索引碼啟動 HiPer,否則 HiPer 將無法獲取授權信息。\n隨後,你可以通過下方的“導出授權信息”功能保存授權文件。\n請注意妥善保管授權文件,在獲取授權 24 小時後,該文件是你的長期授權的唯一憑證,丟失該文件將無法通過索引碼進行聯機。
multiplayer.persistence.import=從文件導入授權信息
multiplayer.persistence.import.button=導入授權文件
multiplayer.persistence.import.failed=導入授權文件失敗。
multiplayer.persistence.import.file_already_exists=授權文件已經存在。
multiplayer.persistence.import.title=選擇授權文件
multiplayer.persistence.import.token_not_match=你選擇的授權文件可能與索引碼不匹配,是否繼續導入?
multiplayer.persistence.license_file=授權文件
multiplayer.powered_by=本服務由 (<a href="https://mcer.cn">速聚</a>) 提供。<a href\="https://docs.hmcl.net/multiplayer/#%E4%BD%BF%E7%94%A8%E6%8E%88%E6%9D%83">授權協議</a>
multiplayer.report=違法違規檢舉
multiplayer.token=索引碼
multiplayer.token.apply=申請索引碼
multiplayer.token.format_invalid=索引碼格式不正確
multiplayer.token.invalid=索引碼不正確。
multiplayer.token.expired=HiPer 證書過期,請重新啟動 HMCL 再試。
multiplayer.token.malformed=HiPer 配置檔案無法解析,請重新啟動 HMCL 再試。
multiplayer.token.prompt=你需要索引碼才能使用多人聯機服務。點擊旁邊的“申請索引碼”查看詳情
multiplayer.off.hint=由於需要注册系統網路服務許可權,啟動 HiPer 時將會向用戶申請管理員許可權。若出現認證提示,允許以啟動 HiPer。
multiplayer.off.start=啟動 HiPer
multiplayer.master=创建方提示
multiplayer.master.hint=1.啟動遊戲\n2.選擇單人遊戲模式進入一個存檔\n3.在遊戲選項菜單內選擇 對局域網開放 選項,之後你可以在遊戲聊天框內看到遊戲提示的端口號(通常是 4~5 位數字)\n4.點擊下方的生成伺服器地址按鈕,輸入端口號,你就可以獲得你的伺服器地址了\n5.該地址需要提供給其他需要加入伺服器的玩家用於添加伺服器。
multiplayer.master.server_address=生成伺服器地址
multiplayer.master.server_address.copy=拷貝
multiplayer.master.video_tutorial=教學影片
multiplayer.master.port=埠號
multiplayer.master.port.validate=在遊戲聊天框中出現的埠號 (0~65535)
multiplayer.slave=參與者提示
multiplayer.slave.hint=1.要求創建方按照上方的 創建方提示 操作\n2.啟動遊戲\n3.選擇多人遊戲模式,選擇添加服務器\n4.遊戲會要求你輸入服務器地址,你只需要向創建方索要服務器地址並輸入,並進入服務器即可。
multiplayer.slave.hint2=1.一般情況下,參與者的遊戲賬戶必須是 微軟賬戶 或 外置登錄賬戶(如 Little Skin),否則你需要將服務器地址輸入至下方的輸入框中並點擊“加入”,在遊戲中選擇多人遊戲模式,進入局域網世界方可加入\n2.一般情況下,參與者的遊戲版本、模組要必須與創建方的一致,否則加入失敗。
multiplayer.slave.server_address=創建方伺服器地址
multiplayer.slave.server_address.start=加入
multiplayer.slave.server_address.stop=退出
multiplayer.slave.video_tutorial=教學影片
multiplayer.session.expiration=索引碼到期時間
datapack=資料包
datapack.add=加入資料包
@@ -870,6 +833,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\
@@ -880,10 +845,12 @@ settings.advanced.custom_commands.hint=自訂命令被調用時將包含如下
\ - $INST_FORGE: 若安裝了 Forge,將會存在本環境變數\n\
\ - $INST_LITELOADER: 若安裝了 LiteLoader,將會存在本環境變數\n\
\ - $INST_OPTIFINE: 若安裝了 OptiFine,將會存在本環境變數\n\
\ - $INST_FABRIC: 若安裝了 Fabric,將會存在本環境變數
\ - $INST_FABRIC: 若安裝了 Fabric,將會存在本環境變數\n\
\ - $INST_QUILT: 若安裝了 Quilt,將會存在本環境變數
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=記憶體永久儲存區域
@@ -916,13 +883,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 ..."
@@ -978,6 +949,7 @@ settings.launcher.proxy.socks=Socks
settings.launcher.proxy.username=帳戶
settings.launcher.theme=主題
settings.launcher.title_transparent=標題欄透明
settings.launcher.turn_off_animations=關閉動畫 (重啟後生效)
settings.launcher.version_list_source=版本列表來源
settings.memory=遊戲記憶體
@@ -1017,13 +989,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=正在檢查更新
@@ -1068,6 +1039,7 @@ version.manage.remove=刪除該版本
version.manage.remove.confirm=真的要刪除版本 %s 嗎? 你將無法找回被刪除的檔案!
version.manage.remove.confirm.trash=真的要刪除版本 %s 嗎? 你可以在系統的資源回收桶 (或垃圾桶) 中還原資料夾 %s 來找回該版本。
version.manage.remove.confirm.independent=由於該遊戲使用了版本隔離,所以刪除該版本將導致該遊戲的存檔等資料一同被刪除,真的要刪除版本 %s 嗎?
version.manage.remove_assets=刪除所有遊戲資源檔案
version.manage.remove_libraries=刪除所有函式庫檔案
version.manage.rename=重新命名該版本
version.manage.rename.message=請輸入新名稱
@@ -27,7 +27,7 @@ about.claim.statement=点击链接以查看全文
about.dependency=依赖
about.legal=法律声明
about.thanks_to=鸣谢
about.thanks_to.bangbang93.statement=提供 BMCLAPI 下载源,请赞助支持 BMCLAPI!
about.thanks_to.bangbang93.statement=提供 BMCLAPI 下载源。请赞助支持 BMCLAPI!
about.thanks_to.contributors=所有通过 Issues、Pull Requests 等方式参与本项目的贡献者
about.thanks_to.contributors.statement=没有开源社区的支持,Hello Minecraft! Launcher 就无法走到今天
about.thanks_to.gamerteam.statement=提供默认背景图
@@ -53,27 +53,29 @@ account.create.yggdrasil=添加 Mojang 帐户
account.create.offline=添加离线模式帐户
account.create.authlibInjector=添加外置登录帐户 (authlib-injector)
account.email=邮箱
account.failed=账户刷新失败!
account.failed=账户刷新失败
account.failed.character_deleted=此角色已被删除
account.failed.connect_authentication_server=无法连接认证服务器,可能是网络问题,请检查设备能否正常上网或使用代理服务
account.failed.connect_injector_server=无法连接认证服务器,可能是网络问题,请检查设备是否能正常上网,检查 URL 是否输入错误,或使用代理服务
account.failed.injector_download_failure=无法下载 authlib-injector,可能是网络问题,请检查设备是否能正常上网、尝试切换下载源或使用代理服务
account.failed.invalid_credentials=您的用户名或密码错误,或登录次数过多被暂时禁止登录,请稍后再试……
account.failed.invalid_credentials=你的用户名或密码错误,或登录次数过多被暂时禁止登录,请稍后再试
account.failed.invalid_password=无效的密码
account.failed.invalid_token=请尝试登出并重新输入密码登录
account.failed.migration=你的帐号需要被迁移至微软帐号。如果你已经迁移,你需要使用微软登录方式登录迁移后的微软帐号。
account.failed.migration=你的帐号需要被迁移至微软帐号。如果你已经迁移,你需要使用微软登录方式登录迁移后的微软帐号
account.failed.no_character=该帐号没有角色
account.failed.server_disconnected=无法访问登录服务器,账户信息刷新失败!\n\
您可以选择“再次刷新账户”重新尝试。\n\
您也可以选择“跳过账户刷新”继续启动游戏,但可能会使账户信息不是最新的。\n\
account.failed.server_disconnected=无法访问登录服务器,账户信息刷新失败。\n\
你可以选择“再次刷新账户”重新尝试。\n\
你也可以选择“跳过账户刷新”继续启动游戏,但可能会使账户信息不是最新的。\n\
若最近没有刷新账户信息,则可能导致账户信息过期失效,\n\
若为 微软账户 启动游戏,账户信息过期失效可能将无法进入需账户验证的服务器。
若为 微软账户 启动游戏,账户信息过期失效可能将无法进入需账户验证的服务器。\n\
若尝试多次无法成功刷新,可尝试重新添加该账户尝试解决该问题。
account.failed.server_response_malformed=无法解析认证服务器响应,可能是服务器故障
account.failed.ssl=连接服务器时发生了 SSL 错误,可能网站证书已过期或你使用的 Java 版本过低,请尝试更新 Java
account.failed.wrong_account=登录了错误的帐号
account.hmcl.hint=你需要点击“登录”按钮,并在打开的网页中完成登录
account.injector.add=添加认证服务器
account.injector.empty=无(点击右侧加号添加)
account.injector.http=警告:此服务器使用不安全的 HTTP 协议,您的密码在登录时会被明文传输!
account.injector.http=警告:此服务器使用不安全的 HTTP 协议,您的密码在登录时会被明文传输。
account.injector.link.homepage=主页
account.injector.link.register=注册
account.injector.server=认证服务器
@@ -84,14 +86,12 @@ account.login.hint=我们不会保存你的密码
account.login.skip=跳过账户刷新
account.login.retry=再次刷新账户
account.login.refresh=重新登录
account.login.refresh.microsoft.hint=<b>因为账户授权失效,需要重新添加你的微软账户</b>\n你需要按照以下步骤添加:\n \n1.点击“确定”按钮\n2.在网页浏览器显示的网站中输入 HMCL 显示的代码(自动拷贝,直接粘贴即可),并点击“下一步”\n3.按照网站的提示登录\n4.当网站提示“是否允许此应用访问你的信息?”的标识时,请点击“是”\n5.当网站提示“大功告成”的标识时,及表示登录成功!然后等待账户完成添加即可!\n \n-若网站提示“出现错误”的标识时,请重新按照以上步骤重新添加\n-若账户添加失败,请重新按照以上步骤重新添加\n<b>-若设备网络环境不佳,可能登录网站加载很慢甚至无法加载,此时请使用 VPN 或代理服务器并重试</b>\n-若登录微软账户的 Token (不是密码)泄露,可点击下方“解除账户绑定”解除登录授权,然后等待 Token 过期
account.login.refresh.microsoft.manual=<b>因为账户授权失效,需要重新添加你的微软账户</b>\n你需要按照以下步骤添加:\n \n1.点击“确定”按钮\n2.在网页浏览器显示的网站中输入:<b>%1$s</b>(已自动拷贝,点此再次拷贝),并点击“下一步”\n3.按照网站的提示登录\n4.当网站提示“是否允许此应用访问你的信息?”的标识时,请点击“是”\n5.当网站提示“大功告成”的标识时,及表示登录成功!然后等待账户完成添加即可!\n \n-若网站提示“出现错误”的标识时,请重新按照以上步骤重新添加\n-若账户添加失败,请重新按照以上步骤重新添加\n-若网站未能显示,请手动在网页浏览器中打开:%2$s\n<b>-若设备网络环境不佳,可能登录网站加载很慢甚至无法加载,此时请使用 VPN 或代理服务器并重试</b>\n-若登录微软账户的 Token (不是密码)泄露,可点击下方“解除账户绑定”解除登录授权,然后等待 Token 过期
account.login.refresh.microsoft.hint=因为账户授权失效,你需要重新添加微软账户
account.logout=登出
account.register=注册
account.manage=帐户列表
account.methods=登录方式
account.methods.authlib_injector=外置登录 (authlib-injector)
account.methods.little_skin=Little Skin
account.methods.microsoft=微软帐户
account.methods.microsoft.birth=如何修改帐户出生日期
account.methods.microsoft.close_page=已完成微软帐户授权,接下来启动器还需要完成剩余登录步骤。你已经可以关闭本页面了。
@@ -103,18 +103,35 @@ account.methods.microsoft.error.missing_xbox_account=你的微软帐户尚未关
account.methods.microsoft.error.no_character=该帐户未包含 Minecraft Java 版购买记录\n若已购买,则可能未创建游戏档案,请点击上方链接创建。\n若确定该账号完成了上述步骤,请先在 Minecraft 官网(minecraft.net)登录一次账户,然后再在启动器登录。
account.methods.microsoft.error.unknown=登录失败,错误码:%d
account.methods.microsoft.logging_in=登录中……
account.methods.microsoft.makegameidsettings=创建档案
account.methods.microsoft.hint=你需要按照以下步骤添加:\n1.点击“登录”按钮\n2.在网页浏览器显示的网站中输入 HMCL 显示的代码(自动拷贝,直接粘贴即可),并点击“下一步”\n3.按照网站的提示登录\n4.当网站提示“是否允许此应用访问你的信息?”的标识时,请点击“是”\n5.当网站提示“大功告成”的标识时,即表示登录成功!然后等待账户完成添加即可!\n-若网站提示“出现错误”的标识时,请重新按照以上步骤重新添加\n-若账户添加失败,请重新按照以上步骤重新添加\n<b>-若设备网络环境不佳,可能登录网站加载很慢甚至无法加载,此时请使用 VPN 或代理服务器并重试</b>\n-若登录微软账户的 Token (不是密码)泄露,可点击下方“解除账户绑定”解除登录授权,然后等待 Token 过期
account.methods.microsoft.manual=你需要按照以下步骤添加:\n1.点击“登录”按钮\n2.在网页浏览器显示的网站中输入:<b>%1$s</b>(已自动拷贝,点此再次拷贝),并点击“下一步”\n3.按照网站的提示登录\n4.当网站提示“是否允许此应用访问你的信息?”的标识时,请点击“是”\n5.当网站提示“大功告成”的标识时,即表示登录成功!然后等待账户完成添加即可\n-若网站提示“出现错误”的标识时,请重新按照以上步骤重新添加\n-若账户添加失败,请重新按照以上步骤重新添加\n-若网站未能显示,请手动在网页浏览器中打开:%2$s\n<b>-若设备网络环境不佳,可能登录网站加载很慢甚至无法加载,此时请使用 VPN 或代理服务器并重试</b>\n-若登录微软账户的 Token (不是密码)泄露,可点击下方“解除账户绑定”解除登录授权,然后等待 Token 过期
account.methods.microsoft.makegameidsettings=创建档案/编辑档案名称
account.methods.microsoft.hint=你需要按照以下步骤添加账户:\n\
1.点击“登录”按钮\n\
2.在网页浏览器显示的网站中输入 HMCL 显示的代码(自动拷贝,直接粘贴即可),并点击“下一步”\n\
3.按照网站的提示登录\n\
4.当网站提示“是否允许此应用访问你的信息?”的标识时,请点击“是”\n\
5.在网站提示“大功告成”后,只需等待账户完成添加即可\n\
-若网站提示“出现错误”的标识时,请重新按照以上步骤重新添加\n-若账户添加失败,请重新按照以上步骤重新添加\n\
<b>-若设备网络环境不佳,可能登录网站加载很慢甚至无法加载,此时请使用 VPN 或代理服务器并重试</b>\n\
-若登录微软账户的 Token (不是密码)泄露,可点击下方“解除账户绑定”解除登录授权,然后等待 Token 过期
account.methods.microsoft.manual=你需要按照以下步骤添加:\n\
1.点击“登录”按钮\n\
2.在网页浏览器显示的网站中输入:<b>%1$s</b>(已自动拷贝,点此再次拷贝),并点击“下一步”\n\
3.按照网站的提示登录\n\
4.当网站提示“是否允许此应用访问你的信息?”的标识时,请点击“是”\n\
5.在网站提示“大功告成”后,只需等待账户完成添加即可\n\
-若网站提示“出现错误”的标识时,请重新按照以上步骤重新添加\n\
-若账户添加失败,请重新按照以上步骤重新添加\n\
-若网站未能显示,请手动在网页浏览器中打开:%2$s\n\
<b>-若设备网络环境不佳,可能登录网站加载很慢甚至无法加载,此时请使用 VPN 或代理服务器并重试</b>\n\
-若登录微软账户的 Token (不是密码)泄露,可点击下方“解除账户绑定”解除登录授权,然后等待 Token 过期
account.methods.microsoft.profile=帐户设置页
account.methods.forgot_password=忘记密码
account.methods.microsoft.snapshot=你正在使用非官方构建的 HMCL,请下载官方构建进行微软登录。
account.methods.microsoft.snapshot.website=官方网站
account.methods.microsoft.waiting_browser=等待在新打开的浏览器窗口中完成登录……
account.methods.offline=离线模式
account.methods.offline.name.special_characters=建议使用英文字符、数字以及下划线命名!
account.methods.offline.name.special_characters=建议使用英文字符、数字以及下划线命名
account.methods.offline.uuid=UUID
account.methods.offline.uuid.hint=UUID 是 Minecraft 对玩家角色的唯一标识符,每个启动器生成 UUID 的方式可能不同。通过修改 UUID 选项至原启动器所生成的 UUID,你可以保证在切换启动器后,游戏还能将你的游戏角色识别为给定 UUID 所对应的角色,从而保留原来角色的背包物品。UUID 选项为高级选项,除非你知道你在做什么,否则你不需要调整该选项!
account.methods.offline.uuid.hint=UUID 是 Minecraft 对玩家角色的唯一标识符,每个启动器生成 UUID 的方式可能不同。通过修改 UUID 选项至原启动器所生成的 UUID,你可以保证在切换启动器后,游戏还能将你的游戏角色识别为给定 UUID 所对应的角色,从而保留原来角色的背包物品。UUID 选项为高级选项,除非你知道你在做什么,否则你不需要调整该选项。
account.methods.offline.uuid.malformed=格式错误
account.methods.yggdrasil=Mojang 帐户
account.methods.yggdrasil.migration=迁移至微软账户
@@ -123,8 +140,8 @@ account.methods.yggdrasil.migration.hint=自 2022 年 3 月 10 日起,Mojang
account.methods.yggdrasil.purchase=购买 Minecraft
account.missing=没有游戏帐户
account.missing.add=点击此处添加帐户
account.move_to_global=转换为全局账户
account.move_to_portable=转换为便携账户
account.move_to_global=转换为全局账户\n该账户的信息会保存至用户目录的配置文件中
account.move_to_portable=转换为便携账户\n该账户的信息会保存至 HMCL 同目录的配置文件中
account.not_logged_in=未登录
account.password=密码
account.portable=便携账户
@@ -176,8 +193,8 @@ button.yes=是
color.recent=推荐
color.custom=自定义颜色
crash.NoClassDefFound=请确认 Hello Minecraft! Launcher 本体是否完整,\n或更新您的 Java。\n可访问\n https://docs.hmcl.net/help.html \n网页寻求帮助!
crash.user_fault=您的系统或 Java 环境可能安装不当导致本软件崩溃,\n请检查您的 Java 环境或您的电脑!\n可以尝试重新安装 Java。\n可访问\n https://docs.hmcl.net/help.html \n网页寻求帮助!
crash.NoClassDefFound=请确认 Hello Minecraft! Launcher 本体是否完整,\n或更新您的 Java。\n你可以访问\n https://docs.hmcl.net/help.html \n页面寻求帮助。
crash.user_fault=您的系统或 Java 环境可能安装不当导致本软件崩溃,\n请检查您的 Java 环境或您的电脑。\n你可以访问\n https://docs.hmcl.net/help.html \n页面寻求帮助。
curse.category.0=全部
@@ -189,7 +206,7 @@ curse.category.4477=小游戏
curse.category.4478=任务
curse.category.4484=多人
curse.category.4476=探索
curse.category.4736=空岛
curse.category.6145=空岛
curse.category.4475=冒险 RPG
curse.category.4487=FTB 整合包
curse.category.4480=有特定地图
@@ -321,10 +338,10 @@ download.javafx.prepare=准备开始下载
exception.access_denied=因为无法访问文件 %s,HMCL 没有对该文件的访问权限,或者该文件被其他程序打开。\n\
请你检查当前操作系统帐户是否能访问该文件,比如非管理员用户可能不能访问其他帐户的个人文件夹内的文件。\n\
对于 Windows 用户,你还可以尝试通过资源监视器查看是否有程序占用了该文件,如果是,请关闭占用此文件相关程序,或者重启电脑再试……\n\
可访问 https://docs.hmcl.net/help.html 网页寻求帮助!
对于 Windows 用户,你还可以尝试通过资源监视器查看是否有程序占用了该文件,如果是,请关闭占用此文件相关程序,或者重启电脑再试。\n\
你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
exception.artifact_malformed=下载的文件无法通过校验。
exception.ssl_handshake=无法建立 SSL 连接,因为当前 Java 虚拟机缺少相关的 SSL 证书。你可以尝试使用其他的 Java 虚拟机启动 HMCL 再试…… \n可访问 https://docs.hmcl.net/help.html 网页寻求帮助!
exception.ssl_handshake=无法建立 SSL 连接,因为当前 Java 虚拟机缺少相关的 SSL 证书。你可以尝试使用其他的 Java 虚拟机启动 HMCL 再试。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
extension.bat=Windows 脚本
extension.mod=模组文件
@@ -332,21 +349,22 @@ extension.png=图片文件
extension.ps1=PowerShell 脚本
extension.sh=Bash 脚本
fatal.javafx.incompatible=缺少 JavaFX 运行环境。\nHMCL 无法在低于 Java 11 且缺少 JavaFX 的 Java 环境上自行补全 JavaFX 运行环境。\n请更换如 Oracle Java 8 等包含 JavaFX 的 Java 运行环境,或者更新到 Java 11 或更高版本。\n可访问 https://docs.hmcl.net/help.html 网页寻求帮助!
fatal.javafx.incompatible.loongson=缺少 JavaFX 运行环境。\n请使用龙芯 JDK 8 (http://www.loongnix.cn/zh/api/java/downloads-jdk8/index.html) 启动 HMCL。\n可访问 https://docs.hmcl.net/help.html 网页寻求帮助!
fatal.javafx.missing=缺少 JavaFX 运行环境。\n如果您使用的是 Java 11 或更高版本,请降级到 Oracle JRE 8(java.com),或者安装 BellSoft Liberica Full JRE(bell-sw.com/pages/downloads/?package=jre-full)。\n如果您使用的是其他 OpenJDK 构建,请确保其包含 OpenJFX!\n可访问 https://docs.hmcl.net/help.html 网页寻求帮助!
fatal.missing_dst_root_ca_x3=当前 Java 平台缺少 DST Root CA X3 证书。\n您依然可以使用 Hello Minecraft! Launcher,但将无法连接到部分站点(如使用 Lets Encrypt 证书的站点),这可能会使 HMCL 无法正常工作。\n请将您的 Java 升级到 8u101 以上以解决此问题。\n可访问 https://docs.hmcl.net/help.html 网页寻求帮助!
fatal.fractureiser=Hello Minecraft! Launcher 检测到你的电脑被 Fractureiser 病毒感染,存在严重安全问题。\n请立即使用杀毒软件进行全盘查杀,随后修改你在此电脑上登陆过的所有账号的密码。
fatal.javafx.incompatible=缺少 JavaFX 运行环境。\nHello Minecraft! Launcher 无法在低于 Java 11 的 Java 环境上自行补全 JavaFX 运行环境,请更新到 Java 11 或更高版本。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
fatal.javafx.incompatible.loongson=缺少 JavaFX 运行环境。\n请使用龙芯 JDK 8 (http://www.loongnix.cn/zh/api/java/downloads-jdk8/index.html) 启动 HMCL。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
fatal.javafx.missing=缺少 JavaFX 运行环境,请使用包含 OpenJFX 的 Java 运行环境启动 Hello Minecraft! Launcher。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
fatal.config_change_owner_root=你正在使用 root 账户启动 Hello Minecraft! Launcher, 这可能导致你未来无法正常使用其他账户正常启动 Hello Minecraft! Launcher。\n是否继续启动?
fatal.config_in_temp_dir=你正在临时文件夹中启动 Hello Minecraft! Launcher, 你的设置和游戏数据可能会丢失,建议将 HMCL 移动至其他位置再启动。\n是否继续启动?
fatal.config_loading_failure=Hello Minecraft! Launcher 无法加载配置文件\n请确保 Hello Minecraft! Launcher 对 "%s" 目录及该目录下的文件拥有读写权限。\n可访问 https://docs.hmcl.net/help.html 网页寻求帮助!
fatal.config_loading_failure.unix=Hello Minecraft! Launcher 无法加载配置文件,因为配置文件是由用户 %1$s 创建的。\n请使用 root 账户启动 HMCL (不推荐),或在终端中执行以下命令将配置文件的所有权变更为当前用户:\n%2$s\n可访问 https://docs.hmcl.net/help.html 网页寻求帮助!
fatal.config_loading_failure=Hello Minecraft! Launcher 无法加载配置文件\n请确保 Hello Minecraft! Launcher 对 "%s" 目录及该目录下的文件拥有读写权限。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
fatal.config_loading_failure.unix=Hello Minecraft! Launcher 无法加载配置文件,因为配置文件是由用户 %1$s 创建的。\n请使用 root 账户启动 HMCL (不推荐),或在终端中执行以下命令将配置文件的所有权变更为当前用户:\n%2$s\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
fatal.mac_app_translocation=由于 macOS 的安全机制,Hello Minecraft! Launcher 被系统隔离至临时文件夹中。\n请将 Hello Minecraft! Launcher 移动到其他文件夹后再尝试启动,否则你的设置和游戏数据可能会在重启后丢失。\n是否继续启动?
fatal.migration_requires_manual_reboot=Hello Minecraft! Launcher 即将完成升级,请重新打开 Hello Minecraft! Launcher。
fatal.apply_update_failure=我们很抱歉 Hello Minecraft! Launcher 无法自动完成升级,因为出现了一些问题……\n但你依可以从 %s 处手动下载 Hello Minecraft! Launcher 来完成升级\n可访问 https://docs.hmcl.net/help.html 网页进行反馈!
fatal.samba=如果您正在通过 Samba 共享的文件夹中运行 HMCL,HMCL 可能无法正常工作,请尝试更新您的 Java 或在本地文件夹内运行 HMCL。\n可访问 https://docs.hmcl.net/help.html 网页寻求帮助!
fatal.apply_update_failure=我们很抱歉 Hello Minecraft! Launcher 无法自动完成升级,因为出现了一些问题。\n但你依可以从 %s 处手动下载 Hello Minecraft! Launcher 来完成升级\n你可以访问 https://docs.hmcl.net/help.html 网页进行反馈。
fatal.samba=如果你正在通过 Samba 共享的文件夹中运行 Hello Minecraft! Launcher,启动器可能无法正常工作。请尝试更新你的 Java 或在本地文件夹内运行 Hello Minecraft! Launcher。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
fatal.illegal_char=由于你的用户文件夹路径中存在非法字符‘=’,你将无法使用外置登录账户以及离线登录更换皮肤功能。
fatal.unsupported_platform=Minecraft 尚未为您的平台提供完善支持,所以可能影响游戏体验或无法启动游戏。\n若无法启动 Minecraft 1.17 及以上版本,可以尝试在版本设置中打开“使用 OpenGL 软渲染器”选项,使用 CPU 渲染以获得更好的兼容性。
fatal.unsupported_platform.osx_arm64=HMCL 已为 Apple Silicon 平台提供支持,使用 ARM 原生 Java 启动游戏以获得更流畅的游戏体验。\n如果你在游戏中遭遇问题,使用 x86-64 架构的 Java 启动游戏可能有更好的兼容性。
fatal.unsupported_platform.windows_arm64=HMCL 已为 Windows on Arm 平台提供原生支持。如果你在游戏中遭遇问题,请尝试使用 x86 架构的 Java 启动游戏。\n\n如果你正在使用<b>高通</b>平台,你可能需要安装 <a href="ms-windows-store://pdp/?productid=9NQPSL29BFFF">OpenGL 兼容包</a>后才能进行游戏。点击链接前往 Microsoft Store 安装兼容包。
fatal.unsupported_platform.osx_arm64=Hello Minecraft! Launcher 已为 Apple Silicon 平台提供支持,使用 ARM 原生 Java 启动游戏以获得更流畅的游戏体验。\n如果你在游戏中遭遇问题,使用 x86-64 架构的 Java 启动游戏可能有更好的兼容性。
fatal.unsupported_platform.windows_arm64=Hello Minecraft! Launcher 已为 Windows on Arm 平台提供原生支持。如果你在游戏中遭遇问题,请尝试使用 x86 架构的 Java 启动游戏。\n\n如果你正在使用<b>高通</b>平台,你可能需要安装 <a href="ms-windows-store://pdp/?productid=9NQPSL29BFFF">OpenGL 兼容包</a>后才能进行游戏。点击链接前往 Microsoft Store 安装兼容包。
feedback=反馈
feedback.channel=反馈渠道
@@ -369,65 +387,67 @@ folder.saves=存档文件夹
folder.screenshots=截图文件夹
game=游戏
game.crash.feedback=查看了日志发现该问题很常见?在 <a href="https://docs.hmcl.net/help.html ">此处</a> 提交反馈,以丰富崩溃分析系统!
game.crash.feedback=查看了日志发现该问题很常见?欢迎在 <a href="https://docs.hmcl.net/help.html ">此处</a> 提交反馈,帮助我们丰富崩溃分析系统!
game.crash.info=游戏信息
game.crash.reason=崩溃原因
game.crash.reason.analyzing=分析中……
game.crash.reason.block=当前游戏因为某个方块不能正常工作,无法继续运行。\n你可以尝试通过 <a href="https://podshot.github.io/MCEdit-Unified">MCEdit</a> 工具编辑存档删除该方块,或者直接删除相应的 Mod。\n方块类型:%1$s\n方块坐标:%2$s \n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.bootstrap_failed=当前游戏因为模组 %1$s 错误,无法继续运行。\n你可以尝试删除或更新该模组以解决问题。\n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.config=当前游戏因为无法解析模组配置文件,无法继续运行\n模组 %1$s 的配置文件 %2$s 无法被解析。\n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.block=当前游戏因为某个方块不能正常工作,无法继续运行。\n你可以尝试通过 <a href="https://podshot.github.io/MCEdit-Unified">MCEdit</a> 工具编辑存档删除该方块,或者直接删除相应的 Mod。\n方块类型:%1$s\n方块坐标:%2$s \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.bootstrap_failed=当前游戏因为模组 %1$s 错误,无法继续运行。\n你可以尝试删除或更新该模组以解决问题。\n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.config=当前游戏因为无法解析模组配置文件,无法继续运行\n模组 %1$s 的配置文件 %2$s 无法被解析。\n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.debug_crash=当前游戏因为手动触发崩溃,无法继续运行。\n事实上游戏并没有问题,问题都是你造成的!
game.crash.reason.duplicated_mod=当前游戏因为模组 %1$s 重复安装,无法继续运行。\n%2$s\n每种 Mod 只能安装一个,请你删除多余的 Mod 再试…… \n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.entity=当前游戏因为某个实体不能正常工作,无法继续运行。\n你可以尝试通过 <a href="https://podshot.github.io/MCEdit-Unified">MCEdit</a> 工具编辑存档删除该实体,或者直接删除相应的 Mod。\n实体类型:%1$s\n实体坐标:%2$s \n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.fabric_version_0_12=Fabric 0.12 及以上版本与当前已经安装的 Mod 可能不兼容,你需要将 Fabric 降级至 0.11.7。 \n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.fabric_warnings=Fabric 提供了一些警告信息:\n%1$s \n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
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\
- 如果你的电脑存在独立显卡,请尝试使用 独立显卡 而非 Intel 核显启动 HMCL 与游戏 <a href="https://www.bing.com/search?q=%E7%8B%AC%E7%AB%8B%E6%98%BE%E5%8D%A1%E5%90%AF%E5%8A%A8%E7%A8%8B%E5%BA%8F">详情</a>;\n\
game.crash.reason.duplicated_mod=当前游戏因为模组 %1$s 重复安装,无法继续运行。\n%2$s\n每种 Mod 只能安装一个,请你删除多余的 Mod 再试。\n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.entity=当前游戏因为某个实体不能正常工作,无法继续运行。\n你可以尝试通过 <a href="https://podshot.github.io/MCEdit-Unified">MCEdit</a> 工具编辑存档删除该实体,或者直接删除相应的 Mod。\n实体类型:%1$s\n实体坐标:%2$s \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.fabric_version_0_12=Fabric 0.12 及以上版本与当前已经安装的 Mod 可能不兼容,你需要将 Fabric 降级至 0.11.7。 \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.fabric_warnings=Fabric 提供了一些警告信息:\n%1$s \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
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\
- 如果你的电脑存在独立显卡,请尝试使用 独立显卡 而非 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\
可在 <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> 寻求帮助
game.crash.reason.jvm_32bit=当前游戏因为内存分配过大,超过了 32 位 Java 内存限制,无法继续运行。\n如果你的电脑是 64 位系统,请下载安装并更换 64 位 Java。<a herf="https://bell-sw.com/pages/downloads/#downloads">下载 Java</a>\n如果你的电脑是 32 位系统,你或许可以重新安装 64 位系统,或换一台新电脑。\n或者,你可以关闭游戏内存的自动分配,并且把内存限制调节为 1024 MB 或以下。\n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.loading_crashed_forge=当前游戏因为模组 %1$s (%2$s) 错误,无法继续运行。\n你可以尝试删除或更新该模组以解决问题。 \n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.loading_crashed_fabric=当前游戏因为模组 %1$s 错误,无法继续运行。\n你可以尝试删除或更新该模组以解决问题。\n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.memory_exceeded=当前游戏因为分配的内存过大,无法继续运行。\n该问题是由于系统页面文件太小导致的。\n你需要在全局(特定)游戏设置中关闭游戏内存的自动分配,并将游戏内存调低至游戏能正常启动为止。\n你还可以尝试调大系统的页面大小。 \n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.mac_jdk_8u261=当前游戏因为你所使用的 Forge 或 OptiFine 与 Java 冲突崩溃。\n请尝试更新 Forge 和 OptiFine,或使用 Java 8u251 及更早版本启动。 \n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.mod=当前游戏因为 %1$s 的问题,无法继续运行。\n你可以更新或删除已经安装的 %1$s 再试…… \n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.mod_resolution=当前游戏因为 Mod 依赖问题,无法继续运行。Fabric 提供了如下信息:\n%1$s \n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.mod_resolution_collection=当前游戏因为前置 Mod 版本不匹配,无法继续运行。\n%1$s 需要前置 Mod:%2$s 才能继续运行。\n这表示你需要更新或降级前置。你可以到下载页的模组下载,或到网上下载 %3$s。 \n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.mod_resolution_conflict=当前游戏因为 Mod 冲突,无法继续运行。\n%1$s 与 %2$s 不能兼容。 \n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.mod_resolution_missing=当前游戏因为缺少 Mod 前置,无法继续运行。\n%1$s 需要前置 Mod:%2$s 才能继续运行。\n这表示你少安装了 Mod,或该 Mod 版本不够。你可以到下载页的模组下载,或到网上下载 %3$s。 \n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.mod_resolution_missing_minecraft=当前游戏因为 Mod 和 Minecraft 游戏版本不匹配,无法继续运行。\n%1$s 需要 Minecraft %2$s 才能运行。\n如果你要继续使用你已经安装的 Mod,你可以选择安装对应的 Minecraft 版本;如果你要继续使用当前 Minecraft 版本,你需要安装对应版本的 Mod。 \n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
- 在游戏设置中打开“使用 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> 寻求帮助。
game.crash.reason.jvm_32bit=当前游戏因为内存分配过大,超过了 32 位 Java 内存限制,无法继续运行。\n如果你的电脑是 64 位系统,请下载安装并更换 64 位 Java。<a herf="https://bell-sw.com/pages/downloads/#downloads">下载 Java</a>\n如果你的电脑是 32 位系统,你或许可以重新安装 64 位系统,或换一台新电脑。\n或者,你可以关闭游戏内存的自动分配,并且把内存限制调节为 1024 MB 或以下。\n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.loading_crashed_forge=当前游戏因为模组 %1$s (%2$s) 错误,无法继续运行。\n你可以尝试删除或更新该模组以解决问题。 \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.loading_crashed_fabric=当前游戏因为模组 %1$s 错误,无法继续运行。\n你可以尝试删除或更新该模组以解决问题。\n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.memory_exceeded=当前游戏因为分配的内存过大,无法继续运行。\n该问题是由于系统页面文件太小导致的。\n你需要在全局(特定)游戏设置中关闭游戏内存的自动分配,并将游戏内存调低至游戏能正常启动为止。\n你还可以尝试调大系统的页面大小。 \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.mac_jdk_8u261=当前游戏因为你所使用的 Forge 或 OptiFine 与 Java 冲突崩溃。\n请尝试更新 Forge 和 OptiFine,或使用 Java 8u251 及更早版本启动。 \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.mod=当前游戏因为 %1$s 的问题,无法继续运行。\n你可以更新或删除已经安装的 %1$s 再试。\n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.mod_resolution=当前游戏因为 Mod 依赖问题,无法继续运行。Fabric 提供了如下信息:\n%1$s \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.mod_resolution_collection=当前游戏因为前置 Mod 版本不匹配,无法继续运行。\n%1$s 需要前置 Mod:%2$s 才能继续运行。\n这表示你需要更新或降级前置。你可以到下载页的模组下载,或到网上下载 %3$s。 \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.mod_resolution_conflict=当前游戏因为 Mod 冲突,无法继续运行。\n%1$s 与 %2$s 不能兼容。 \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.mod_resolution_missing=当前游戏因为缺少 Mod 前置,无法继续运行。\n%1$s 需要前置 Mod:%2$s 才能继续运行。\n这表示你少安装了 Mod,或该 Mod 版本不够。你可以到下载页的模组下载,或到网上下载 %3$s。 \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.mod_resolution_missing_minecraft=当前游戏因为 Mod 和 Minecraft 游戏版本不匹配,无法继续运行。\n%1$s 需要 Minecraft %2$s 才能运行。\n如果你要继续使用你已经安装的 Mod,你可以选择安装对应的 Minecraft 版本;如果你要继续使用当前 Minecraft 版本,你需要安装对应版本的 Mod。 \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.mod_resolution_mod_version=%1$s (版本号 %2$s)
game.crash.reason.mod_resolution_mod_version.any=%1$s (任意版本)
game.crash.reason.forge_repeat_installation=当前游戏因为 Forge 重复安装,无法继续运行。<a href="https://github.com/huanghongxun/HMCL/issues/1880">此为已知问题</a>\n建议将日志上传反馈至 Github ,以便我们找到更多线索并修复此问题。\n目前你可以到 自动安装 里头卸载 Forge 并重新安装。\n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.forge_repeat_installation=当前游戏因为 Forge 重复安装,无法继续运行。<a href="https://github.com/huanghongxun/HMCL/issues/1880">此为已知问题</a>\n建议将日志上传反馈至 Github ,以便我们找到更多线索并修复此问题。\n目前你可以到 自动安装 里头卸载 Forge 并重新安装。\n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.optifine_repeat_installation=当前游戏因为 Optifine 重复安装,无法继续运行。\n请删除 Mod 文件夹下的 Optifine 或前往 游戏管理-自动安装 卸载自动安装的 Optifine。
game.crash.reason.modmixin_failure=当前游戏因为某些 Mod 注入失败,无法继续运行。\n这一般代表着该 Mod 存在 Bug,或与当前环境不兼容。\n你可以查看日志寻找出错模组。\n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.forge_error=Forge 可能已经提供了错误信息。\n你可以查看日志,并根据错误报告中的日志信息进行对应处。\n如果没有看到报错信息,可以查看错误报告了解错误具体是如何发生的。\n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.mod_resolution0=当前游戏因为一些 Mod 出现问题,无法继续运行。\n你可以查看日志寻找出错模组。\n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.fabric_reports_an_error_and_gives_a_solution=Fabric 可能已经提供了错误信息。\n你可以查看日志,并根据错误报告中的日志信息进行对应处。\n如果没有看到报错信息,可以查看错误报告了解错误具体是如何发生的。\n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.modmixin_failure=当前游戏因为某些 Mod 注入失败,无法继续运行。\n这一般代表着该 Mod 存在 Bug,或与当前环境不兼容。\n你可以查看日志寻找出错模组。\n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.forge_error=Forge 可能已经提供了错误信息。\n你可以查看日志,并根据错误报告中的日志信息进行对应处。\n如果没有看到报错信息,可以查看错误报告了解错误具体是如何发生的。\n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.mod_resolution0=当前游戏因为一些 Mod 出现问题,无法继续运行。\n你可以查看日志寻找出错模组。\n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.fabric_reports_an_error_and_gives_a_solution=Fabric 可能已经提供了错误信息。\n你可以查看日志,并根据错误报告中的日志信息进行对应处。\n如果没有看到报错信息,可以查看错误报告了解错误具体是如何发生的。\n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.java_version_is_too_high=当前游戏因为 Java 虚拟机版本过高,无法继续运行。\n请在全局(特定)游戏设置的 Java 路径选项卡中改用较低版本的 Java,然后再启动游戏。\n如果没有,可以从 <a href="https://www.java.com/download/">java.com(Java8)</a> 或 <a href="https://bell-sw.com/pages/downloads">BellSoft Liberica Full JRE(Java17)</a> 等平台下载、安装一个(安装完后需重启启动器)。
game.crash.reason.optifine_is_not_compatible_with_forge=当前游戏因为 OptiFine 与当前版本的 Forge 不兼容,导致了游戏崩溃。\n请前往 <a href="https://optifine.net/downloads">OptiFine 官网</a> 查看 OptiFine 所兼容的 Forge 版本,并严格按照对应版本重新安装游戏或在 版本设置 - 自动安装 中更换版本。\n经测试,Forge 版本过高或过低都可能导致崩溃。 \n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.mod_files_are_decompressed=当前游戏因为 Mod 文件被解压了,无法继续运行。\n请直接把整个 Mod 文件放进 Mod 文件夹中即可!\n若解压就会导致游戏出错,请删除 Mod 文件夹中已被解压的 Mod,然后再启动游戏。 \n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.too_many_mods_lead_to_exceeding_the_id_limit=当前游戏因为您所安装的 Mod 过多,超出了游戏的 ID 限制,无法继续运行。\n请尝试安装 <a href="https://www.curseforge.com/minecraft/mc-mods/jeid">JEID</a> 等修复 Mod,或删除部分大型 Mod。 \n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.optifine_causes_the_world_to_fail_to_load=当前游戏可能因为 OptiFine ,无法继续运行。\n该问题只在特定 OptiFine 版本中出现,你可以尝试在 版本设置 - 自动安装 中更换 OptiFine 的版本。 \n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.modlauncher_8=当前游戏因为您所使用的 Forge 版本与当前使用的 Java 冲突崩溃。请尝试更新 Forge,或使用 Java 8u312/11.0.13/17.0.1 及更早版本启动\n <a href="https://www.oracle.com/java/technologies/javase/javase8u211-later-archive-downloads.html#:~:text=Java%20SE%20Development%20Kit%208u311">8u312 及更早版本下载</a> <a href="https://www.oracle.com/java/technologies/javase/jdk11-archive-downloads.html#:~:text=Java%20SE%20Development%20Kit%2011.0.13">11.0.13 及更早版本下载</a> <a href="https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html#:~:text=Java%20SE%20Development%20Kit%2017.0.1">17.0.1 及更早版本下载</a>\n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.no_class_def_found_error=当前游戏因为代码不完整,无法继续运行。\n你的游戏可能缺失了某个 Mod,或者某些 Mod 文件不完整,或者 Mod 与游戏的版本不匹配。\n你可能需要重新安装游戏和 Mod,或请求他人帮助。\n缺失:\n%1$s \n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
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.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> 寻求帮助
game.crash.reason.unknown=原因未知,请点击日志按钮查看详细信息!\n<b>!!!请不要将本界面【截图】给他人!!!</b>如果你要求助他人,请你点击左下角导出游戏崩溃信息后将导出的文件发送给他人以供分析!!!
game.crash.reason.unsatisfied_link_error=当前游戏因为缺少本地库,无法继续运行。\n这些本地库缺失:%1$s。\n如果你在全局(特定)游戏设置中修改了本地库路径选项,请你修改回预设模式。\n如果已经在预设模式下,请检查本地库缺失是否是 Mod 引起的,或由 HMCL 引起的。如果你确定是 HMCL 引起的,建议你向我们反馈。\n如果你确实需要自定义本地库路径,你需要保证其中包含缺失的本地库! \n可在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助
game.crash.reason.mod_name=当前游戏因为 Mod 文件名称问题,无法继续运行。\nMod 文件名称应只使用英文全半角的大小写字母(Aa~Zz)、数字(0~9)、横线(-)、下划线(_)和点(.)。\n请到 Mod 文件夹中将所有不合规的 Mod 文件名称添加一个上述的合规的字符。\n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.incomplete_forge_installation=当前游戏因为 Forge 安装不完整,无法继续运行。\n请在 版本设置 - 自动安装 中卸载 Forge 并重新安装。\n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.optifine_is_not_compatible_with_forge=当前游戏因为 OptiFine 与当前版本的 Forge 不兼容,导致了游戏崩溃。\n请前往 <a href="https://optifine.net/downloads">OptiFine 官网</a> 查看 OptiFine 所兼容的 Forge 版本,并严格按照对应版本重新安装游戏或在 版本设置 - 自动安装 中更换版本。\n经测试,Forge 版本过高或过低都可能导致崩溃。 \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.mod_files_are_decompressed=当前游戏因为 Mod 文件被解压了,无法继续运行。\n请直接把整个 Mod 文件放进 Mod 文件夹中即可。\n解压 Mod 会导致游戏出错,请删除 Mod 文件夹中已被解压的 Mod,然后再启动游戏。 \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.too_many_mods_lead_to_exceeding_the_id_limit=当前游戏因为您所安装的 Mod 过多,超出了游戏的 ID 限制,无法继续运行。\n请尝试安装 <a href="https://www.curseforge.com/minecraft/mc-mods/jeid">JEID</a> 等修复 Mod,或删除部分大型 Mod。 \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.optifine_causes_the_world_to_fail_to_load=当前游戏可能因为 OptiFine ,无法继续运行。\n该问题只在特定 OptiFine 版本中出现,你可以尝试在 版本设置 - 自动安装 中更换 OptiFine 的版本。 \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.modlauncher_8=当前游戏因为您所使用的 Forge 版本与当前使用的 Java 冲突崩溃,请尝试更新 Forge。\n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.reason.no_class_def_found_error=当前游戏因为代码不完整,无法继续运行。\n你的游戏可能缺失了某个 Mod,或者某些 Mod 文件不完整,或者 Mod 与游戏的版本不匹配。\n你可能需要重新安装游戏和 Mod,或请求他人帮助。\n缺失:\n%1$s \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
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.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> 寻求帮助。
game.crash.reason.unknown=原因未知,请点击日志按钮查看详细信息。\n<b>请不要将本界面截图给他人!</b>如果你要求助他人,请你点击左下角导出游戏崩溃信息后将导出的文件发送给他人以供分析。
game.crash.reason.unsatisfied_link_error=当前游戏因为缺少本地库,无法继续运行。\n这些本地库缺失:%1$s。\n如果你在全局(特定)游戏设置中修改了本地库路径选项,请你修改回预设模式。\n如果已经在预设模式下,请检查本地库缺失是否是 Mod 引起的,或由 HMCL 引起的。如果你确定是 HMCL 引起的,建议你向我们反馈。\n如果你确实需要自定义本地库路径,你需要保证其中包含缺失的本地库! \n你可以在 <a href="https://docs.hmcl.net/help.html ">此处</a> 寻求帮助。
game.crash.title=游戏意外退出
game.directory=游戏路径
game.version=游戏版本
@@ -451,14 +471,14 @@ install.failed.downloading.timeout=下载超时:%s
install.failed.install_online=无法识别要安装的组件。如果你要安装 Mod,你需要在模组管理页面安装模组。
install.failed.malformed=下载的文件格式损坏。您可以在设置-下载中切换到其他下载源来尝试解决此问题。
install.failed.optifine_conflict=暂不支持 OptiFine, Fabric 或 OptiFine , Forge 同时安装在 Minecraft 1.13 及以上版本
install.failed.optifine_forge_1.17=Minecraft 1.17.1 下,仅 OptiFine H1 Pre2 及以上版本能兼容 Forge。你可以从 OptiFine 测试版中选择最新版本。
install.failed.optifine_forge_1.17=Minecraft 1.17.1 下,仅 OptiFine H1 Pre2 及以上版本能兼容 Forge。你可以从 OptiFine 测试版中选择最新版本。
install.failed.version_mismatch=该组件需要的游戏版本为 %s,但实际的游戏版本为 %s。
install.installer.change_version=%s,该版本与当前游戏不兼容,您需要点击此处更换版本或删除
install.installer.choose=选择 %s 版本
install.installer.depend=需要先安装 %s
install.installer.fabric=Fabric
install.installer.fabric-api=Fabric API
install.installer.fabric-api.warning=警告:Fabric API 是一个 Mod,将会被安装到新游戏的 Mod 文件夹,请你在安装游戏后不要修改当前游戏的版本隔离/游戏运行路径设置,如果你在之后修改了相关设置,Fabric API 需要被重新安装!
install.installer.fabric-api.warning=警告:Fabric API 是一个 Mod,将会被安装到新游戏的 Mod 文件夹,请你在安装游戏后不要修改当前游戏的版本隔离/游戏运行路径设置,如果你在之后修改了相关设置,Fabric API 需要被重新安装。
install.installer.forge=Forge
install.installer.game=Minecraft
install.installer.incompatible=与 %s 不兼容
@@ -508,8 +528,8 @@ launch.advice.vanilla_linux_java_8=对于 Linux x86-64 平台,Minecraft 1.12.2
launch.advice.vanilla_x86.translation=Minecraft 尚未为您的平台提供完善支持,所以可能影响游戏体验或无法启动游戏。\n你可以在 <a href="https://docs.microsoft.com/java/openjdk/download"> 这里 </a> 下载 <b> X86-64 </b> 架构的 Java 以获得更完整的体验。
launch.failed=启动失败
launch.failed.cannot_create_jvm=截获到无法创建 Java 虚拟机,可能是 Java 参数有问题,可以在设置中开启无参数模式启动。
launch.failed.creating_process=启动失败,在创建新进程时发生错误,可能是 Java 路径错误……
launch.failed.command_too_long=命令长度超过限制,无法创建 bat 脚本,请导出为 PowerShell 脚本!\n若不清楚如何导出为 PowerShell 脚本,请前往 设置 - 反馈 寻求帮助
launch.failed.creating_process=启动失败,在创建新进程时发生错误,可能是 Java 路径错误。
launch.failed.command_too_long=命令长度超过限制,无法创建 bat 脚本,请导出为 PowerShell 脚本。
launch.failed.decompressing_natives=未能解压游戏本地库。
launch.failed.download_library=未能下载游戏依赖 %s
launch.failed.executable_permission=未能为启动文件添加执行权限。
@@ -545,7 +565,7 @@ launcher.cache_directory.disabled=禁用(总是使用游戏路径)
launcher.cache_directory.invalid=无法创建自定义的缓存目录,恢复默认设置
launcher.contact=联系我们
launcher.crash=Hello Minecraft! Launcher 遇到了无法处理的错误,请复制下列内容并点击右下角的按钮反馈 bug。
launcher.crash_out_dated=Hello Minecraft! Launcher 遇到了无法处理的错误,已检测到您的启动器不是最新版本,请更新后再试……
launcher.crash_out_dated=Hello Minecraft! Launcher 遇到了无法处理的错误,已检测到您的启动器不是最新版本,请更新后再试。
launcher.update_java=请更新您的 Java
login.empty_username=你还未设置用户名!
@@ -603,7 +623,7 @@ modpack.files.servers_dat=多人游戏服务器列表
modpack.install=安装 %s 整合包
modpack.installing=正在安装整合包
modpack.introduction=支持 Curse、Modrinth、MultiMC、MCBBS 整合包。
modpack.invalid=无效的整合包升级文件,可能是下载时出现问题……
modpack.invalid=无效的整合包升级文件,可能是下载时出现问题。
modpack.mismatched_type=整合包类型不匹配,当前游戏是 %s 整合包,但是提供的整合包更新文件是 %s 整合包。
modpack.name=整合包名称
modpack.not_a_valid_name=不是一个有效的整合包名称
@@ -616,19 +636,19 @@ modpack.task.install=导入整合包
modpack.task.install.error=无法识别该整合包,目前仅支持导入 Curse、Modrinth、MultiMC、MCBBS 整合包。
modpack.task.install.will=将会安装整合包:
modpack.type.curse=Curse
modpack.type.curse.tolerable_error=但未能完成该整合包所需的依赖下载,您可以在启动该游戏版本时继续该整合包文件的下载。由于网络问题,您可能需要重试多次……
modpack.type.curse.error=未能完成该整合包所需的依赖下载,请多次重试或设置代理……
modpack.type.curse.tolerable_error=但未能完成该整合包所需的依赖下载,您可以在启动该游戏版本时继续该整合包文件的下载。由于网络问题,您可能需要重试多次。
modpack.type.curse.error=未能完成该整合包所需的依赖下载,请多次重试或设置代理。
modpack.type.curse.not_found=部分必需文件已经在网络中被删除并且再也无法下载,请尝试该整合包的最新版本或者安装其他整合包。
modpack.type.manual.warning=您大概需要直接解压该整合包,并使用其自带的启动器即可开始游戏,而不需要导入整合包!该整合包不是由启动器导出的整合包,而是人工打包 .minecraft 文件夹而来的,这类整合包通常附带游戏启动器。HMCL 可以尝试导入该整合包,但不保证可用性,是否继续?
modpack.type.manual.warning=该整合包由发布者手动打包,其中可能已经包含启动器,建议尝试解压后使用其自带的启动器运行游戏。\nHMCL 可以尝试导入该整合包,但不保证可用性,是否继续?
modpack.type.mcbbs=我的世界中文论坛整合包标准
modpack.type.mcbbs.export=可以被 Hello Minecraft! Launcher (HMCL) 和 MultiMC 导入
modpack.type.mcbbs.export=可以被 Hello Minecraft! Launcher 和 MultiMC 导入
modpack.type.modrinth=Modrinth
modpack.type.multimc=MultiMC
modpack.type.multimc.export=可以被 Hello Minecraft! Launcher (HMCL) 和 MultiMC 导入
modpack.type.multimc.export=可以被 Hello Minecraft! Launcher 和 MultiMC 导入
modpack.type.server=服务器自动更新整合包
modpack.type.server.export=允许服务器管理员远程更新游戏客户端
modpack.type.server.malformed=服务器整合包配置格式错误,请联系服务器管理员解决此问题
modpack.unsupported=该整合包不被支持。仅 Curse、Modrinth、MultiMC、MCBBS 整合包受支持!
modpack.unsupported=Hello Minecraft! Launcher 不支持该整合包格式
modpack.update=正在升级整合包
modpack.wizard=导出整合包向导
modpack.wizard.step.1=基本设置
@@ -712,72 +732,14 @@ mods.mcmod.page=MC 百科页面
mods.mcmod.search=MC 百科搜索
mods.modrinth=Modrinth
mods.name=名称
mods.not_modded=你需要先在自动安装页面安装 Fabric、Forge 或 LiteLoader 才能进行模组管理!
mods.not_modded=你需要先在自动安装页面安装 Fabric、Forge 或 LiteLoader 才能进行模组管理。
mods.restore=回退
mods.url=官方页面
mods.update_modpack_mod.warning=更新整合包中的 Mod 可能导致整合包损坏,使整合包无法正常启动。该操作不可逆,确定要更新吗?
multiplayer=多人联机
multiplayer.agreement.prompt=多人联机功能由 速聚 提供。使用前,你需要先同意多人联机服务提供方 速聚 的用户协议与免责声明。\n你需要了解,HMCL 仅为 速聚 提供多人联机服务入口,使用中遇到的任何问题由 速聚 负责处理。\n您在使用多人联机服务过程中所遇到的任何问题与纠纷(包括其付费业务)均与 HMCL 无关,请与 速聚 协商解决。
multiplayer.download=正在下载依赖……
multiplayer.download.failed=初始化失败,部分文件未能完成下载……
multiplayer.download.success=多人联机初始化完成√
multiplayer.download.unsupported=多人联机依赖不支持当前系统或平台!
multiplayer.error.failed_get_device=HiPer 无法创建网络设备,可能是 HiPer 正在运行中。请关闭正在运行中的其他启动器,或通过任务管理器终止 HiPer。
multiplayer.error.failed_load_config=HiPer 初始化失败,可能是联机索引码存在问题或索引码已过期,请检查索引码是否输入正确!你可向多人联机提供方联系寻求帮助。
multiplayer.error.failed_sudo.linux=HiPer 需要管理员权限才能运行,你可以通过设置环境变量 HMCL_ASKPASS 配置 HiPer 如何申请权限。\n若将 HMCL_ASKPASS 设置为 'false',请通过配置 sudoers 文件允许 '%s' 免密获取 root 权限,重启后再次启动 HMCL 即可;\n若将 HMCL_ASKPASS 设置为 'user',请先配置环境变量 SUDO_ASKPASS,HMCL 将使用 sudo --askpass 启动 HiPer (具体请参见 man sudo)。
multiplayer.error.failed_sudo.mac=HiPer 需要管理员权限才能运行。是否要授予 HiPer 管理员权限?\n点击“是”授予 HiPer 管理员权限,然后重启计算机后再次打开启动器即可联机。
multiplayer.error.failed_sudo.windows=HiPer 需要管理员权限才能运行,请使用管理员权限重新启动 HMCL。是否查看教程?
multiplayer.error.file_not_found=已发现 HiPer 有可用的更新,请重新进入多人联机页面以更新 HiPer。
multiplayer.error.session_expired=本次使用时间已结束,若要继续使用,请更换联机索引码
multiplayer.exit=HiPer 意外退出,退出码 %d
multiplayer.help.1=管理员权限打开
multiplayer.help.2=多人联机教程
multiplayer.help.3=创建方帮助
multiplayer.help.4=参与者帮助
multiplayer.help.text=测试能否联机
multiplayer.hint=多人联机服务正在维护中。
multiplayer.hint.details=查看详情
multiplayer.persistence=授权信息管理
multiplayer.persistence.export=将授权信息导出至文件
multiplayer.persistence.export.button=导出授权文件
multiplayer.persistence.export.failed=导出授权文件失败,请检查您所选择的保存路径。
multiplayer.persistence.export.file_not_exists=获取授权文件失败,请检查索引码是否输入正确。
multiplayer.persistence.export.title=选择授权文件保存路径
multiplayer.persistence.hint=此功能为长期授权 (有效期超过 24 小时) 用户提供,如果你正在使用短期授权 (有效期不超过 24 小时),则无需使用本功能。\n当你使用长期授权时,请在获取授权后的 24 小时内使用索引码启动 HiPer,否则 HiPer 将无法获取授权信息。\n随后,你可以通过下方的“导出授权信息”功能保存授权文件。\n请注意妥善保管授权文件,在获取授权 24 小时后,该文件是你的长期授权的唯一凭证,丢失该文件将无法通过索引码进行联机。
multiplayer.persistence.import=从文件导入授权信息
multiplayer.persistence.import.button=导入授权文件
multiplayer.persistence.import.failed=导入授权文件失败。
multiplayer.persistence.import.file_already_exists=授权文件已经存在。
multiplayer.persistence.import.title=选择授权文件
multiplayer.persistence.import.token_not_match=你选择的授权文件可能与索引码不匹配,是否继续导入?
multiplayer.persistence.license_file=授权文件
multiplayer.powered_by=HMCL 多人联机由 (<a href="https://mcer.cn">速聚</a>) 授权提供 <a href\="https://docs.hmcl.net/multiplayer/#%E4%BD%BF%E7%94%A8%E6%8E%88%E6%9D%83">授权协议</a>
multiplayer.report=违法违规举报
multiplayer.token=索引码
multiplayer.token.apply=获取联机索引码
multiplayer.token.expired=HiPer 证书过期,请更换联机索引码!
multiplayer.token.format_invalid=索引码格式不正确
multiplayer.token.invalid=索引码不正确。
multiplayer.token.malformed=HiPer 配置文件无法解析,可能是输入的联机索引码有误,或是获取证书服务器宕机。\n请检查输入的联机索引码!你可向多人联机提供方联系寻求帮助。
multiplayer.token.prompt=你需要联机索引码才能使用多人联机!点击“获取联机索引码”查看详情
multiplayer.off.hint=由于需要注册系统网络服务权限,启动 HiPer 时将会向用户申请管理员权限。若出现认证提示,允许以启动 HiPer。
multiplayer.off.start=启动 HiPer
multiplayer.master=创建方提示
multiplayer.master.hint=1.启动游戏\n2.选择单人游戏模式进入一个存档\n3.在游戏选项菜单内选择 对局域网开放 选项,之后你可以在游戏聊天框内看到游戏提示的端口号(通常是 4~5 位数字)\n4.点击下方的生成服务器地址按钮,输入端口号,你就可以获得你的服务器地址了\n5.该地址需要提供给其他需要加入服务器的玩家用于添加服务器。
multiplayer.master.server_address=生成服务器地址
multiplayer.master.server_address.copy=拷贝
multiplayer.master.video_tutorial=创建方视频教程
multiplayer.master.port=游戏局域网开放端口号
multiplayer.master.port.validate=在游戏聊天框中出现的端口号 (0~65535)
multiplayer.slave=参与者提示
multiplayer.slave.hint=1.要求创建方按照上方的 创建方提示 操作\n2.启动游戏\n3.选择多人游戏模式,选择添加服务器\n4.游戏会要求你输入服务器地址,你只需要向创建方索要服务器地址并输入,并进入服务器即可。
multiplayer.slave.hint2=1.一般情况下,参与者的游戏账户必须是 微软账户 或 外置登录账户(如 Little Skin),否则你需要将服务器地址输入至下方的输入框中并点击“加入”,在游戏中选择多人游戏模式,进入局域网世界方可加入,具体操作方法详见左侧的 帮助 \n2.一般情况下,参与者的游戏版本、模组要必须与创建方的一致,否则加入失败。
multiplayer.slave.server_address=创建方服务器地址
multiplayer.slave.server_address.start=加入
multiplayer.slave.server_address.stop=退出
multiplayer.slave.video_tutorial=参与者视频教程
multiplayer.session.expiration=索引码到期时间
datapack=数据包
datapack.add=添加数据包
@@ -788,7 +750,7 @@ datapack.title=世界 %s - 数据包
world=世界
world.add=添加世界
world.datapack=管理数据包
world.datapack.1_13=仅 Minecraft 1.13 及之后的版本支持数据包!
world.datapack.1_13=仅 Minecraft 1.13 及之后的版本支持数据包
world.description=%s。 上一次游戏时间: %s。 游戏版本: %s。
world.download=存档下载
world.export=导出此世界
@@ -869,6 +831,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\
@@ -879,10 +843,12 @@ settings.advanced.custom_commands.hint=自定义命令被调用时将包含如
\ - $INST_FORGE: 若安装了 Forge,将会存在本环境变量\n\
\ - $INST_LITELOADER: 若安装了 LiteLoader,将会存在本环境变量\n\
\ - $INST_OPTIFINE: 若安装了 OptiFine,将会存在本环境变量\n\
\ - $INST_FABRIC: 若安装了 Fabric,将会存在本环境变量
\ - $INST_FABRIC: 若安装了 Fabric,将会存在本环境变量\n\
\ - $INST_QUILT: 若安装了 Quilt,将会存在本环境变量
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=内存永久保存区域
@@ -915,13 +881,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 ..."
@@ -942,7 +912,7 @@ settings.game.java_directory.template=%s(%s)
settings.game.management=管理
settings.game.working_directory=版本隔离(建议使用模组时开启“各版本隔离”,改后需移动存档模组等相关游戏文件)
settings.game.working_directory.choose=选择运行路径
settings.game.working_directory.hint=在“运行路径(版本隔离)”选项中开启“各版本独立”使当前版本独立存放设置、存档、模组等数据,使用模组时建议开启此选项以避免不同版本模组冲突。修改此选项后请自行移动存档等文件!
settings.game.working_directory.hint=在“运行路径(版本隔离)”选项中开启“各版本独立”使当前版本独立存放设置、存档、模组等数据,使用模组时建议开启此选项以避免不同版本模组冲突。修改此选项后请自行移动存档等文件。
settings.icon=游戏图标
@@ -977,6 +947,7 @@ settings.launcher.proxy.socks=Socks
settings.launcher.proxy.username=帐户
settings.launcher.theme=主题
settings.launcher.title_transparent=标题栏透明
settings.launcher.turn_off_animations=关闭动画 (重启后生效)
settings.launcher.version_list_source=版本列表源
settings.memory=游戏内存
@@ -998,7 +969,7 @@ settings.type.global.manage=全局游戏设置
settings.type.global.edit=编辑全局版本设置
settings.type.special.enable=启用游戏特定设置(不影响其他游戏版本)
settings.type.special.edit=编辑游戏特定设置
settings.type.special.edit.hint=当前游戏版本 [%s] 启用了游戏特定设置,因此本页面选项不对该游戏生效!点击链接前往该游戏版本的游戏特定设置页
settings.type.special.edit.hint=当前游戏版本 [%s] 启用了游戏特定设置,因此本页面选项不对该游戏生效。点击链接前往该游戏版本的游戏特定设置页
sponsor=赞助
sponsor.bmclapi=国内下载源由 BMCLAPI 和我的世界中文论坛 (MCBBS) 提供高速下载服务。BMCLAPI 为公益服务,赞助 BMCLAPI 可以帮助作者更好的提供稳定高速的下载服务,[点击此处查阅详细信息]
@@ -1016,13 +987,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=正在检查更新
@@ -1067,10 +1037,11 @@ version.manage.remove=删除该版本
version.manage.remove.confirm=真的要删除版本 %s 吗?你将无法找回被删除的文件!!!
version.manage.remove.confirm.trash=真的要删除版本 %s 吗?你可以在系统的回收站中恢复文件夹 %s 来找回该版本。
version.manage.remove.confirm.independent=由于该游戏使用了版本隔离,所以删除该版本将导致该游戏的存档等数据一同被删除!真的要删除版本 %s 吗?
version.manage.remove_assets=删除所有游戏资源文件
version.manage.remove_libraries=删除所有库文件
version.manage.rename=重命名该版本
version.manage.rename.message=请输入要修改的名称
version.manage.rename.fail=重命名版本失败,可能文件被占用或者名字有特殊字符……
version.manage.rename.fail=重命名版本失败,可能文件被占用或者名字有特殊字符
version.settings=游戏设置
version.update=更新整合包
+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
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 941 KiB

+6 -10
View File
@@ -4,17 +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")
api("org.apache.commons:commons-lang3:3.12.0")
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;
}
@@ -273,6 +273,6 @@ public class YggdrasilService {
.create();
public static final String PROFILE_URL = "https://aka.ms/MinecraftMigration";
public static final String MIGRATION_FAQ_URL = "https://help.minecraft.net/hc/en-us/articles/360050865492-JAVA-Account-Migration-FAQ";
public static final String PURCHASE_URL = "https://www.minecraft.net/store/minecraft-java-bedrock-edition-pc";
public static final String MIGRATION_FAQ_URL = "https://help.minecraft.net/articles/360050865492";
public static final String PURCHASE_URL = "https://www.microsoft.com/store/productId/9NXP44L49SHJ";
}
@@ -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")
@@ -88,7 +88,7 @@ public final class CrashReportAnalyzer {
MODLAUNCHER_8(Pattern.compile("java\\.lang\\.NoSuchMethodError: ('void sun\\.security\\.util\\.ManifestEntryVerifier\\.<init>\\(java\\.util\\.jar\\.Manifest\\)'|sun\\.security\\.util\\.ManifestEntryVerifier\\.<init>\\(Ljava/util/jar/Manifest;\\)V)")),
// Manually triggerd debug crash
DEBUG_CRASH(Pattern.compile("Manually triggered debug crash")),
CONFIG(Pattern.compile("Failed loading config file (?<file>.*?) of type SERVER for modid (?<id>.*)"), "id", "file"),
CONFIG(Pattern.compile("Failed loading config file (?<file>.*?) of type (.*?) for modid (?<id>.*)"), "id", "file"),
// Fabric gives some warnings
FABRIC_WARNINGS(Pattern.compile("(Warnings were found!|Incompatible mod set!)(.*?)[\\n\\r]+(?<reason>[^\\[]+)\\["), "reason"),
// Game crashed when ticking entity
@@ -113,6 +113,15 @@ public final class CrashReportAnalyzer {
FORGE_REPEAT_INSTALLATION(Pattern.compile("--launchTarget, fmlclient, --fml.forgeVersion,[\\w\\W]*?--launchTarget, fmlclient, --fml.forgeVersion,[\\w\\W\\n\\r]*?MultipleArgumentsForOptionException: Found multiple arguments for option gameDir, but you asked for only one")),//https://github.com/huanghongxun/HMCL/issues/1880
OPTIFINE_REPEAT_INSTALLATION(Pattern.compile("ResolutionException: Module optifine reads another module named optifine")),//Optifine 重复安装(及Mod文件夹有,自动安装也有)
JAVA_VERSION_IS_TOO_HIGH(Pattern.compile("(Unable to make protected final java\\.lang\\.Class java\\.lang\\.ClassLoader\\.defineClass|java\\.lang\\.NoSuchFieldException: ucp|Unsupported class file major version|because module java\\.base does not export|java\\.lang\\.ClassNotFoundException: jdk\\.nashorn\\.api\\.scripting\\.NashornScriptEngineFactory|java\\.lang\\.ClassNotFoundException: java\\.lang\\.invoke\\.LambdaMetafactory)")),//Java版本过高
//Forge 默认会把每一个 mod jar 都当做一个 JPMS 的模块(Module)加载。在这个 jar 没有给出 module-info 声明的情况下,JPMS 会采用这样的顺序决定 module 名字:
//1. META-INF/MANIFEST.MF 里的 Automatic-Module-Name
//2. 根据文件名生成。文件名里的 .jar 后缀名先去掉,然后检查是否有 -(\\d+(\\.|$)) 的部分,有的话只取 - 前面的部分,- 后面的部分成为 module 的版本号(即尝试判断文件名里是否有版本号,有的话去掉),然后把不是拉丁字母和数字的字符(正则表达式 [^A-Za-z0-9])都换成点,然后把连续的多个点换成一个点,最后去掉开头和结尾的点。那么
//按照 2.,如果你的文件名是拔刀剑.jar,那么这么一通流程下来,你得到的 module 名就是空字符串,而这是不允许的。(来自 @Föhn 说明)
MOD_NAME(Pattern.compile("Invalid module name: '' is not a Java identifier")),
//Forge 安装不完整
INCOMPLETE_FORGE_INSTALLATION(Pattern.compile("(java\\.io\\.UncheckedIOException: java\\.io\\.IOException: Invalid paths argument, contained no existing paths: \\[(.*?)\\\\libraries\\\\net\\\\minecraftforge\\\\forge\\\\(.*?)\\\\forge-(.*?)-client\\.jar\\]|Failed to find Minecraft resource version (.*?) at (.*?)\\\\libraries\\\\net\\\\minecraftforge\\\\forge\\\\(.*?)\\\\forge-(.*?)-client\\.jar|Cannot find launch target fmlclient, unable to launch)")),
// TwilightForest is not compatible with OptiFine on Minecraft 1.16
TWILIGHT_FOREST_OPTIFINE(Pattern.compile("java.lang.IllegalArgumentException: (.*) outside of image bounds (.*)"));
@@ -186,7 +186,7 @@ public class DefaultGameRepository implements GameRepository {
} catch (JsonParseException ignored) {
}
LOG.warning("Cannot parse version json + " + file.toString() + "\n" + jsonText);
LOG.warning("Cannot parse version json: " + file.toString() + "\n" + jsonText);
throw new JsonParseException("Version json incorrect");
}
@@ -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);
@@ -17,50 +17,51 @@
*/
package org.jackhuang.hmcl.game;
import org.apache.commons.lang3.Range;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.util.Lang;
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.versioning.VersionNumber;
import org.jackhuang.hmcl.util.versioning.VersionRange;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Objects;
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LAUNCH_WRAPPER_MAIN;
import static org.jackhuang.hmcl.util.versioning.VersionRange.*;
public enum JavaVersionConstraint {
// Minecraft>=1.13 requires Java 8
VANILLA_JAVA_8(JavaVersionConstraint.RULE_MANDATORY, versionRange("1.13", JavaVersionConstraint.MAX), versionRange("1.8", JavaVersionConstraint.MAX)),
VANILLA_JAVA_8(JavaVersionConstraint.RULE_MANDATORY, atLeast("1.13"), atLeast("1.8")),
// Minecraft 1.17 requires Java 16
VANILLA_JAVA_16(JavaVersionConstraint.RULE_MANDATORY, versionRange("1.17", JavaVersionConstraint.MAX), versionRange("16", JavaVersionConstraint.MAX)),
VANILLA_JAVA_16(JavaVersionConstraint.RULE_MANDATORY, atLeast("1.17"), atLeast("16")),
// Minecraft>=1.18 requires Java 17
VANILLA_JAVA_17(JavaVersionConstraint.RULE_MANDATORY, versionRange("1.18", JavaVersionConstraint.MAX), versionRange("17", JavaVersionConstraint.MAX)),
VANILLA_JAVA_17(JavaVersionConstraint.RULE_MANDATORY, atLeast("1.18"), atLeast("17")),
// Minecraft<=1.7.2+Forge requires Java<=7, But LegacyModFixer may fix that problem. So only suggest user using Java 7.
MODDED_JAVA_7(JavaVersionConstraint.RULE_SUGGESTED, versionRange(JavaVersionConstraint.MIN, "1.7.2"), versionRange(JavaVersionConstraint.MIN, "1.7.999")) {
MODDED_JAVA_7(JavaVersionConstraint.RULE_SUGGESTED, atMost("1.7.2"), atMost("1.7.999")) {
@Override
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
return version != null && analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE);
}
},
MODDED_JAVA_8(JavaVersionConstraint.RULE_SUGGESTED, versionRange("1.7.10", "1.16.999"), versionRange("1.8", "1.8.999")) {
MODDED_JAVA_8(JavaVersionConstraint.RULE_SUGGESTED, between("1.7.10", "1.16.999"), between("1.8", "1.8.999")) {
@Override
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE);
}
},
MODDED_JAVA_16(JavaVersionConstraint.RULE_SUGGESTED, versionRange("1.17", "1.17.999"), versionRange("16", "16.999")) {
MODDED_JAVA_16(JavaVersionConstraint.RULE_SUGGESTED, between("1.17", "1.17.999"), between("16", "16.999")) {
@Override
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE);
}
},
MODDED_JAVA_17(JavaVersionConstraint.RULE_SUGGESTED, versionRange("1.18", JavaVersionConstraint.MAX), versionRange("17", "17.999")) {
MODDED_JAVA_17(JavaVersionConstraint.RULE_SUGGESTED, atLeast("1.18"), between("17", "17.999")) {
@Override
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
@@ -68,7 +69,7 @@ public enum JavaVersionConstraint {
}
},
// LaunchWrapper<=1.12 will crash because LaunchWrapper assumes the system class loader is an instance of URLClassLoader (Java 8)
LAUNCH_WRAPPER(JavaVersionConstraint.RULE_MANDATORY, versionRange("0", "1.12.999"), versionRange("0", "1.8.999")) {
LAUNCH_WRAPPER(JavaVersionConstraint.RULE_MANDATORY, atMost("1.12.999"), atMost("1.8.999")) {
@Override
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
@@ -80,9 +81,9 @@ public enum JavaVersionConstraint {
}
},
// Minecraft>=1.13 may crash when generating world on Java [1.8,1.8.0_51)
VANILLA_JAVA_8_51(JavaVersionConstraint.RULE_SUGGESTED, versionRange("1.13", JavaVersionConstraint.MAX), versionRange("1.8.0_51", JavaVersionConstraint.MAX)),
VANILLA_JAVA_8_51(JavaVersionConstraint.RULE_SUGGESTED, atLeast("1.13"), atLeast("1.8.0_51")),
// Minecraft with suggested java version recorded in game json is restrictedly constrained.
GAME_JSON(JavaVersionConstraint.RULE_MANDATORY, versionRange(JavaVersionConstraint.MIN, JavaVersionConstraint.MAX), versionRange(JavaVersionConstraint.MIN, JavaVersionConstraint.MAX)) {
GAME_JSON(JavaVersionConstraint.RULE_MANDATORY, VersionRange.all(), VersionRange.all()) {
@Override
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
@@ -92,19 +93,19 @@ public enum JavaVersionConstraint {
}
@Override
public Range<VersionNumber> getJavaVersionRange(Version version) {
public VersionRange getJavaVersionRange(Version version) {
String javaVersion;
if (Objects.requireNonNull(version.getJavaVersion()).getMajorVersion() >= 9) {
javaVersion = "" + version.getJavaVersion().getMajorVersion();
} else {
javaVersion = "1." + version.getJavaVersion().getMajorVersion();
}
return JavaVersionConstraint.versionRange(javaVersion, JavaVersionConstraint.MAX);
return atLeast(javaVersion);
}
},
// On Linux, JDK 9+ cannot launch Minecraft<=1.12.2, since JDK 9+ does not accept loading native library built in different arch.
// For example, JDK 9+ 64-bit cannot load 32-bit lwjgl native library.
VANILLA_LINUX_JAVA_8(JavaVersionConstraint.RULE_MANDATORY, versionRange("0", "1.12.999"), versionRange(JavaVersionConstraint.MIN, "1.8.999")) {
VANILLA_LINUX_JAVA_8(JavaVersionConstraint.RULE_MANDATORY, atMost("1.12.999"), atMost("1.8.999")) {
@Override
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
@@ -119,7 +120,7 @@ public enum JavaVersionConstraint {
}
},
// Minecraft currently does not provide official support for architectures other than x86 and x86-64.
VANILLA_X86(JavaVersionConstraint.RULE_SUGGESTED, versionRange(JavaVersionConstraint.MIN, JavaVersionConstraint.MAX), versionRange(JavaVersionConstraint.MIN, JavaVersionConstraint.MAX)) {
VANILLA_X86(JavaVersionConstraint.RULE_SUGGESTED, VersionRange.all(), VersionRange.all()) {
@Override
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
@@ -138,7 +139,7 @@ public enum JavaVersionConstraint {
}
},
// Minecraft 1.16+Forge with crash because JDK-8273826
MODLAUNCHER_8(JavaVersionConstraint.RULE_SUGGESTED, versionRange("1.16.3", "1.17.1"), versionRange(JavaVersionConstraint.MIN, JavaVersionConstraint.MAX)) {
MODLAUNCHER_8(JavaVersionConstraint.RULE_SUGGESTED, between("1.16.3", "1.17.1"), VersionRange.all()) {
@Override
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
@@ -158,7 +159,7 @@ public enum JavaVersionConstraint {
case "1.16.5":
return forgePatchVersion.compareTo(VersionNumber.asVersion("36.2.23")) <= 0;
case "1.17.1":
return versionRange("37.0.60", "37.0.75").contains(forgePatchVersion);
return between("37.0.60", "37.0.75").contains(forgePatchVersion);
default:
return false;
}
@@ -184,10 +185,10 @@ public enum JavaVersionConstraint {
};;
private final int type;
private final Range<VersionNumber> gameVersionRange;
private final Range<VersionNumber> javaVersionRange;
private final VersionRange gameVersionRange;
private final VersionRange javaVersionRange;
JavaVersionConstraint(int type, Range<VersionNumber> gameVersionRange, Range<VersionNumber> javaVersionRange) {
JavaVersionConstraint(int type, VersionRange gameVersionRange, VersionRange javaVersionRange) {
this.type = type;
this.gameVersionRange = gameVersionRange;
this.javaVersionRange = javaVersionRange;
@@ -197,11 +198,11 @@ public enum JavaVersionConstraint {
return type;
}
public Range<VersionNumber> getGameVersionRange() {
public VersionRange getGameVersionRange() {
return gameVersionRange;
}
public Range<VersionNumber> getJavaVersionRange(Version version) {
public VersionRange getJavaVersionRange(Version version) {
return javaVersionRange;
}
@@ -224,12 +225,12 @@ public enum JavaVersionConstraint {
public static final List<JavaVersionConstraint> ALL = Lang.immutableListOf(values());
public static VersionRanges findSuitableJavaVersionRange(VersionNumber gameVersion, Version version) {
Range<VersionNumber> mandatoryJavaRange = versionRange(MIN, MAX);
Range<VersionNumber> suggestedJavaRange = versionRange(MIN, MAX);
VersionRange mandatoryJavaRange = VersionRange.all();
VersionRange suggestedJavaRange = VersionRange.all();
LibraryAnalyzer analyzer = version != null ? LibraryAnalyzer.analyze(version) : null;
for (JavaVersionConstraint java : ALL) {
if (java.appliesToVersion(gameVersion, version, null, analyzer)) {
Range<VersionNumber> javaVersionRange = java.getJavaVersionRange(version);
VersionRange javaVersionRange = java.getJavaVersionRange(version);
if (java.type == RULE_MANDATORY) {
mandatoryJavaRange = mandatoryJavaRange.intersectionWith(javaVersionRange);
suggestedJavaRange = suggestedJavaRange.intersectionWith(javaVersionRange);
@@ -297,31 +298,20 @@ public enum JavaVersionConstraint {
public static final int RULE_MANDATORY = 1;
public static final int RULE_SUGGESTED = 2;
public static final String MIN = "0";
public static final String MAX = "10000";
public static final class VersionRanges {
private final VersionRange mandatory;
private final VersionRange suggested;
private static Range<VersionNumber> versionRange(String fromInclusive, String toExclusive) {
return Range.between(VersionNumber.asVersion(fromInclusive), VersionNumber.asVersion(toExclusive));
}
static Range<VersionNumber> versionIs(String version) {
return Range.is(VersionNumber.asVersion(version));
}
public static class VersionRanges {
private final Range<VersionNumber> mandatory;
private final Range<VersionNumber> suggested;
public VersionRanges(Range<VersionNumber> mandatory, Range<VersionNumber> suggested) {
public VersionRanges(VersionRange mandatory, VersionRange suggested) {
this.mandatory = mandatory;
this.suggested = suggested;
}
public Range<VersionNumber> getMandatory() {
public VersionRange getMandatory() {
return mandatory;
}
public Range<VersionNumber> getSuggested() {
public VersionRange getSuggested() {
return suggested;
}
}
@@ -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
}
@@ -180,22 +180,14 @@ public class DefaultLauncher extends Launcher {
res.addDefault("-Duser.home=", options.getGameDir().getParent());
// Using G1GC with its settings by default
if (options.getJava().getParsedVersion() >= JavaVersion.JAVA_8) {
boolean addG1Args = true;
for (String javaArg : options.getJavaArguments()) {
if ("-XX:-UseG1GC".equals(javaArg) || (javaArg.startsWith("-XX:+Use") && javaArg.endsWith("GC"))) {
addG1Args = false;
break;
}
}
if (addG1Args) {
res.addUnstableDefault("UnlockExperimentalVMOptions", true);
res.addUnstableDefault("UseG1GC", true);
res.addUnstableDefault("G1NewSizePercent", "20");
res.addUnstableDefault("G1ReservePercent", "20");
res.addUnstableDefault("MaxGCPauseMillis", "50");
res.addUnstableDefault("G1HeapRegionSize", "32m");
}
if (options.getJava().getParsedVersion() >= JavaVersion.JAVA_8
&& res.noneMatch(arg -> "-XX:-UseG1GC".equals(arg) || (arg.startsWith("-XX:+Use") && arg.endsWith("GC")))) {
res.addUnstableDefault("UnlockExperimentalVMOptions", true);
res.addUnstableDefault("UseG1GC", true);
res.addUnstableDefault("G1NewSizePercent", "20");
res.addUnstableDefault("G1ReservePercent", "20");
res.addUnstableDefault("MaxGCPauseMillis", "50");
res.addUnstableDefault("G1HeapRegionSize", "32m");
}
res.addUnstableDefault("UseAdaptiveSizePolicy", false);
@@ -215,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())
@@ -470,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);
@@ -495,6 +500,12 @@ public class DefaultLauncher extends Launcher {
if (analyzer.has(LibraryAnalyzer.LibraryType.OPTIFINE)) {
env.put("INST_OPTIFINE", "1");
}
if (analyzer.has(LibraryAnalyzer.LibraryType.QUILT)) {
env.put("INST_QUILT", "1");
}
env.putAll(options.getEnvironmentVariables());
return env;
}
@@ -38,9 +38,9 @@ public interface RemoteModRepository {
Type getType();
enum SortType {
POPULARITY,
NAME,
DATE_CREATED,
POPULARITY,
LAST_UPDATED,
AUTHOR,
TOTAL_DOWNLOADS
@@ -89,6 +89,7 @@ public interface RemoteModRepository {
}
String[] DEFAULT_GAME_VERSIONS = new String[]{
"1.20", "1.20.1",
"1.19.4", "1.19.3", "1.19.2", "1.19.1", "1.19",
"1.18.2", "1.18.1", "1.18",
"1.17.1", "1.17",
@@ -97,6 +97,8 @@ public class McbbsModpackExportTask extends Task<Void> {
addons.add(new McbbsModpackManifest.Addon(OPTIFINE.getPatchId(), optifineVersion)));
analyzer.getVersion(FABRIC).ifPresent(fabricVersion ->
addons.add(new McbbsModpackManifest.Addon(FABRIC.getPatchId(), fabricVersion)));
analyzer.getVersion(QUILT).ifPresent(quiltVersion ->
addons.add(new McbbsModpackManifest.Addon(QUILT.getPatchId(), quiltVersion)));
List<Library> libraries = new ArrayList<>();
// TODO libraries
@@ -0,0 +1,28 @@
package org.jackhuang.hmcl.util;
import java.util.Objects;
public final class Holder<T> {
public T value;
@Override
public int hashCode() {
return Objects.hashCode(value);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof Holder))
return false;
return Objects.equals(this.value, ((Holder<?>) obj).value);
}
@Override
public String toString() {
return "Holder[" + value + "]";
}
}
@@ -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;
}
@@ -211,6 +211,10 @@ public final class CommandBuilder {
return raw.removeIf(i -> pred.test(i.arg));
}
public boolean noneMatch(Predicate<String> predicate) {
return raw.stream().noneMatch(it -> predicate.test(it.arg));
}
@Override
public String toString() {
return raw.stream().map(i -> i.parse ? parse(i.arg) : i.arg).collect(Collectors.joining(" "));
@@ -293,7 +297,7 @@ public final class CommandBuilder {
}
public static String toBatchStringLiteral(String s) {
return containsEscape(s, " \t\"^&<>|") ? '"' + escape(s, '\\', '"') : s;
return containsEscape(s, " \t\"^&<>|") ? '"' + escape(s, '\\', '"') + '"' : s;
}
public static String toShellStringLiteral(String s) {
@@ -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) {
@@ -347,6 +469,14 @@ public class VersionNumber implements Comparable<VersionNumber> {
return canonical;
}
public VersionNumber min(VersionNumber that) {
return this.compareTo(that) <= 0 ? this : that;
}
public VersionNumber max(VersionNumber that) {
return this.compareTo(that) >= 0 ? this : that;
}
@Override
public boolean equals(Object o) {
return o instanceof VersionNumber && canonical.equals(((VersionNumber) o).canonical);
@@ -356,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);
}
@@ -0,0 +1,165 @@
package org.jackhuang.hmcl.util.versioning;
import java.util.Objects;
public final class VersionRange {
private static final VersionRange EMPTY = new VersionRange(null, null);
private static final VersionRange ALL = new VersionRange(null, null);
public static VersionRange empty() {
return EMPTY;
}
public static VersionRange all() {
return ALL;
}
public static VersionRange between(String minimum, String maximum) {
return between(VersionNumber.asVersion(minimum), VersionNumber.asVersion(maximum));
}
public static VersionRange between(VersionNumber minimum, VersionNumber maximum) {
assert minimum.compareTo(maximum) <= 0;
return new VersionRange(minimum, maximum);
}
public static VersionRange atLeast(String minimum) {
return atLeast(VersionNumber.asVersion(minimum));
}
public static VersionRange atLeast(VersionNumber minimum) {
assert minimum != null;
return new VersionRange(minimum, null);
}
public static VersionRange atMost(String maximum) {
return atMost(VersionNumber.asVersion(maximum));
}
public static VersionRange atMost(VersionNumber maximum) {
assert maximum != null;
return new VersionRange(null, maximum);
}
private final VersionNumber minimum;
private final VersionNumber maximum;
private VersionRange(VersionNumber minimum, VersionNumber maximum) {
this.minimum = minimum;
this.maximum = maximum;
}
public VersionNumber getMinimum() {
return minimum;
}
public VersionNumber getMaximum() {
return maximum;
}
public boolean isEmpty() {
return this == EMPTY;
}
public boolean isAll() {
return !isEmpty() && minimum == null && maximum == null;
}
public boolean contains(String versionNumber) {
if (versionNumber == null) return false;
return contains(VersionNumber.asVersion(versionNumber));
}
public boolean contains(VersionNumber versionNumber) {
if (versionNumber == null) return false;
if (isEmpty()) return false;
if (isAll()) return true;
return (minimum == null || minimum.compareTo(versionNumber) <= 0) && (maximum == null || maximum.compareTo(versionNumber) >= 0);
}
public boolean isOverlappedBy(final VersionRange that) {
if (this.isEmpty() || that.isEmpty())
return false;
if (this.isAll() || that.isAll())
return true;
if (this.minimum == null)
return that.minimum == null || that.minimum.compareTo(this.maximum) <= 0;
if (this.maximum == null)
return that.maximum == null || that.maximum.compareTo(this.minimum) >= 0;
return that.contains(minimum) || that.contains(maximum) || (that.minimum != null && contains(that.minimum));
}
public VersionRange intersectionWith(VersionRange that) {
if (this.isAll())
return that;
if (that.isAll())
return this;
if (!isOverlappedBy(that))
return EMPTY;
VersionNumber newMinimum;
if (this.minimum == null)
newMinimum = that.minimum;
else if (that.minimum == null)
newMinimum = this.minimum;
else
newMinimum = this.minimum.max(that.minimum);
VersionNumber newMaximum;
if (this.maximum == null)
newMaximum = that.maximum;
else if (that.maximum == null)
newMaximum = this.maximum;
else
newMaximum = this.maximum.min(that.maximum);
return new VersionRange(newMinimum, newMaximum);
}
@Override
public int hashCode() {
if (isEmpty())
return 1121763849; // Magic Number
if (isAll())
return -475303149; // Magic Number
return Objects.hash(minimum) ^ Objects.hash(maximum);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof VersionRange))
return false;
VersionRange that = (VersionRange) obj;
return this.isEmpty() == that.isEmpty() && this.isAll() == that.isAll()
&& Objects.equals(this.minimum, that.minimum)
&& Objects.equals(this.maximum, that.maximum);
}
@Override
public String toString() {
if (isEmpty())
return "EMPTY";
if (isAll())
return "ALL";
if (minimum == null)
return "At most " + maximum;
if (maximum == null)
return "At least " + minimum;
return "[" + minimum + ".." + maximum + "]";
}
}
@@ -209,6 +209,13 @@ public class CrashReportAnalyzerTest {
CrashReportAnalyzer.Rule.GRAPHICS_DRIVER);
}
@Test
public void modName() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/mod_name.txt")),
CrashReportAnalyzer.Rule.MOD_NAME);
}
@Test
public void openj9() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
@@ -347,6 +354,27 @@ public class CrashReportAnalyzerTest {
CrashReportAnalyzer.Rule.OPTIFINE_REPEAT_INSTALLATION);
}
@Test
public void incompleteForgeInstallation() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/incomplete_forge_installation.txt")),
CrashReportAnalyzer.Rule.INCOMPLETE_FORGE_INSTALLATION);
}
@Test
public void incompleteForgeInstallation2() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/incomplete_forge_installation2.txt")),
CrashReportAnalyzer.Rule.INCOMPLETE_FORGE_INSTALLATION);
}
@Test
public void incompleteForgeInstallation3() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/incomplete_forge_installation3.txt")),
CrashReportAnalyzer.Rule.INCOMPLETE_FORGE_INSTALLATION);
}
@Test
public void forgeRepeatInstallation() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
@@ -17,8 +17,8 @@
*/
package org.jackhuang.hmcl.game;
import org.apache.commons.lang3.Range;
import org.jackhuang.hmcl.util.versioning.VersionNumber;
import org.jackhuang.hmcl.util.versioning.VersionRange;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
@@ -32,8 +32,6 @@ public class JavaVersionConstraintTest {
null
);
assertEquals(
Range.between(VersionNumber.asVersion("16"), VersionNumber.asVersion(JavaVersionConstraint.MAX)),
range.getMandatory());
assertEquals(VersionRange.atLeast("16"), range.getMandatory());
}
}
@@ -1,124 +0,0 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2020 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 org.jackhuang.hmcl.util.versioning.VersionNumber;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
public class VersionNumberTest {
@Test
public void testCanonical() {
VersionNumber u, v;
v = VersionNumber.asVersion("3.2.0.0");
assertEquals("3.2", v.getCanonical());
v = VersionNumber.asVersion("3.2.0.0-5");
assertEquals("3.2-5", v.getCanonical());
v = VersionNumber.asVersion("3.2.0.0-0");
assertEquals("3.2", v.getCanonical());
v = VersionNumber.asVersion("3.2--------");
assertEquals("3.2", v.getCanonical());
v = VersionNumber.asVersion("1.7.2$%%^@&snapshot-3.1.1");
assertEquals("1.7.2$%%^@&snapshot-3.1.1", v.getCanonical());
}
@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);
}
@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",
"1.6.4",
"1.6.4-Forge9.11.1.1345",
"1.7.10",
"1.7.10-1614",
"1.7.10-1614-test",
"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.10forge1614_FTBInfinity_server",
"1.8",
"1.8-forge1577",
"1.8.9",
"1.8.9-forge1902",
"1.9");
input.sort(Comparator.comparing(VersionNumber::asVersion));
}
}
@@ -0,0 +1,153 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2020 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.versioning;
import org.junit.jupiter.api.Test;
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() {
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--------"));
}
@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"));
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"));
}
private static void assertLessThan(String s1, String s2) {
Supplier<String> messageSupplier = () -> String.format("%s should be less than %s", s1, s2);
VersionNumber v1 = VersionNumber.asVersion(s1);
VersionNumber v2 = VersionNumber.asVersion(s2);
assertTrue(v1.compareTo(v2) < 0, messageSupplier);
assertTrue(v2.compareTo(v1) > 0, messageSupplier);
}
@Test
public void testComparator() {
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() {
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.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.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",
"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);
}
}
@@ -0,0 +1,130 @@
package org.jackhuang.hmcl.util.versioning;
import org.junit.jupiter.api.Test;
import static org.jackhuang.hmcl.util.versioning.VersionRange.*;
import static org.junit.jupiter.api.Assertions.*;
public class VersionRangeTest {
@Test
public void testContains() {
assertTrue(between("10", "20").contains("10"));
assertTrue(between("10", "20").contains("15"));
assertTrue(between("10", "20").contains("20"));
assertFalse(between("10", "20").contains("5"));
assertFalse(between("10", "20").contains("25"));
assertTrue(between("10", "10").contains("10"));
assertFalse(between("10", "10").contains("5"));
assertFalse(between("10", "10").contains("15"));
assertTrue(atLeast("10").contains("10"));
assertTrue(atLeast("10").contains("20"));
assertFalse(atLeast("10").contains("5"));
assertTrue(atMost("10").contains("10"));
assertTrue(atMost("10").contains("5"));
assertFalse(atMost("10").contains("20"));
assertFalse(empty().contains("0"));
assertFalse(empty().contains("10"));
assertTrue(all().contains("0"));
assertTrue(all().contains("10"));
assertFalse(all().contains((String) null));
assertFalse(empty().contains((String) null));
assertFalse(between("0", "10").contains((String) null));
assertFalse(atLeast("10").contains((String) null));
assertFalse(atMost("10").contains((String) null));
assertFalse(all().contains((VersionNumber) null));
assertFalse(empty().contains((VersionNumber) null));
assertFalse(between("0", "10").contains((VersionNumber) null));
assertFalse(atLeast("10").contains((VersionNumber) null));
assertFalse(atMost("10").contains((VersionNumber) null));
}
private static void assertIsOverlappedBy(boolean value, VersionRange range1, VersionRange range2) {
assertEquals(value, range1.isOverlappedBy(range2));
assertEquals(value, range2.isOverlappedBy(range1));
}
@Test
public void testIsOverlappedBy() {
assertIsOverlappedBy(true, all(), all());
assertIsOverlappedBy(false, all(), empty());
assertIsOverlappedBy(false, empty(), empty());
assertIsOverlappedBy(true, all(), between("10", "20"));
assertIsOverlappedBy(true, all(), atLeast("10"));
assertIsOverlappedBy(true, all(), atMost("10"));
assertIsOverlappedBy(false, empty(), between("10", "20"));
assertIsOverlappedBy(false, empty(), atLeast("10"));
assertIsOverlappedBy(false, empty(), atMost("10"));
assertIsOverlappedBy(true, between("10", "20"), between("10", "20"));
assertIsOverlappedBy(true, between("10", "20"), between("5", "20"));
assertIsOverlappedBy(true, between("10", "20"), between("5", "15"));
assertIsOverlappedBy(true, between("10", "20"), between("5", "10"));
assertIsOverlappedBy(false, between("10", "20"), between("5", "5"));
assertIsOverlappedBy(true, between("10", "20"), between("10", "30"));
assertIsOverlappedBy(true, between("10", "20"), between("15", "30"));
assertIsOverlappedBy(true, between("10", "20"), between("20", "30"));
assertIsOverlappedBy(false, between("10", "20"), between("21", "30"));
assertIsOverlappedBy(true, between("10", "20"), atLeast("5"));
assertIsOverlappedBy(true, between("10", "20"), atLeast("10"));
assertIsOverlappedBy(true, between("10", "20"), atLeast("15"));
assertIsOverlappedBy(true, between("10", "20"), atLeast("20"));
assertIsOverlappedBy(false, between("10", "20"), atLeast("25"));
assertIsOverlappedBy(true, atLeast("10"), atLeast("10"));
assertIsOverlappedBy(true, atLeast("10"), atLeast("20"));
assertIsOverlappedBy(true, atLeast("10"), atLeast("5"));
assertIsOverlappedBy(true, atLeast("10"), atMost("10"));
assertIsOverlappedBy(true, atLeast("10"), atMost("20"));
assertIsOverlappedBy(false, atLeast("10"), atMost("5"));
}
private static void assertIntersectionWith(VersionRange range1, VersionRange range2, VersionRange result) {
assertEquals(result, range1.intersectionWith(range2));
assertEquals(result, range2.intersectionWith(range1));
}
@Test
public void testIntersectionWith() {
assertIntersectionWith(all(), all(), all());
assertIntersectionWith(all(), empty(), empty());
assertIntersectionWith(all(), between("10", "20"), between("10", "20"));
assertIntersectionWith(all(), atLeast("10"), atLeast("10"));
assertIntersectionWith(all(), atMost("10"), atMost("10"));
assertIntersectionWith(empty(), empty(), empty());
assertIntersectionWith(empty(), between("10", "20"), empty());
assertIntersectionWith(empty(), atLeast("10"), empty());
assertIntersectionWith(empty(), atMost("10"), empty());
assertIntersectionWith(between("10", "20"), between("10", "20"), between("10", "20"));
assertIntersectionWith(between("10", "20"), between("5", "20"), between("10", "20"));
assertIntersectionWith(between("10", "20"), between("10", "25"), between("10", "20"));
assertIntersectionWith(between("10", "20"), between("5", "25"), between("10", "20"));
assertIntersectionWith(between("10", "20"), between("15", "20"), between("15", "20"));
assertIntersectionWith(between("10", "20"), between("10", "15"), between("10", "15"));
assertIntersectionWith(between("10", "20"), between("14", "16"), between("14", "16"));
assertIntersectionWith(between("10", "20"), atLeast("5"), between("10", "20"));
assertIntersectionWith(between("10", "20"), atLeast("10"), between("10", "20"));
assertIntersectionWith(between("10", "20"), atLeast("15"), between("15", "20"));
assertIntersectionWith(between("10", "20"), atLeast("20"), between("20", "20"));
assertIntersectionWith(between("10", "20"), atLeast("25"), empty());
assertIntersectionWith(between("10", "20"), atMost("25"), between("10", "20"));
assertIntersectionWith(between("10", "20"), atMost("20"), between("10", "20"));
assertIntersectionWith(between("10", "20"), atMost("15"), between("10", "15"));
assertIntersectionWith(between("10", "20"), atMost("10"), between("10", "10"));
assertIntersectionWith(between("10", "20"), atMost("5"), empty());
assertIntersectionWith(atLeast("10"), atMost("10"), between("10", "10"));
assertIntersectionWith(atLeast("10"), atMost("20"), between("10", "20"));
assertIntersectionWith(atLeast("10"), atMost("5"), empty());
}
}
@@ -0,0 +1,38 @@
[authlib-injector] [INFO] Logging file: D:\❄❄❄❄❄❄❄❄\FileRecv\.minecraft\authlib-injector.log
[authlib-injector] [INFO] Version: 1.2.2
[authlib-injector] [INFO] Authentication server: http://localhost:59486
[authlib-injector] [WARNING] You are using HTTP protocol, which is INSECURE! Please switch to HTTPS if possible.
[11:25:59] [main/INFO]: ModLauncher running: args [--username, 666, --version, 1.19.2, --gameDir, D:\❄❄❄❄❄❄❄❄\FileRecv\.minecraft, --assetsDir, D:\❄❄❄❄❄❄❄❄\FileRecv\.minecraft\assets, --assetIndex, 1.19, --uuid, 1ee797995aa33433bd3790203ecd5ce7, --accessToken, ❄❄❄❄❄❄❄❄, --clientId, ${clientid}, --xuid, ${auth_xuid}, --userType, legacy, --versionType, HMCL 3.5.3.230, --width, 854, --height, 480, --launchTarget, forgeclient, --fml.forgeVersion, 43.1.47, --fml.mcVersion, 1.19.2, --fml.forgeGroup, net.minecraftforge, --fml.mcpVersion, 20220805.130853]
[11:26:00] [main/INFO]: ModLauncher 10.0.8+10.0.8+main.0ef7e830 starting: java version 17.0.1 by Microsoft; OS Windows 10 arch amd64 version 10.0
[11:26:00] [main/INFO]: SpongePowered MIXIN Subsystem Version=0.8.5 Source=union:/D:/❄❄❄❄❄❄❄❄/FileRecv/.minecraft/libraries/org/spongepowered/mixin/0.8.5/mixin-0.8.5.jar%2391!/ Service=ModLauncher Env=CLIENT
[11:26:00] [main/INFO]: Found mod file CTM-1.19.2-1.1.6+8.jar of type MOD with provider {mods folder locator at D:\❄❄❄❄❄❄❄❄\FileRecv\.minecraft\mods}
[11:26:00] [main/INFO]: Found mod file Patchouli-1.19.2-77.jar of type MOD with provider {mods folder locator at D:\❄❄❄❄❄❄❄❄\FileRecv\.minecraft\mods}
[11:26:00] [main/INFO]: Found mod file twilightforest-fabric-1.19.2-4.2.301.jar of type MOD with provider {mods folder locator at D:\❄❄❄❄❄❄❄❄\FileRecv\.minecraft\mods}
Exception in thread "main" java.io.UncheckedIOException: java.io.IOException: Invalid paths argument, contained no existing paths: [D:\❄❄❄❄❄❄❄❄\FileRecv\.minecraft\libraries\net\minecraft\client\1.19.2-20220805.130853\client-1.19.2-20220805.130853-srg.jar, D:\❄❄❄❄❄❄❄❄\FileRecv\.minecraft\libraries\net\minecraft\client\1.19.2-20220805.130853\client-1.19.2-20220805.130853-extra.jar, D:\❄❄❄❄❄❄❄❄\FileRecv\.minecraft\libraries\net\minecraftforge\forge\1.19.2-43.1.47\forge-1.19.2-43.1.47-client.jar]
at cpw.mods.securejarhandler/cpw.mods.jarhandling.impl.Jar.<init>(Jar.java:82)
at cpw.mods.securejarhandler/cpw.mods.jarhandling.SecureJar.from(SecureJar.java:70)
at MC-BOOTSTRAP/fmlloader@1.19.2-43.1.47/net.minecraftforge.fml.loading.moddiscovery.ModJarMetadata.buildFile(ModJarMetadata.java:41)
at MC-BOOTSTRAP/fmlloader@1.19.2-43.1.47/net.minecraftforge.fml.loading.moddiscovery.MinecraftLocator.scanMods(MinecraftLocator.java:37)
at MC-BOOTSTRAP/fmlloader@1.19.2-43.1.47/net.minecraftforge.fml.loading.moddiscovery.ModDiscoverer.discoverMods(ModDiscoverer.java:74)
at MC-BOOTSTRAP/fmlloader@1.19.2-43.1.47/net.minecraftforge.fml.loading.FMLLoader.beginModScan(FMLLoader.java:166)
at MC-BOOTSTRAP/fmlloader@1.19.2-43.1.47/net.minecraftforge.fml.loading.FMLServiceProvider.beginScanning(FMLServiceProvider.java:86)
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.TransformationServiceDecorator.runScan(TransformationServiceDecorator.java:112)
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.TransformationServicesHandler.lambda$runScanningTransformationServices$8(TransformationServicesHandler.java:100)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at java.base/java.util.HashMap$ValueSpliterator.forEachRemaining(HashMap.java:1779)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:575)
at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260)
at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:616)
at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:622)
at java.base/java.util.stream.ReferencePipeline.toList(ReferencePipeline.java:627)
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.TransformationServicesHandler.runScanningTransformationServices(TransformationServicesHandler.java:102)
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.TransformationServicesHandler.initializeTransformationServices(TransformationServicesHandler.java:55)
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.Launcher.run(Launcher.java:87)
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.Launcher.main(Launcher.java:77)
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:26)
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:23)
at cpw.mods.bootstraplauncher@1.1.2/cpw.mods.bootstraplauncher.BootstrapLauncher.main(BootstrapLauncher.java:141)
Caused by: java.io.IOException: Invalid paths argument, contained no existing paths: [D:\❄❄❄❄❄❄❄❄\FileRecv\.minecraft\libraries\net\minecraft\client\1.19.2-20220805.130853\client-1.19.2-20220805.130853-srg.jar, D:\❄❄❄❄❄❄❄❄\FileRecv\.minecraft\libraries\net\minecraft\client\1.19.2-20220805.130853\client-1.19.2-20220805.130853-extra.jar, D:\❄❄❄❄❄❄❄❄\FileRecv\.minecraft\libraries\net\minecraftforge\forge\1.19.2-43.1.47\forge-1.19.2-43.1.47-client.jar]
... 25 more
@@ -0,0 +1,21 @@
2023-03-18 12:54:04,303 main ERROR Error processing element Queue ([Appenders: null]): CLASS_NOT_FOUND
[12:54:05] [main/INFO]: ModLauncher running: args [--username, 2333, --version, Minecraft Pixelmon, --gameDir, C:\Users\Administrator\Downloads\.minecraft, --assetsDir, C:\Users\Administrator\Downloads\.minecraft\assets, --assetIndex, 1.16, --uuid, 19a73ce1f6c13f9bbdbaecaab8291e74, --accessToken, ❄❄❄❄❄❄❄❄, --userType, legacy, --versionType, HMCL 3.5.4, --width, 854, --height, 480, --launchTarget, fmlclient, --fml.forgeVersion, 36.2.34, --fml.mcVersion, 1.16.5, --fml.forgeGroup, net.minecraftforge, --fml.mcpVersion, 20210115.111550]
[12:54:05] [main/INFO]: ModLauncher 8.1.3+8.1.3+main-8.1.x.c94d18ec starting: java version 1.8.0_361 by Oracle Corporation
[12:54:08] [main/INFO]: Added Lets Encrypt root certificates as additional trust
[12:54:08] [main/INFO]: SpongePowered MIXIN Subsystem Version=0.8.4 Source=file:/C:/Users/Administrator/Downloads/.minecraft/libraries/org/spongepowered/mixin/0.8.4/mixin-0.8.4.jar Service=ModLauncher Env=CLIENT
[12:54:09] [main/FATAL]: Failed to find Minecraft resource version 1.16.5-20210115.111550 at C:\Users\Administrator\Downloads\.minecraft\libraries\net\minecraftforge\forge\1.16.5-36.2.34\forge-1.16.5-36.2.34-client.jar
[12:54:09] [main/INFO]: [java.lang.ThreadGroup:uncaughtException:-1]: java.lang.RuntimeException: Missing minecraft resource!
[12:54:09] [main/INFO]: [java.lang.ThreadGroup:uncaughtException:-1]: at net.minecraftforge.fml.loading.FMLCommonLaunchHandler.lambda$validatePaths$4(FMLCommonLaunchHandler.java:124)
[12:54:09] [main/INFO]: [java.lang.ThreadGroup:uncaughtException:-1]: at java.util.Spliterators$ArraySpliterator.forEachRemaining(Unknown Source)
[12:54:09] [main/INFO]: [java.lang.ThreadGroup:uncaughtException:-1]: at java.util.stream.ReferencePipeline$Head.forEach(Unknown Source)
[12:54:09] [main/INFO]: [java.lang.ThreadGroup:uncaughtException:-1]: at net.minecraftforge.fml.loading.FMLCommonLaunchHandler.validatePaths(FMLCommonLaunchHandler.java:121)
[12:54:09] [main/INFO]: [java.lang.ThreadGroup:uncaughtException:-1]: at net.minecraftforge.fml.loading.FMLLoader.setupLaunchHandler(FMLLoader.java:202)
[12:54:09] [main/INFO]: [java.lang.ThreadGroup:uncaughtException:-1]: at net.minecraftforge.fml.loading.FMLServiceProvider.initialize(FMLServiceProvider.java:94)
[12:54:09] [main/INFO]: [java.lang.ThreadGroup:uncaughtException:-1]: at cpw.mods.modlauncher.TransformationServiceDecorator.onInitialize(TransformationServiceDecorator.java:68)
[12:54:09] [main/INFO]: [java.lang.ThreadGroup:uncaughtException:-1]: at cpw.mods.modlauncher.TransformationServicesHandler.lambda$initialiseTransformationServices$7(TransformationServicesHandler.java:107)
[12:54:09] [main/INFO]: [java.lang.ThreadGroup:uncaughtException:-1]: at java.util.HashMap$Values.forEach(Unknown Source)
[12:54:09] [main/INFO]: [java.lang.ThreadGroup:uncaughtException:-1]: at cpw.mods.modlauncher.TransformationServicesHandler.initialiseTransformationServices(TransformationServicesHandler.java:107)
[12:54:09] [main/INFO]: [java.lang.ThreadGroup:uncaughtException:-1]: at cpw.mods.modlauncher.TransformationServicesHandler.initializeTransformationServices(TransformationServicesHandler.java:59)
[12:54:09] [main/INFO]: [java.lang.ThreadGroup:uncaughtException:-1]: at cpw.mods.modlauncher.Launcher.run(Launcher.java:76)
[12:54:09] [main/INFO]: [java.lang.ThreadGroup:uncaughtException:-1]: at cpw.mods.modlauncher.Launcher.main(Launcher.java:66)
Exception in thread "main"
@@ -0,0 +1,9 @@
2023-03-16 22:19:06,155 main ERROR Error processing element Queue ([Appenders: null]): CLASS_NOT_FOUND
[22:19:06] [main/INFO]: ModLauncher running: args [--username, 111, --version, RAD2-1.16.5, --gameDir, C:\Users\Administrator\Desktop\HMCL\.minecraft\versions\RAD2-1.16.5, --assetsDir, C:\Users\Administrator\Desktop\HMCL\.minecraft\assets, --assetIndex, 1.16, --uuid, 2a386545f1ff370eab061a766a28340a, --accessToken, ❄❄❄❄❄❄❄❄, --userType, legacy, --versionType, HMCL 3.5.3.228, --width, 854, --height, 480, --launchTarget, fmlclient, --fml.forgeVersion, 36.2.39, --fml.mcVersion, 1.16.5, --fml.forgeGroup, net.minecraftforge, --fml.mcpVersion, 20210115.111550]
[22:19:06] [main/INFO]: ModLauncher 8.1.3+8.1.3+main-8.1.x.c94d18ec starting: java version 1.8.0_362 by BellSoft
[22:19:06] [main/INFO]: SpongePowered MIXIN Subsystem Version=0.8.4 Source=file:/C:/Users/Administrator/Desktop/HMCL/.minecraft/libraries/org/spongepowered/mixin/0.8.4/mixin-0.8.4.jar Service=ModLauncher Env=CLIENT
[22:19:06] [main/ERROR]: Cannot find launch target fmlclient, unable to launch
Exception in thread "main" java.lang.RuntimeException: Cannot find launch target
at cpw.mods.modlauncher.LaunchServiceHandler.validateLaunchTarget(LaunchServiceHandler.java:87)
at cpw.mods.modlauncher.Launcher.run(Launcher.java:78)
at cpw.mods.modlauncher.Launcher.main(Launcher.java:66)
@@ -0,0 +1,39 @@
[23:16:40] [main/INFO]: ModLauncher running: args [--username, ygu, --version, 1.19.3, --gameDir, D:\MC\.minecraft, --assetsDir, D:\MC\.minecraft\assets, --assetIndex, 2, --uuid, 28ae47c586363ebfae84cce7b2d47198, --accessToken, ❄❄❄❄❄❄❄❄, --clientId, ${clientid}, --xuid, ${auth_xuid}, --userType, legacy, --versionType, HMCL 3.5.4, --width, 854, --height, 480, --launchTarget, forgeclient, --fml.forgeVersion, 44.1.23, --fml.mcVersion, 1.19.3, --fml.forgeGroup, net.minecraftforge, --fml.mcpVersion, 20221207.122022]
[23:16:40] [main/INFO]: ModLauncher 10.0.8+10.0.8+main.0ef7e830 starting: java version 17.0.6 by Microsoft; OS Windows 11 arch amd64 version 10.0
Exception in thread "main" java.lang.IllegalArgumentException: : Invalid module name: '' is not a Java identifier
at java.base/jdk.internal.module.Checks.requireModuleName(Checks.java:59)
at java.base/java.lang.module.ModuleDescriptor$Builder.<init>(ModuleDescriptor.java:1515)
at java.base/java.lang.module.ModuleDescriptor.newAutomaticModule(ModuleDescriptor.java:2395)
at cpw.mods.securejarhandler/cpw.mods.jarhandling.impl.SimpleJarMetadata.descriptor(SimpleJarMetadata.java:13)
at cpw.mods.securejarhandler/cpw.mods.jarhandling.impl.Jar.computeDescriptor(Jar.java:51)
at cpw.mods.securejarhandler/cpw.mods.jarhandling.impl.Jar$JarModuleDataProvider.descriptor(Jar.java:276)
at MC-BOOTSTRAP/fmlloader@1.19.3-44.1.23/net.minecraftforge.fml.loading.ModDirTransformerDiscoverer.visitFile(ModDirTransformerDiscoverer.java:62)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1845)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
at MC-BOOTSTRAP/fmlloader@1.19.3-44.1.23/net.minecraftforge.fml.loading.ModDirTransformerDiscoverer.scan(ModDirTransformerDiscoverer.java:50)
at MC-BOOTSTRAP/fmlloader@1.19.3-44.1.23/net.minecraftforge.fml.loading.ModDirTransformerDiscoverer.candidates(ModDirTransformerDiscoverer.java:33)
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.serviceapi.ITransformerDiscoveryService.candidates(ITransformerDiscoveryService.java:48)
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.TransformationServicesHandler.lambda$discoverServices$14(TransformationServicesHandler.java:125)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at java.base/java.util.AbstractList$RandomAccessSpliterator.forEachRemaining(AbstractList.java:720)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:575)
at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260)
at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:616)
at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:622)
at java.base/java.util.stream.ReferencePipeline.toList(ReferencePipeline.java:627)
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.TransformationServicesHandler.discoverServices(TransformationServicesHandler.java:127)
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.Launcher.run(Launcher.java:86)
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.Launcher.main(Launcher.java:77)
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:26)
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:23)
at cpw.mods.bootstraplauncher@1.1.2/cpw.mods.bootstraplauncher.BootstrapLauncher.main(BootstrapLauncher.java:141)
+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 =