Compare commits

...
Author SHA1 Message Date
Glavo 0d9a275fb3 [release/3.6] 检测到配置文件由高版本启动器创建时禁止保存配置文件 (#4449)
https://github.com/HMCL-dev/HMCL/pull/4414
2025-09-11 01:19:56 +08:00
Glavoand3gf8jv4dv ab72ac3d10 [release/3.6] chore(l10n): change translation from 版本 to 实例 in zh-cn for term INSTANCE (#4448)
https://github.com/HMCL-dev/HMCL/pull/4376

---------

Co-authored-by: 3gf8jv4dv <3gf8jv4dv@gmail.com>
2025-09-11 00:24:53 +08:00
Glavoand辞庐 9d5f8a9f0e [release/3.6] 修复 LWJGL 路径显示错误的问题 (#4447)
https://github.com/HMCL-dev/HMCL/pull/4192

Co-authored-by: 辞庐 <109708109+CiiLu@users.noreply.github.com>
2025-09-11 00:15:13 +08:00
GlavoandWulian233 fe2e96345b [release/3.6] 修复世界管理页面不支持显示极限模式的问题 (#4446)
https://github.com/HMCL-dev/HMCL/pull/4321

Co-authored-by: Wulian233 <1055917385@qq.com>
2025-09-11 00:05:23 +08:00
Glavoand辞庐 881fa2858f [release/3.6] 修复整合包格式不匹配提示未被正确格式化的问题 (#4445)
https://github.com/HMCL-dev/HMCL/pull/4176

Co-authored-by: 辞庐 <109708109+CiiLu@users.noreply.github.com>
2025-09-11 00:01:11 +08:00
Glavo a0b95866be [release/3.6] Update microsoft_auth.html (#4444)
https://github.com/HMCL-dev/HMCL/pull/4388
2025-09-10 23:53:35 +08:00
Glavo 93bbef385d [release/3.6] 修复 RemoteMod::getIntegrityCheck 未选择正确的算法的问题 (#4443)
https://github.com/HMCL-dev/HMCL/pull/4238
2025-09-10 23:44:09 +08:00
Glavoand神奇的赵二 9dcc2727aa [release/3.6] 新增内存显示条禁用样式 (#4442)
https://github.com/HMCL-dev/HMCL/pull/4185

Co-authored-by: 神奇的赵二 (OrzMiku) <miku@ecy.pink>
2025-09-10 23:39:12 +08:00
Glavoand辞庐 865c8588d8 [release/3.6] 修复“下载线程数”的指示器被提示覆盖的问题 (#4441)
https://github.com/HMCL-dev/HMCL/pull/4153

Co-authored-by: 辞庐 <109708109+CiiLu@users.noreply.github.com>
2025-09-10 23:34:52 +08:00
3gf8jv4dvand辞庐 419612bd3e [release/3.6] backport language file changes to the 3.6 branch (#4319)
https://github.com/HMCL-dev/HMCL/pull/3690
https://github.com/HMCL-dev/HMCL/pull/4149
https://github.com/HMCL-dev/HMCL/pull/4188
https://github.com/HMCL-dev/HMCL/pull/4189
https://github.com/HMCL-dev/HMCL/pull/4199
https://github.com/HMCL-dev/HMCL/pull/4200
https://github.com/HMCL-dev/HMCL/pull/4303

---------

Co-authored-by: 辞庐 <109708109+CiiLu@users.noreply.github.com>
2025-09-10 23:34:13 +08:00
Glavo d16eded1ad [release/3.6] 弃用 Java 11 支持 (#4422) 2025-09-10 23:24:51 +08:00
Glavo c3a47655f0 [release/3.6] 更新模组和整合包数据 (#4440)
https://github.com/HMCL-dev/HMCL/pull/4439
2025-09-10 23:21:30 +08:00
Glavo 34842a5f26 [release/3.6] 修复无法加载 WebP 图标的问题 (#4436)
https://github.com/HMCL-dev/HMCL/pull/4171
2025-09-10 23:20:27 +08:00
Glavo 6dd7d53c20 [release/3.6] 修复 backgroundType 为 null 时启动器崩溃的问题 (#4438)
https://github.com/HMCL-dev/HMCL/pull/4437
2025-09-10 22:58:18 +08:00
Glavo 1654c652e0 [release/3.6] 在 ./.hmcl/java 中搜索 Java (#4434)
https://github.com/HMCL-dev/HMCL/pull/4179
2025-09-10 21:37:45 +08:00
Glavoand辞庐 881f5e7874 [release/3.6] 模组下载列表翻页后自动回到顶部 (#4433)
https://github.com/HMCL-dev/HMCL/pull/4170

Co-authored-by: 辞庐 <109708109+CiiLu@users.noreply.github.com>
2025-09-10 21:28:03 +08:00
Glavo 73f0eb472a [release/3.6] 修复首次打开子页面时切换动画丢失的问题 (#4431)
https://github.com/HMCL-dev/HMCL/pull/4365
2025-09-10 21:17:00 +08:00
Glavo e13b8385e0 [release/3.6] 在 Windows 7/8/8.1 上使用 llvmpipe-loader 加载 mesa (#4430)
https://github.com/HMCL-dev/HMCL/pull/4224
2025-09-10 21:01:36 +08:00
Glavo a6e4651307 [release/3.6] 修复非 Windows 平台上游戏进程的 user.home 可能被设置为 null 的问题 (#4427)
https://github.com/HMCL-dev/HMCL/pull/4241
2025-09-10 20:55:33 +08:00
Glavo 554e573d7f [release/3.6] 支持通过环境变量强制使用 GPU 渲染和控制动画帧率 (#4426)
https://github.com/HMCL-dev/HMCL/pull/4164
2025-09-10 20:46:51 +08:00
Glavo 3096f933af [release/3.6] 修复导入异常格式的数据包时启动器崩溃的问题 (#4425)
https://github.com/HMCL-dev/HMCL/pull/4152
2025-09-10 20:41:29 +08:00
GlavoandWulian233 ff3b486f58 [release/3.6] 修复游戏版本列表错误地过滤掉部分版本的问题 (#4424)
https://github.com/HMCL-dev/HMCL/pull/4066
https://github.com/HMCL-dev/HMCL/pull/4244

---------

Co-authored-by: Wulian233 <1055917385@qq.com>
2025-09-10 20:36:01 +08:00
GlavoandRoj234 708e0326a7 [release/3.6] 修复多次启动游戏时内存泄露的问题 (#4423)
https://github.com/HMCL-dev/HMCL/pull/4121

Co-authored-by: Roj234 <82699138+roj234@users.noreply.github.com>
2025-09-10 20:27:12 +08:00
GlavoandWulian233 f7a5c7a310 [release/3.6] 修复 1.21.5 以上的地图跳转种子地图错误 (#4317)
https://github.com/HMCL-dev/HMCL/pull/4274

Co-authored-by: Wulian233 <1055917385@qq.com>
2025-08-22 20:48:56 +08:00
Glavo 633e8f8fe2 [release/3.6] Update amdgpu.ids (#4316) 2025-08-22 20:44:36 +08:00
Glavoand辞庐 440ea1b21d [release/3.6] fix: 无法从 BMCLAPI 下载 NeoForge 所有所需文件 (#4313)
https://github.com/HMCL-dev/HMCL/pull/4174

Co-authored-by: 辞庐 <109708109+CiiLu@users.noreply.github.com>
2025-08-22 20:36:18 +08:00
Glavo f9dd7a1e64 修复 InstallersPage 意外地显示滚动条的问题 (#4097)
This reverts commit c669026312.
2025-07-19 22:36:30 +08:00
Glavo cbb83d4f15 修复无法使用部分日志字体的问题 (#4096) 2025-07-19 14:56:31 +08:00
Glavo bab863287d Update versions.txt to 1.21.8 (#4085) 2025-07-18 00:08:44 +08:00
Glavo e4c479754b Bump HMCLauncher to 3.6.0.6 (#4086) 2025-07-16 18:50:57 +08:00
Glavo 8ebf58e8fa Fix #4072: 修复无法使用部分中文字体的问题 (#4074) 2025-07-09 19:39:28 +08:00
辞庐 064b39d753 修复添加游戏文件夹页面缺少灰色背景的问题 (#4065) 2025-07-06 15:46:08 +08:00
Glavo c669026312 Fix #4067: 修复使用 JavaFX 25 启动时下载界面布局错位的问题 (#4069) 2025-07-05 22:29:07 +08:00
Glavo d9b95b28b2 Update PLATFORM.md (#4062) 2025-07-04 17:06:41 +08:00
Glavo d492884927 使用 Java 8 运行时禁止更新 (#4032) 2025-07-02 23:44:27 +08:00
3gf8jv4dv cf4fde5592 chore(locale): add shader category translation for russian and spanish (#4042) 2025-07-01 07:58:40 +08:00
Glavo fd959c41c3 Update versions.txt to 1.21.7 (#4035) 2025-07-01 07:52:54 +08:00
3gf8jv4dv e34037c055 chore(locale): add world management translation for russian and spanish (#4041)
Some terminology is consistent with Minecraft Wiki.
2025-06-29 07:13:44 +08:00
3gf8jv4dv 417c804f48 chore(locale): add tip translation of deprecated java ver for ru and es (#4043)
and fix a previous typo.
2025-06-29 07:12:34 +08:00
Glavo ea6eced136 更新 mod_data.txt (#4040) 2025-06-29 04:07:48 +08:00
Glavo 432465ab46 修复部分 Linux 环境中未正确选择默认字体的问题 (#4036) 2025-06-27 17:23:27 +08:00
Burning_TNT 03c95cd726 Fix: 压制 DiscoJavaDistribution 编译警告 (#4038)
* Fix: Suppress compile-time warning.

* Fix: typo

* Adjust F*** -> Fucking.
2025-06-27 17:16:40 +08:00
Burning_TNT 730febca8c Fix: MultiMC allows libraries with the same artifact descriptor string. (#4016) 2025-06-21 21:18:53 +08:00
Kieran Moy bc41499d1c fix typo (#4015) 2025-06-19 17:19:09 +08:00
3gf8jv4dv 60fae5cc88 chore: update versions.txt to 1.21.6 (#4011) 2025-06-18 23:58:12 +08:00
Glavo c11837baa4 修复光影下载页面中标签名称未翻译的问题 (#4007) 2025-06-17 09:07:47 +08:00
Glavo aaf1218536 修复 macOS 上无法读取 CPU 信息的问题 (#4009) 2025-06-16 11:48:05 +08:00
Glavo 88637b33bc 更新 GROUPS_URL (#4004) 2025-06-15 09:34:24 +08:00
Burning_TNT 462eea3539 Fix #3997: 模组搜索返回结果为空时下载界面异常 (#3998) 2025-06-14 21:23:39 +08:00
Burning_TNT d624086594 解析 MultiMC 整合包中未声明但在依赖链上的组件 (#4003) 2025-06-14 21:23:21 +08:00
Burning_TNT fa8b3a952b Fix #3999: 自动安装更改 MC 版本后无法点击安装键 (#4001) 2025-06-14 21:12:59 +08:00
Burning_TNT c70a6c3372 移除漏洞反馈 Issue 模板中的系统信息 (#4000) 2025-06-14 21:12:33 +08:00
Glavo 0616c37de7 创建世界管理页面 (#3991) 2025-06-13 14:28:51 +08:00
Burning_TNT b72056f7fe Fix #3994: 修复 Controller 未能读取 JAVA_VERSION_TIP 的问题 (#3995) 2025-06-12 20:49:55 +08:00
Glavo 3ebe8dce44 修复在 schematics 文件夹不存在的情况下,打开投影管理页面会抛出异常的问题 (#3996) 2025-06-12 20:46:04 +08:00
Glavo a9c28573b8 Bump HMCLauncher to 3.6.0.4 (#3992) 2025-06-12 12:38:45 +08:00
9bead7e34e 支持下载光影 (#2794)
* Clean up duplicated codes in DownloadPage.

* Remove useless codes. Provide support for downloading shaderpacks from Modrinth.

* Fix.

* Update HMCL/src/main/resources/assets/lang/I18N_ja.properties

Co-authored-by: 3gf8jv4dv <3gf8jv4dv@gmail.com>

* Update HMCL/src/main/resources/assets/lang/I18N_ru.properties

Co-authored-by: 3gf8jv4dv <3gf8jv4dv@gmail.com>

* Update HMCL/src/main/resources/assets/lang/I18N_es.properties

Co-authored-by: 3gf8jv4dv <3gf8jv4dv@gmail.com>

* Update Icon

---------

Co-authored-by: Glavo <zjx001202@gmail.com>
Co-authored-by: 3gf8jv4dv <3gf8jv4dv@gmail.com>
2025-06-11 19:30:47 +08:00
3gf8jv4dvandGlavo d0b36ffa15 fix(i18n): localize MiB in VersionSettingsPage (#3984)
For Slavic languages (Russian, Ukrainian, etc.) they need translations.

Co-authored-by: Glavo <zjx001202@gmail.com>
2025-06-11 01:43:05 +08:00
辞庐andGlavo ea081333e7 自动安装页面隐藏 mcbbs 项 (#3836)
* update

* CheckStyle

* Update InstallerListPage.java

CheckStyle

---------

Co-authored-by: Glavo <zjx001202@gmail.com>
2025-06-11 01:25:59 +08:00
3gf8jv4dv 2b99da430f chore(locale): add folder.schematics to spanish and russian (#3983) 2025-06-11 01:16:20 +08:00
Wulian233andGlavo f1ea316bad feat: 更新日志中仅显示本次更新内容 (#3974)
* 更新日志中仅显示本次更新内容

---------

Co-authored-by: Glavo <zjx001202@gmail.com>
2025-06-11 00:57:30 +08:00
3gf8jv4dv 9565c3bdfe fix(locale): fix units of information in russian (#3985) 2025-06-11 00:32:28 +08:00
3gf8jv4dv 710fd0f075 fix(locale): fix units of information in japanese (#3986) 2025-06-11 00:29:23 +08:00
78 changed files with 6427 additions and 1598 deletions

No files matched your search

+1 -11
View File
@@ -20,16 +20,6 @@ body:
* Your launcher is the **latest nightly build**. You can click [here](https://zkitefly.github.io/HMCL-Snapshot-Update/en) to download the latest nightly build.
If your issue does not fall into the above two categories, you can choose another type of issue or directly go to the [QQ group](https://docs.hmcl.net/groups.html) or [Discord server](https://discord.gg/jVvC7HfM6U) for help.
- type: input
id: platform
attributes:
label: 平台 | Platform
description: |
请输入你遇到问题的平台。
Please enter the platform on which you encountered the bug.
placeholder: e.g. Windows 11
validations:
required: true
- type: textarea
id: bug-report
attributes:
@@ -56,4 +46,4 @@ body:
If your launcher does not crash, please DO NOT EXIT your launcher, click "Export Launcher Logs" in the "Settings → General → Debug" of the launcher, and drag the exported log to the following input field.
**ATTENTION: The crash report or log file is the key to resolving the bug. Please upload them!**
validations:
required: true
required: true
+1 -1
View File
@@ -44,7 +44,7 @@ dependencies {
implementation(libs.java.info)
if (launcherExe == null) {
implementation("org.glavo.hmcl:HMCLauncher:3.6.0.3")
implementation(libs.hmclauncher)
}
}
@@ -105,6 +105,10 @@ public final class Launcher extends Application {
return;
}
if (ConfigHolder.isUnsupportedVersion()) {
showAlert(AlertType.WARNING, i18n("fatal.config_unsupported_version"));
}
if (Metadata.HMCL_CURRENT_DIRECTORY.toString().indexOf('=') >= 0) {
Main.showWarningAndContinue(i18n("fatal.illegal_char"));
}
@@ -62,6 +62,21 @@ public final class Main {
createHMCLDirectories();
LOG.start(Metadata.HMCL_CURRENT_DIRECTORY.resolve("logs"));
if ("true".equalsIgnoreCase(System.getenv("HMCL_FORCE_GPU")))
System.getProperties().putIfAbsent("prism.forceGPU", "true");
String animationFrameRate = System.getenv("HMCL_ANIMATION_FRAME_RATE");
if (animationFrameRate != null) {
try {
if (Integer.parseInt(animationFrameRate) <= 0)
throw new NumberFormatException(animationFrameRate);
System.getProperties().putIfAbsent("javafx.animation.pulse", animationFrameRate);
} catch (NumberFormatException e) {
LOG.warning("Invalid animation frame rate: " + animationFrameRate);
}
}
checkDirectoryPath();
if (JavaRuntime.CURRENT_VERSION < 9)
@@ -19,10 +19,13 @@ package org.jackhuang.hmcl;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.io.JarUtils;
import org.jackhuang.hmcl.util.platform.Architecture;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import org.jetbrains.annotations.Nullable;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.EnumSet;
/**
* Stores metadata about this application.
@@ -35,6 +38,9 @@ public final class Metadata {
public static final String FULL_NAME = "Hello Minecraft! Launcher";
public static final String VERSION = System.getProperty("hmcl.version.override", JarUtils.getManifestAttribute("Implementation-Version", "@develop@"));
public static final int MINIMUM_REQUIRED_JAVA_VERSION = 8;
public static final int MINIMUM_SUPPORTED_JAVA_VERSION = 17;
public static final String TITLE = NAME + " " + VERSION;
public static final String FULL_TITLE = FULL_NAME + " v" + VERSION;
@@ -47,7 +53,7 @@ public final class Metadata {
public static final String CONTACT_URL = DOCS_URL + "/help.html";
public static final String CHANGELOG_URL = DOCS_URL + "/changelog/";
public static final String EULA_URL = DOCS_URL + "/eula/hmcl.html";
public static final String GROUPS_URL = DOCS_URL + "/groups.html";
public static final String GROUPS_URL = "https://www.bilibili.com/opus/905435541874409529";
public static final String BUILD_CHANNEL = JarUtils.getManifestAttribute("Build-Channel", "nightly");
public static final String GITHUB_SHA = JarUtils.getManifestAttribute("GitHub-SHA", null);
@@ -93,4 +99,32 @@ public final class Metadata {
public static boolean isNightly() {
return !isStable() && !isDev();
}
public static @Nullable String getSuggestedJavaDownloadLink() {
if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX && Architecture.SYSTEM_ARCH == Architecture.LOONGARCH64_OW)
return "https://www.loongnix.cn/zh/api/java/downloads-jdk21/index.html";
else {
EnumSet<Architecture> supportedArchitectures;
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS)
supportedArchitectures = EnumSet.of(Architecture.X86_64, Architecture.X86, Architecture.ARM64);
else if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX)
supportedArchitectures = EnumSet.of(
Architecture.X86_64, Architecture.X86,
Architecture.ARM64, Architecture.ARM32,
Architecture.RISCV64, Architecture.LOONGARCH64
);
else if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS)
supportedArchitectures = EnumSet.of(Architecture.X86_64, Architecture.ARM64);
else
supportedArchitectures = EnumSet.noneOf(Architecture.class);
if (supportedArchitectures.contains(Architecture.SYSTEM_ARCH))
return String.format("https://docs.hmcl.net/downloads/%s/%s.html",
OperatingSystem.CURRENT_OS.getCheckedName(),
Architecture.SYSTEM_ARCH.getCheckedName()
);
else
return null;
}
}
}
@@ -59,6 +59,7 @@ import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.lang.ref.WeakReference;
import static javafx.application.Platform.runLater;
import static javafx.application.Platform.setImplicitExit;
@@ -125,6 +126,9 @@ public final class LauncherHelper {
}
private void launch0() {
// https://github.com/HMCL-dev/HMCL/pull/4121
PROCESSES.removeIf(it -> it.get() == null);
HMCLGameRepository repository = profile.getRepository();
DefaultDependencyManager dependencyManager = profile.getDependency();
AtomicReference<Version> version = new AtomicReference<>(MaintainTask.maintain(repository, repository.getResolvedVersion(selectedVersion)));
@@ -156,8 +160,10 @@ public final class LauncherHelper {
}),
Task.composeAsync(() -> {
Renderer renderer = setting.getRenderer();
if (renderer != Renderer.DEFAULT && OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
Library lib = NativePatcher.getMesaLoader(java);
if (renderer != Renderer.DEFAULT
&& OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS
&& OperatingSystem.WINDOWS_VERSION != null) {
Library lib = NativePatcher.getWindowsMesaLoader(java, renderer, OperatingSystem.WINDOWS_VERSION);
if (lib == null)
return null;
File file = dependencyManager.getGameRepository().getLibraryFile(version.get(), lib);
@@ -209,7 +215,7 @@ public final class LauncherHelper {
}
}).thenAcceptAsync(process -> { // process is LaunchTask's result
if (scriptFile == null) {
PROCESSES.add(process);
PROCESSES.add(new WeakReference<>(process));
if (launcherVisibility == LauncherVisibility.CLOSE)
Launcher.stopApplication();
else
@@ -896,10 +902,10 @@ public final class LauncherHelper {
}
public static final Queue<ManagedProcess> PROCESSES = new ConcurrentLinkedQueue<>();
public static final Queue<WeakReference<ManagedProcess>> PROCESSES = new ConcurrentLinkedQueue<>();
public static void stopManagedProcesses() {
while (!PROCESSES.isEmpty())
Optional.ofNullable(PROCESSES.poll()).ifPresent(ManagedProcess::stop);
Optional.ofNullable(PROCESSES.poll()).map(WeakReference::get).ifPresent(ManagedProcess::stop);
}
}
@@ -30,6 +30,7 @@ import org.jackhuang.hmcl.util.io.NetworkUtils;
import java.io.IOException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@@ -103,6 +104,7 @@ public final class OAuthServer extends NanoHTTPD implements OAuth.Session {
String html;
try {
html = IOUtils.readFullyAsString(OAuthServer.class.getResourceAsStream("/assets/microsoft_auth.html"))
.replace("%lang%", Locale.getDefault().toLanguageTag())
.replace("%close-page%", i18n("account.methods.microsoft.close_page"));
} catch (IOException e) {
LOG.error("Failed to load html", e);
@@ -56,6 +56,7 @@ public final class JavaManager {
}
public static final HMCLJavaRepository REPOSITORY = new HMCLJavaRepository(Metadata.HMCL_GLOBAL_DIRECTORY.resolve("java"));
public static final HMCLJavaRepository LOCAL_REPOSITORY = new HMCLJavaRepository(Metadata.HMCL_CURRENT_DIRECTORY.resolve("java"));
public static String getMojangJavaPlatform(Platform platform) {
if (platform.getOperatingSystem() == OperatingSystem.WINDOWS) {
@@ -561,6 +562,10 @@ public final class JavaManager {
for (JavaRuntime java : REPOSITORY.getAllJava(platform)) {
javaRuntimes.put(java.getBinary(), java);
}
for (JavaRuntime java : LOCAL_REPOSITORY.getAllJava(platform)) {
javaRuntimes.put(java.getBinary(), java);
}
}
private static void searchAllOfficialJava(Map<Path, JavaRuntime> javaRuntimes, Path directory, boolean verify) {
@@ -50,6 +50,7 @@ import java.util.TreeMap;
public final class Config implements Observable {
public static final int CURRENT_VERSION = 2;
public static final int CURRENT_UI_VERSION = 0;
public static final Gson CONFIG_GSON = new GsonBuilder()
@@ -45,6 +45,7 @@ public final class ConfigHolder {
private static GlobalConfig globalConfigInstance;
private static boolean newlyCreated;
private static boolean ownerChanged = false;
private static boolean unsupportedVersion = false;
public static Config config() {
if (configInstance == null) {
@@ -72,6 +73,10 @@ public final class ConfigHolder {
return ownerChanged;
}
public static boolean isUnsupportedVersion() {
return unsupportedVersion;
}
public static void init() throws IOException {
if (configInstance != null) {
throw new IllegalStateException("Configuration is already loaded");
@@ -82,7 +87,8 @@ public final class ConfigHolder {
LOG.info("Config location: " + configLocation);
configInstance = loadConfig();
configInstance.addListener(source -> FileSaver.save(configLocation, configInstance.toJson()));
if (!unsupportedVersion)
configInstance.addListener(source -> FileSaver.save(configLocation, configInstance.toJson()));
globalConfigInstance = loadGlobalConfig();
globalConfigInstance.addListener(source -> FileSaver.save(GLOBAL_CONFIG_PATH, globalConfigInstance.toJson()));
@@ -162,7 +168,14 @@ public final class ConfigHolder {
if (deserialized == null) {
LOG.info("Config is empty");
} else {
ConfigUpgrader.upgradeConfig(deserialized, content);
int configVersion = deserialized.getConfigVersion();
if (configVersion < Config.CURRENT_VERSION) {
ConfigUpgrader.upgradeConfig(deserialized, content);
} else if (configVersion > Config.CURRENT_VERSION) {
unsupportedVersion = true;
LOG.warning(String.format("Current HMCL only support the configuration version up to %d. However, the version now is %d.", Config.CURRENT_VERSION, configVersion));
}
return deserialized;
}
} catch (JsonParseException e) {
@@ -31,8 +31,6 @@ final class ConfigUpgrader {
private ConfigUpgrader() {
}
private static final int CURRENT_VERSION = 2;
/**
* This method is for the compatibility with old HMCL versions.
*
@@ -42,17 +40,10 @@ final class ConfigUpgrader {
static void upgradeConfig(Config deserialized, String rawContent) {
int configVersion = deserialized.getConfigVersion();
if (configVersion == CURRENT_VERSION) {
if (configVersion >= Config.CURRENT_VERSION)
return;
}
if (configVersion > CURRENT_VERSION) {
LOG.warning(String.format("Current HMCL only support the configuration version up to %d. However, the version now is %d.", CURRENT_VERSION, configVersion));
deserialized.setConfigVersion(CURRENT_VERSION);
return;
}
LOG.info(String.format("Updating configuration from %d to %d.", configVersion, CURRENT_VERSION));
LOG.info(String.format("Updating configuration from %d to %d.", configVersion, Config.CURRENT_VERSION));
Map<?, ?> rawJson = Collections.unmodifiableMap(new Gson().<Map<?, ?>>fromJson(rawContent, Map.class));
if (configVersion < 1) {
@@ -100,6 +91,6 @@ final class ConfigUpgrader {
}
}
deserialized.setConfigVersion(CURRENT_VERSION);
deserialized.setConfigVersion(Config.CURRENT_VERSION);
}
}
@@ -26,6 +26,8 @@ import org.jackhuang.hmcl.util.Lazy;
import org.jackhuang.hmcl.util.io.JarUtils;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import org.jackhuang.hmcl.util.platform.SystemUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@@ -35,6 +37,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Locale;
import java.util.Objects;
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
@@ -78,7 +81,7 @@ public final class FontManager {
return null;
});
private static final ObjectProperty<Font> fontProperty;
private static final ObjectProperty<FontReference> fontProperty;
static {
String fontFamily = config().getLauncherFontFamily();
@@ -87,10 +90,16 @@ public final class FontManager {
if (fontFamily == null)
fontFamily = System.getenv("HMCL_FONT");
Font font = fontFamily == null ? DEFAULT_FONT.get() : Font.font(fontFamily, DEFAULT_FONT_SIZE);
fontProperty = new SimpleObjectProperty<>(font);
FontReference fontReference;
if (fontFamily == null) {
Font defaultFont = DEFAULT_FONT.get();
fontReference = defaultFont != null ? new FontReference(defaultFont) : null;
} else
fontReference = new FontReference(fontFamily);
LOG.info("Font: " + (font != null ? font.getName() : Font.getDefault().getName()));
fontProperty = new SimpleObjectProperty<>(fontReference);
LOG.info("Font: " + (fontReference != null ? fontReference.getFamily() : "System"));
fontProperty.addListener((obs, oldValue, newValue) -> {
if (newValue != null)
config().setLauncherFontFamily(newValue.getFamily());
@@ -163,6 +172,16 @@ public final class FontManager {
}
}
if (family.indexOf(',') >= 0) {
for (String candidateFamily : family.split(",")) {
for (Font font : fonts) {
if (font.getFamily().equalsIgnoreCase(candidateFamily)) {
return font;
}
}
}
}
LOG.warning(String.format("Family '%s' not found in font file '%s'", family, path));
return fonts[0];
} catch (NoSuchMethodException | IllegalAccessException ignored) {
@@ -179,20 +198,62 @@ public final class FontManager {
}
}
public static ObjectProperty<Font> fontProperty() {
public static ObjectProperty<FontReference> fontProperty() {
return fontProperty;
}
public static Font getFont() {
public static FontReference getFont() {
return fontProperty.get();
}
public static void setFont(Font font) {
public static void setFont(FontReference font) {
fontProperty.set(font);
}
public static void setFontFamily(String fontFamily) {
setFont(fontFamily != null ? Font.font(fontFamily, DEFAULT_FONT_SIZE) : null);
setFont(fontFamily != null ? new FontReference(fontFamily) : null);
}
// https://github.com/HMCL-dev/HMCL/issues/4072
public static final class FontReference {
private final @NotNull String family;
private final @Nullable String style;
public FontReference(@NotNull String family) {
this.family = Objects.requireNonNull(family);
this.style = null;
}
public FontReference(@NotNull Font font) {
this.family = font.getFamily();
this.style = font.getStyle();
}
public @NotNull String getFamily() {
return family;
}
public @Nullable String getStyle() {
return style;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof FontReference))
return false;
FontReference that = (FontReference) o;
return Objects.equals(family, that.family) && Objects.equals(style, that.style);
}
@Override
public int hashCode() {
return Objects.hash(family, style);
}
@Override
public String toString() {
return String.format("FontReference[family='%s', style='%s']", family, style);
}
}
private FontManager() {
@@ -22,7 +22,6 @@ import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
@@ -80,9 +79,9 @@ public final class StyleSheets {
private static String getFontStyleSheet() {
final String defaultCss = "/assets/css/font.css";
final Font font = FontManager.getFont();
final FontManager.FontReference font = FontManager.getFont();
if (font == null || font == Font.getDefault())
if (font == null || "System".equals(font.getFamily()))
return defaultCss;
String fontFamily = font.getFamily();
@@ -115,11 +114,7 @@ public final class StyleSheets {
StringBuilder builder = new StringBuilder();
builder.append(".root {");
if (fontFamily == null)
// https://github.com/HMCL-dev/HMCL/pull/3423
builder.append("-fx-font-family: -fx-base-font-family;");
else
builder.append("-fx-font-family:\"").append(fontFamily).append("\";");
builder.append("-fx-font-family:\"").append(fontFamily).append("\";");
if (weight != null)
builder.append("-fx-font-weight:").append(weight).append(";");
@@ -63,7 +63,6 @@ import org.jackhuang.hmcl.util.platform.Architecture;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import java.io.File;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -311,48 +310,22 @@ public final class Controllers {
}
}
if (JavaRuntime.CURRENT_VERSION < 10) {
Integer shownTipVersion = null;
if (JavaRuntime.CURRENT_VERSION < Metadata.MINIMUM_SUPPORTED_JAVA_VERSION) {
Number shownTipVersion = null;
try {
shownTipVersion = (Integer) config().getShownTips().get(JAVA_VERSION_TIP);
shownTipVersion = (Number) config().getShownTips().get(JAVA_VERSION_TIP);
} catch (ClassCastException e) {
LOG.warning("Invalid type for shown tips key: " + JAVA_VERSION_TIP, e);
}
if (shownTipVersion == null || shownTipVersion < 11) {
String downloadLink = null;
if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX && Architecture.SYSTEM_ARCH == Architecture.LOONGARCH64_OW)
downloadLink = "https://www.loongnix.cn/zh/api/java/downloads-jdk21/index.html";
else {
EnumSet<Architecture> supportedArchitectures;
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS)
supportedArchitectures = EnumSet.of(Architecture.X86_64, Architecture.X86, Architecture.ARM64);
else if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX)
supportedArchitectures = EnumSet.of(
Architecture.X86_64, Architecture.X86,
Architecture.ARM64, Architecture.ARM32,
Architecture.RISCV64, Architecture.LOONGARCH64
);
else if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS)
supportedArchitectures = EnumSet.of(Architecture.X86_64, Architecture.ARM64);
else
supportedArchitectures = EnumSet.noneOf(Architecture.class);
if (supportedArchitectures.contains(Architecture.SYSTEM_ARCH))
downloadLink = String.format("https://docs.hmcl.net/downloads/%s/%s.html",
OperatingSystem.CURRENT_OS.getCheckedName(),
Architecture.SYSTEM_ARCH.getCheckedName()
);
}
if (shownTipVersion == null || shownTipVersion.intValue() < Metadata.MINIMUM_SUPPORTED_JAVA_VERSION) {
MessageDialogPane.Builder builder = new MessageDialogPane.Builder(i18n("fatal.deprecated_java_version"), null, MessageType.WARNING);
String downloadLink = Metadata.getSuggestedJavaDownloadLink();
if (downloadLink != null)
builder.addHyperLink(i18n("fatal.deprecated_java_version.download_link", 21), downloadLink);
Controllers.dialog(builder
.ok(() -> config().getShownTips().put(JAVA_VERSION_TIP, 11))
.ok(() -> config().getShownTips().put(JAVA_VERSION_TIP, Metadata.MINIMUM_SUPPORTED_JAVA_VERSION))
.build());
}
}
@@ -27,6 +27,7 @@ import javafx.beans.WeakInvalidationListener;
import javafx.beans.WeakListener;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.*;
import javafx.event.Event;
import javafx.event.EventDispatcher;
@@ -54,9 +55,8 @@ import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.util.Duration;
import javafx.util.StringConverter;
import org.glavo.png.PNGType;
import org.glavo.png.PNGWriter;
import org.glavo.png.javafx.PNGJavaFXUtils;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.animation.AnimationUtils;
import org.jackhuang.hmcl.util.*;
@@ -78,11 +78,16 @@ import javax.imageio.stream.ImageInputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.awt.image.BufferedImage;
import java.io.*;
import java.lang.ref.WeakReference;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.List;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@@ -779,6 +784,21 @@ public final class FXUtils {
}
}
public static Image loadWebPImage(InputStream input,
int requestedWidth, int requestedHeight,
boolean preserveRatio, boolean smooth) throws IOException {
WebPImageReaderSpi spi = new WebPImageReaderSpi();
ImageReader reader = spi.createReaderInstance(null);
BufferedImage bufferedImage;
try (ImageInputStream imageInput = ImageIO.createImageInputStream(input)) {
reader.setInput(imageInput, true, true);
bufferedImage = reader.read(0, reader.getDefaultReadParam());
} finally {
reader.dispose();
}
return SwingFXUtils.toFXImage(bufferedImage, requestedWidth, requestedHeight, preserveRatio, smooth);
}
public static Image loadImage(Path path) throws Exception {
try (InputStream input = Files.newInputStream(path)) {
if ("webp".equalsIgnoreCase(FileUtils.getExtension(path)))
@@ -792,6 +812,42 @@ public final class FXUtils {
}
}
public static Image loadImage(Path path,
int requestedWidth, int requestedHeight,
boolean preserveRatio, boolean smooth) throws Exception {
try (FileChannel channel = FileChannel.open(path, StandardOpenOption.READ)) {
String ext = FileUtils.getExtension(path).toLowerCase(Locale.ROOT);
if ("webp".equalsIgnoreCase(ext))
return loadWebPImage(Channels.newInputStream(channel),
requestedWidth, requestedHeight, preserveRatio, smooth);
if (!IMAGE_EXTENSIONS.contains(ext)) {
byte[] header = new byte[12];
ByteBuffer buffer = ByteBuffer.wrap(header);
//noinspection StatementWithEmptyBody
while (buffer.hasRemaining() && channel.read(buffer) > 0) {
}
channel.position(0L);
if (!buffer.hasRemaining()) {
// WebP File
if (header[0] == 'R' && header[1] == 'I' && header[2] == 'F' && header[3] == 'F' &&
header[8] == 'W' && header[9] == 'E' && header[10] == 'B' && header[11] == 'P') {
return loadWebPImage(Channels.newInputStream(channel),
requestedWidth, requestedHeight, preserveRatio, smooth);
}
}
}
Image image = new Image(Channels.newInputStream(channel),
requestedWidth, requestedHeight,
preserveRatio, smooth);
if (image.isError())
throw image.getException();
return image;
}
}
public static Image loadImage(URL url) throws Exception {
URLConnection connection = NetworkUtils.createConnection(url);
if (connection instanceof HttpURLConnection) {
@@ -851,15 +907,37 @@ public final class FXUtils {
}
}
/**
* Load image from the internet. It will cache the data of images for the further usage.
* The cached data will be deleted when HMCL is closed or hidden.
*
* @param url the url of image. The image resource should be a file on the internet.
* @return the image resource within the jar.
*/
public static Image newRemoteImage(String url) {
return newRemoteImage(url, 0, 0, false, false, false);
public static Task<Image> getRemoteImageTask(String url, int requestedWidth, int requestedHeight, boolean preserveRatio, boolean smooth) {
return Task.composeAsync(() -> {
Path currentPath = remoteImageCache.get(url);
if (currentPath != null) {
if (Files.isReadable(currentPath))
return Task.completed(currentPath);
// The file is unavailable or unreadable.
remoteImageCache.remove(url);
try {
Files.deleteIfExists(currentPath);
} catch (IOException e) {
LOG.warning("An exception encountered while deleting broken cached image file.", e);
}
}
Path newPath = Files.createTempFile("hmcl-net-resource-cache-", ".cache");
return new FileDownloadTask(NetworkUtils.toURL(url), newPath.toFile())
.thenSupplyAsync(() -> {
Path otherPath = remoteImageCache.putIfAbsent(url, newPath);
if (otherPath == null)
return newPath;
else {
// The image has been loaded in another task. Delete the image here in order not to pollute the tmp folder.
Files.delete(newPath);
return otherPath;
}
});
}).thenApplyAsync(path -> loadImage(path, requestedWidth, requestedHeight, preserveRatio, smooth));
}
/**
@@ -877,52 +955,17 @@ public final class FXUtils {
* the specified bounding box
* @return the image resource within the jar.
*/
public static Image newRemoteImage(String url, double requestedWidth, double requestedHeight, boolean preserveRatio, boolean smooth, boolean backgroundLoading) {
Path currentPath = remoteImageCache.get(url);
if (currentPath != null) {
if (Files.isReadable(currentPath)) {
try (InputStream inputStream = Files.newInputStream(currentPath)) {
return new Image(inputStream, requestedWidth, requestedHeight, preserveRatio, smooth);
} catch (IOException e) {
LOG.warning("An exception encountered while reading data from cached image file.", e);
}
}
// The file is unavailable or unreadable.
remoteImageCache.remove(url);
try {
Files.deleteIfExists(currentPath);
} catch (IOException e) {
LOG.warning("An exception encountered while deleting broken cached image file.", e);
}
}
Image image = new Image(url, requestedWidth, requestedHeight, preserveRatio, smooth, backgroundLoading);
image.progressProperty().addListener((observable, oldValue, newValue) -> {
if (newValue.doubleValue() >= 1.0 && !image.isError() && image.getPixelReader() != null && image.getWidth() > 0.0 && image.getHeight() > 0.0) {
Task.runAsync(() -> {
Path newPath = Files.createTempFile("hmcl-net-resource-cache-", ".cache");
try ( // Make sure the file is released from JVM before we put the path into remoteImageCache.
OutputStream outputStream = Files.newOutputStream(newPath);
PNGWriter writer = new PNGWriter(outputStream, PNGType.RGBA, PNGWriter.DEFAULT_COMPRESS_LEVEL)
) {
writer.write(PNGJavaFXUtils.asArgbImage(image));
} catch (IOException e) {
try {
Files.delete(newPath);
} catch (IOException e2) {
e2.addSuppressed(e);
throw e2;
}
throw e;
public static ObservableValue<Image> newRemoteImage(String url, int requestedWidth, int requestedHeight, boolean preserveRatio, boolean smooth) {
SimpleObjectProperty<Image> image = new SimpleObjectProperty<>();
getRemoteImageTask(url, requestedWidth, requestedHeight, preserveRatio, smooth)
.whenComplete(Schedulers.javafx(), (result, exception) -> {
if (exception == null) {
image.set(result);
} else {
LOG.warning("An exception encountered while loading remote image: " + url, exception);
}
if (remoteImageCache.putIfAbsent(url, newPath) != null) {
Files.delete(newPath); // The image has been loaded in another task. Delete the image here in order not to pollute the tmp folder.
}
}).start();
}
});
})
.start();
return image;
}
@@ -127,7 +127,7 @@ public class GameCrashWindow extends Stage {
crashReport = CrashReportAnalyzer.extractCrashReport(rawLog);
}
return pair(CrashReportAnalyzer.anaylze(rawLog), crashReport != null ? CrashReportAnalyzer.findKeywordsFromCrashReport(crashReport) : new HashSet<>());
return pair(CrashReportAnalyzer.analyze(rawLog), crashReport != null ? CrashReportAnalyzer.findKeywordsFromCrashReport(crashReport) : new HashSet<>());
}), Task.supplyAsync(() -> {
Path latestLog = repository.getRunDirectory(version.getId()).toPath().resolve("logs/latest.log");
if (!Files.isReadable(latestLog)) {
@@ -142,7 +142,7 @@ public class GameCrashWindow extends Stage {
return pair(new HashSet<CrashReportAnalyzer.Result>(), new HashSet<String>());
}
return pair(CrashReportAnalyzer.anaylze(log), CrashReportAnalyzer.findKeywordsFromCrashReport(log));
return pair(CrashReportAnalyzer.analyze(log), CrashReportAnalyzer.findKeywordsFromCrashReport(log));
})).whenComplete(Schedulers.javafx(), (taskResult, exception) -> {
loading.set(false);
@@ -22,6 +22,7 @@ import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import org.jackhuang.hmcl.util.StringUtils;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
@@ -159,28 +160,19 @@ public final class HTMLRenderer {
private void appendImage(Node node) {
String src = node.absUrl("src");
URI imageUri = null;
try {
if (!src.isEmpty())
imageUri = URI.create(src);
} catch (Exception ignored) {
}
String alt = node.attr("alt");
if (imageUri != null) {
URI uri = URI.create(src);
if (StringUtils.isNotBlank(src)) {
String widthAttr = node.attr("width");
String heightAttr = node.attr("height");
double width = 0;
double height = 0;
int width = 0;
int height = 0;
if (!widthAttr.isEmpty() && !heightAttr.isEmpty()) {
try {
width = Double.parseDouble(widthAttr);
height = Double.parseDouble(heightAttr);
width = (int) Double.parseDouble(widthAttr);
height = (int) Double.parseDouble(heightAttr);
} catch (NumberFormatException ignored) {
}
@@ -190,10 +182,12 @@ public final class HTMLRenderer {
}
}
Image image = FXUtils.newRemoteImage(uri.toString(), width, height, true, true, false);
if (image.isError()) {
LOG.warning("Failed to load image: " + uri, image.getException());
} else {
try {
Image image = FXUtils.getRemoteImageTask(src, width, height, true, true)
.run();
if (image == null)
throw new AssertionError("Image loading task returned null");
ImageView imageView = new ImageView(image);
if (hyperlink != null) {
URI target = resolveLink(hyperlink);
@@ -204,6 +198,8 @@ public final class HTMLRenderer {
}
children.add(imageView);
return;
} catch (Throwable e) {
LOG.warning("Failed to load image: " + src, e);
}
}
@@ -307,8 +307,8 @@ public final class LogWindow extends Stage {
listView.scrollTo(listView.getItems().size() - 1);
});
listView.setStyle("-fx-font-family: " + Lang.requireNonNullElse(config().getFontFamily(), FXUtils.DEFAULT_MONOSPACE_FONT)
+ "; -fx-font-size: " + config().getFontSize() + "px;");
listView.setStyle("-fx-font-family: \"" + Lang.requireNonNullElse(config().getFontFamily(), FXUtils.DEFAULT_MONOSPACE_FONT)
+ "\"; -fx-font-size: " + config().getFontSize() + "px;");
Holder<Object> lastCell = new Holder<>();
listView.setCellFactory(x -> new ListCell<Log>() {
{
@@ -58,12 +58,14 @@ public enum SVG {
DRESSER("M4 21V5Q4 4.175 4.5875 3.5875T6 3H18Q18.825 3 19.4125 3.5875T20 5V21H18V19H6V21H4ZM6 11H11V5H6V11ZM13 7H18V5H13V7ZM13 11H18V9H13V11ZM10 16H14V14H10V16ZM6 13V17H18V13H6ZM6 13V17 13Z"),
EDIT("M5 19H6.425L16.2 9.225 14.775 7.8 5 17.575V19ZM3 21V16.75L16.2 3.575Q16.5 3.3 16.8625 3.15T17.625 3Q18.025 3 18.4 3.15T19.05 3.6L20.425 5Q20.725 5.275 20.8625 5.65T21 6.4Q21 6.8 20.8625 7.1625T20.425 7.825L7.25 21H3ZM19 6.4 17.6 5 19 6.4ZM15.475 8.525 14.775 7.8 16.2 9.225 15.475 8.525Z"),
ERROR("M12 17Q12.425 17 12.7125 16.7125T13 16Q13 15.575 12.7125 15.2875T12 15Q11.575 15 11.2875 15.2875T11 16Q11 16.425 11.2875 16.7125T12 17ZM11 13H13V7H11V13ZM12 22Q9.925 22 8.1 21.2125T4.925 19.075Q3.575 17.725 2.7875 15.9T2 12Q2 9.925 2.7875 8.1T4.925 4.925Q6.275 3.575 8.1 2.7875T12 2Q14.075 2 15.9 2.7875T19.075 4.925Q20.425 6.275 21.2125 8.1T22 12Q22 14.075 21.2125 15.9T19.075 19.075Q17.725 20.425 15.9 21.2125T12 22ZM12 20Q15.35 20 17.675 17.675T20 12Q20 8.65 17.675 6.325T12 4Q8.65 4 6.325 6.325T4 12Q4 15.35 6.325 17.675T12 20ZM12 12Z"),
EXPLORE("M12 12Zm0 8q-3.325 0-5.6625-2.3375T4 12Q4 8.675 6.3375 6.3375T12 4q3.325-0 5.6625 2.3375T20 12q0 3.325-2.3375 5.6625T12 20Zm0 2q2.075-0 3.9-.7875t3.175-2.1375q1.35-1.35 2.1375-3.175T22 12q-0-2.075-.7875-3.9T19.075 4.925q-1.35-1.35-3.175-2.1375T12 2q-2.075 0-3.9.7875T4.925 4.925Q3.575 6.275 2.7875 8.1T2 12q0 2.075.7875 3.9T4.925 19.075q1.35 1.35 3.175 2.1375T12 22Zm0-8.5q.625 0 1.0625-.4375T13.5 12t-.4375-1.0625T12 10.5t-1.0625.4375T10.5 12t.4375 1.0625T12 13.5Zm-4.5 3 2-7 7-2-2 7-7 2Z"),
EXTENSION("M8.8 21H5Q4.175 21 3.5875 20.4125T3 19V15.2Q4.2 15.2 5.1 14.4375T6 12.5Q6 11.325 5.1 10.5625T3 9.8V6Q3 5.175 3.5875 4.5875T5 4H9Q9 2.95 9.725 2.225T11.5 1.5Q12.55 1.5 13.275 2.225T14 4H18Q18.825 4 19.4125 4.5875T20 6V10Q21.05 10 21.775 10.725T22.5 12.5Q22.5 13.55 21.775 14.275T20 15V19Q20 19.825 19.4125 20.4125T18 21H14.2Q14.2 19.75 13.4125 18.875T11.5 18Q10.375 18 9.5875 18.875T8.8 21ZM5 19H7.125Q7.725 17.35 9.05 16.675T11.5 16Q12.625 16 13.95 16.675T15.875 19H18V13H20Q20.2 13 20.35 12.85T20.5 12.5Q20.5 12.3 20.35 12.15T20 12H18V6H12V4Q12 3.8 11.85 3.65T11.5 3.5Q11.3 3.5 11.15 3.65T11 4V6H5V8.2Q6.35 8.7 7.175 9.875T8 12.5Q8 13.925 7.175 15.1T5 16.8V19ZM11.5 12.5Z"),
FEEDBACK("M12 15Q12.425 15 12.7125 14.7125T13 14Q13 13.575 12.7125 13.2875T12 13Q11.575 13 11.2875 13.2875T11 14Q11 14.425 11.2875 14.7125T12 15ZM11 11H13V5H11V11ZM2 22V4Q2 3.175 2.5875 2.5875T4 2H20Q20.825 2 21.4125 2.5875T22 4V16Q22 16.825 21.4125 17.4125T20 18H6L2 22ZM5.15 16H20V4H4V17.125L5.15 16ZM4 16V4 16Z"),
FOLDER("M4 20Q3.175 20 2.5875 19.4125T2 18V6Q2 5.175 2.5875 4.5875T4 4H10L12 6H20Q20.825 6 21.4125 6.5875T22 8V18Q22 18.825 21.4125 19.4125T20 20H4ZM4 18H20V8H11.175L9.175 6H4V18ZM4 18V6 18Z"),
FOLDER_COPY("M3 21Q2.175 21 1.5875 20.4125T1 19V6H3V19H20V21H3ZM7 17Q6.175 17 5.5875 16.4125T5 15V4Q5 3.175 5.5875 2.5875T7 2H12L14 4H21Q21.825 4 22.4125 4.5875T23 6V15Q23 15.825 22.4125 16.4125T21 17H7ZM7 15H21V6H13.175L11.175 4H7V15ZM7 15V4 15Z"),
FOLDER_OPEN("M4 20Q3.175 20 2.5875 19.4125T2 18V6Q2 5.175 2.5875 4.5875T4 4H10L12 6H20Q20.825 6 21.4125 6.5875T22 8H11.175L9.175 6H4V18L6.4 10H23.5L20.925 18.575Q20.725 19.225 20.1875 19.6125T19 20H4ZM6.1 18H19L20.8 12H7.9L6.1 18ZM6.1 18 7.9 12 6.1 18ZM4 8V6 8Z"),
FORMAT_LIST_BULLETED("M9 19V17H21V19H9ZM9 13V11H21V13H9ZM9 7V5H21V7H9ZM5 20Q4.175 20 3.5875 19.4125T3 18Q3 17.175 3.5875 16.5875T5 16Q5.825 16 6.4125 16.5875T7 18Q7 18.825 6.4125 19.4125T5 20ZM5 14Q4.175 14 3.5875 13.4125T3 12Q3 11.175 3.5875 10.5875T5 10Q5.825 10 6.4125 10.5875T7 12Q7 12.825 6.4125 13.4125T5 14ZM5 8Q4.175 8 3.5875 7.4125T3 6Q3 5.175 3.5875 4.5875T5 4Q5.825 4 6.4125 4.5875T7 6Q7 6.825 6.4125 7.4125T5 8Z"),
FORT("M1 21V17l2-2V9L1 7V3H3V5H5V3H7V5H9V3h2V7L9 9v1h6V9L13 7V3h2V5h2V3h2V5h2V3h2V7L21 9v6l2 2v4H14V18q0-.825-.5875-1.4125T12 16q-.825 0-1.4125.5875T10 18v3H1Zm2-2H8V18q0-1.65 1.175-2.825T12 14q1.65 0 2.825 1.175T16 18v1h5V17.825l-2-2V8.175L20.175 7h-4.35L17 8.175V12H7V8.175L8.175 7H3.825L5 8.175v7.65l-2 2V19Zm9-6Z"),
FOR_YOU("M12 12Q14.025 12 16.225 11.5875T20 10.5V20.5Q18.5 21.175 16.35 21.5875T12 22Q9.8 22 7.65 21.5875T4 20.5V10.5Q5.575 11.175 7.775 11.5875T12 12ZM18 19V13.25Q16.75 13.6 15.1125 13.8T12 14Q10.525 14 8.8875 13.8T6 13.25V19Q7.25 19.45 8.875 19.725T12 20Q13.5 20 15.125 19.725T18 19ZM12 2Q13.65 2 14.825 3.175T16 6Q16 7.65 14.825 8.825T12 10Q10.35 10 9.175 8.825T8 6Q8 4.35 9.175 3.175T12 2ZM12 8Q12.825 8 13.4125 7.4125T14 6Q14 5.175 13.4125 4.5875T12 4Q11.175 4 10.5875 4.5875T10 6Q10 6.825 10.5875 7.4125T12 8ZM12 6ZM12 16.625Z"),
GAMEPAD("M12 7.65ZM16.35 12ZM7.65 12ZM12 16.35ZM12 10.5 9 7.5V2H15V7.5L12 10.5ZM16.5 15 13.5 12 16.5 9H22V15H16.5ZM2 15V9H7.5L10.5 12 7.5 15H2ZM9 22V16.5L12 13.5 15 16.5V22H9ZM12 7.65 13 6.65V4H11V6.65L12 7.65ZM4 13H6.65L7.65 12 6.65 11H4V13ZM11 20H13V17.35L12 16.35 11 17.35V20ZM17.35 13H20V11H17.35L16.35 12 17.35 13Z"),
GLOBE_BOOK("M3.075 13Q3.05 12.75 3.0375 12.5T3.025 12Q3.025 10.125 3.725 8.4875T5.65 5.6375Q6.875 4.425 8.5 3.7125T12 3Q13.875 3 15.5125 3.7125T18.3625 5.6375Q19.575 6.85 20.2875 8.4875T21 12Q21 12.25 20.9875 12.5T20.95 13H18.925Q18.975 12.75 18.9875 12.5T19 12Q19 11.75 18.9875 11.5T18.925 11H15.975Q16 11.25 16 11.5V12.5Q16 12.75 15.975 13H14V12.175Q14 11.875 13.9875 11.575T13.95 11H10.075Q10.05 11.275 10.0375 11.575T10.025 12.175V13H8.05Q8.025 12.75 8.025 12.5V11.5Q8.025 11.25 8.05 11H5.1Q5.05 11.25 5.0375 11.5T5.025 12Q5.025 12.25 5.0375 12.5T5.1 13H3.075ZM5.7 9H8.275Q8.475 7.925 8.775 7.0625T9.425 5.5Q8.225 5.95 7.25 6.8625T5.7 9ZM10.35 9H13.65Q13.4 7.925 13.025 6.9T12 5Q11.35 5.875 10.9625 6.9T10.35 9ZM15.75 9H18.325Q17.75 7.775 16.7625 6.8625T14.575 5.5Q14.925 6.25 15.2375 7.0875T15.75 9ZM11 21V20Q11 18.75 10.125 17.875T8 17H2V15H8Q9.2 15 10.2375 15.525T12 17Q12.725 16.05 13.7625 15.525T16 15H22V17H16Q14.75 17 13.875 17.875T13 20V21H11Z"),
@@ -73,9 +75,11 @@ public enum SVG {
INFO("M11 17H13V11H11V17ZM12 9Q12.425 9 12.7125 8.7125T13 8Q13 7.575 12.7125 7.2875T12 7Q11.575 7 11.2875 7.2875T11 8Q11 8.425 11.2875 8.7125T12 9ZM12 22Q9.925 22 8.1 21.2125T4.925 19.075Q3.575 17.725 2.7875 15.9T2 12Q2 9.925 2.7875 8.1T4.925 4.925Q6.275 3.575 8.1 2.7875T12 2Q14.075 2 15.9 2.7875T19.075 4.925Q20.425 6.275 21.2125 8.1T22 12Q22 14.075 21.2125 15.9T19.075 19.075Q17.725 20.425 15.9 21.2125T12 22ZM12 20Q15.35 20 17.675 17.675T20 12Q20 8.65 17.675 6.325T12 4Q8.65 4 6.325 6.325T4 12Q4 15.35 6.325 17.675T12 20ZM12 12Z"),
KEYBOARD_ARROW_DOWN("M12 15.4 6 9.4 7.4 8 12 12.6 16.6 8 18 9.4 12 15.4Z"),
KEYBOARD_ARROW_UP("M12 10.8 7.4 15.4 6 14 12 8 18 14 16.6 15.4 12 10.8Z"),
LANDSCAPE("M1 18l6-8 4.5 6H19L14 9.35l-2.5 3.3L10.25 11 14 6l9 12H1Zm13.025-2ZM5 16H9L7 13.325 5 16ZH9 5Z"),
LIST("M7 9V7H21V9H7ZM7 13V11H21V13H7ZM7 17V15H21V17H7ZM4 9Q3.575 9 3.2875 8.7125T3 8Q3 7.575 3.2875 7.2875T4 7Q4.425 7 4.7125 7.2875T5 8Q5 8.425 4.7125 8.7125T4 9ZM4 13Q3.575 13 3.2875 12.7125T3 12Q3 11.575 3.2875 11.2875T4 11Q4.425 11 4.7125 11.2875T5 12Q5 12.425 4.7125 12.7125T4 13ZM4 17Q3.575 17 3.2875 16.7125T3 16Q3 15.575 3.2875 15.2875T4 15Q4.425 15 4.7125 15.2875T5 16Q5 16.425 4.7125 16.7125T4 17Z"),
LISTS("M2 20V16H6V20H2ZM8 20V16H22V20H8ZM2 14V10H6V14H2ZM8 14V10H22V14H8ZM2 8V4H6V8H2ZM8 8V4H22V8H8Z"),
LOCAL_CAFE("M4 21V19H20V21H4ZM8 17Q6.35 17 5.175 15.825T4 13V3H20Q20.825 3 21.4125 3.5875T22 5V8Q22 8.825 21.4125 9.4125T20 10H18V13Q18 14.65 16.825 15.825T14 17H8ZM8 15H14Q14.825 15 15.4125 14.4125T16 13V5H6V13Q6 13.825 6.5875 14.4125T8 15ZM18 8H20V5H18V8ZM8 15H6 16 8Z"),
LOCATION_CITY("M3 21V7H9V5l3-3 3 3v6h6V21H3Zm2-2H7V17H5v2Zm0-4H7V13H5v2Zm0-4H7V9H5v2Zm6 8h2V17H11v2Zm0-4h2V13H11v2Zm0-4h2V9H11v2Zm0-4h2V5H11V7Zm6 12h2V17H17v2Zm0-4h2V13H17v2Z"),
MENU("M3 18V16H21V18H3ZM3 13V11H21V13H3ZM3 8V6H21V8H3Z"),
MICROSOFT("M4 20H22v2H4V13H20v7h2V4H20v7H4V4h7V20h2V4h9V2H2V22H4"), // Not Material
MINIMIZE("M6 21V19H18V21H6Z"),
@@ -104,6 +108,8 @@ public enum SVG {
TRIP("M4 21Q3.175 21 2.5875 20.4125T2 19V8Q2 7.175 2.5875 6.5875T4 6H8V4Q8 3.175 8.5875 2.5875T10 2H14Q14.825 2 15.4125 2.5875T16 4V6H20Q20.825 6 21.4125 6.5875T22 8V19Q22 19.825 21.4125 20.4125T20 21H4ZM10 6H14V4H10V6ZM6 8H4V19H6V8ZM16 19V8H8V19H16ZM18 8V19H20V8H18ZM12 13.5Z"),
TUNE("M11 21V15H13V17H21V19H13V21H11ZM3 19V17H9V19H3ZM7 15V13H3V11H7V9H9V15H7ZM11 13V11H21V13H11ZM15 9V3H17V5H21V7H17V9H15ZM3 7V5H13V7H3Z"),
UPDATE("M12 21Q10.125 21 8.4875 20.2875T5.6375 18.3625Q4.425 17.15 3.7125 15.5125T3 12Q3 10.125 3.7125 8.4875T5.6375 5.6375Q6.85 4.425 8.4875 3.7125T12 3Q14.05 3 15.8875 3.875T19 6.35V4H21V10H15V8H17.75Q16.725 6.6 15.225 5.8T12 5Q9.075 5 7.0375 7.0375T5 12Q5 14.925 7.0375 16.9625T12 19Q14.625 19 16.5875 17.3T18.9 13H20.95Q20.575 16.425 18.0125 18.7125T12 21ZM14.8 16.2 11 12.4V7H13V11.6L16.2 14.8 14.8 16.2Z"),
VISIBILITY("M12 16q1.875 0 3.1875-1.3125T16.5 11.5 15.1875 8.3125 12 7 8.8125 8.3125 7.5 11.5t1.3125 3.1875T12 16Zm0-1.8q-1.125 0-1.9125-.7875T9.3 11.5t.7875-1.9125T12 8.8q1.125 0 1.9125.7875T14.7 11.5q0 1.125-.7875 1.9125T12 14.2ZM12 19q-3.65 0-6.65-2.0375T1 11.5Q2.35 8.075 5.35 6.0375T12 4q3.65 0 6.65 2.0375T23 11.5q-1.35 3.425-4.35 5.4625T12 19Zm0-7.5ZM12 17q2.825 0 5.1875-1.4875T20.8 11.5q-1.25-2.525-3.6125-4.0125T12 6 6.8125 7.4875 3.2 11.5q1.25 2.525 3.6125 4.0125T12 17Z"),
VISIBILITY_OFF("M16.1 13.3l-1.45-1.45q.225-1.175-.675-2.2t-2.325-.8L10.2 7.4q.425-.2.8625-.3T12 7q1.875 0 3.1875 1.3125T16.5 11.5q0 .5-.1.9375t-.3.8625Zm3.2 3.15-1.45-1.4q.95-.725 1.6875-1.5875T20.8 11.5q-1.25-2.525-3.5875-4.0125T12 6q-.725 0-1.425.1T9.2 6.4L7.65 4.85q1.025-.425 2.1-.6375T12 4q3.775 0 6.725 2.0875T23 11.5q-.575 1.475-1.5125 2.7375T19.3 16.45Zm.5 6.15-4.2-4.15q-.875.275-1.7625.4125T12 19q-3.775 0-6.725-2.0875T1 11.5q.525-1.325 1.325-2.4625T4.15 7L1.4 4.2 2.8 2.8 21.2 21.2l-1.4 1.4ZM5.55 8.4q-.725.65-1.325 1.425T3.2 11.5q1.25 2.525 3.5875 4.0125T12 17q.5 0 .975-.0625T13.95 16.8l-.9-.95q-.275.075-.525.1125T12 16q-1.875 0-3.1875-1.3125T7.5 11.5q0-.275.0375-.525T7.65 10.45L5.55 8.4Zm7.975 2.325ZM9.75 12.6Z"),
WARNING("M1 21 12 2 23 21H1ZM4.45 19H19.55L12 6 4.45 19ZM12 18Q12.425 18 12.7125 17.7125T13 17Q13 16.575 12.7125 16.2875T12 16Q11.575 16 11.2875 16.2875T11 17Q11 17.425 11.2875 17.7125T12 18ZM11 15H13V10H11V15ZM12 12.5Z"),
WB_SUNNY("M11 4V1H13V4H11ZM11 23V20H13V23H11ZM20 13V11H23V13H20ZM1 13V11H4V13H1ZM18.7 6.7 17.3 5.3 19.05 3.5 20.5 4.95 18.7 6.7ZM4.95 20.5 3.5 19.05 5.3 17.3 6.7 18.7 4.95 20.5ZM19.05 20.5 17.3 18.7 18.7 17.3 20.5 19.05 19.05 20.5ZM5.3 6.7 3.5 4.95 4.95 3.5 6.7 5.3 5.3 6.7ZM12 18Q9.5 18 7.75 16.25T6 12Q6 9.5 7.75 7.75T12 6Q14.5 6 16.25 7.75T18 12Q18 14.5 16.25 16.25T12 18ZM12 16Q13.675 16 14.8375 14.8375T16 12Q16 10.325 14.8375 9.1625T12 8Q10.325 8 9.1625 9.1625T8 12Q8 13.675 9.1625 14.8375T12 16ZM12 12Z"),
;
@@ -127,11 +133,10 @@ public enum SVG {
return pane;
}
Group svg = new Group(path);
double scale = size / 24;
svg.setScaleX(scale);
svg.setScaleY(scale);
return svg;
path.setScaleX(scale);
path.setScaleY(scale);
return new Group(path);
}
public Node createIcon(ObservableValue<? extends Paint> fill, double size) {
@@ -51,7 +51,7 @@ public final class UpgradeDialog extends JFXDialogLayout {
Task.supplyAsync(Schedulers.io(), () -> {
Document document = Jsoup.parse(new URL(url), 30 * 1000);
Node node = document.selectFirst("#nowchange");
if (node == null)
if (node == null || !"h1".equals(node.nodeName()))
throw new IOException("Cannot find #nowchange in document");
HTMLRenderer renderer = new HTMLRenderer(uri -> {
@@ -60,6 +60,9 @@ public final class UpgradeDialog extends JFXDialogLayout {
});
do {
if ("h1".equals(node.nodeName()) && !"nowchange".equals(node.attr("id"))) {
break;
}
renderer.appendNode(node);
node = node.nextSibling();
} while (node != null);
@@ -30,8 +30,7 @@ public class TransitionPane extends StackPane implements AnimationHandler {
private Duration duration;
private Node previousNode, currentNode;
{
currentNode = getChildren().stream().findFirst().orElse(null);
public TransitionPane() {
FXUtils.setOverflowHidden(this);
}
@@ -91,8 +90,12 @@ public class TransitionPane extends StackPane implements AnimationHandler {
private void updateContent(Node newView) {
if (getWidth() > 0 && getHeight() > 0) {
previousNode = currentNode;
if (previousNode == null)
previousNode = EMPTY_PANE;
if (previousNode == null) {
if (getChildren().isEmpty())
previousNode = EMPTY_PANE;
else
previousNode = getChildren().get(0);
}
} else
previousNode = EMPTY_PANE;
@@ -261,6 +261,7 @@ public final class TaskListPane extends StackPane {
case "hmcl.install.fabric": message = i18n("install.installer.install", i18n("install.installer.fabric") + " " + stageValue); break;
case "hmcl.install.fabric-api": message = i18n("install.installer.install", i18n("install.installer.fabric-api") + " " + stageValue); break;
case "hmcl.install.quilt": message = i18n("install.installer.install", i18n("install.installer.quilt") + " " + stageValue); break;
case "hmcl.install.quilt-api": message = i18n("install.installer.install", i18n("install.installer.quilt-api") + " " + stageValue); break;
default: message = i18n(stageKey); break;
}
// @formatter:on
@@ -198,6 +198,8 @@ public class DecoratorController {
private Background getBackground() {
EnumBackgroundImage imageType = config().getBackgroundImageType();
if (imageType == null)
imageType = EnumBackgroundImage.DEFAULT;
Image image = null;
switch (imageType) {
@@ -311,8 +313,9 @@ public class DecoratorController {
if (navigator.getCurrentPage() instanceof DecoratorPage) {
DecoratorPage page = (DecoratorPage) navigator.getCurrentPage();
// FIXME: Get WorldPage working first, and revisit this later
page.closePage();
if (page.isPageCloseable()) {
page.closePage();
return;
}
}
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hmcl.ui.download;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import org.jackhuang.hmcl.download.DownloadProvider;
@@ -58,6 +59,8 @@ class AdditionalInstallersPage extends AbstractInstallersPage {
reload();
});
}
installable.bind(Bindings.createBooleanBinding(() -> compatible.get() && txtName.validate(), txtName.textProperty(), compatible));
}
@Override
@@ -25,6 +25,7 @@ import org.jackhuang.hmcl.download.*;
import org.jackhuang.hmcl.download.game.GameRemoteVersion;
import org.jackhuang.hmcl.mod.RemoteMod;
import org.jackhuang.hmcl.mod.curse.CurseForgeRemoteModRepository;
import org.jackhuang.hmcl.mod.modrinth.ModrinthRemoteModRepository;
import org.jackhuang.hmcl.setting.DownloadProviders;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Profiles;
@@ -72,6 +73,7 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
private final TabHeader.Tab<DownloadListPage> modTab = new TabHeader.Tab<>("modTab");
private final TabHeader.Tab<DownloadListPage> modpackTab = new TabHeader.Tab<>("modpackTab");
private final TabHeader.Tab<DownloadListPage> resourcePackTab = new TabHeader.Tab<>("resourcePackTab");
private final TabHeader.Tab<DownloadListPage> shaderTab = new TabHeader.Tab<>("shaderTab");
private final TabHeader.Tab<DownloadListPage> worldTab = new TabHeader.Tab<>("worldTab");
private final TransitionPane transitionPane = new TransitionPane();
private final DownloadNavigator versionPageNavigator = new DownloadNavigator();
@@ -98,8 +100,9 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
}));
modTab.setNodeSupplier(loadVersionFor(() -> HMCLLocalizedDownloadListPage.ofMod((profile, version, file) -> download(profile, version, file, "mods"), true)));
resourcePackTab.setNodeSupplier(loadVersionFor(() -> HMCLLocalizedDownloadListPage.ofResourcePack((profile, version, file) -> download(profile, version, file, "resourcepacks"), true)));
shaderTab.setNodeSupplier(loadVersionFor(() -> new DownloadListPage(ModrinthRemoteModRepository.SHADER_PACKS, (profile, version, file) -> download(profile, version, file, "shaderpacks"), true)));
worldTab.setNodeSupplier(loadVersionFor(() -> new DownloadListPage(CurseForgeRemoteModRepository.WORLDS)));
tab = new TabHeader(newGameTab, modpackTab, modTab, resourcePackTab, worldTab);
tab = new TabHeader(newGameTab, modpackTab, modTab, resourcePackTab, shaderTab, worldTab);
Profiles.registerVersionsListener(this::loadVersions);
@@ -115,6 +118,7 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
.startCategory(i18n("download.content").toUpperCase(Locale.ROOT))
.addNavigationDrawerTab(tab, modTab, i18n("mods"), SVG.EXTENSION)
.addNavigationDrawerTab(tab, resourcePackTab, i18n("resourcepack"), SVG.TEXTURE)
.addNavigationDrawerTab(tab, shaderTab, i18n("download.shader"), SVG.WB_SUNNY)
.addNavigationDrawerTab(tab, worldTab, i18n("world"), SVG.PUBLIC);
FXUtils.setLimitWidth(sideBar, 200);
setLeft(sideBar);
@@ -186,6 +190,9 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
if (resourcePackTab.isInitialized()) {
resourcePackTab.getNode().loadVersion(profile, null);
}
if (shaderTab.isInitialized()) {
shaderTab.getNode().loadVersion(profile, null);
}
if (worldTab.isInitialized()) {
worldTab.getNode().loadVersion(profile, null);
}
@@ -18,11 +18,11 @@
package org.jackhuang.hmcl.ui.download;
import javafx.scene.Node;
import org.jackhuang.hmcl.game.ModpackHelper;
import org.jackhuang.hmcl.game.ManuallyCreatedModpackException;
import org.jackhuang.hmcl.mod.ModpackCompletionException;
import org.jackhuang.hmcl.game.ModpackHelper;
import org.jackhuang.hmcl.mod.MismatchedModpackTypeException;
import org.jackhuang.hmcl.mod.Modpack;
import org.jackhuang.hmcl.mod.ModpackCompletionException;
import org.jackhuang.hmcl.mod.UnsupportedModpackException;
import org.jackhuang.hmcl.mod.server.ServerModpackManifest;
import org.jackhuang.hmcl.setting.Profile;
@@ -102,7 +102,7 @@ public class ModpackInstallWizardProvider implements WizardProvider {
} catch (UnsupportedModpackException | ManuallyCreatedModpackException e) {
Controllers.dialog(i18n("modpack.unsupported"), i18n("message.error"), MessageType.ERROR);
} catch (MismatchedModpackTypeException e) {
Controllers.dialog(i18n("modpack.mismatched_type"), i18n("message.error"), MessageType.ERROR);
Controllers.dialog(i18n("modpack.mismatched_type", e.getRequired(), e.getFound()), i18n("message.error"), MessageType.ERROR);
} catch (IOException e) {
Controllers.dialog(i18n("modpack.invalid"), i18n("message.error"), MessageType.ERROR);
}
@@ -124,6 +124,7 @@ public class DownloadSettingsPage extends StackPane {
{
HBox hbox = new HBox(8);
hbox.setStyle("-fx-view-order: -1;"); // prevent the indicator from being covered by the hint
hbox.setAlignment(Pos.CENTER);
hbox.setPadding(new Insets(0, 0, 0, 30));
hbox.disableProperty().bind(config().autoDownloadThreadsProperty());
@@ -61,6 +61,8 @@ public final class ProfilePage extends BorderPane implements DecoratorPage {
* @param profile null if creating a new profile.
*/
public ProfilePage(Profile profile) {
getStyleClass().add("gray-background");
this.profile = profile;
String profileDisplayName = Optional.ofNullable(profile).map(Profiles::getProfileDisplayName).orElse("");
@@ -18,8 +18,9 @@ import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import org.jackhuang.hmcl.util.platform.Platform;
import org.jetbrains.annotations.Nullable;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Locale;
@@ -52,7 +53,7 @@ public final class AdvancedVersionSettingPage extends StackPane implements Decor
private final MultiFileItem.FileOption<NativesDirectoryType> nativesDirCustomOption;
private final JFXComboBox<Renderer> cboRenderer;
public AdvancedVersionSettingPage(Profile profile, String versionId, VersionSetting versionSetting) {
public AdvancedVersionSettingPage(Profile profile, @Nullable String versionId, VersionSetting versionSetting) {
this.profile = profile;
this.versionId = versionId;
this.versionSetting = versionSetting;
@@ -239,8 +240,27 @@ public final class AdvancedVersionSettingPage extends StackPane implements Decor
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()));
nativesDirSublist.subtitleProperty().bind(Bindings.createStringBinding(() -> {
if (versionSetting.getNativesDirType() == NativesDirectoryType.VERSION_FOLDER) {
String nativesDirName = "natives-" + Platform.SYSTEM_PLATFORM;
if (versionId == null) {
return String.format("%s/%s/%s",
profile.getRepository().getBaseDirectory().toPath().resolve("versions").toAbsolutePath().normalize(),
i18n("settings.advanced.natives_directory.default.version_id"),
nativesDirName
);
} else {
return profile.getRepository().getVersionRoot(versionId).toPath()
.toAbsolutePath().normalize()
.resolve(nativesDirName)
.toString();
}
} else if (versionSetting.getNativesDirType() == NativesDirectoryType.CUSTOM) {
return versionSetting.getNativesDir();
} else {
return null;
}
}, versionSetting.nativesDirProperty(), versionSetting.nativesDirTypeProperty()));
}
void unbindProperties() {
@@ -17,8 +17,6 @@
*/
package org.jackhuang.hmcl.ui.versions;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.collections.ObservableList;
import javafx.scene.control.Skin;
import javafx.stage.FileChooser;
@@ -28,7 +26,6 @@ import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.ListPageBase;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
@@ -41,24 +38,17 @@ import java.util.Objects;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class DatapackListPage extends ListPageBase<DatapackListPageSkin.DatapackInfoObject> implements DecoratorPage {
private final ReadOnlyObjectWrapper<State> state = new ReadOnlyObjectWrapper<>();
public final class DatapackListPage extends ListPageBase<DatapackListPageSkin.DatapackInfoObject> {
private final Path worldDir;
private final Datapack datapack;
// Strongly referencing items, preventing GC collection
@SuppressWarnings("FieldCanBeLocal")
private final ObservableList<DatapackListPageSkin.DatapackInfoObject> items;
public DatapackListPage(String worldName, Path worldDir) {
this.worldDir = worldDir;
state.set(State.fromTitle(i18n("datapack.title", worldName)));
public DatapackListPage(WorldManagePage worldManagePage) {
this.worldDir = worldManagePage.getWorld().getFile();
datapack = new Datapack(worldDir.resolve("datapacks"));
datapack.loadFromDir();
setItems(items = MappedObservableList.create(datapack.getInfo(), DatapackListPageSkin.DatapackInfoObject::new));
setItems(MappedObservableList.create(datapack.getInfo(), DatapackListPageSkin.DatapackInfoObject::new));
FXUtils.applyDragListener(this, it -> Objects.equals("zip", FileUtils.getExtension(it)),
mods -> mods.forEach(this::installSingleDatapack), this::refresh);
@@ -91,11 +81,6 @@ public class DatapackListPage extends ListPageBase<DatapackListPageSkin.Datapack
.start();
}
@Override
public ReadOnlyObjectProperty<State> stateProperty() {
return state.getReadOnlyProperty();
}
public void add() {
FileChooser chooser = new FileChooser();
chooser.setTitle(i18n("datapack.choose_datapack"));
@@ -20,7 +20,6 @@ package org.jackhuang.hmcl.ui.versions;
import com.jfoenix.controls.JFXCheckBox;
import com.jfoenix.controls.JFXListView;
import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject;
import com.jfoenix.effects.JFXDepthManager;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.geometry.Insets;
@@ -29,87 +28,57 @@ import javafx.scene.control.SelectionMode;
import javafx.scene.control.SkinBase;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.mod.Datapack;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.construct.FloatListCell;
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.util.Holder;
import org.jackhuang.hmcl.util.StringUtils;
import static org.jackhuang.hmcl.ui.FXUtils.ignoreEvent;
import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.createToolbarButton;
import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.createToolbarButton2;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
class DatapackListPageSkin extends SkinBase<DatapackListPage> {
final class DatapackListPageSkin extends SkinBase<DatapackListPage> {
DatapackListPageSkin(DatapackListPage skinnable) {
super(skinnable);
BorderPane root = new BorderPane();
root.getStyleClass().add("content-background");
StackPane pane = new StackPane();
pane.setPadding(new Insets(10));
pane.getStyleClass().addAll("notice-pane");
ComponentList root = new ComponentList();
root.getStyleClass().add("no-padding");
JFXListView<DatapackInfoObject> listView = new JFXListView<>();
{
HBox toolbar = new HBox();
toolbar.getStyleClass().add("jfx-tool-bar-second");
JFXDepthManager.setDepth(toolbar, 1);
toolbar.setPickOnBounds(false);
toolbar.getChildren().add(createToolbarButton(i18n("button.refresh"), SVG.REFRESH, skinnable::refresh));
toolbar.getChildren().add(createToolbarButton(i18n("datapack.add"), SVG.ADD, skinnable::add));
toolbar.getChildren().add(createToolbarButton(i18n("button.remove"), SVG.DELETE, () -> {
toolbar.getChildren().add(createToolbarButton2(i18n("button.refresh"), SVG.REFRESH, skinnable::refresh));
toolbar.getChildren().add(createToolbarButton2(i18n("datapack.add"), SVG.ADD, skinnable::add));
toolbar.getChildren().add(createToolbarButton2(i18n("button.remove"), SVG.DELETE, () -> {
Controllers.confirm(i18n("button.remove.confirm"), i18n("button.remove"), () -> {
skinnable.removeSelected(listView.getSelectionModel().getSelectedItems());
}, null);
}));
toolbar.getChildren().add(createToolbarButton(i18n("mods.enable"), SVG.CHECK, () ->
toolbar.getChildren().add(createToolbarButton2(i18n("mods.enable"), SVG.CHECK, () ->
skinnable.enableSelected(listView.getSelectionModel().getSelectedItems())));
toolbar.getChildren().add(createToolbarButton(i18n("mods.disable"), SVG.CLOSE, () ->
toolbar.getChildren().add(createToolbarButton2(i18n("mods.disable"), SVG.CLOSE, () ->
skinnable.disableSelected(listView.getSelectionModel().getSelectedItems())));
root.setTop(toolbar);
root.getContent().add(toolbar);
}
{
SpinnerPane center = new SpinnerPane();
ComponentList.setVgrow(center, Priority.ALWAYS);
center.getStyleClass().add("large-spinner-pane");
center.loadingProperty().bind(skinnable.loadingProperty());
listView.setCellFactory(x -> new FloatListCell<DatapackInfoObject>(listView) {
JFXCheckBox checkBox = new JFXCheckBox();
TwoLineListItem content = new TwoLineListItem();
BooleanProperty booleanProperty;
{
Region clippedContainer = (Region)listView.lookup(".clipped-container");
HBox container = new HBox(8);
container.setPadding(new Insets(0, 0, 0, 6));
container.setAlignment(Pos.CENTER_LEFT);
pane.getChildren().add(container);
pane.setPadding(new Insets(8, 8, 8, 0));
if (clippedContainer != null) {
maxWidthProperty().bind(clippedContainer.widthProperty());
prefWidthProperty().bind(clippedContainer.widthProperty());
minWidthProperty().bind(clippedContainer.widthProperty());
}
container.getChildren().setAll(checkBox, content);
}
@Override
protected void updateControl(DatapackInfoObject dataItem, boolean empty) {
if (empty) return;
content.setTitle(dataItem.getTitle());
content.setSubtitle(dataItem.getSubtitle());
if (booleanProperty != null) {
checkBox.selectedProperty().unbindBidirectional(booleanProperty);
}
checkBox.selectedProperty().bindBidirectional(booleanProperty = dataItem.active);
}
});
Holder<Object> lastCell = new Holder<>();
listView.setCellFactory(x -> new DatapackInfoListCell(listView, lastCell));
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
Bindings.bindContent(listView.getItems(), skinnable.getItems());
@@ -117,10 +86,11 @@ class DatapackListPageSkin extends SkinBase<DatapackListPage> {
ignoreEvent(listView, KeyEvent.KEY_PRESSED, e -> e.getCode() == KeyCode.ESCAPE);
center.setContent(listView);
root.setCenter(center);
root.getContent().add(center);
}
getChildren().setAll(root);
pane.getChildren().setAll(root);
getChildren().setAll(pane);
}
static class DatapackInfoObject extends RecursiveTreeObject<DatapackInfoObject> {
@@ -144,4 +114,36 @@ class DatapackListPageSkin extends SkinBase<DatapackListPage> {
return packInfo;
}
}
private static final class DatapackInfoListCell extends MDListCell<DatapackInfoObject> {
final JFXCheckBox checkBox = new JFXCheckBox();
final TwoLineListItem content = new TwoLineListItem();
BooleanProperty booleanProperty;
DatapackInfoListCell(JFXListView<DatapackInfoObject> listView, Holder<Object> lastCell) {
super(listView, lastCell);
HBox container = new HBox(8);
container.setPickOnBounds(false);
container.setAlignment(Pos.CENTER_LEFT);
HBox.setHgrow(content, Priority.ALWAYS);
content.setMouseTransparent(true);
setSelectable();
StackPane.setMargin(container, new Insets(8));
container.getChildren().setAll(checkBox, content);
getContainer().getChildren().setAll(container);
}
@Override
protected void updateControl(DatapackInfoObject dataItem, boolean empty) {
if (empty) return;
content.setTitle(dataItem.getTitle());
content.setSubtitle(dataItem.getSubtitle());
if (booleanProperty != null) {
checkBox.selectedProperty().unbindBidirectional(booleanProperty);
}
checkBox.selectedProperty().bindBidirectional(booleanProperty = dataItem.active);
}
}
}
@@ -42,6 +42,7 @@ import javafx.scene.layout.*;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.mod.RemoteMod;
import org.jackhuang.hmcl.mod.RemoteModRepository;
import org.jackhuang.hmcl.mod.modrinth.ModrinthRemoteModRepository;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
@@ -189,7 +190,9 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
}
protected String getLocalizedCategory(String category) {
return i18n("curse.category." + category);
return repository instanceof ModrinthRemoteModRepository
? i18n("modrinth.category." + category)
: i18n("curse.category." + category);
}
private String getLocalizedCategoryIndent(ModDownloadListPageSkin.CategoryIndented category) {
@@ -200,7 +203,11 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
}
protected String getLocalizedOfficialPage() {
return i18n("mods.curseforge");
if (repository instanceof ModrinthRemoteModRepository) {
return i18n("mods.modrinth");
} else {
return i18n("mods.curseforge");
}
}
protected Profile.ProfileVersion getProfileVersion() {
@@ -222,6 +229,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
}
private static class ModDownloadListPageSkin extends SkinBase<DownloadListPage> {
private final JFXListView<RemoteMod> listView = new JFXListView<>();
protected ModDownloadListPageSkin(DownloadListPage control) {
super(control);
@@ -356,11 +364,12 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
}
currentFilterID.set(filterID.get());
int pageOffset = control.pageOffset.get();
getSkinnable().search(gameVersionField.getSelectionModel().getSelectedItem(),
Optional.ofNullable(categoryComboBox.getSelectionModel().getSelectedItem())
.map(CategoryIndented::getCategory)
.orElse(null),
control.pageOffset.get(),
pageOffset == -1 ? 0 : pageOffset,
nameField.getText(),
sortComboBox.getSelectionModel().getSelectedItem());
};
@@ -432,13 +441,17 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
int pageOffset = control.pageOffset.get();
int pageCount = control.pageCount.get();
boolean disablePrevious = pageOffset == 0;
boolean disableAll = pageCount >= -1 && pageCount <= 1;
boolean disablePrevious = disableAll || pageOffset == 0;
firstPageButton.setDisable(disablePrevious);
previousPageButton.setDisable(disablePrevious);
boolean disableNext = pageOffset == pageCount - 1;
boolean disableNext = disableAll || pageOffset == pageCount - 1;
nextPageButton.setDisable(disableNext);
lastPageButton.setDisable(disableNext || pageCount == -1);
lastPageButton.setDisable(disableNext);
listView.scrollTo(0);
};
FXUtils.onChange(control.pageCount, pageCountN -> {
@@ -494,7 +507,6 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
}
});
JFXListView<RemoteMod> listView = new JFXListView<>();
spinnerPane.setContent(listView);
Bindings.bindContent(listView.getItems(), getSkinnable().items);
FXUtils.onClicked(listView, () -> {
@@ -530,7 +542,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
.collect(Collectors.toList()));
if (StringUtils.isNotBlank(dataItem.getIconUrl())) {
imageView.setImage(FXUtils.newRemoteImage(dataItem.getIconUrl(), 40, 40, true, true, true));
imageView.imageProperty().bind(FXUtils.newRemoteImage(dataItem.getIconUrl(), 40, 40, true, true));
}
}
});
@@ -220,7 +220,7 @@ public class DownloadPage extends Control implements DecoratorPage {
{
ImageView imageView = new ImageView();
if (StringUtils.isNotBlank(getSkinnable().addon.getIconUrl())) {
imageView.setImage(FXUtils.newRemoteImage(getSkinnable().addon.getIconUrl(), 40, 40, true, true, true));
imageView.imageProperty().bind(FXUtils.newRemoteImage(getSkinnable().addon.getIconUrl(), 40, 40, true, true));
}
descriptionPane.getChildren().add(FXUtils.limitingSize(imageView, 40, 40));
@@ -359,7 +359,7 @@ public class DownloadPage extends Control implements DecoratorPage {
.collect(Collectors.toList()));
if (StringUtils.isNotBlank(addon.getIconUrl())) {
imageView.setImage(FXUtils.newRemoteImage(addon.getIconUrl(), 40, 40, true, true, true));
imageView.imageProperty().bind(FXUtils.newRemoteImage(addon.getIconUrl(), 40, 40, true, true));
}
} else {
content.setTitle(i18n("mods.broken_dependency.title"));
@@ -114,6 +114,8 @@ public class InstallerListPage extends ListPageBase<InstallerItem> implements Ve
for (LibraryAnalyzer.LibraryMark mark : analyzer) {
String libraryId = mark.getLibraryId();
String libraryVersion = mark.getLibraryVersion();
if ("mcbbs".equals(libraryId))
continue;
// we have done this library above.
if (LibraryAnalyzer.LibraryType.fromPatchId(libraryId) != null)
@@ -50,6 +50,7 @@ import java.io.File;
import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;
@@ -206,6 +207,7 @@ public final class SchematicsPage extends ListPageBase<SchematicsPage.Item> impl
}
}
}
} catch (NoSuchFileException ignored) {
} catch (IOException e) {
LOG.warning("Failed to load schematics in " + dir, e);
}
@@ -315,7 +315,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
txtMaxMemory.textProperty().bindBidirectional(maxMemory, SafeStringConverter.fromInteger());
txtMaxMemory.setValidators(new NumberValidator(i18n("input.number"), false));
lowerBoundPane.getChildren().setAll(label, slider, txtMaxMemory, new Label("MiB"));
lowerBoundPane.getChildren().setAll(label, slider, txtMaxMemory, new Label(i18n("settings.memory.unit.mib")));
}
StackPane progressBarPane = new StackPane();
@@ -37,15 +37,17 @@ public final class WorldBackupTask extends Task<Path> {
private final World world;
private final Path backupsDir;
private final boolean needLock;
public WorldBackupTask(World world, Path backupsDir) {
public WorldBackupTask(World world, Path backupsDir, boolean needLock) {
this.world = world;
this.backupsDir = backupsDir;
this.needLock = needLock;
}
@Override
public void execute() throws Exception {
try (FileChannel lockChannel = world.lock()) {
try (FileChannel lockChannel = needLock ? world.lock() : null) {
Files.createDirectories(backupsDir);
String time = LocalDateTime.now().format(WorldBackupsPage.TIME_FORMATTER);
String baseName = time + "_" + world.getFileName();
@@ -18,9 +18,6 @@
package org.jackhuang.hmcl.ui.versions;
import com.jfoenix.controls.JFXButton;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
@@ -41,7 +38,6 @@ import org.jackhuang.hmcl.ui.*;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
import org.jackhuang.hmcl.ui.construct.RipplerContainer;
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.util.Pair;
import org.jackhuang.hmcl.util.StringUtils;
import org.jetbrains.annotations.NotNull;
@@ -65,33 +61,24 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG;
/**
* @author Glavo
*/
public final class WorldBackupsPage extends ListPageBase<WorldBackupsPage.BackupInfo> implements DecoratorPage {
public final class WorldBackupsPage extends ListPageBase<WorldBackupsPage.BackupInfo> {
static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss");
private final World world;
private final Path backupsDir;
private final boolean isReadOnly;
private final Pattern backupFileNamePattern;
private final ObjectProperty<State> state = new SimpleObjectProperty<>();
public WorldBackupsPage(World world, Path backupsDir) {
this.backupsDir = backupsDir;
this.world = world;
public WorldBackupsPage(WorldManagePage worldManagePage) {
this.world = worldManagePage.getWorld();
this.backupsDir = worldManagePage.getBackupsDir();
this.isReadOnly = worldManagePage.isReadOnly();
this.backupFileNamePattern = Pattern.compile("(?<datetime>[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}-[0-9]{2}-[0-9]{2})_" + Pattern.quote(world.getFileName()) + "( (?<count>[0-9]+))?\\.zip");
this.state.set(State.fromTitle(i18n("world.backup.title", world.getWorldName())));
loadBackups();
refresh();
}
@Override
public ReadOnlyObjectProperty<State> stateProperty() {
return state;
}
@Override
public void refresh() {
loadBackups();
}
private void loadBackups() {
setLoading(true);
Task.supplyAsync(() -> {
if (Files.isDirectory(backupsDir)) {
@@ -140,7 +127,7 @@ public final class WorldBackupsPage extends ListPageBase<WorldBackupsPage.Backup
}
void createBackup() {
Controllers.taskDialog(new WorldBackupTask(world, backupsDir).setName(i18n("world.backup.processing")).thenApplyAsync(path -> {
Controllers.taskDialog(new WorldBackupTask(world, backupsDir, false).setName(i18n("world.backup.processing")).thenApplyAsync(path -> {
Matcher matcher = backupFileNamePattern.matcher(path.getFileName().toString());
if (!matcher.matches()) {
throw new AssertionError("Wrong backup file name" + path);
@@ -176,7 +163,13 @@ public final class WorldBackupsPage extends ListPageBase<WorldBackupsPage.Backup
@Override
protected List<Node> initializeToolbar(WorldBackupsPage skinnable) {
return Arrays.asList(createToolbarButton2(i18n("button.refresh"), SVG.REFRESH, skinnable::refresh), createToolbarButton2(i18n("world.backup.create.new_one"), SVG.ARCHIVE, skinnable::createBackup));
JFXButton createBackup = createToolbarButton2(i18n("world.backup.create.new_one"), SVG.ARCHIVE, skinnable::createBackup);
createBackup.setDisable(isReadOnly);
return Arrays.asList(
createToolbarButton2(i18n("button.refresh"), SVG.REFRESH, skinnable::refresh),
createBackup
);
}
}
@@ -20,25 +20,27 @@ package org.jackhuang.hmcl.ui.versions;
import com.github.steveice10.opennbt.tag.builtin.*;
import com.jfoenix.controls.JFXComboBox;
import com.jfoenix.controls.JFXTextField;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.effect.BoxBlur;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.game.World;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.text.DecimalFormat;
import java.time.Instant;
@@ -52,38 +54,14 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
/**
* @author Glavo
*/
public final class WorldInfoPage extends SpinnerPane implements DecoratorPage {
public final class WorldInfoPage extends SpinnerPane {
private final WorldManagePage worldManagePage;
private final World world;
private CompoundTag levelDat;
private final ObjectProperty<State> stateProperty;
private FileChannel sessionLockChannel;
@Override
public boolean back() {
closePage();
return true;
}
@Override
public void closePage() {
if (sessionLockChannel != null) {
try {
sessionLockChannel.close();
} catch (IOException e) {
LOG.warning("Failed to close session lock channel", e);
}
sessionLockChannel = null;
}
}
public WorldInfoPage(World world) {
this.world = world;
this.stateProperty = new SimpleObjectProperty<>(State.fromTitle(i18n("world.info.title", world.getWorldName())));
this.getStyleClass().add("gray-background");
public WorldInfoPage(WorldManagePage worldManagePage) {
this.worldManagePage = worldManagePage;
this.world = worldManagePage.getWorld();
this.setLoading(true);
Task.supplyAsync(this::loadWorldInfo)
@@ -103,18 +81,9 @@ public final class WorldInfoPage extends SpinnerPane implements DecoratorPage {
if (!Files.isDirectory(world.getFile()))
throw new IOException("Not a valid world directory");
try {
sessionLockChannel = world.lock();
} catch (IOException ignored) {
}
return world.readLevelDat();
}
private boolean isReadOnly() {
return sessionLockChannel == null;
}
private void updateControls() {
CompoundTag dataTag = levelDat.get("Data");
CompoundTag worldGenSettings = dataTag.get("WorldGenSettings");
@@ -161,9 +130,22 @@ public final class WorldInfoPage extends SpinnerPane implements DecoratorPage {
BorderPane randomSeedPane = new BorderPane();
{
HBox left = new HBox(8);
BorderPane.setAlignment(left, Pos.CENTER_LEFT);
left.setAlignment(Pos.CENTER_LEFT);
randomSeedPane.setLeft(left);
Label label = new Label(i18n("world.info.random_seed"));
BorderPane.setAlignment(label, Pos.CENTER_LEFT);
randomSeedPane.setLeft(label);
SimpleBooleanProperty visibility = new SimpleBooleanProperty();
StackPane visibilityButton = new StackPane();
visibilityButton.setCursor(Cursor.HAND);
FXUtils.setLimitWidth(visibilityButton, 12);
FXUtils.setLimitHeight(visibilityButton, 12);
FXUtils.onClicked(visibilityButton, () -> visibility.set(!visibility.get()));
left.getChildren().setAll(label, visibilityButton);
Label randomSeedLabel = new Label();
FXUtils.copyOnDoubleClick(randomSeedLabel);
@@ -174,6 +156,14 @@ public final class WorldInfoPage extends SpinnerPane implements DecoratorPage {
if (tag instanceof LongTag) {
randomSeedLabel.setText(tag.getValue().toString());
}
BoxBlur blur = new BoxBlur();
blur.setIterations(3);
FXUtils.onChangeAndOperate(visibility, isVisibility -> {
SVG icon = isVisibility ? SVG.VISIBILITY : SVG.VISIBILITY_OFF;
visibilityButton.getChildren().setAll(icon.createIcon(Theme.blackFill(), 12));
randomSeedLabel.setEffect(isVisibility ? null : blur);
});
}
BorderPane lastPlayedPane = new BorderPane();
@@ -210,7 +200,7 @@ public final class WorldInfoPage extends SpinnerPane implements DecoratorPage {
OptionToggleButton allowCheatsButton = new OptionToggleButton();
{
allowCheatsButton.setTitle(i18n("world.info.allow_cheats"));
allowCheatsButton.setDisable(isReadOnly());
allowCheatsButton.setDisable(worldManagePage.isReadOnly());
Tag tag = dataTag.get("allowCommands");
if (tag instanceof ByteTag) {
@@ -233,7 +223,7 @@ public final class WorldInfoPage extends SpinnerPane implements DecoratorPage {
OptionToggleButton generateFeaturesButton = new OptionToggleButton();
{
generateFeaturesButton.setTitle(i18n("world.info.generate_features"));
generateFeaturesButton.setDisable(isReadOnly());
generateFeaturesButton.setDisable(worldManagePage.isReadOnly());
Tag tag = worldGenSettings != null ? worldGenSettings.get("generate_features") : dataTag.get("MapFeatures");
if (tag instanceof ByteTag) {
@@ -260,7 +250,7 @@ public final class WorldInfoPage extends SpinnerPane implements DecoratorPage {
difficultyPane.setLeft(label);
JFXComboBox<Difficulty> difficultyBox = new JFXComboBox<>(Difficulty.items);
difficultyBox.setDisable(isReadOnly());
difficultyBox.setDisable(worldManagePage.isReadOnly());
BorderPane.setAlignment(difficultyBox, Pos.CENTER_RIGHT);
difficultyPane.setRight(difficultyBox);
@@ -366,19 +356,32 @@ public final class WorldInfoPage extends SpinnerPane implements DecoratorPage {
playerGameTypePane.setLeft(label);
JFXComboBox<GameType> gameTypeBox = new JFXComboBox<>(GameType.items);
gameTypeBox.setDisable(isReadOnly());
gameTypeBox.setDisable(worldManagePage.isReadOnly());
BorderPane.setAlignment(gameTypeBox, Pos.CENTER_RIGHT);
playerGameTypePane.setRight(gameTypeBox);
Tag tag = player.get("playerGameType");
Tag hardcoreTag = dataTag.get("hardcore");
boolean isHardcore = hardcoreTag instanceof ByteTag && ((ByteTag) hardcoreTag).getValue() == 1;
if (tag instanceof IntTag) {
IntTag intTag = (IntTag) tag;
GameType gameType = GameType.of(intTag.getValue());
GameType gameType = GameType.of(intTag.getValue(), isHardcore);
if (gameType != null) {
gameTypeBox.setValue(gameType);
gameTypeBox.valueProperty().addListener((o, oldValue, newValue) -> {
if (newValue != null) {
intTag.setValue(newValue.ordinal());
if (newValue == GameType.HARDCORE) {
intTag.setValue(0); // survival (hardcore worlds are survival+hardcore flag)
if (hardcoreTag instanceof ByteTag) {
((ByteTag) hardcoreTag).setValue((byte) 1);
}
} else {
intTag.setValue(newValue.ordinal());
if (hardcoreTag instanceof ByteTag) {
((ByteTag) hardcoreTag).setValue((byte) 0);
}
}
saveLevelDat();
}
});
@@ -397,7 +400,7 @@ public final class WorldInfoPage extends SpinnerPane implements DecoratorPage {
healthPane.setLeft(label);
JFXTextField healthField = new JFXTextField();
healthField.setDisable(isReadOnly());
healthField.setDisable(worldManagePage.isReadOnly());
healthField.setPrefWidth(50);
healthField.setAlignment(Pos.CENTER_RIGHT);
BorderPane.setAlignment(healthField, Pos.CENTER_RIGHT);
@@ -431,7 +434,7 @@ public final class WorldInfoPage extends SpinnerPane implements DecoratorPage {
foodLevelPane.setLeft(label);
JFXTextField foodLevelField = new JFXTextField();
foodLevelField.setDisable(isReadOnly());
foodLevelField.setDisable(worldManagePage.isReadOnly());
foodLevelField.setPrefWidth(50);
foodLevelField.setAlignment(Pos.CENTER_RIGHT);
BorderPane.setAlignment(foodLevelField, Pos.CENTER_RIGHT);
@@ -465,7 +468,7 @@ public final class WorldInfoPage extends SpinnerPane implements DecoratorPage {
xpLevelPane.setLeft(label);
JFXTextField xpLevelField = new JFXTextField();
xpLevelField.setDisable(isReadOnly());
xpLevelField.setDisable(worldManagePage.isReadOnly());
xpLevelField.setPrefWidth(50);
xpLevelField.setAlignment(Pos.CENTER_RIGHT);
BorderPane.setAlignment(xpLevelField, Pos.CENTER_RIGHT);
@@ -510,11 +513,6 @@ public final class WorldInfoPage extends SpinnerPane implements DecoratorPage {
}
}
@Override
public ReadOnlyObjectProperty<State> stateProperty() {
return stateProperty;
}
private static final class Dimension {
static final Dimension OVERWORLD = new Dimension(null);
static final Dimension THE_NETHER = new Dimension(i18n("world.info.dimension.the_nether"));
@@ -621,12 +619,13 @@ public final class WorldInfoPage extends SpinnerPane implements DecoratorPage {
}
private enum GameType {
SURVIVAL, CREATIVE, ADVENTURE, SPECTATOR;
SURVIVAL, CREATIVE, ADVENTURE, SPECTATOR, HARDCORE;
static final ObservableList<GameType> items = FXCollections.observableList(Arrays.asList(values()));
static GameType of(int d) {
return d >= 0 && d <= items.size() ? items.get(d) : null;
static GameType of(int d, boolean hardcore) {
if (hardcore && d == 0) return HARDCORE; // hardcore + survival
return d >= 0 && d < 4 ? items.get(d) : null;
}
@Override
@@ -24,7 +24,6 @@ import org.jackhuang.hmcl.game.World;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.wizard.SinglePageWizardProvider;
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
import java.io.File;
import java.nio.file.Path;
@@ -66,20 +65,7 @@ public final class WorldListItem extends Control {
FXUtils.openFolder(world.getFile().toFile());
}
public void manageDatapacks() {
if (world.getGameVersion() == null || // old game will not write game version to level.dat
GameVersionNumber.compare(world.getGameVersion(), "1.13") < 0) {
Controllers.dialog(i18n("world.datapack.1_13"));
return;
}
Controllers.navigate(new DatapackListPage(world.getWorldName(), world.getFile()));
}
public void showInfo() {
Controllers.navigate(new WorldInfoPage(world));
}
public void showBackupPage() {
Controllers.navigate(new WorldBackupsPage(world, backupsDir));
public void showManagePage() {
Controllers.navigate(new WorldManagePage(world, backupsDir));
}
}
@@ -18,10 +18,12 @@
package org.jackhuang.hmcl.ui.versions;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXPopup;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.SkinBase;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
@@ -29,8 +31,9 @@ import org.jackhuang.hmcl.game.World;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.construct.RipplerContainer;
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.util.ChunkBaseApp;
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
import java.time.Instant;
@@ -40,12 +43,14 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class WorldListItemSkin extends SkinBase<WorldListItem> {
private final BorderPane root;
public WorldListItemSkin(WorldListItem skinnable) {
super(skinnable);
World world = skinnable.getWorld();
BorderPane root = new BorderPane();
root = new BorderPane();
root.getStyleClass().add("md-list-cell");
root.setPadding(new Insets(8));
@@ -64,6 +69,7 @@ public final class WorldListItemSkin extends SkinBase<WorldListItem> {
{
TwoLineListItem item = new TwoLineListItem();
root.setCenter(item);
item.setMouseTransparent(true);
if (world.getWorldName() != null)
item.setTitle(parseColorEscapes(world.getWorldName()));
item.setSubtitle(i18n("world.datetime", formatDateTime(Instant.ofEpochMilli(world.getLastPlayed())), world.getGameVersion() == null ? i18n("message.unknown") : world.getGameVersion()));
@@ -79,42 +85,58 @@ public final class WorldListItemSkin extends SkinBase<WorldListItem> {
root.setRight(right);
right.setAlignment(Pos.CENTER_RIGHT);
JFXButton btnReveal = new JFXButton();
right.getChildren().add(btnReveal);
FXUtils.installFastTooltip(btnReveal, i18n("reveal.in_file_manager"));
btnReveal.getStyleClass().add("toggle-icon4");
btnReveal.setGraphic(SVG.FOLDER_OPEN.createIcon(Theme.blackFill(), -1));
btnReveal.setOnAction(event -> skinnable.reveal());
JFXButton btnExport = new JFXButton();
right.getChildren().add(btnExport);
FXUtils.installFastTooltip(btnExport, i18n("world.export"));
btnExport.getStyleClass().add("toggle-icon4");
btnExport.setGraphic(SVG.OUTPUT.createIcon(Theme.blackFill(), -1));
btnExport.setOnAction(event -> skinnable.export());
JFXButton btnBackup = new JFXButton();
right.getChildren().add(btnBackup);
FXUtils.installFastTooltip(btnBackup, i18n("world.backup"));
btnBackup.getStyleClass().add("toggle-icon4");
btnBackup.setGraphic(SVG.ARCHIVE.createIcon(Theme.blackFill(), -1));
btnBackup.setOnAction(event -> skinnable.showBackupPage());
JFXButton btnDatapack = new JFXButton();
right.getChildren().add(btnDatapack);
FXUtils.installFastTooltip(btnDatapack, i18n("world.datapack"));
btnDatapack.getStyleClass().add("toggle-icon4");
btnDatapack.setGraphic(SVG.EXTENSION.createIcon(Theme.blackFill(), -1));
btnDatapack.setOnAction(event -> skinnable.manageDatapacks());
JFXButton btnInfo = new JFXButton();
right.getChildren().add(btnInfo);
FXUtils.installFastTooltip(btnInfo, i18n("world.info"));
btnInfo.getStyleClass().add("toggle-icon4");
btnInfo.setGraphic(SVG.INFO.createIcon(Theme.blackFill(), -1));
btnInfo.setOnAction(event -> skinnable.showInfo());
JFXButton btnMore = new JFXButton();
right.getChildren().add(btnMore);
btnMore.getStyleClass().add("toggle-icon4");
btnMore.setGraphic(SVG.MORE_VERT.createIcon(Theme.blackFill(), -1));
btnMore.setOnAction(event -> showPopupMenu(JFXPopup.PopupHPosition.RIGHT, 0, root.getHeight()));
}
getChildren().setAll(new RipplerContainer(root));
RipplerContainer container = new RipplerContainer(root);
container.setOnMouseClicked(event -> {
if (event.getClickCount() != 1)
return;
if (event.getButton() == MouseButton.PRIMARY)
skinnable.showManagePage();
else if (event.getButton() == MouseButton.SECONDARY)
showPopupMenu(JFXPopup.PopupHPosition.LEFT, event.getX(), event.getY());
});
getChildren().setAll(container);
}
// Popup Menu
public void showPopupMenu(JFXPopup.PopupHPosition hPosition, double initOffsetX, double initOffsetY) {
PopupMenu popupMenu = new PopupMenu();
JFXPopup popup = new JFXPopup(popupMenu);
WorldListItem item = getSkinnable();
World world = item.getWorld();
popupMenu.getContent().addAll(
new IconedMenuItem(SVG.SETTINGS, i18n("world.manage.button"), item::showManagePage, popup));
if (ChunkBaseApp.isSupported(world)) {
popupMenu.getContent().addAll(
new MenuSeparator(),
new IconedMenuItem(SVG.EXPLORE, i18n("world.chunkbase.seed_map"), () -> ChunkBaseApp.openSeedMap(world), popup),
new IconedMenuItem(SVG.VISIBILITY, i18n("world.chunkbase.stronghold"), () -> ChunkBaseApp.openStrongholdFinder(world), popup),
new IconedMenuItem(SVG.FORT, i18n("world.chunkbase.nether_fortress"), () -> ChunkBaseApp.openNetherFortressFinder(world), popup)
);
if (GameVersionNumber.compare(world.getGameVersion(), "1.13") >= 0) {
popupMenu.getContent().add(new IconedMenuItem(SVG.LOCATION_CITY, i18n("world.chunkbase.end_city"),
() -> ChunkBaseApp.openEndCityFinder(world), popup));
}
}
popupMenu.getContent().addAll(
new MenuSeparator(),
new IconedMenuItem(SVG.OUTPUT, i18n("world.export"), item::export, popup),
new IconedMenuItem(SVG.FOLDER_OPEN, i18n("folder.world"), item::reveal, popup));
popup.show(root, JFXPopup.PopupVPosition.TOP, hPosition, initOffsetX, initOffsetY);
}
}
@@ -0,0 +1,168 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2025 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.ui.versions;
import com.jfoenix.controls.JFXPopup;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Insets;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.game.World;
import org.jackhuang.hmcl.ui.FXUtils;
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.ui.decorator.DecoratorAnimatedPage;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.util.ChunkBaseApp;
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
/**
* @author Glavo
*/
public final class WorldManagePage extends DecoratorAnimatedPage implements DecoratorPage {
private final ObjectProperty<State> state;
private final World world;
private final Path backupsDir;
private final TabHeader header;
private final TabHeader.Tab<WorldInfoPage> worldInfoTab = new TabHeader.Tab<>("worldInfoPage");
private final TabHeader.Tab<WorldBackupsPage> worldBackupsTab = new TabHeader.Tab<>("worldBackupsPage");
private final TabHeader.Tab<DatapackListPage> datapackTab = new TabHeader.Tab<>("datapackListPage");
private final TransitionPane transitionPane = new TransitionPane();
private FileChannel sessionLockChannel;
public WorldManagePage(World world, Path backupsDir) {
this.world = world;
this.backupsDir = backupsDir;
this.state = new SimpleObjectProperty<>(State.fromTitle(i18n("world.manage.title", world.getWorldName())));
this.header = new TabHeader(worldInfoTab, worldBackupsTab);
if (world.getGameVersion() != null && // old game will not write game version to level.dat
GameVersionNumber.compare(world.getGameVersion(), "1.13") >= 0) {
header.getTabs().add(datapackTab);
}
worldInfoTab.setNodeSupplier(() -> new WorldInfoPage(this));
worldBackupsTab.setNodeSupplier(() -> new WorldBackupsPage(this));
datapackTab.setNodeSupplier(() -> new DatapackListPage(this));
header.select(worldInfoTab);
transitionPane.setContent(worldInfoTab.getNode(), ContainerAnimations.NONE);
FXUtils.onChange(header.getSelectionModel().selectedItemProperty(), newValue ->
transitionPane.setContent(newValue.getNode(), ContainerAnimations.FADE));
setCenter(transitionPane);
BorderPane left = new BorderPane();
FXUtils.setLimitWidth(left, 200);
VBox.setVgrow(left, Priority.ALWAYS);
setLeft(left);
AdvancedListBox sideBar = new AdvancedListBox()
.addNavigationDrawerTab(header, worldInfoTab, i18n("world.info"), SVG.INFO)
.addNavigationDrawerTab(header, worldBackupsTab, i18n("world.backup"), SVG.ARCHIVE)
.addNavigationDrawerTab(header, datapackTab, i18n("world.datapack"), SVG.EXTENSION);
left.setTop(sideBar);
AdvancedListBox toolbar = new AdvancedListBox();
if (ChunkBaseApp.isSupported(world)) {
PopupMenu popupMenu = new PopupMenu();
JFXPopup popup = new JFXPopup(popupMenu);
popupMenu.getContent().addAll(
new IconedMenuItem(SVG.EXPLORE, i18n("world.chunkbase.seed_map"), () -> ChunkBaseApp.openSeedMap(world), popup),
new IconedMenuItem(SVG.VISIBILITY, i18n("world.chunkbase.stronghold"), () -> ChunkBaseApp.openStrongholdFinder(world), popup),
new IconedMenuItem(SVG.FORT, i18n("world.chunkbase.nether_fortress"), () -> ChunkBaseApp.openNetherFortressFinder(world), popup)
);
if (GameVersionNumber.compare(world.getGameVersion(), "1.13") >= 0) {
popupMenu.getContent().add(
new IconedMenuItem(SVG.LOCATION_CITY, i18n("world.chunkbase.end_city"), () -> ChunkBaseApp.openEndCityFinder(world), popup));
}
toolbar.addNavigationDrawerItem(i18n("world.chunkbase"), SVG.EXPLORE, null, chunkBaseMenuItem ->
chunkBaseMenuItem.setOnAction(e ->
popup.show(chunkBaseMenuItem,
JFXPopup.PopupVPosition.BOTTOM, JFXPopup.PopupHPosition.LEFT,
chunkBaseMenuItem.getWidth(), 0)));
}
toolbar.addNavigationDrawerItem(i18n("settings.game.exploration"), SVG.FOLDER_OPEN, () -> FXUtils.openFolder(world.getFile().toFile()), null);
BorderPane.setMargin(toolbar, new Insets(0, 0, 12, 0));
left.setBottom(toolbar);
// Does it need to be done in the background?
try {
sessionLockChannel = world.lock();
LOG.info("Acquired lock on world " + world.getFileName());
} catch (IOException ignored) {
}
}
@Override
public ReadOnlyObjectProperty<State> stateProperty() {
return state;
}
public World getWorld() {
return world;
}
public Path getBackupsDir() {
return backupsDir;
}
public boolean isReadOnly() {
return sessionLockChannel == null;
}
@Override
public boolean back() {
closePage();
return true;
}
@Override
public void closePage() {
if (sessionLockChannel != null) {
try {
sessionLockChannel.close();
LOG.info("Releases the lock on world " + world.getFileName());
} catch (IOException e) {
LOG.warning("Failed to close session lock channel", e);
}
sessionLockChannel = null;
}
}
}
@@ -27,6 +27,7 @@ import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.UpgradeDialog;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.ui.SwingUtils;
@@ -102,6 +103,16 @@ public final class UpdateHandler {
return;
}
if (JavaRuntime.CURRENT_VERSION < Metadata.MINIMUM_SUPPORTED_JAVA_VERSION) {
MessageDialogPane.Builder builder = new MessageDialogPane.Builder(i18n("fatal.deprecated_java_version.update"), i18n("message.error"), MessageType.ERROR);
String downloadLink = Metadata.getSuggestedJavaDownloadLink();
if (downloadLink != null)
builder.addHyperLink(i18n("fatal.deprecated_java_version.download_link", 21), downloadLink);
builder.ok(null);
Controllers.dialog(builder.build());
return;
}
Controllers.dialog(new UpgradeDialog(version, () -> {
Path downloaded;
try {
@@ -0,0 +1,127 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2025 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.game.World;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
public final class ChunkBaseApp {
private static final String CHUNK_BASE_URL = "https://www.chunkbase.com";
private static final GameVersionNumber MIN_GAME_VERSION = GameVersionNumber.asGameVersion("1.7");
private static final String[] SEED_MAP_GAME_VERSIONS = {
"1.21.6", "1.21.5", "1.21.4", "1.21.2", "1.21", "1.20", "1.19.3",
"1.19", "1.18", "1.17", "1.16", "1.15", "1.14", "1.13", "1.12",
"1.11", "1.10", "1.9", "1.8", "1.7"
};
public static final String[] STRONGHOLD_FINDER_GAME_VERSIONS = {
"1.20", "1.19.3", "1.19", "1.18", "1.16", "1.13", "1.9", "1.7"
};
public static final String[] NETHER_FORTRESS_GAME_VERSIONS = {
"1.18", "1.16", "1.7"
};
public static final String[] END_CITY_GAME_VERSIONS = {
"1.19", "1.13"
};
public static boolean isSupported(@NotNull World world) {
return world.getSeed() != null && world.getGameVersion() != null &&
GameVersionNumber.asGameVersion(world.getGameVersion()).compareTo(MIN_GAME_VERSION) >= 0;
}
public static ChunkBaseApp newBuilder(String app, long seed) {
return new ChunkBaseApp(new StringBuilder(CHUNK_BASE_URL).append("/apps/").append(app).append("#seed=").append(seed));
}
public static void openSeedMap(World world) {
assert isSupported(world);
newBuilder("seed-map", Objects.requireNonNull(world.getSeed()))
.addPlatform(GameVersionNumber.asGameVersion(world.getGameVersion()), world.isLargeBiomes(), SEED_MAP_GAME_VERSIONS)
.open();
}
public static void openStrongholdFinder(World world) {
assert isSupported(world);
newBuilder("stronghold-finder", Objects.requireNonNull(world.getSeed()))
.addPlatform(GameVersionNumber.asGameVersion(world.getGameVersion()), world.isLargeBiomes(), STRONGHOLD_FINDER_GAME_VERSIONS)
.open();
}
public static void openNetherFortressFinder(World world) {
assert isSupported(world);
newBuilder("nether-fortress-finder", Objects.requireNonNull(world.getSeed()))
.addPlatform(GameVersionNumber.asGameVersion(world.getGameVersion()), false, NETHER_FORTRESS_GAME_VERSIONS)
.open();
}
public static void openEndCityFinder(World world) {
assert isSupported(world);
newBuilder("endcity-finder", Objects.requireNonNull(world.getSeed()))
.addPlatform(GameVersionNumber.asGameVersion(world.getGameVersion()), false, END_CITY_GAME_VERSIONS)
.open();
}
private final StringBuilder builder;
private ChunkBaseApp(StringBuilder builder) {
this.builder = builder;
}
public ChunkBaseApp add(String key, String value) {
builder.append('&').append(key).append('=').append(value);
return this;
}
public ChunkBaseApp addPlatform(GameVersionNumber gameVersion, boolean largeBiomes, String[] versionList) {
String version = null;
for (String candidateVersion : versionList) {
if (gameVersion.compareTo(candidateVersion) >= 0) {
version = candidateVersion;
break;
}
}
if (version == null) {
version = versionList[versionList.length - 1]; // Use the last version if no suitable version found
}
add("platform", "java_" + version.replace('.', '_') + (largeBiomes ? "_lb" : ""));
return this;
}
public void open() {
FXUtils.openLink(builder.toString());
}
@Override
public String toString() {
return builder.toString();
}
}
@@ -26,7 +26,10 @@ import org.jackhuang.hmcl.util.platform.Architecture;
import org.jackhuang.hmcl.java.JavaRuntime;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import org.jackhuang.hmcl.util.platform.Platform;
import org.jackhuang.hmcl.util.platform.windows.WindowsVersion;
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -178,7 +181,19 @@ public final class NativePatcher {
return version.setLibraries(newLibraries);
}
public static Library getMesaLoader(JavaRuntime javaVersion) {
return getNatives(javaVersion.getPlatform()).get("mesa-loader");
public static @Nullable Library getWindowsMesaLoader(@NotNull JavaRuntime javaVersion, @NotNull Renderer renderer, @NotNull WindowsVersion windowsVersion) {
if (renderer == Renderer.DEFAULT)
return null;
if (windowsVersion.compareTo(WindowsVersion.WINDOWS_10) >= 0) {
return getNatives(javaVersion.getPlatform()).get("mesa-loader");
} else if (windowsVersion.compareTo(WindowsVersion.WINDOWS_7) >= 0) {
if (renderer == Renderer.LLVMPIPE)
return getNatives(javaVersion.getPlatform()).get("software-renderer-loader");
else
return null;
} else {
return null;
}
}
}
@@ -120,5 +120,43 @@ public final class SwingFXUtils {
pw.setPixels(0, 0, bw, bh, pf, data, offset, scan);
return wimg;
}
public static WritableImage toFXImage(BufferedImage bimg, double requestedWidth, double requestedHeight, boolean preserveRatio, boolean smooth) {
if (requestedWidth <= 0. || requestedHeight <= 0.) {
return toFXImage(bimg, null);
}
int width = (int) requestedWidth;
int height = (int) requestedHeight;
// Calculate actual dimensions if preserveRatio is true
if (preserveRatio) {
double originalWidth = bimg.getWidth();
double originalHeight = bimg.getHeight();
double scaleX = requestedWidth / originalWidth;
double scaleY = requestedHeight / originalHeight;
double scale = Math.min(scaleX, scaleY);
width = (int) (originalWidth * scale);
height = (int) (originalHeight * scale);
}
// Create scaled BufferedImage
BufferedImage scaledImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
Graphics2D g2d = scaledImage.createGraphics();
try {
if (smooth) {
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
g2d.drawImage(bimg, 0, 0, width, height, null);
} finally {
g2d.dispose();
}
return toFXImage(scaledImage, null);
}
}
@@ -106,14 +106,26 @@
-fx-background-color: -fx-base-darker-color;
}
.memory-used:disabled {
-fx-opacity: 0.4;
}
.memory-allocate {
-fx-background-color: derive(-fx-base-color, 100%);
}
.memory-allocate:disabled {
-fx-opacity: 0.4;
}
.memory-total {
-fx-background-color: -fx-base-rippler-color;
}
.memory-total:disabled {
-fx-opacity: 0.4;
}
.update-label {
-fx-text-fill: red;
}
@@ -332,6 +332,7 @@ download=Download
download.hint=Install games and modpacks or download mods, resource packs, and worlds.
download.code.404=File "%s" not found on the remote server.
download.content=Addons
download.shader=Shaders
download.curseforge.unavailable=This HMCL build does not support access to CurseForge. Please use the official build to access CurseForge.
download.existing=The file cannot be saved because it already exists. You can click "Save As" to save the file elsewhere.
download.external_link=Visit Download Website
@@ -386,6 +387,9 @@ fatal.config_loading_failure=Cannot load configuration files.\n\
For macOS, try putting HMCL somewhere with permissions other than "Desktop", "Downloads", and "Documents" and try again.
fatal.config_loading_failure.unix=Hello Minecraft! Launcher could not load the configuration file because it was created by user "%1$s".\n\
Please open HMCL as root user (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.config_unsupported_version=The current configuration file was created by a newer version of Hello Minecraft! Launcher, and this version of HMCL cannot load it properly.\n\
Please update and restart HMCL.\n\
Before updating the launcher, any settings you modify will not be saved.
fatal.mac_app_translocation=Hello Minecraft! Launcher is isolated to a temporary directory by the OS due to macOS security mechanisms.\n\
Please move HMCL to a different directory before attempting to open. Otherwise, your settings and game data may be lost after restarting.\n\
Do you still want to continue?
@@ -397,11 +401,15 @@ fatal.apply_update_failure=We are sorry, but Hello Minecraft! Launcher is unable
fatal.apply_update_need_win7=Hello Minecraft! Launcher cannot automatically update on Windows XP/Vista.\n\
\n\
You can update manually by downloading a newer launcher version from %s.
fatal.deprecated_java_version=HMCL will require Java 11 or later to run in the future, but will still support launching games with Java 8.\n\
fatal.deprecated_java_version=HMCL will require Java 17 or later to run in the future, but will still support launching games with Java 6~16.\n\
\n\
It is recommended to install the latest version of Java to ensure that HMCL works properly.\n\
\n\
You can continue to keep the old version of Java. HMCL can recognize and manage multiple Java versions and will automatically select the appropriate Java for you based on the game version.
fatal.deprecated_java_version.update=HMCL will require Java 17 or later to run in the future. Please install the latest version of Java to ensure that HMCL can complete the upgrade.\n\
\n\
You can continue to keep the old version of Java.\
HMCL can recognize and manage multiple Java versions and will automatically select the appropriate Java for you based on the game version.
fatal.deprecated_java_version.download_link=Download Java %d
fatal.samba=If you opened Hello Minecraft! Launcher from a Samba network drive, some features might not be working. Please try updating your Java or moving the launcher to another directory.
fatal.illegal_char=Your user path contains an illegal character "=". You will not be able to use authlib-injector or change the skin of your offline account.
@@ -437,6 +445,7 @@ folder.shaderpacks=Shader Packs
folder.saves=Saves
folder.schematics=Schematics
folder.screenshots=Screenshots
folder.world=World Directory
game=Games
game.crash.feedback=<b>Please do not share screenshots of this window with others!</b> If you ask for help from others, please click <b>"Export Crash Logs"</b> and send the exported file to others for analysis.
@@ -489,7 +498,7 @@ game.crash.reason.java_version_is_too_high=The game crashed because the Java ver
Please use the previous major Java version in "Global/Instance-specific Settings → Java" and then launch the game.\n\
\n\
If not, you can download it from <a href="https://www.java.com/download/">java.com (Java 8)</a> or <a href="https://bell-sw.com/pages/downloads/#downloads">BellSoft Liberica Full JRE (Java 17)</a> and other distributions to download and install one (restart the launcher after installation).
game.crash.reason.need_jdk11=The game crashed because of an inappropriate Java VM version.\n\
game.crash.reason.need_jdk11=The game crashed because of an inappropriate Java version.\n\
\n\
You need to download and install Java 11, and set it in "Global/Instance-specific Settings → Java".
game.crash.reason.mod_name=The game crashed because of mod filename problems.\n\
@@ -559,7 +568,7 @@ game.crash.reason.optifine_causes_the_world_to_fail_to_load=The game may not con
game.crash.reason.jdk_9=The game crashed because the Java version is too new for this instance.\n\
\n\
You need to download and install Java 8 and choose it in "Global/Instance-specific Settings → Java".
game.crash.reason.jvm_32bit=The game crashed because the current memory allocation exceeded the 32-bit Java VM limit.\n\
game.crash.reason.jvm_32bit=The game crashed because the current memory allocation exceeded the 32-bit JVM limit.\n\
\n\
If your OS is 64-bit, please install and use a 64-bit Java version. Otherwise, you may need to reinstall a 64-bit OS or get a moderner computer.\n\
\n\
@@ -640,7 +649,7 @@ game.crash.reason.opengl_not_supported=The game crashed because your graphics dr
Or, you can update your graphic driver to the latest version and then try again.\n\
\n\
If your computer has a dedicated graphics card, please make sure the game is indeed using it for rendering. If the problem persists, please consider getting a new graphics card or a new computer.
game.crash.reason.openj9=The game is unable to run on an OpenJ9 VM. Please switch to a Java that uses the Hotspot VM in "Global/Instance-specific Settings → Java" and relaunch the game. If you do not have one, you can download one.
game.crash.reason.openj9=The game is unable to run on an OpenJ9 JVM. Please switch to a Java that uses the Hotspot JVM in "Global/Instance-specific Settings → Java" and relaunch the game. If you do not have one, you can download one.
game.crash.reason.out_of_memory=The game crashed because the computer ran out of memory.\n\
\n\
Maybe there is not enough memory available or too many mods installed. You can try resolving it by increasing the allocated memory in "Global/Instance-specific Settings → Memory".\n\
@@ -654,7 +663,7 @@ game.crash.reason.stacktrace=The crash reason is unknown. You can view its detai
There are some keywords that might contain some mod names. You can search them online to figure out the problem yourself.\n\
\n\
%s
game.crash.reason.too_old_java=The game crashed because you are using an outdated Java VM version.\n\
game.crash.reason.too_old_java=The game crashed because you are using an outdated Java version.\n\
\n\
You need to switch to a newer Java version (%1$s) in "Global/Instance-specific Settings → Java" and then relaunch the game. You can download Java from <a href="https://learn.microsoft.com/java/openjdk/download">here</a>.
game.crash.reason.unknown=We are not able to figure out why the game crashed. Please refer to the game logs.
@@ -763,10 +772,10 @@ lang.default=Use System Locales
launch.advice=%s Do you still want to continue to launch?
launch.advice.multi=The following problems were detected:\n\n%s\n\nThese problems may prevent you from launching the game or affect gaming experience.\nDo you still want to continue to launch?
launch.advice.java.auto=The current Java VM version is not compatible with the instance.\n\nClick "Yes" to automatically choose the most compatible Java VM version. Or, you can navigate to "Global/Instance-specific Settings → Java" to choose one yourself.
launch.advice.java.auto=The current Java version is not compatible with the instance.\n\nClick "Yes" to automatically choose the most compatible Java version. Or, you can navigate to "Global/Instance-specific Settings → Java" to choose one yourself.
launch.advice.java.modded_java_7=Minecraft 1.7.2 and previous versions require Java 7 or earlier.
launch.advice.corrected=We have resolved the Java VM problem. If you still want to use your choice of Java version, you can disable "Do not check Java VM compatibility" in "Global/Instance-specific Settings → Advanced Settings".
launch.advice.uncorrected=If you still want to use your choice of Java version, you can disable "Do not check Java VM compatibility" in "Global/Instance-specific Settings → Advanced Settings".
launch.advice.corrected=We have resolved the Java problem. If you still want to use your choice of Java version, you can disable "Do not check JVM compatibility" in "Global/Instance-specific Settings → Advanced Settings".
launch.advice.uncorrected=If you still want to use your choice of Java version, you can disable "Do not check JVM compatibility" in "Global/Instance-specific Settings → Advanced Settings".
launch.advice.different_platform=The 64-bit Java version is recommended for your device, but you have installed a 32-bit one.
launch.advice.forge2760_liteloader=Forge version 2760 is not compatible with LiteLoader. Please consider upgrading Forge to version 2773 or later.
launch.advice.forge28_2_2_optifine=Forge version 28.2.2 or later is not compatible with OptiFine. Please consider downgrading Forge to version 28.2.1 or earlier.
@@ -784,7 +793,7 @@ launch.advice.vanilla_linux_java_8=Minecraft 1.12.2 or earlier only supports Jav
launch.advice.vanilla_x86.translation=Minecraft is not fully supported on your platform, so you may experience missing features or even be unable to launch the game.\nYou can download Java for the <b>x86-64</b> architecture <a href="https://learn.microsoft.com/java/openjdk/download">here</a> for a full gaming experience.
launch.advice.unknown=The game cannot be launched due to the following reasons:
launch.failed=Failed to launch
launch.failed.cannot_create_jvm=We are unable to create a Java VM. It may be caused by incorrect Java VM arguments. You can try resolving it by removing all arguments you added in "Global/Instance-specific Settings → Advanced Settings → Java VM Options".
launch.failed.cannot_create_jvm=We are unable to create a JVM. It may be caused by incorrect JVM arguments. You can try resolving it by removing all arguments you added in "Global/Instance-specific Settings → Advanced Settings → JVM Options".
launch.failed.creating_process=We are unable to create a new process. Please check your Java path.\n
launch.failed.command_too_long=The command length exceeds the maximum length of a batch script. Please try exporting it as a PowerShell script.
launch.failed.decompressing_natives=Failed to extract native libraries.\n
@@ -863,7 +872,7 @@ modpack.choose.local.detail=You can drag the modpack file here.
modpack.choose.remote=Download from URL
modpack.choose.remote.detail=A direct download link to the remote modpack file is required.
modpack.choose.repository=Download Modpack from CurseForge or Modrinth
modpack.choose.repository.detail=Remember to go back to this page and drop the modpack file here after the modpack is downloaded.
modpack.choose.repository.detail=You can choose the desired modpack on the next page.
modpack.choose.remote.tooltip=Please enter your modpack URL
modpack.completion=Downloading dependencies
modpack.desc=Describe your modpack, including an introduction and probably some changelog. Markdown and images from URL are currently supported.
@@ -932,33 +941,50 @@ modpack.wizard.step.initialization.warning=Before making a modpack, please make
modpack.wizard.step.initialization.server=Click here for more information on how to make a server modpack that can be automatically updated.
modrinth.category.adventure=Adventure
modrinth.category.atmosphere=Atmosphere
modrinth.category.audio=Audio
modrinth.category.babric=Babric
modrinth.category.blocks=Blocks
modrinth.category.bloom=Bloom
modrinth.category.bta-babric=BTA (Babric)
modrinth.category.bukkit=Bukkit
modrinth.category.bungeecord=BungeeCord
modrinth.category.canvas=Canvas
modrinth.category.cartoon=Cartoon
modrinth.category.challenging=Challenging
modrinth.category.core-shaders=Core Shaders
modrinth.category.colored-lighting=Colored Lighting
modrinth.category.combat=Combat
modrinth.category.core-shaders=Core Shaders
modrinth.category.cursed=Cursed
modrinth.category.datapack=Datapack
modrinth.category.decoration=Decoration
modrinth.category.economy=Economy
modrinth.category.entities=Entities
modrinth.category.environment=Environment
modrinth.category.equipment=Equipment
modrinth.category.fabric=Fabric
modrinth.category.fantasy=Fantasy
modrinth.category.folia=Folia
modrinth.category.foliage=Foliage
modrinth.category.fonts=Fonts
modrinth.category.food=Food
modrinth.category.forge=Forge
modrinth.category.game-mechanics=Game Mechanics
modrinth.category.gui=GUI
modrinth.category.high=High
modrinth.category.iris=Iris
modrinth.category.items=Items
modrinth.category.java-agent=Java Agent
modrinth.category.kitchen-sink=Kitchen-Sink
modrinth.category.legacy-fabric=Legacy Fabric
modrinth.category.library=Library
modrinth.category.lightweight=Lightweight
modrinth.category.liteloader=LiteLoader
modrinth.category.locale=Locale
modrinth.category.low=Low
modrinth.category.magic=Magic
modrinth.category.management=Management
modrinth.category.medium=Medium
modrinth.category.minecraft=Minecraft
modrinth.category.minigame=Minigame
modrinth.category.misc=Misc
@@ -968,13 +994,23 @@ modrinth.category.models=Models
modrinth.category.modloader=Modloader
modrinth.category.multiplayer=Multiplayer
modrinth.category.neoforge=NeoForge
modrinth.category.nilloader=NilLoader
modrinth.category.optifine=OptiFine
modrinth.category.optimization=Optimization
modrinth.category.ornithe=Ornithe
modrinth.category.paper=Paper
modrinth.category.path-tracing=Path Tracing
modrinth.category.pbr=PBR
modrinth.category.potato=Potato
modrinth.category.purpur=Purpur
modrinth.category.quests=Quests
modrinth.category.quilt=Quilt
modrinth.category.realistic=Realistic
modrinth.category.reflections=Reflections
modrinth.category.rift=Rift
modrinth.category.screenshot=Screenshot
modrinth.category.semi-realistic=Semi-realistic
modrinth.category.shadows=Shadows
modrinth.category.simplistic=Simplistic
modrinth.category.social=Social
modrinth.category.spigot=Spigot
@@ -985,12 +1021,11 @@ modrinth.category.themed=Themed
modrinth.category.transportation=Transportation
modrinth.category.tweaks=Tweaks
modrinth.category.utility=Utility
modrinth.category.vanilla=Vanilla
modrinth.category.vanilla-like=Vanilla-like
modrinth.category.velocity=Velocity
modrinth.category.waterfall=Waterfall
modrinth.category.worldgen=Worldgen
modrinth.category.datapack=Datapack
modrinth.category.folia=Folia
modrinth.category.8x-=8x-
modrinth.category.16x=16x
modrinth.category.32x=32x
@@ -1061,7 +1096,7 @@ datapack.title=World [%s] - Datapacks
web.failed=Failed to load page
web.open_in_browser=Do you want to open this address in a browser:\n%s
web.view_in_browser=View in browser
web.view_in_browser=View all in browser
world=Worlds
world.add=Add World
@@ -1072,9 +1107,12 @@ world.backup.create.locked=The world is currently in use. Please close the game
world.backup.create.success=Successfully created a new backup: %s
world.backup.delete=Delete this backup
world.backup.processing=Backing up ...
world.backup.title=World [%s] - Backups
world.datapack=Manage Datapacks
world.datapack.1_13=Only Minecraft 1.13 or later supports datapacks.
world.chunkbase=Chunk Base
world.chunkbase.end_city=End City
world.chunkbase.seed_map=Seed Map
world.chunkbase.stronghold=Stronghold
world.chunkbase.nether_fortress=Nether Fortress
world.datapack=Datapacks
world.datetime=Last played on %s
world.download=Download World
world.download.title=Download World - %1s
@@ -1089,7 +1127,6 @@ world.import.choose=Choose world archive you want to import
world.import.failed=Failed to import this world: %s
world.import.invalid=Failed to parse the world.
world.info=World Information
world.info.title=World [%s] - Information
world.info.basic=Basic Information
world.info.allow_cheats=Allow Commands/Cheats
world.info.dimension.the_nether=The Nether
@@ -1108,6 +1145,7 @@ world.info.player.food_level=Hunger Level
world.info.player.game_type=Game Mode
world.info.player.game_type.adventure=Adventure
world.info.player.game_type.creative=Creative
world.info.player.game_type.hardcore=Hardcore
world.info.player.game_type.spectator=Spectator
world.info.player.game_type.survival=Survival
world.info.player.health=Health
@@ -1120,6 +1158,8 @@ world.info.time=Game Time
world.info.time.format=%s days
world.locked=In use
world.manage=Worlds
world.manage.button=World Management
world.manage.title=World - %s
world.name=World Name
world.name.enter=Enter the world name
world.show_all=Show All
@@ -1209,18 +1249,18 @@ settings.advanced.custom_commands.hint=The following environment variables are p
\ · $INST_FABRIC: set if Fabric is installed.\n\
\ · $INST_QUILT: set if Quilt is installed.
settings.advanced.dont_check_game_completeness=Do not check game integrity
settings.advanced.dont_check_jvm_validity=Do not check Java VM compatibility
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
settings.advanced.java_permanent_generation_space.prompt=in MiB
settings.advanced.jvm=Java VM Options
settings.advanced.jvm_args=Java VM Arguments
settings.advanced.jvm_args.prompt=\ · If the arguments entered in "Java VM arguments" are the same as the default arguments, it will not be added.\n\
\ · Enter any GC arguments in "Java VM arguments", and the G1 argument of the default arguments will be disabled.\n\
\ · Enable "Do not add default Java VM arguments" to launch the game without adding default arguments.
settings.advanced.jvm=JVM Options
settings.advanced.jvm_args=JVM Arguments
settings.advanced.jvm_args.prompt=\ · If the arguments entered in "JVM Arguments" are the same as the default arguments, it will not be added.\n\
\ · Enter any GC arguments in "JVM Arguments", and the G1 argument of the default arguments will be disabled.\n\
\ · Enable "Do not add default JVM arguments" to launch the game without adding default arguments.
settings.advanced.launcher_visibility.close=Close the launcher after the game launches
settings.advanced.launcher_visibility.hide=Hide the launcher after the game launches
settings.advanced.launcher_visibility.hide_and_reopen=Hide the launcher and show it when the game closes
@@ -1232,11 +1272,12 @@ settings.advanced.natives_directory=Native Library Path
settings.advanced.natives_directory.choose=Choose the location of the desired native library
settings.advanced.natives_directory.custom=Custom
settings.advanced.natives_directory.default=Default
settings.advanced.natives_directory.default.version_id=<Version ID>
settings.advanced.natives_directory.hint=This option is intended only for users of Apple silicon or other not officially supported platforms. Please do not edit this option unless you know what you are doing.\n\
\n\
Before proceeding, please make sure all libraries (e.g. lwjgl.dll, libopenal.so) are provided in your desired directory.\n\
Note: It is recommended to use a fully English-letters path for the specified local library file. Otherwise it may lead to game launch failure.
settings.advanced.no_jvm_args=Do not add default Java VM arguments
settings.advanced.no_jvm_args=Do not add default JVM arguments
settings.advanced.precall_command=Pre-launch Command
settings.advanced.precall_command.prompt=Commands to execute before the game launches
settings.advanced.process_priority=Process Priority
@@ -1331,6 +1372,7 @@ settings.memory.allocate.manual=%1$.1f GiB Allocated
settings.memory.allocate.manual.exceeded=%1$.1f GiB Allocated (%3$.1f GiB Available)
settings.memory.auto_allocate=Automatically Allocate
settings.memory.lower_bound=Minimum Memory
settings.memory.unit.mib=MiB
settings.memory.used_per_total=%1$.1f GiB Used / %2$.1f GiB Total
settings.physical_memory=Physical Memory Size
settings.show_log=Show Logs
@@ -333,6 +333,7 @@ download=Descargar
download.hint=Instalar juegos y modpacks o descargar mods, paquetes de recursos y mundos.
download.code.404=Archivo no encontrado en el servidor remoto: %s
download.content=Complementos
download.shader=Sombreadores
download.curseforge.unavailable=Esta versión de HMCL no permite acceder a CurseForge. Utilice la versión oficial para acceder a CurseForge.
download.existing=El archivo no se puede guardar porque ya existe. Puedes hacer clic en «Guardar como» para guardar el archivo en otro lugar.
download.external_link=Abrir sitio web
@@ -399,11 +400,16 @@ fatal.apply_update_failure=Lo sentimos, pero Hello Minecraft! Launcher no puede
fatal.apply_update_need_win7=Hello Minecraft! Launcher no puede actualizarse automáticamente en Windows XP/Vista.\n\
\n\
Puedes actualizar manualmente descargando una versión más reciente del launcher desde %s.
fatal.deprecated_java_version=HMCL requerirá Java 11 o posterior para funcionar en el futuro, pero seguirá siendo compatible con el lanzamiento de juegos con Java 8.\n\
fatal.deprecated_java_version=HMCL requerirá Java 17 o posterior para funcionar en el futuro, pero seguirá siendo compatible con el lanzamiento de juegos con Java 6~16.\n\
\n\
Se recomienda instalar la última versión de Java para garantizar el correcto funcionamiento de HMCL.\n\
\n\
Puede seguir manteniendo la versión antigua de Java. HMCL puede reconocer y gestionar varias versiones de Java y elegirá automáticamente la adecuada para usted en función de la versión del juego.
Puedes conservar la versión antigua de Java. HMCL puede reconocer y gestionar varias versiones de Java y elegirá automáticamente la adecuada para usted en función de la versión del juego.
fatal.deprecated_java_version.update=HMCL requerirá Java 17 o posterior para funcionar en el futuro. Por favor, instalar la última versión de Java para asegurarse de que el lanzador puede completar la actualización.\n\
\n\
Puedes conservar la versión antigua de Java.\
HMCL puede reconocer y gestionar varias versiones de Java \
y elegirá automáticamente la adecuada para usted en función de la versión del juego.
fatal.deprecated_java_version.download_link=Descargar Java %d
fatal.samba=Si ha abierto Hello Minecraft! Launcher desde una unidad de red Samba, es posible que algunas funciones no funcionen. Intenta actualizar tu Java o mover el launcher a otro directorio.
fatal.illegal_char=Su ruta de usuario contiene un carácter ilegal «=», por lo que algunas características podrían no funcionar correctamente.\n\
@@ -438,7 +444,9 @@ folder.mod=Mods
folder.resourcepacks=Paquetes de recursos
folder.shaderpacks=Paquetes de sombreados
folder.saves=Mundos
folder.schematics=Esquemas
folder.screenshots=Capturas de pantalla
folder.world=Directorio del mundo
game=Juegos
game.crash.feedback=<b>¡Por favor, no comparta capturas de pantalla de esta interfaz con otras personas!</b> Si pide ayuda a otras personas, haga clic en <b>Exportar registros de colgado</b> y envíe el archivo exportado a otras personas para que lo analicen.
@@ -491,7 +499,7 @@ game.crash.reason.graphics_driver=El juego se ha colgado debido a un problema co
Si tu ordenador tiene una tarjeta gráfica discreta, tienes que comprobar si el juego utiliza gráficos integrados/núcleo. Si es así, inicie el launcher utilizando su tarjeta gráfica discreta. Si el problema persiste, probablemente debería considerar la posibilidad de adquirir una nueva tarjeta gráfica o un nuevo ordenador.\n\
\n\
Si utiliza su tarjeta gráfica integrada, tenga en cuenta que Minecraft 1.16.5 o anterior requiere <a href="https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html">Java 1.8.0_51 o más antiguo</a> para la serie de procesadores Intel(R) Core(TM) 3000 o anteriores.
game.crash.reason.macos_failed_to_find_service_port_for_display=El juego no puede continuar debido a un fallo al inicializar la ventana OpenGL en la plataforma Apple silicon.\n\
game.crash.reason.macos_failed_to_find_service_port_for_display=El juego no puede continuar debido a un fallo al inicializar la ventana OpenGL en la plataforma con chips de Apple.\n\
\n\
Para este problema, HMCL no tiene soluciones directas por el momento. Intente abrir cualquier navegador y ponerlo en pantalla completa, luego vuelva a HMCL, inicie el juego y <b>vuelva rápidamente a la página del navegador</b> antes de que aparezca la ventana del juego, espere a que aparezca la ventana del juego y luego vuelva a la ventana del juego.
game.crash.reason.illegal_access_error=El juego se ha colgado por culpa de algún(os) mod(s).\n\
@@ -506,7 +514,7 @@ game.crash.reason.jdk_9=El juego se ha colgado porque la versión de Java es dem
game.crash.reason.need_jdk11=El juego se ha colgado debido a una versión inadecuada de Java.\n\
\n\
Debes descargar e instalar Java 11 y configurarlo en «Config. Global/Específica de instancia → Java».
game.crash.reason.jvm_32bit=El juego se ha colgado porque la asignación de memoria actual excedía el límite de 32 bits JVM.\n\
game.crash.reason.jvm_32bit=El juego se ha colgado porque la asignación de memoria actual excedía el límite de 32 bits de la JVM.\n\
\n\
Si su sistema operativo es de 64 bits, instale y utilice una versión de Java de 64 bits. De lo contrario, es posible que tenga que volver a instalar un sistema operativo de 64 bits o adquirir un ordenador más moderno.\n\
\n\
@@ -639,7 +647,7 @@ game.crash.reason.stacktrace=El motivo del cuelgue es desconocido. Puedes ver lo
Hay algunas palabras clave que pueden contener algunos nombres de mods. Puedes buscarlas en Internet para averiguar el problema tú mismo.\n\
\n\
%s
game.crash.reason.too_old_java=El juego se ha colgado porque estás utilizando una versión de Java VM obsoleta.\n\
game.crash.reason.too_old_java=El juego se ha colgado porque estás utilizando una versión de Java obsoleta.\n\
\n\
Tienes que cambiar a una versión de Java más reciente (%1$s) en «Config. Global/Específica de instancia → Java» y, a continuación, volver a ejecutar el juego. Puede descargar Java desde <a href="https://learn.microsoft.com/java/openjdk/download">aquí</a>.
game.crash.reason.unknown=No somos capaces de averiguar por qué se ha colgado el juego. Por favor, refiérase a los registros del juego.
@@ -767,10 +775,10 @@ lang.default=Usar idioma del sistema
launch.advice=%s ¿Todavía quieres continuar?
launch.advice.multi=Se han detectado los siguientes problemas:\n\n%s\n\nEstos problemas pueden impedir que inicies el juego o afectar a la experiencia de juego.\n\n¿Todavía quieres continuar?
launch.advice.java.auto=La versión actual de la máquina virtual de Java no cumple los requisitos del juego.\n\nHaga clic en «Sí» para elegir automáticamente la versión de Java VM más compatible. O bien, puede navegar hasta «Config. Global/Específica de instancia → Java» para elegir uno usted mismo.
launch.advice.java.auto=La versión actual de Java no es compatible con la instancia.\n\nHaga clic en «Sí» para elegir automáticamente la versión de Java más compatible. O bien, puede navegar hasta «Config. Global/Específica de instancia → Java» para elegir uno usted mismo.
launch.advice.java.modded_java_7=Minecraft 1.7.2 y versiones anteriores requieren Java 7 o anterior.
launch.advice.corrected=Hemos solucionado el problema de la máquina virtual de Java. Si todavía quieres utilizar la versión de Java que elijas, puedes desactivar la opción «No comprobar la compatibilidad de la JVM» en «Config. Global/Específica de instancia → Configuración avanzada».
launch.advice.uncorrected=Si todavía quieres utilizar la versión de Java que elijas, puedes desactivar la opción «No comprobar la compatibilidad de la JVM» en «Config. Global/Específica de instancia → Configuración avanzada».
launch.advice.corrected=Hemos resuelto el problema de Java. Si aún desea utilizar la versión de Java que elija, puede desactivar «No comprobar la compatibilidad con JVM» en «Config. Global/Específica de instancia → Configuración avanzada».
launch.advice.uncorrected=Si aún desea utilizar la versión de Java que elija, puede desactivar «No comprobar la compatibilidad con JVM» en «Config. Global/Específica de instancia → Configuración avanzada».
launch.advice.different_platform=Se recomienda la versión de 64 bits de Java para tu dispositivo, pero has instalado una de 32 bits.
launch.advice.forge2760_liteloader=La versión 2760 o posterior de Forge no es compatible con LiteLoader, por favor considere actualizar Forge a la versión 2773 o posterior.
launch.advice.forge28_2_2_optifine=La versión 28.2.2 o posterior de Forge no es compatible con OptiFine. Considere la posibilidad de actualizar Forge a la versión 28.2.1 o anterior.
@@ -788,7 +796,7 @@ launch.advice.vanilla_linux_java_8=Minecraft 1.12.2 o inferior sólo admite Java
launch.advice.vanilla_x86.translation=Minecraft no proporciona actualmente soporte oficial para arquitecturas distintas de x86 y x86-64.\n\nPor favor, instale Java para x86-64 para jugar a Minecraft a través del entorno de traducción Rosetta, o descargue sus bibliotecas nativas correspondientes y especifique su ruta.
launch.advice.unknown=El juego no puede iniciarse por las siguientes razones:
launch.failed=No se puede ejecutar
launch.failed.cannot_create_jvm=No podemos crear una máquina virtual Java. Puede deberse a un problema con los argumentos de ejecución proporcionados, puede intentar solucionarlo eliminando todos los argumentos que haya añadido en la configuración de la instancia.
launch.failed.cannot_create_jvm=No podemos crear una JVM. Puede deberse a un problema con los argumentos de ejecución proporcionados, puede intentar solucionarlo eliminando todos los argumentos que haya añadido en la configuración de la instancia.
launch.failed.creating_process=No podemos crear un nuevo proceso, por favor compruebe la ruta de Java.\n
launch.failed.command_too_long=La longitud del comando excede la longitud máxima de un script bat. Por favor, intente exportarlo como un script de PowerShell.
launch.failed.decompressing_natives=No se han podido descomprimir las bibliotecas nativas.\n
@@ -868,7 +876,7 @@ modpack.choose.remote=Descargar desde Internet
modpack.choose.remote.detail=Se requiere un enlace de descarga directa al archivo modpack remoto
modpack.choose.remote.tooltip=Por favor, introduzca la URL de su modpack
modpack.choose.repository=Descargar Modpack de CurseForge o Modrinth
modpack.choose.repository.detail=Recuerda volver a esta página y soltar aquí el archivo del modpack una vez descargado.
modpack.choose.repository.detail=Puedes elegir el modpack que desees en la página siguiente.
modpack.completion=Descargando dependencias
modpack.desc=Describa su modpack, incluyendo una introducción y probablemente algún registro de cambios. Actualmente se admiten Markdown e imágenes desde URL.
modpack.description=Descripción
@@ -936,33 +944,50 @@ modpack.wizard.step.initialization.warning=Antes de hacer un modpack, por favor
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
modrinth.category.atmosphere=Atmósfera
modrinth.category.audio=Audio
modrinth.category.babric=Babric
modrinth.category.blocks=Bloqueos
modrinth.category.bloom=Resplandor
modrinth.category.bta-babric=BTA (Babric)
modrinth.category.bukkit=Bukkit
modrinth.category.bungeecord=BungeeCord
modrinth.category.canvas=Canvas
modrinth.category.cartoon=Caricatura
modrinth.category.challenging=Desafío
modrinth.category.core-shaders=Sombreadores de núcleo
modrinth.category.colored-lighting=Iluminación de color
modrinth.category.combat=Combate
modrinth.category.core-shaders=Sombreadores de núcleo
modrinth.category.cursed=Maldición
modrinth.category.datapack=Paquete de datos
modrinth.category.decoration=Decoración
modrinth.category.economy=Economía
modrinth.category.entities=Entidades
modrinth.category.environment=Medio ambiente
modrinth.category.equipment=Equipo
modrinth.category.fabric=Fabric
modrinth.category.fantasy=Fantasía
modrinth.category.folia=Folia
modrinth.category.foliage=Vegetación
modrinth.category.fonts=Fonts
modrinth.category.food=Alimentos
modrinth.category.forge=Forge
modrinth.category.game-mechanics=Mecánica de juego
modrinth.category.gui=GUI
modrinth.category.high=Alto
modrinth.category.iris=Iris
modrinth.category.items=Objetos
modrinth.category.java-agent=Java Agent
modrinth.category.kitchen-sink=Fregadero de cocina
modrinth.category.legacy-fabric=Legacy Fabric
modrinth.category.library=Biblioteca
modrinth.category.lightweight=Peso ligero
modrinth.category.liteloader=LiteLoader
modrinth.category.locale=Locale
modrinth.category.low=Bajo
modrinth.category.magic=Magia
modrinth.category.management=Gestión
modrinth.category.medium=Medio
modrinth.category.minecraft=Minecraft
modrinth.category.minigame=Minijuego
modrinth.category.misc=Misceláneo
@@ -972,13 +997,23 @@ modrinth.category.models=Modelos
modrinth.category.modloader=Cargador de mods
modrinth.category.multiplayer=Multijugador
modrinth.category.neoforge=NeoForge
modrinth.category.nilloader=NilLoader
modrinth.category.optifine=OptiFine
modrinth.category.optimization=Optimización
modrinth.category.ornithe=Ornithe
modrinth.category.paper=Paper
modrinth.category.path-tracing=Trazado de rutas
modrinth.category.pbr=PBR
modrinth.category.potato=Mínimo
modrinth.category.purpur=Purpur
modrinth.category.quests=Misiones
modrinth.category.quilt=Quilt
modrinth.category.realistic=Realista
modrinth.category.reflections=Reflexiones
modrinth.category.rift=Rift
modrinth.category.screenshot=Extremo
modrinth.category.semi-realistic=Semirrealista
modrinth.category.shadows=Sombras
modrinth.category.simplistic=Simplista
modrinth.category.social=Social
modrinth.category.spigot=Spigot
@@ -989,12 +1024,11 @@ modrinth.category.themed=Temática
modrinth.category.transportation=Transporte
modrinth.category.tweaks=Ajustes
modrinth.category.utility=Utilidad
modrinth.category.vanilla=Vainilla
modrinth.category.vanilla-like=Similar a la vainilla
modrinth.category.velocity=Velocity
modrinth.category.waterfall=Waterfall
modrinth.category.worldgen=Generación de mundos
modrinth.category.datapack=Paquete de datos
modrinth.category.folia=Folia
modrinth.category.8x-=8x-
modrinth.category.16x=16x
modrinth.category.32x=32x
@@ -1076,9 +1110,12 @@ world.backup.create.locked=El mundo está actualmente en uso. Por favor, cierra
world.backup.create.success=Creada con éxito una nueva copia de seguridad: %s
world.backup.delete=Eliminar esta copia de seguridad
world.backup.processing=Creando nueva copia de seguridad ...
world.backup.title=Mundo [%s] - Copias de seguridad
world.datapack=Gestionar paquetes de datos
world.datapack.1_13=Sólo Minecraft 1.13 o posterior soporta paquetes de datos.
world.chunkbase=Chunk Base
world.chunkbase.end_city=Ciudad del End
world.chunkbase.seed_map=Vista previa de la generación mundial
world.chunkbase.stronghold=Fortaleza
world.chunkbase.nether_fortress=Fortaleza del Nether
world.datapack=Paquetes de datos
world.datetime=Jugado por última vez en %s
world.download=Descargar Mundo
world.download.title=Descargar mundo - %1s
@@ -1093,7 +1130,6 @@ world.import.choose=Elija el archivo de mundo que desea importar
world.import.failed=No se ha podido importar este mundo: %s
world.import.invalid=No se ha podido analizar el mundo.
world.info=Información del mundo
world.info.title=Mundo [%s] - Información
world.info.basic=Información básica
world.info.allow_cheats=Permitir comandos/trucos
world.info.dimension.the_nether=El Nether
@@ -1124,6 +1160,8 @@ world.info.time=Tiempo de juego
world.info.time.format=%s días
world.locked=En uso
world.manage=Mundos
world.manage.button=Administrar
world.manage.title=Mundo - %s
world.name=Nombre del mundo
world.name.enter=Introducir el nombre del mundo
world.show_all=Mostrar todo
@@ -1213,18 +1251,18 @@ settings.advanced.custom_commands.hint=Se proporcionan las siguientes variables
\ · $INST_FABRIC: disponible si Fabric está instalado.\n\
\ · $INST_QUILT: disponible si Quilt está instalado.
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.dont_check_jvm_validity=No comprobar la compatibilidad con JVM
settings.advanced.dont_patch_natives=No intente sustituir automáticamente las bibliotecas nativas
settings.advanced.environment_variables=Variables de entorno
settings.advanced.game_dir.default=Por defecto («.minecraft/»)
settings.advanced.game_dir.independent=Aislar («.minecraft/versions/<nombre de la instancia>/», excepto para los activos y las bibliotecas)
settings.advanced.java_permanent_generation_space=Espacio PermGen
settings.advanced.java_permanent_generation_space.prompt=en MiB
settings.advanced.jvm=Opciones de la máquina virtual de Java
settings.advanced.jvm_args=Argumentos de la máquina virtual de Java
settings.advanced.jvm_args.prompt=\ · Si los argumentos introducidos en «Argumentos de la máquina virtual de Java» son los mismos que los argumentos por defecto, no se añadirán.\n\
\ · Introduzca cualquier argumento GC en «Argumentos de la máquina virtual de Java», y se desactivará el argumento G1 de los argumentos por defecto.\n\
\ · Activa «No añadir argumentos por defecto de JVM» para ejecutar el juego sin añadir argumentos por defecto.
settings.advanced.jvm=Opciones de JVM
settings.advanced.jvm_args=Argumentos JVM
settings.advanced.jvm_args.prompt=\ · Si los argumentos introducidos en «Argumentos JVM» son los mismos que los argumentos por defecto, no se añadirán.\n\
\ · Introduzca cualquier argumento GC en «Argumentos JVM», y se desactivará el argumento G1 de los argumentos por defecto.\n\
\ · Activa «No añadir argumentos JVM por defecto» para ejecutar el juego sin añadir argumentos por defecto.
settings.advanced.launcher_visibility.close=Cerrar el launcher después de ejecutar el juego.
settings.advanced.launcher_visibility.hide=Ocultar el launcher después de ejecutar el juego.
settings.advanced.launcher_visibility.hide_and_reopen=Ocultar el launcher y volver a abrirlo cuando se cierra el juego.
@@ -1236,10 +1274,10 @@ settings.advanced.natives_directory=Ruta de la biblioteca nativa
settings.advanced.natives_directory.choose=Elija la ubicación de la biblioteca nativa deseada
settings.advanced.natives_directory.custom=Personalizado
settings.advanced.natives_directory.default=Por defecto
settings.advanced.natives_directory.hint=Esta opción está pensada sólo para usuarios de Apple M1 u otras plataformas no soportadas oficialmente. Por favor, no modifique esta opción a menos que sepa lo que está haciendo.\n\
settings.advanced.natives_directory.hint=Esta opción está pensada sólo para usuarios de chips de Apple u otras plataformas no soportadas oficialmente. Por favor, no modifique esta opción a menos que sepa lo que está haciendo.\n\
\n\
Antes de proceder, por favor, asegúrese de que todas las bibliotecas (por ejemplo, lwjgl.dll, libopenal.so) se proporcionan en su directorio deseado.
settings.advanced.no_jvm_args=No añadir argumentos por defecto de JVM
settings.advanced.no_jvm_args=No añadir argumentos JVM por defecto
settings.advanced.precall_command=Comando pre-lanzamiento:
settings.advanced.precall_command.prompt=El comando se ejecuta antes de que los juegos se lance
settings.advanced.process_priority=Prioridad del proceso
@@ -1334,6 +1372,7 @@ settings.memory.allocate.manual=%1$.1f GiB Asignados
settings.memory.allocate.manual.exceeded=%1$.1f GiB Asignados (%3$.1f GiB Disponibles)
settings.memory.auto_allocate=Asignar automáticamente
settings.memory.lower_bound=Memoria mínima
settings.memory.unit.mib=MiB
settings.memory.used_per_total=%1$.1f GiB Utilizados / %2$.1f GiB Totales
settings.physical_memory=Tamaño de la memoria física
settings.show_log=Mostrar registros
@@ -289,6 +289,7 @@ download=ダウンロード
download.hint=ゲームや modpack をインストールするか、mod、リソース パック、マップをダウンロードします
download.code.404=リモートサーバーにファイルが見つかりません:%s
download.content=ゲームコンテンツ
download.shader=シェーダー
download.existing=ファイルは既に存在するため、保存できません。「名前を付けて保存」を選択して、ファイルを別の場所に保存できます。
download.external_link=ダウンロードサイトを開く
download.failed=%1$s のダウンロードに失敗しました、応答コード:%2$d
@@ -700,7 +701,6 @@ datapack.title=World %s -データパック
world=マップ
world.add=マップを追加(.zip)
world.datapack=データパックの管理
world.datapack.1_13=Minecraft1.13以降のバージョンのみがデータパックをサポートします。
world.datetime=最終ゲーム時刻:%s
world.download=ダウンロード
world.export=このマップをエクスポートする
@@ -713,7 +713,8 @@ world.import.already_exists=このマップはすでに存在しています。
world.import.choose=インポートするzipファイルを選択してください
world.import.failed=このマップをインポートできません:%s
world.import.invalid=無効なワールドzipファイル
world.manage=マップ / データパック
world.manage=マップ
world.manage.title=マップ - %s
world.name=マップ名
world.name.enter=マップ名を入力してください
world.show_all=すべて表示
@@ -790,7 +791,7 @@ settings.advanced.natives_directory=ネイティブライブラリパス(例
settings.advanced.natives_directory.choose=ネイティブライブラリパスを選択してください
settings.advanced.natives_directory.custom=Custom(ゲームが実行時に必要とするネイティブライブラリを提供する必要があります)
settings.advanced.natives_directory.default=Standard(ネイティブライブラリは当社が提供します)
settings.advanced.natives_directory.hint=このオプションは、AppleM1またはその他の公式にサポートされていないプラットフォーム/アーキテクチャでゲームをプレイする場合にのみ使用されます。\nこのオプションを変更する意味がわからない場合は、このオプションを変更しないことをお勧めします。、またはゲームが起動しません。\nこのオプションを変更する必要がある場合は、ゲームを実行するために必要なすべてのネイティブライブラリをカスタムネイティブディレクトリの下に配置する必要があります。lwjgl.dll(liblwjgl.so)、openal.dll(libopenal.so)。HMCLは、不足しているネイティブライブラリを提供しません。
settings.advanced.natives_directory.hint=このオプションは、Appleシリコンまたはその他の公式にサポートされていないプラットフォーム/アーキテクチャでゲームをプレイする場合にのみ使用されます。\nこのオプションを変更する意味がわからない場合は、このオプションを変更しないことをお勧めします。、またはゲームが起動しません。\nこのオプションを変更する必要がある場合は、ゲームを実行するために必要なすべてのネイティブライブラリをカスタムネイティブディレクトリの下に配置する必要があります。lwjgl.dll(liblwjgl.so)、openal.dll(libopenal.so)。HMCLは、不足しているネイティブライブラリを提供しません。
settings.advanced.no_jvm_args=デフォルトのJVM引数なし
settings.advanced.precall_command=起動前コマンド
settings.advanced.precall_command.prompt=ゲーム開始前に実行されます
@@ -867,13 +868,14 @@ settings.launcher.title_transparent=タイトルの透明性
settings.launcher.version_list_source=バージョンリストソース
settings.memory=ゲームメモリ
settings.memory.allocate.auto=最小 %1$.1f GB /実際の %2$.1f GB
settings.memory.allocate.auto.exceeded=最小 %1$.1f GB /実際の %2$.1f GB(%3$.1f GB使用可能)
settings.memory.allocate.manual=割り当て %1$.1f GB
settings.memory.allocate.manual.exceeded=割り当て %1$.1f GB(%3$.1f GB使用可能)
settings.memory.allocate.auto=最小 %1$.1f GiB /実際の %2$.1f GiB
settings.memory.allocate.auto.exceeded=最小 %1$.1f GiB /実際の %2$.1f GiB(%3$.1f GiB使用可能)
settings.memory.allocate.manual=割り当て %1$.1f GiB
settings.memory.allocate.manual.exceeded=割り当て %1$.1f GiB(%3$.1f GiB使用可能)
settings.memory.auto_allocate=メモリサイズの自動割り当て
settings.memory.lower_bound=最小メモリ
settings.memory.used_per_total=%1$.1f GB使用済み/%2$.1fGB合計
settings.memory.unit.mib=MiB
settings.memory.used_per_total=%1$.1f GiB使用済み/%2$.1fGiB合計
settings.physical_memory=物理メモリサイズ
settings.show_log=ログを表示
settings.tabs.installers=Installers
@@ -334,6 +334,7 @@ download=Скачать
download.hint=Установить игры и модпаки или скачать моды, пакеты ресурсов и миры.
download.code.404=Файл «%s» не найден на удаленном сервере.
download.content=Аддоны
download.shader=Шейдеры
download.curseforge.unavailable=Эта сборка HMCL не поддерживает доступ к CurseForge. Пожалуйста, используйте официальную сборку для доступа к CurseForge.
download.existing=Файл существует и по этому не может быть сохранён. Можно использовать «Сохранить как», чтобы сохранить файл в другом месте.
download.external_link=Открыть сайт
@@ -397,11 +398,16 @@ fatal.apply_update_failure=Мы сожалеем, лаунчер не смог
Вы можете обновить программу вручную, скачав более новую версию с %s.
fatal.apply_update_need_win7=Лаунчер не может автоматически обновляться на Windows XP/Vista.\n\
Вы можете обновить программу вручную, скачав более новую версию с %s.
fatal.deprecated_java_version=В будущем для работы HMCL потребуется Java 11 или более поздняя версия, однако запуск игр будет поддерживаться и на Java 8.\n\
fatal.deprecated_java_version=В будущем для работы HMCL потребуется Java 17 или более поздняя версия, однако запуск игр будет поддерживаться и на Java 6~16.\n\
\n\
Рекомендуется установить последнюю версию Java, чтобы обеспечить правильную работу HMCL.\n\
\n\
Вы можете продолжать использовать старую версию Java. HMCL может распознавать и управлять несколькими версиями Java и автоматически выбирать подходящую Java в зависимости от версии игры.
fatal.deprecated_java_version.update=В будущем для работы HMCL потребуется Java 17 или более поздняя версия. Пожалуйста, установить последнюю версию Java, чтобы убедиться, что приложение сможет завершить обновление.\n\
\n\
Вы можете продолжать использовать старую версию Java.\
HMCL может распознавать и управлять несколькими версиями Java \
и автоматически выбирать подходящую Java в зависимости от версии игры.
fatal.deprecated_java_version.download_link=Скачать Java %d
fatal.samba=Если вы пытаетесь открыть лаунчер в общей папке Samba, он может не работать, попробуйте обновить Java или запустить лаунчер из локальной папки.
fatal.illegal_char=Недопустимый символ «=» в пути к папке пользователя. Лаунчер может работать, но некоторые функции не будут работать.\n\
@@ -436,7 +442,9 @@ folder.mod=Моды
folder.resourcepacks=Пакеты ресурсов
folder.shaderpacks=Пакеты шейдеров
folder.saves=Миры
folder.schematics=Схемы
folder.screenshots=Снимки экрана
folder.world=Папка мира
game=Игры
game.crash.feedback=<b>Пожалуйста, не делитесь скриншотами этого интерфейса с другими!</b> Если вы просите помощи у других, нажмите <b>«Экспорт журналов с ошибками»</b> и отправьте экспортированный файл другим для анализа.
@@ -534,7 +542,7 @@ game.crash.reason.jvm_32bit=Игра отказала из-за того, что
\n\
Если ваша ОС 64-разрядная, установите и используйте 64-разрядная Java. Если ваша ОС 32-разрядная, вам может потребоваться переустановить 64-разрядную ОС или приобрести более современный компьютер.\n\
\n\
Или вы можете отключить опцию «Автовыделение» в разделе «Глобальные настройки / Раздельные настройки для сборки → Оперативная память» и указать максимальный размер выделяемой памяти 1024 МБ или меньше.
Или вы можете отключить опцию «Автовыделение» в разделе «Глобальные настройки / Раздельные настройки для сборки → Оперативная память» и указать максимальный размер выделяемой памяти 1024 МиБ или меньше.
game.crash.reason.loading_crashed_forge=Игра отказала из-за мода «%1$s» (%2$s).\n\
\n\
Вы можете попробовать удалить или обновить его.
@@ -776,9 +784,9 @@ launch.advice.java8_51_1_13=Minecraft 1.13 может аварийно заве
launch.advice.java9=Вы не сможете запустить Minecraft 1.12 и ниже с Java 9 и более новыми версиями Java. Используйте вместо этого Java 8.
launch.advice.modded_java=Некоторые моды могут быть несовместимы с новыми версиями Java. Рекомендуется использовать Java %s для запуска Minecraft %s.
launch.advice.newer_java=Вы используете старую версию Java для запуска игры. Рекомендуется обновить Java 8, иначе некоторые моды могут привести к сбою игры.
launch.advice.not_enough_space=Вы выделили слишком много памяти, поскольку размер физической памяти составляет %d МБ, ваша игра может рухнуть.
launch.advice.not_enough_space=Вы выделили слишком много памяти, поскольку размер физической памяти составляет %d МиБ, ваша игра может рухнуть.
launch.advice.require_newer_java_version=Для текущей версии игры требуется Java %s, но мы не смогли ее найти. Хотите скачать ее сейчас?
launch.advice.too_large_memory_for_32bit=Вы выделили слишком много памяти, из-за 32-разрядной JRE ваша игра может рухнуть. Максимальный объем памяти для 32-разрядных систем составляет 1024 МБ.
launch.advice.too_large_memory_for_32bit=Вы выделили слишком много памяти, из-за 32-разрядной JRE ваша игра может рухнуть. Максимальный объем памяти для 32-разрядных систем составляет 1024 МиБ.
launch.advice.vanilla_linux_java_8=На Linux x86-64, Minecraft 1.12.2 и ниже может работать только на Java 8.\nВерсии Java 9 и выше не могут загружать 32-битные нативные библиотеки, такие как liblwjgl.so.
launch.advice.modlauncher8=Используемая вами версия Forge несовместима с текущей версией Java. Попробуйте обновить Forge.
launch.advice.vanilla_x86.translation=Minecraft еще не полностью поддерживается на вашей платформе, поэтому вы можете столкнуться с отсутствием функций или даже не сможете запустить игру.\n\
@@ -867,7 +875,7 @@ modpack.choose.remote=Скачать
modpack.choose.remote.detail=Необходима прямая ссылка на скачивание модпака
modpack.choose.remote.tooltip=Введите URL-адрес модпака
modpack.choose.repository=Скачать модпак с CurseForge или Modrinth
modpack.choose.repository.detail=Не забудьте вернуться на эту страницу и перетащить файл модпака сюда после того, как модпак будет скачан.
modpack.choose.repository.detail=Вы сможете выбрать нужный вам модпак на следующей странице.
modpack.completion=Скачивание зависимостей
modpack.desc=Опишите модпак, включая введение и журнал изменений. Поддерживаются Markdown и изображения по URL-адресу.
modpack.description=Описание
@@ -935,33 +943,50 @@ modpack.wizard.step.initialization.warning=Перед созданием мод
modpack.wizard.step.initialization.server=Нажмите здесь для получения дополнительных руководств по созданию модпака на сервере, который может автообновляться.
modrinth.category.adventure=Приключение
modrinth.category.atmosphere=Атмосфера
modrinth.category.audio=Аудио
modrinth.category.babric=Babric
modrinth.category.blocks=Блоки
modrinth.category.bloom=Свечение
modrinth.category.bta-babric=BTA (Babric)
modrinth.category.bukkit=Bukkit
modrinth.category.bungeecord=BungeeCord
modrinth.category.canvas=Canvas
modrinth.category.cartoon=Мультяшный
modrinth.category.challenging=Вызов
modrinth.category.core-shaders=Ядро шейдеров
modrinth.category.colored-lighting=Цветное освещение
modrinth.category.combat=Бой
modrinth.category.core-shaders=Ядро шейдеров
modrinth.category.cursed=Cursed
modrinth.category.datapack=Набор данных
modrinth.category.decoration=Украшения
modrinth.category.economy=Экономика
modrinth.category.entities=Сущности
modrinth.category.environment=Окружающая среда
modrinth.category.equipment=Оборудование
modrinth.category.fabric=Fabric
modrinth.category.fantasy=Фэнтези
modrinth.category.folia=Folia
modrinth.category.foliage=Растительность
modrinth.category.fonts=Шрифты
modrinth.category.food=Еда
modrinth.category.forge=Forge
modrinth.category.game-mechanics=Игровая механика
modrinth.category.gui=ГИП
modrinth.category.high=Высокий
modrinth.category.iris=Iris
modrinth.category.items=Предметы
modrinth.category.java-agent=Java Agent
modrinth.category.kitchen-sink=Kitchen-Sink
modrinth.category.legacy-fabric=Legacy Fabric
modrinth.category.library=Библиотека
modrinth.category.lightweight=Легкий
modrinth.category.liteloader=LiteLoader
modrinth.category.locale=Язык
modrinth.category.low=Низкий
modrinth.category.magic=Магия
modrinth.category.management=Управление
modrinth.category.medium=Средний
modrinth.category.minecraft=Minecraft
modrinth.category.minigame=Мини-игра
modrinth.category.misc=Разное
@@ -971,13 +996,23 @@ modrinth.category.models=Модели
modrinth.category.modloader=Мод-загрузчик
modrinth.category.multiplayer=Сетевая игра
modrinth.category.neoforge=NeoForge
modrinth.category.nilloader=NilLoader
modrinth.category.optifine=OptiFine
modrinth.category.optimization=Оптимизация
modrinth.category.ornithe=Ornithe
modrinth.category.paper=Paper
modrinth.category.path-tracing=Трассировка пути
modrinth.category.pbr=PBR
modrinth.category.potato=Минимум
modrinth.category.purpur=Purpur
modrinth.category.quests=Квесты
modrinth.category.quilt=Quilt
modrinth.category.realistic=Реалистичный
modrinth.category.reflections=Отражения
modrinth.category.rift=Rift
modrinth.category.screenshot=Экстрим
modrinth.category.semi-realistic=Полуреалистичный
modrinth.category.shadows=Тени
modrinth.category.simplistic=Упрощенный
modrinth.category.social=Социальная
modrinth.category.spigot=Spigot
@@ -988,12 +1023,11 @@ modrinth.category.themed=Тематический
modrinth.category.transportation=Транспорт
modrinth.category.tweaks=Усовершенствование
modrinth.category.utility=Утилиты
modrinth.category.vanilla=Ванильное
modrinth.category.vanilla-like=Ванильное
modrinth.category.velocity=Velocity
modrinth.category.waterfall=Waterfall
modrinth.category.worldgen=Генерация мира
modrinth.category.datapack=Набор данных
modrinth.category.folia=Folia
modrinth.category.8x-=8x-
modrinth.category.16x=16x
modrinth.category.32x=32x
@@ -1075,9 +1109,12 @@ world.backup.create.locked=В настоящее время мир находи
world.backup.create.success=Успешно создано новое резервное копирование: %s
world.backup.delete=Удалить эту резервную копию
world.backup.processing=Создание новой резервной копии ...
world.backup.title=Мир [%s] - Резервные копии
world.datapack=Управлять наборами данных
world.datapack.1_13=Только Minecraft 1.13 или новее поддерживает наборы данных.
world.chunkbase=Chunk Base
world.chunkbase.end_city=Город Края
world.chunkbase.seed_map=Предпросмотр генерации мира
world.chunkbase.stronghold=Крепость
world.chunkbase.nether_fortress=Крепость Нижнего мира
world.datapack=Наборы данных
world.datetime=Последний запуск игры %s
world.download=Скачать мир
world.download.title=Скачать мир - %1s
@@ -1092,7 +1129,6 @@ world.import.choose=Выберите архив мира, который хот
world.import.failed=Не удалось импортировать этот мир\: %s
world.import.invalid=Не удалось разобрать мир.
world.info=Сведения о мире
world.info.title=Мир [%s] - Сведения
world.info.basic=Основные сведения
world.info.allow_cheats=Разрешить команды/читы
world.info.dimension.the_nether=Нижний мир
@@ -1123,6 +1159,8 @@ world.info.time=Время игры
world.info.time.format=%s дн.
world.locked=В эксплуатации
world.manage=Миры
world.manage.button=Управлять
world.manage.title=Мир - %s
world.name=Название мира
world.name.enter=Введите название мира
world.show_all=Показать все
@@ -1212,18 +1250,18 @@ settings.advanced.custom_commands.hint=Пользовательские кома
\ · $INST_FABRIC: устанавливается, если установлен Fabric.\n\
\ · $INST_QUILT: устанавливается, если установлен Quilt.
settings.advanced.dont_check_game_completeness=Не проверять целостность игры
settings.advanced.dont_check_jvm_validity=Не проверять совместимость Java VM
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 и библиотек)
settings.advanced.java_permanent_generation_space=Пространство PermGen
settings.advanced.java_permanent_generation_space.prompt=в МБ
settings.advanced.jvm=Настройки Java VM
settings.advanced.jvm_args=Аргументы Java VM
settings.advanced.jvm_args.prompt=\ · Если параметр, введённый в «Аргументы Java VM», совпадает с параметром по умолчанию, он не будет добавлен.\n\
\ · Введите любые параметры GC в «Аргументы Java VM», параметр G1 по умолчанию будет отключен.\n\
\ · Нажмите «Не добавлять аргументы Java VM по умолчанию» ниже, чтобы запустить игру без добавления аргументов по умолчанию.
settings.advanced.java_permanent_generation_space.prompt=в МиБ
settings.advanced.jvm=Настройки JVM
settings.advanced.jvm_args=Аргументы JVM
settings.advanced.jvm_args.prompt=\ · Если параметр, введённый в «Аргументы JVM», совпадает с параметром по умолчанию, он не будет добавлен.\n\
\ · Введите любые параметры GC в «Аргументы JVM», параметр G1 по умолчанию будет отключен.\n\
\ · Нажмите «Не добавлять аргументы JVM по умолчанию» ниже, чтобы запустить игру без добавления аргументов по умолчанию.
settings.advanced.launcher_visibility.close=Закрывать после запуска игры
settings.advanced.launcher_visibility.hide=Скрыть после запуска игры
settings.advanced.launcher_visibility.hide_and_reopen=Скрыть после запуска и показать после закрытия игры
@@ -1239,7 +1277,7 @@ settings.advanced.natives_directory.hint=Параметр предназначе
\n\
Прежде чем продолжить, убедитесь что все библиотеки (например, lwjgl.dll, libopenal.so) находятся в нужной папке.\n\
Примечание: Рекомендуется использовать полностью английские пути для указанных файлов локальных библиотек, иначе игра может не запуститься.
settings.advanced.no_jvm_args=Не добавлять аргументы Java VM по умолчанию
settings.advanced.no_jvm_args=Не добавлять аргументы JVM по умолчанию
settings.advanced.precall_command=Команда перед запуском
settings.advanced.precall_command.prompt=Команды, которые необходимо выполнить перед запуском игры
settings.advanced.process_priority=Приоритет процесса
@@ -1328,13 +1366,14 @@ settings.launcher.turn_off_animations=Отключить анимацию (Пр
settings.launcher.version_list_source=Список версий
settings.memory=Оперативная память
settings.memory.allocate.auto=Минимум %1$.1f ГБ / Выделено %2$.1f ГБ
settings.memory.allocate.auto.exceeded=Минимум %1$.1f ГБ / Выделено %2$.1f ГБ (Доступно %3$.1f ГБ)
settings.memory.allocate.manual=Выделено %1$.1f ГБ
settings.memory.allocate.manual.exceeded=Выделено %1$.1f ГБ (Доступно %3$.1f ГБ)
settings.memory.allocate.auto=Минимум %1$.1f ГиБ / Выделено %2$.1f ГиБ
settings.memory.allocate.auto.exceeded=Минимум %1$.1f ГиБ / Выделено %2$.1f ГиБ (Доступно %3$.1f ГиБ)
settings.memory.allocate.manual=Выделено %1$.1f ГиБ
settings.memory.allocate.manual.exceeded=Выделено %1$.1f ГиБ (Доступно %3$.1f ГиБ)
settings.memory.auto_allocate=Автовыделение
settings.memory.lower_bound=Мин. объём памяти
settings.memory.used_per_total=Использовано %1$.1f ГБ / Всего %2$.1f ГБ
settings.memory.unit.mib=МиБ
settings.memory.used_per_total=Использовано %1$.1f ГиБ / Всего %2$.1f ГиБ
settings.physical_memory=Размер физической памяти
settings.show_log=Показать логи
settings.tabs.installers=Погрузчики
@@ -97,7 +97,7 @@ account.register=註冊
account.manage=帳戶清單
account.copy_uuid=複製該帳戶的 UUID
account.methods=登入方式
account.methods.authlib_injector=外置登入
account.methods.authlib_injector=外部登入
account.methods.microsoft=Microsoft 帳戶
account.methods.microsoft.birth=如何變更帳戶出生日期
account.methods.microsoft.deauthorize=移除應用存取權
@@ -334,6 +334,7 @@ download=下載
download.hint=安裝遊戲和模組包或下載模組、資源包和地圖
download.code.404=遠端伺服器沒有需要下載的檔案:%s
download.content=遊戲內容
download.shader=光影
download.curseforge.unavailable=這個 HMCL 版本不支援訪問 CurseForge。請使用官方版本進行下載。
download.existing=檔案已存在,無法儲存。你可以將檔案儲存至其他地方。
download.external_link=開啟下載網站
@@ -359,7 +360,7 @@ exception.access_denied=無法存取檔案「%s」。因為 HMCL 沒有對該檔
請你檢查目前作業系統帳戶是否能訪存取檔案,比如非管理員使用者可能不能訪問其他帳戶的個人目錄內的檔案。\n\
對於 Windows 使用者,你還可以嘗試透過資源監視器查看是否有程式占用了該檔案。如果是,你可以關閉占用此檔案的程式,或者重啟電腦再試。
exception.artifact_malformed=下載的檔案正確,但無法透過校驗。
exception.ssl_handshake=無法建立 SSL 連線。因為目前 Java 虛擬機缺少相關的 SSL 證書。你可以嘗試使用其他的 Java 虛擬機或關閉網路代理開啟 HMCL 再試。
exception.ssl_handshake=無法建立 SSL 連線。目前 Java 缺少相關的 SSL 證書。你可以嘗試使用其他 Java 或關閉網路代理開啟 HMCL 再試。
extension.bat=Windows 批次檔
extension.mod=模組檔案
@@ -375,11 +376,13 @@ fatal.config_change_owner_root=你正在使用 root 帳戶開啟 Hello Minecraft
fatal.config_in_temp_dir=你正在暫存目錄中開啟 Hello Minecraft! Launcher,你的設定和遊戲資料可能會遺失。建議將 HMCL 移動至其他位置再開啟。\n是否繼續開啟?
fatal.config_loading_failure=Hello Minecraft! Launcher 無法載入設定檔案。\n請確保 HMCL 對「%s」目錄及該目錄下的檔案擁有讀寫權限。
fatal.config_loading_failure.unix=Hello Minecraft! Launcher 無法載入設定檔案,因為設定檔案是由使用者「%1$s」建立的。\n請使用 root 帳戶開啟 HMCL (不推薦),或在終端中執行以下指令將設定檔案的所有權變更為目前使用者:\n%2$s
fatal.config_unsupported_version=當前設定檔案是由更高版本的 Hello Minecraft! Launcher 建立的,目前版本的 HMCL 無法正常載入。請更新並重新啟動 HMCL。\n在更新啟動器之前,你所做的所有設定更改都不會被保存。
fatal.mac_app_translocation=由於 macOS 的安全機制,Hello Minecraft! Launcher 被系統隔離至暫存目錄中。\n請將 HMCL 移動到其他目錄後再嘗試開啟,否則你的設定和遊戲資料可能會在重啟後遺失。\n是否繼續開啟?
fatal.migration_requires_manual_reboot=Hello Minecraft! Launcher 即將升級完成,請重新開啟 HMCL。
fatal.apply_update_failure=我們很抱歉 Hello Minecraft! Launcher 無法自動完成升級程式,因為出現了一些問題。\n但你依然可以從 %s 處手動下載 HMCL 來完成升級。
fatal.apply_update_need_win7=Hello Minecraft! Launcher 無法在 Windows XP/Vista 上進行自動更新。請從 %s 處手動下載 HMCL 來完成升級。
fatal.deprecated_java_version=HMCL 未來需要 Java 11 或更高版本才能執行,但依然支援使用 Java 8 啟動遊戲。建議安裝最新版本的 Java 以確保 HMCL 正常執行。\n你可以繼續保留舊版本 Java。HMCL 能夠識別與管理多個 Java,並會自動根據遊戲版本為你選取合適的 Java。
fatal.deprecated_java_version=HMCL 未來需要 Java 17 或更高版本才能執行,但依然支援使用 Java 6~16 啟動遊戲。建議安裝最新版本的 Java 以確保 HMCL 正常執行。\n你可以繼續保留舊版本 Java。HMCL 能夠識別與管理多個 Java,並會自動根據遊戲版本為你選取合適的 Java。
fatal.deprecated_java_version.update=更高版本的 HMCL 需要 Java 17 或更高版本才能執行,請安裝最新版本的 Java 以確保 HMCL 能夠完成升級。\n你可以繼續保留舊版本 Java。HMCL 能夠識別與管理多個 Java,並會自動根據遊戲版本為你選取合適的 Java。
fatal.deprecated_java_version.download_link=下載 Java %d
fatal.samba=如果您正在透過 Samba 共亯的目錄中開啟 Hello Minecraft! Launcher,啟動器可能無法正常工作,請嘗試更新您的 Java 或在本機目錄內開啟 HMCL。
fatal.illegal_char=由於您的使用者目錄路徑中存在無效字元『=』,您將無法使用外部登入帳戶以及離線登入更換外觀功能。
@@ -408,6 +411,7 @@ folder.shaderpacks=著色器包目錄
folder.saves=遊戲存檔目錄
folder.schematics=原理圖目錄
folder.screenshots=截圖目錄
folder.world=世界目錄
game=遊戲
game.crash.feedback=<b>請不要將本介面截圖給他人!</b>如果你要求助他人,請你點擊左下角「匯出遊戲崩潰資訊」後將匯出的檔案發送給他人以供分析。\n你可以點擊下方的「幫助」前往社群尋求幫助。
@@ -428,13 +432,13 @@ game.crash.reason.modmixin_failure=目前遊戲由於某些模組注入失敗,
game.crash.reason.mod_repeat_installation=目前遊戲由於重複安裝了多個相同的模組,無法繼續執行。\n每個模組只能出現一次,請刪除重複的模組,然後再啟動遊戲。
game.crash.reason.forge_error=Forge/NeoForge 可能已經提供了錯誤資訊。\n你可以查看日誌,並根據錯誤報告中的日誌資訊進行對應處。\n如果沒有看到報錯資訊,可以查看錯誤報告了解錯誤具體是如何發生的。\n%1$s
game.crash.reason.mod_resolution0=目前遊戲由於一些模組出現問題,無法繼續執行。\n你可以查看日誌尋找出錯模組。
game.crash.reason.need_jdk11=目前遊戲由於 Java 虛擬機版本不合適,無法繼續執行。\n你需要下載安裝 Java 11,並在「(全域/實例特定) 遊戲設定 → 遊戲 Java」中將 Java 設定為 11.x 的版本。
game.crash.reason.need_jdk11=目前遊戲由於 Java 版本不合適,無法繼續執行。\n你需要下載安裝 Java 11,並在「(全域/實例特定) 遊戲設定 → 遊戲 Java」中將 Java 設定為 11.x 的版本。
game.crash.reason.java_version_is_too_high=目前遊戲由於使用的 Java 版本過高而崩潰了,無法繼續執行。\n請在「(全域/實例特定) 遊戲設定 → 遊戲 Java」中改用較低版本的 Java,然後再啟動遊戲。\n如果沒有,可以從 <a href="https://www.java.com/download/">java.com (Java 8)</a> 或 <a href="https://bell-sw.com/pages/downloads/#downloads">BellSoft Liberica Full JRE (Java 17)</a> 等平台下載、安裝一個 (安裝完後需重啟啟動器)。
game.crash.reason.mod_name=目前遊戲由於模組檔案名稱問題,無法繼續執行。\n模組檔案名稱應只使用半型的大小寫字母 (Aa~Zz)、數位 (0~9)、橫線 (-)、底線 (_)和點 (.)。\n請到模組目錄中將所有不合規的模組檔案名稱修正為上述合規字元。
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=目前遊戲由於你使用的某些模組/光影包/資源包/紋理包出現問題,無法繼續執行。\n請先嘗試禁用你所使用的模組/光影包/資源包/紋理包再試。
game.crash.reason.gl_operation_failure=目前遊戲由於你使用的某些模組/光影包/資源包/紋理包出現問題,無法繼續執行。\n請先嘗試停用你所使用的模組/光影包/資源包/紋理包再試。
game.crash.reason.graphics_driver=目前遊戲由於你的顯示卡驅動存在問題崩潰了。請嘗試升級你的顯示卡驅動到最新版本後再嘗試啟動遊戲。\n\
如果你的電腦同時存在獨立顯示卡與整合式顯示卡,你需要檢查遊戲是否使用整合式顯示卡啟動。如果是,請嘗試使用獨立顯示卡開啟 HMCL 與遊戲。如果仍有問題,你可能需要考慮換一個新顯示卡或新電腦。\n\
如果你確實需要使用整合式顯示卡,請檢查你電腦的 CPU 是否為 Intel(R) Core(TM) 3000 系列或更舊的處理器。如果是,對於 Minecraft 1.16.5 及更低版本,請你將遊戲所使用的 Java 版本降級至 1.8.0_51 及更低版本;否則你需要更換獨立顯示卡或新電腦。
@@ -442,7 +446,7 @@ game.crash.reason.macos_failed_to_find_service_port_for_display=目前遊戲由
game.crash.reason.illegal_access_error=目前遊戲由於某些模組的問題,無法繼續執行。\n如果你認識「%1$s」,你可以更新或刪除對應模組再試。
game.crash.reason.install_mixinbootstrap=目前遊戲由於缺失 MixinBootstrap,無法繼續執行。\n你可以嘗試安裝 <a href="https://www.curseforge.com/minecraft/mc-mods/mixinbootstrap">MixinBootstrap</a> 解決該問題。若安裝後崩潰,可以嘗試在該模組的檔案名前新增半形驚嘆號 (!) 解決。
game.crash.reason.jdk_9=目前遊戲由於 Java 版本過高,無法繼續執行。\n你需要下載安裝 Java 8,並在「(全域/實例特定) 遊戲設定 → 遊戲 Java」中將 Java 設定為 1.8.x 的版本。
game.crash.reason.jvm_32bit=目前遊戲由於記憶體分配過大,超過了 32 位 Java 記憶體限制,無法繼續執行。\n如果你的電腦是 64 位系統,請下載安裝並更換 64 位 Java。\n如果你的電腦是 32 位系統,你或許可以重新安裝 64 位系統,或換一台新電腦。\n或者,你可以關閉「(全域/實例特定) 遊戲設定 → 遊戲記憶體」中的「自動分配」,並且把記憶體分配值調節為 1024 MiB 或以下。
game.crash.reason.jvm_32bit=目前遊戲由於記憶體分配過大,超過了 32 位 Java 虛擬機記憶體限制,無法繼續執行。\n如果你的電腦是 64 位系統,請下載安裝並更換 64 位 Java。\n如果你的電腦是 32 位系統,你或許可以重新安裝 64 位系統,或換一台新電腦。\n或者,你可以關閉「(全域/實例特定) 遊戲設定 → 遊戲記憶體」中的「自動分配」,並且把記憶體分配值調節為 1024 MiB 或以下。
game.crash.reason.loading_crashed_forge=目前遊戲由於模組「%1$s (%2$s)」錯誤,無法繼續執行。\n你可以嘗試刪除或更新該模組以解決問題。
game.crash.reason.loading_crashed_fabric=目前遊戲由於模組「%1$s」錯誤,無法繼續執行。\n你可以嘗試刪除或更新該模組以解決問題。
game.crash.reason.mac_jdk_8u261=目前遊戲由於你所使用的 Forge 或 OptiFine 與 Java 衝突而崩潰。\n請嘗試更新 Forge 和 OptiFine,或使用 Java 8u251 及更早版本啟動。
@@ -470,11 +474,11 @@ game.crash.reason.rtss_forest_sodium=目前遊戲由於 RivaTuner Statistics Ser
game.crash.reason.no_class_def_found_error=目前遊戲由於程式碼不完整,無法繼續執行。\n你的遊戲可能缺失了某個模組,或者某些模組檔案不完整,或者模組與遊戲的版本不匹配。\n你可能需要重新安裝遊戲和模組,或請求他人幫助。\n缺失:%1$s
game.crash.reason.no_such_method_error=目前遊戲由於程式碼不完整,無法繼續執行。\n你的遊戲可能缺失了某個模組,或者某些模組檔案不完整,或者模組與遊戲的版本不匹配。\n你可能需要重新安裝遊戲和模組,或請求他人幫助。
game.crash.reason.opengl_not_supported=目前遊戲由於你的顯示卡驅動存在問題,無法繼續執行。\n原因是 OpenGL 不受支援,你現在是否在遠端桌面或者串流模式下?如果是,請直接使用原電腦啟動遊戲。\n或者嘗試升級你的顯示卡驅動到最新版本後再嘗試啟動遊戲。如果你的電腦同時存在獨立顯示卡與整合式顯示卡,你需要檢查遊戲是否使用整合式顯示卡啟動。如果是,請嘗試使用獨立顯示卡開啟 HMCL 與遊戲。如果仍有問題,你可能需要考慮換一個新顯示卡或新電腦。
game.crash.reason.openj9=目前遊戲無法執行在 OpenJ9 虛擬機上。請你在「(全域/實例特定) 遊戲設定 → 遊戲 Java」中更換 Hotspot Java 虛擬機,並重新啟動遊戲。如果沒有下載安裝,你可以在網路上自行下載。
game.crash.reason.openj9=目前遊戲無法執行在 OpenJ9 虛擬機上。請你在「(全域/實例特定) 遊戲設定 → 遊戲 Java」中更換為使用 Hotspot 虛擬機的 Java,並重新啟動遊戲。如果沒有下載安裝,你可以在網路上自行下載。
game.crash.reason.out_of_memory=目前遊戲由於記憶體不足,無法繼續執行。\n這可能是記憶體分配過小,或者模組數量過多導致的。\n你可以在「(全域/實例特定) 遊戲設定 → 遊戲記憶體」中調整遊戲記憶體分配值以允許遊戲在更大的記憶體下執行。\n如果仍然出現該錯誤,你可能需要換一台更好的電腦。
game.crash.reason.resolution_too_high=目前遊戲由於資源包/紋理包解析度過高,無法繼續執行。\n你可以更換一個解析度更低的資源包/紋理包,或者更換一個視訊記憶體更大的顯示卡。
game.crash.reason.stacktrace=原因未知。請點擊日誌按鈕查看詳細訊息。\n下面是一些關鍵字,其中可能包含模組名稱,你可以透過搜尋的方式尋找有關訊息。\n%s
game.crash.reason.too_old_java=目前遊戲由於 Java 虛擬機版本過低,無法繼續執行。\n你需要在「(全域/實例特定) 遊戲設定 → 遊戲 Java」中更換 %1$s 或更高版本的 Java 虛擬機,並重新啟動遊戲。如果沒有下載安裝,你可以點擊 <a href="https://learn.microsoft.com/java/openjdk/download">此處</a> 下載 Microsoft JDK。
game.crash.reason.too_old_java=目前遊戲由於 Java 版本過低,無法繼續執行。\n你需要在「(全域/實例特定) 遊戲設定 → 遊戲 Java」中更換 %1$s 或更高版本的 Java,並重新啟動遊戲。如果沒有下載安裝,你可以點擊 <a href="https://learn.microsoft.com/java/openjdk/download">此處</a> 下載 Microsoft JDK。
game.crash.reason.unknown=原因未知。請點擊日誌按鈕查看詳細訊息。
game.crash.reason.unsatisfied_link_error=目前遊戲由於缺少本機庫,無法繼續執行。\n這些本機庫缺失:%1$s。\n如果你在「(全域/實例特定) 遊戲設定 → 進階設定」中修改了本機庫路徑選項,請你修改回預設模式。\n如果你正在使用預設模式,請檢查遊戲目錄路徑是否只包含英文字母、數字和底線。\n如果是,那麼請檢查是否為模組或 HMCL 導致了本機庫缺失的問題。如果你確定是 HMCL 引起的,建議你向我們回報。\n<b>對於 Windows 使用者,你還可以嘗試在「控制台 → 時鐘和區域 → 地區 → 系統管理 → 變更系統區域設定」中,關閉「Beta:使用 Unicode UTF-8 提供全球語言支援」選項;</b>\n<b>或將遊戲目錄路徑中的所有非英文字母的名稱 (例如中文、空格等) 修改為英文字母。</b>\n如果你確實需要自訂本機庫路徑,你需要保證其中包含缺失的本機庫!
game.crash.title=遊戲意外退出
@@ -541,9 +545,9 @@ install.success=安裝成功
java.add=添加 Java
java.add.failed=Java 無效或與目前平臺不相容
java.disable=禁用此 Java
java.disable.confirm=你確定要禁用此 Java 嗎?
java.disabled.management=管理已禁用的 Java
java.disable=停用此 Java
java.disable.confirm=你確定要停用此 Java 嗎?
java.disabled.management=管理已停用的 Java
java.disabled.management.remove=從清單中移除此 Java
java.disabled.management.restore=重新啟用此 Java
java.download=下載 Java
@@ -575,10 +579,10 @@ lang.default=使用系統語言
launch.advice=%s 是否繼續啟動?
launch.advice.multi=檢測到以下問題:\n\n%s\n\n這些問題可能導致遊戲無法正常啟動或影響遊戲體驗,是否繼續啟動?
launch.advice.java.auto=目前選取的 Java 虛擬機版本不滿足遊戲要求,是否自動選取合適的 Java 虛擬機版本?\n或者你可以到「(全域/實例特定) 遊戲設定 → 遊戲 Java」中選取一個合適的 Java 虛擬機版本。
launch.advice.java.auto=目前選取的 Java 版本不滿足遊戲要求,是否自動選取合適的 Java 版本?\n或者你可以到「(全域/實例特定) 遊戲設定 → 遊戲 Java」中選取一個合適的 Java 版本。
launch.advice.java.modded_java_7=Minecraft 1.7.2 及更低版本需要 Java 7 及更低版本。
launch.advice.corrected=我們已經修正了問題。如果你確實希望使用你自訂的 Java 虛擬機,你可以在「(全域/實例特定) 遊戲設定 → 進階設定」中往下滑,開啟「不檢查 JVM 與遊戲的相容性」。
launch.advice.uncorrected=如果你確實希望使用你自訂的 Java 虛擬機,你可以在「(全域/實例特定) 遊戲設定 → 進階設定」中往下滑,開啟「不檢查 JVM 與遊戲的相容性」。
launch.advice.corrected=我們已經修正了 Java 版本問題。如果你確實希望使用你自訂的 Java,你可以在「(全域/實例特定) 遊戲設定 → 進階設定」中往下滑,開啟「不檢查 Java 虛擬機與遊戲的相容性」。
launch.advice.uncorrected=如果你確實希望使用你自訂的 Java,你可以在「(全域/實例特定) 遊戲設定 → 進階設定」中往下滑,開啟「不檢查 Java 虛擬機與遊戲的相容性」。
launch.advice.different_platform=你正在使用 32 位元 Java 啟動遊戲。建議更換至 64 位元 Java。
launch.advice.forge2760_liteloader=Forge 14.23.5.2760 與 LiteLoader 不相容。請更新 Forge 至 14.23.5.2773 或更高版本。
launch.advice.forge28_2_2_optifine=Forge 28.2.2 及更高版本與 OptiFine 不相容,請降級 Forge 至 28.2.1 或更低版本。
@@ -596,7 +600,7 @@ launch.advice.vanilla_linux_java_8=對於 Linux x86-64 平台,Minecraft 1.12.2
launch.advice.vanilla_x86.translation=Minecraft 尚未為你的平臺提供完善支援,所以可能影響遊戲體驗或無法啟動遊戲。\n你可以在 <a href="https://learn.microsoft.com/java/openjdk/download">這裡</a> 下載 <b>x86-64</b> 架構的 Java 以獲得更完整的體驗。\n是否繼續啟動?
launch.advice.unknown=由於以下原因,無法繼續啟動遊戲:
launch.failed=啟動失敗
launch.failed.cannot_create_jvm=無法建立 Java 虛擬機,可能是 Java 參數有問題。可以在「(全域/實例特定) 遊戲設定 → 進階設定 → Java 虛擬機設定」中移除所有 Java 虛擬機參數後,嘗試再次啟動遊戲。
launch.failed.cannot_create_jvm=無法建立 Java 虛擬機。可能是虛擬機參數有問題。可以在「(全域/實例特定) 遊戲設定 → 進階設定 → Java 虛擬機設定」中移除所有虛擬機參數後,嘗試再次啟動遊戲。
launch.failed.creating_process=啟動失敗,在建立新處理程式時發生錯誤。可能是 Java 路徑錯誤。
launch.failed.command_too_long=指令長度超過限制,無法建立批次檔指令碼,請匯出為 PowerShell 指令碼。
launch.failed.decompressing_natives=無法解壓縮遊戲本機庫。
@@ -607,7 +611,7 @@ launch.failed.execution_policy.failed_to_set=設定執行策略失敗
launch.failed.execution_policy.hint=目前執行策略封鎖你執行 PowerShell 指令碼。\n點擊「確定」允許目前使用者執行本機 PowerShell 指令碼,或點擊「取消」保持現狀。
launch.failed.exited_abnormally=遊戲非正常退出。請查看日誌檔案,或聯絡他人尋求幫助。
launch.failed.java_version_too_low=你所指定的 Java 版本過低。請重新設定 Java 版本。
launch.failed.no_accepted_java=找不到適合目前遊戲使用的 Java。是否使用預設 Java 啟動遊戲?點擊「是」使用預設 Java 繼續啟動遊戲,\n或者請到「(全域/實例特定) 遊戲設定 → 遊戲 Java」中選取一個合適的 Java 虛擬機版本。
launch.failed.no_accepted_java=找不到適合目前遊戲使用的 Java。是否使用預設 Java 啟動遊戲?點擊「是」使用預設 Java 繼續啟動遊戲,\n或者請到「(全域/實例特定) 遊戲設定 → 遊戲 Java」中選取一個合適的 Java 版本。
launch.failed.sigkill=遊戲被使用者或系統強制終止。
launch.state.dependencies=處理遊戲相依元件
launch.state.done=啟動完成
@@ -675,7 +679,7 @@ modpack.choose.local.detail=你可以直接將模組包檔案拖入本頁面以
modpack.choose.remote=從網路下載模組包
modpack.choose.remote.detail=需要提供模組包的下載連結
modpack.choose.repository=從 CurseForge/Modrinth 下載模組包
modpack.choose.repository.detail=下載後記得回到這個介面,把模組包拖進來哦
modpack.choose.repository.detail=可直接在跳轉到的頁面安裝模組包
modpack.choose.remote.tooltip=要下載的模組包的連結
modpack.completion=下載模組包相關檔案
modpack.desc=描述你要製作的模組包,比如模組包注意事項和更新紀錄。支援 Markdown (圖片請上傳至網路)。
@@ -742,33 +746,50 @@ modpack.wizard.step.initialization.warning=在製作模組包前,請你確認
modpack.wizard.step.initialization.server=點選此處查看有關伺服器自動更新模組包的製作教學
modrinth.category.adventure=冒險
modrinth.category.atmosphere=氛圍
modrinth.category.audio=聲音
modrinth.category.babric=Babric
modrinth.category.blocks=方塊
modrinth.category.bloom=泛光
modrinth.category.bta-babric=BTA (Babric)
modrinth.category.bukkit=Bukkit
modrinth.category.bungeecord=BungeeCord
modrinth.category.canvas=Canvas
modrinth.category.cartoon=卡通
modrinth.category.challenging=高難度
modrinth.category.core-shaders=核心著色器
modrinth.category.colored-lighting=彩色光照
modrinth.category.combat=戰鬥
modrinth.category.core-shaders=核心著色器
modrinth.category.cursed=Cursed
modrinth.category.datapack=資料包
modrinth.category.decoration=裝飾
modrinth.category.economy=經濟
modrinth.category.entities=實體
modrinth.category.environment=環境
modrinth.category.equipment=裝備
modrinth.category.fabric=Fabric
modrinth.category.fantasy=幻想
modrinth.category.folia=Folia
modrinth.category.foliage=植被
modrinth.category.fonts=字體
modrinth.category.food=食物
modrinth.category.forge=Forge
modrinth.category.game-mechanics=遊戲機制
modrinth.category.gui=GUI
modrinth.category.high=高
modrinth.category.iris=Iris
modrinth.category.items=物品
modrinth.category.java-agent=Java Agent
modrinth.category.kitchen-sink=大雜燴
modrinth.category.legacy-fabric=Legacy Fabric
modrinth.category.library=支援庫
modrinth.category.lightweight=輕量
modrinth.category.liteloader=LiteLoader
modrinth.category.locale=在地化
modrinth.category.low=低
modrinth.category.magic=魔法
modrinth.category.management=管理
modrinth.category.medium=中
modrinth.category.minecraft=Minecraft
modrinth.category.minigame=小遊戲
modrinth.category.misc=其他
@@ -778,13 +799,23 @@ modrinth.category.models=模型
modrinth.category.modloader=ModLoader
modrinth.category.multiplayer=多人
modrinth.category.neoforge=NeoForge
modrinth.category.nilloader=NilLoader
modrinth.category.optifine=OptiFine
modrinth.category.optimization=最佳化
modrinth.category.ornithe=Ornithe
modrinth.category.paper=Paper
modrinth.category.path-tracing=路徑追蹤
modrinth.category.pbr=PBR
modrinth.category.potato=極低
modrinth.category.purpur=Purpur
modrinth.category.quests=任務
modrinth.category.quilt=Quilt
modrinth.category.realistic=寫實
modrinth.category.reflections=反射
modrinth.category.rift=Rift
modrinth.category.screenshot=極高
modrinth.category.semi-realistic=半寫實
modrinth.category.shadows=陰影
modrinth.category.simplistic=簡單
modrinth.category.social=社交
modrinth.category.spigot=Spigot
@@ -795,12 +826,11 @@ modrinth.category.themed=主題
modrinth.category.transportation=運輸
modrinth.category.tweaks=最佳化
modrinth.category.utility=實用
modrinth.category.vanilla=原生
modrinth.category.vanilla-like=類原生
modrinth.category.velocity=Velocity
modrinth.category.waterfall=Waterfall
modrinth.category.worldgen=世界生成
modrinth.category.datapack=資料包
modrinth.category.folia=Folia
mods=模組
mods.add=新增模組
@@ -861,22 +891,25 @@ datapack.choose_datapack=選取要匯入的資料包壓縮檔
datapack.extension=資料包
datapack.title=世界 [%s] - 資料包
web.failed=加載頁面失敗
web.failed=載入頁面失敗
web.open_in_browser=是否要在瀏覽器中開啟此連結:\n%s
web.view_in_browser=在瀏覽器中查看
web.view_in_browser=在瀏覽器中查看完整日誌
world=世界
world.add=新增世界
world.backup=世界備份
world.backup.create.new_one=建立新備份
world.backup.create.failed=創建備份失敗。\n%s
world.backup.create.failed=建立備份失敗。\n%s
world.backup.create.locked=該世界正在使用中,請關閉遊戲後重試。
world.backup.create.success=成功創建新備份:%s
world.backup.delete=删除此備份
world.backup.create.success=成功建立新備份:%s
world.backup.delete=刪除此備份
world.backup.processing=正在備份中……
world.backup.title=世界 [%s] - 備份
world.datapack=管理資料包
world.datapack.1_13=僅 Minecraft 1.13 及之後的版本支援資料包
world.chunkbase=世界地圖
world.chunkbase.end_city=終界城地圖
world.chunkbase.seed_map=種子地圖
world.chunkbase.stronghold=要塞地圖
world.chunkbase.nether_fortress=地獄要塞地圖
world.datapack=資料包管理
world.datetime=上一次遊戲時間: %s
world.download=下載世界
world.download.title=世界下載 - %1s
@@ -890,7 +923,6 @@ world.import.choose=選取要匯入的存檔壓縮檔
world.import.failed=無法匯入此世界: %s
world.import.invalid=無法識別的存檔壓縮檔
world.info=世界資訊
world.info.title=世界 [%s] - 世界資訊
world.info.basic=基本資訊
world.info.allow_cheats=允許指令(作弊)
world.info.dimension.the_nether=地獄
@@ -909,6 +941,7 @@ world.info.player.food_level=饑餓值
world.info.player.game_type=遊戲模式
world.info.player.game_type.adventure=冒險
world.info.player.game_type.creative=創造
world.info.player.game_type.hardcore=極限
world.info.player.game_type.spectator=旁觀者
world.info.player.game_type.survival=生存
world.info.player.health=生命值
@@ -921,7 +954,9 @@ world.info.time=遊戲內時間
world.info.time.format=%s 天
world.locked=使用中
world.game_version=遊戲版本
world.manage=世界/資料包
world.manage=世界管理
world.manage.button=世界管理
world.manage.title=世界管理 - %s
world.name=世界名稱
world.name.enter=輸入世界名稱
world.show_all=全部顯示
@@ -959,7 +994,7 @@ schematics.create_directory.prompt=請輸入新目錄名稱
schematics.create_directory.failed=建立目錄失敗
schematics.create_directory.failed.already_exists=目錄已存在
schematics.create_directory.failed.empty_name=名稱不能為空
schematics.create_directory.failed.invalid_name=名稱中包含非法字元
schematics.create_directory.failed.invalid_name=名稱中包含無效字元
schematics.info.description=描述
schematics.info.enclosing_size=框大小
schematics.info.name=名稱
@@ -1007,7 +1042,7 @@ settings.advanced.custom_commands.hint=自訂指令被呼叫時將包含如下
\ · $INST_FABRIC: 若安裝了 Fabric,將會存在本環境變數;\n\
\ · $INST_QUILT: 若安裝了 Quilt,將會存在本環境變數。
settings.advanced.dont_check_game_completeness=不檢查遊戲完整性
settings.advanced.dont_check_jvm_validity=不檢查 JVM 與遊戲的相容性
settings.advanced.dont_check_jvm_validity=不檢查 Java 虛擬機與遊戲的相容性
settings.advanced.dont_patch_natives=不嘗試自動取代本機庫
settings.advanced.environment_variables=環境變數
settings.advanced.game_dir.default=預設 (".minecraft/")
@@ -1017,8 +1052,8 @@ settings.advanced.java_permanent_generation_space.prompt=單位 MiB
settings.advanced.jvm=Java 虛擬機設定
settings.advanced.jvm_args=Java 虛擬機參數
settings.advanced.jvm_args.prompt=\ · 若在「Java 虛擬機參數」中輸入的參數與預設參數相同,則不會新增;\n\
\ · 若在「Java 虛擬機參數」輸入任何 GC 參數,預設參數的 G1 參數會被禁用;\n\
\ · 啟用下方「不新增預設的 JVM 參數」可在啟動遊戲時不新增預設參數。
\ · 若在「Java 虛擬機參數」輸入任何 GC 參數,預設參數的 G1 參數會被停用;\n\
\ · 啟用下方「不新增預設的 Java 虛擬機參數」可在啟動遊戲時不新增預設參數。
settings.advanced.launcher_visibility.close=遊戲啟動後結束啟動器
settings.advanced.launcher_visibility.hide=遊戲啟動後隱藏啟動器
settings.advanced.launcher_visibility.hide_and_reopen=隱藏啟動器並在遊戲結束後重新開啟
@@ -1030,8 +1065,9 @@ settings.advanced.natives_directory=本機庫路徑 (LWJGL)
settings.advanced.natives_directory.choose=選取本機庫路徑
settings.advanced.natives_directory.custom=自訂 (由你提供遊戲需要的本機庫)
settings.advanced.natives_directory.default=預設 (由啟動器提供遊戲本機庫)
settings.advanced.natives_directory.hint=本選項提供給 Apple M1 等未受遊戲官方支援的平台來自訂遊戲本機庫。如果你不知道本選項的含義,請你不要修改本選項,否則會導致遊戲無法啟動。\n\n如果你要修改本選項,你需要保證自訂目錄下有遊戲所需的本機庫檔案,如 lwjgl.dll (liblwjgl.so), openal.dll (libopenal.so) 等檔案。啟動器不會幫你補全缺少的本機庫檔案。\n\n注意:建議指定的本機庫檔案路徑使用全英文字元,否則可能導致遊戲啟動失敗。
settings.advanced.no_jvm_args=不新增預設的 JVM 參數
settings.advanced.natives_directory.default.version_id=<版本 ID>
settings.advanced.natives_directory.hint=本選項提供給 Apple Silicon 等未受遊戲官方支援的平台來自訂遊戲本機庫。如果你不知道本選項的含義,請你不要修改本選項,否則會導致遊戲無法啟動。\n\n如果你要修改本選項,你需要保證自訂目錄下有遊戲所需的本機庫檔案,如 lwjgl.dll (liblwjgl.so), openal.dll (libopenal.so) 等檔案。啟動器不會幫你補全缺少的本機庫檔案。\n\n注意:建議指定的本機庫檔案路徑使用全英文字元,否則可能導致遊戲啟動失敗。
settings.advanced.no_jvm_args=不新增預設的 Java 虛擬機參數
settings.advanced.precall_command=遊戲啟動前執行指令
settings.advanced.precall_command.prompt=將在遊戲啟動前呼叫使用
settings.advanced.process_priority=處理程式優先度
@@ -1078,7 +1114,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=遊戲圖示
@@ -1124,6 +1160,7 @@ settings.memory.allocate.manual=遊戲分配 %1$.1f GiB
settings.memory.allocate.manual.exceeded=遊戲分配 %1$.1f GiB (%3$.1f GiB 可用)
settings.memory.auto_allocate=自動分配
settings.memory.lower_bound=最低分配
settings.memory.unit.mib=MiB
settings.memory.used_per_total=已使用 %1$.1f GiB / 總記憶體 %2$.1f GiB
settings.physical_memory=實體記憶體大小
settings.show_log=查看日誌
@@ -181,7 +181,7 @@ archive.version=版本
assets.download=下载资源
assets.download_all=检查资源文件完整性
assets.index.malformed=资源文件的索引文件损坏。你可以在相应版本的“版本管理”页面中,点击左下角“管理 → 更新游戏资源文件”以修复该问题。\n你可以点击右上角帮助按钮进行求助。
assets.index.malformed=资源文件的索引文件损坏。你可以在相应实例的“实例管理”页面中,点击左下角“管理 → 更新游戏资源文件”以修复该问题。\n你可以点击右上角帮助按钮进行求助。
button.cancel=取消
button.change_source=切换下载源
@@ -342,6 +342,7 @@ download=下载
download.hint=安装游戏和整合包或下载模组、资源包和地图
download.code.404=远程服务器不包含需要下载的文件: %s\n你可以点击右上角帮助按钮进行求助。
download.content=游戏内容
download.shader=光影
download.curseforge.unavailable=此 HMCL 版本不支持访问 CurseForge。请使用官方版本进行下载。
download.existing=文件已存在,无法保存。你可以将文件保存至其他地方。
download.external_link=打开下载网站
@@ -368,7 +369,7 @@ exception.access_denied=无法访问文件“%s”。HMCL 没有对该文件的
对于 Windows 用户,你还可以尝试通过资源监视器查看是否有程序占用了该文件。如果是,请关闭占用该文件的程序,或者重启电脑再试。\n\
如遇到问题,你可以点击右上角帮助按钮进行求助。
exception.artifact_malformed=下载的文件无法通过校验。\n你可以点击右上角帮助按钮进行求助。
exception.ssl_handshake=无法建立 SSL 连接。当前 Java 虚拟机缺少相关的 SSL 证书。你可以尝试使用其他 Java 虚拟机启动 HMCL 再试。\n你可以点击右上角帮助按钮进行求助。
exception.ssl_handshake=无法建立 SSL 连接。当前 Java 缺少相关的 SSL 证书。你可以尝试使用其他 Java 启动 HMCL 再试。\n你可以点击右上角帮助按钮进行求助。
extension.bat=Windows 脚本
extension.mod=模组文件
@@ -384,15 +385,17 @@ fatal.config_change_owner_root=你正在使用 root 账户启动 Hello Minecraft
fatal.config_in_temp_dir=你正在临时文件夹中启动 Hello Minecraft! Launcher,你的设置和游戏数据可能会丢失。建议将 HMCL 移动至其他位置再启动。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。\n是否继续启动?
fatal.config_loading_failure=Hello Minecraft! Launcher 无法加载配置文件。\n请确保 HMCL 对“%s”文件夹及该文件夹下的文件拥有读写权限。\n对于 macOS,尝试将 HMCL 放在除“桌面”“下载”“文稿”之外的有权限的地方再试。\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_unsupported_version=当前配置文件是由更高版本的 Hello Minecraft! Launcher 创建的,当前版本的 HMCL 无法正常加载。请更新并重启 HMCL。\n在更新启动器之前,你修改的所有设置都不会被保存。\n如果遇到问题,你可以点击右上角帮助按钮进行求助。
fatal.mac_app_translocation=由于 macOS 的安全机制,Hello Minecraft! Launcher 被系统隔离至临时文件夹中。\n请将 HMCL 移动到其他文件夹后再尝试启动,否则你的设置和游戏数据可能会在重启后丢失。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。\n是否继续启动?
fatal.migration_requires_manual_reboot=Hello Minecraft! Launcher 即将完成升级,请重新打开 HMCL。\n如遇到问题,你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
fatal.apply_update_failure=我们很抱歉 Hello Minecraft! Launcher 无法自动完成升级,因为出现了一些问题。\n但你依可以从 %s 手动下载 HMCL 来完成升级。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
fatal.apply_update_need_win7=Hello Minecraft! Launcher 无法在 Windows XP/Vista 上进行自动更新。请从 %s 手动下载 HMCL 来完成升级。
fatal.deprecated_java_version=HMCL 未来需要 Java 11 或更高版本才能运行,但依然支持使用 Java 8 启动游戏。建议安装最新版本的 Java 以确保 HMCL 能正常工作。\n你可以继续保留旧版本 Java。HMCL 能够识别与管理多个 Java,并会自动根据游戏版本为你选择合适的 Java。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
fatal.deprecated_java_version=HMCL 未来需要 Java 17 或更高版本才能运行,但依然支持使用 Java 6~16 启动游戏。建议安装最新版本的 Java 以确保 HMCL 能正常工作。\n你可以继续保留旧版本 Java。HMCL 能够识别与管理多个 Java,并会自动根据游戏版本为你选择合适的 Java。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
fatal.deprecated_java_version.update=更高版本的 HMCL 需要 Java 17 或更高版本才能运行,请安装最新版本的 Java 以确保 HMCL 能够完成升级。\n你可以继续保留旧版本 Java。HMCL 能够识别与管理多个 Java,并会自动根据游戏版本为你选择合适的 Java。
fatal.deprecated_java_version.download_link=下载 Java %d
fatal.samba=如果你正在通过 Samba 共享的文件夹中运行 Hello Minecraft! Launcher,启动器可能无法正常工作。请尝试更新你的 Java 或在本地文件夹内运行 HMCL。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
fatal.illegal_char=由于你的用户文件夹路径中存在非法字符“=”,你将无法使用外置登录账户以及离线登录更换皮肤功能。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
fatal.unsupported_platform=Minecraft 尚未对你的平台提供完善支持,所以可能影响游戏体验或无法启动游戏。\n若无法启动 Minecraft 1.17 及更高版本,可以尝试在“(全局/版本特定) 游戏设置 → 高级设置 → 调试选项”中将“渲染器”切换为“软渲染器”,以获得更好的兼容性。\n如遇到问题,你可以点击右上角帮助按钮进行求助。
fatal.unsupported_platform=Minecraft 尚未对你的平台提供完善支持,所以可能影响游戏体验或无法启动游戏。\n若无法启动 Minecraft 1.17 及更高版本,可以尝试在“(全局/实例特定) 游戏设置 → 高级设置 → 调试选项”中将“渲染器”切换为“软渲染器”,以获得更好的兼容性。\n如遇到问题,你可以点击右上角帮助按钮进行求助。
fatal.unsupported_platform.loongarch=Hello Minecraft! Launcher 已为龙芯提供支持。\n如果遇到问题,你可以点击右上角帮助按钮进行求助。
fatal.unsupported_platform.macos_arm64=Hello Minecraft! Launcher 已为 Apple Silicon 平台提供支持。使用 ARM 原生 Java 启动游戏以获得更流畅的游戏体验。\n如果你在游戏中遇到问题,使用 x86-64 架构的 Java 启动游戏可能有更好的兼容性。\n如遇到问题,你可以点击右上角帮助按钮进行求助。
fatal.unsupported_platform.windows_arm64=Hello Minecraft! Launcher 已为 Windows on Arm 平台提供原生支持。如果你在游戏中遇到问题,请尝试使用 x86 架构的 Java 启动游戏。\n如果你正在使用 <b>高通</b> 平台,你可能需要安装 <a href="ms-windows-store://pdp/?productid=9NQPSL29BFFF">OpenGL 兼容包</a> 后才能进行游戏。点击链接前往 Microsoft Store 安装兼容包。\n如遇到问题,你可以点击右上角帮助按钮进行求助。
@@ -409,7 +412,7 @@ feedback.qq_group.statement=欢迎加入 HMCL 用户群,加入后请遵守群
file=文件
folder.config=配置文件夹
folder.game=游戏文件夹
folder.game=实例运行文件夹
folder.logs=日志文件夹
folder.mod=模组文件夹
folder.resourcepacks=资源包文件夹
@@ -417,6 +420,7 @@ folder.shaderpacks=光影包文件夹
folder.saves=存档文件夹
folder.schematics=原理图文件夹
folder.screenshots=截图文件夹
folder.world=世界文件夹
game=游戏
game.crash.feedback=<b>请不要将本界面截图给他人!</b>如果你要向他人求助,请你点击左下角<b>“导出游戏崩溃信息”</b>后将导出的文件发送给他人以供分析。\n你可以点击下方的<b>“帮助”</b>前往交流群寻求帮助。
@@ -444,12 +448,12 @@ game.crash.reason.graphics_driver=当前游戏由于显卡驱动问题而崩溃
game.crash.reason.macos_failed_to_find_service_port_for_display=当前游戏由于 Apple Silicon 平台下初始化 OpenGL 窗口失败,无法继续运行。\n对于该问题,HMCL 暂无直接性的解决方案。请你尝试任意打开一个浏览器并全屏,然后再回到 HMCL 启动游戏。在弹出游戏窗口前<b>迅速切回浏览器页面</b>,等待游戏窗口出现后再切回游戏窗口。
game.crash.reason.illegal_access_error=当前游戏由于某些模组的问题,无法继续运行。\n如果你认识“%1$s”,你可以更新或删除对应模组再试。
game.crash.reason.install_mixinbootstrap=当前游戏由于缺失 MixinBootstrap,无法继续运行。\n你可以尝试安装 <a href="https://www.curseforge.com/minecraft/mc-mods/mixinbootstrap">MixinBootstrap</a> 解决该问题。若安装后崩溃,可以尝试在该模组的文件名开头添加半角感叹号 (!) 解决。
game.crash.reason.need_jdk11=当前游戏由于 Java 虚拟机版本不合适,无法继续运行。\n你需要下载安装 <a href="https://bell-sw.com/pages/downloads/#downloads">Java 11</a>,并在“(全局/版本特定) 游戏设置 → 游戏 Java”中将 Java 设置为 11.x 的版本。
game.crash.reason.jdk_9=当前游戏由于 Java 版本过高,无法继续运行。\n你需要下载安装 <a href="https://bell-sw.com/pages/downloads/#downloads">Java 8</a>,并在“(全局/版本特定) 游戏设置 → 游戏 Java”中将 Java 设置为 1.8.x 的版本。
game.crash.reason.jvm_32bit=当前游戏由于内存分配过大,超过了 32 位 Java 内存限制,无法继续运行。\n如果你的电脑是 64 位系统,请下载安装并更换 64 位 Java。<a href="https://bell-sw.com/pages/downloads/#downloads">下载 Java</a>\n如果你的电脑是 32 位系统,你或许可以重新安装 64 位系统,或换一台新电脑。\n或者,你可以在“(全局/版本特定) 游戏设置 → 游戏内存”中关闭“自动分配内存”,并把内存分配值调整为 1024 MiB 或以下。
game.crash.reason.need_jdk11=当前游戏由于 Java 版本不合适,无法继续运行。\n你需要下载安装 <a href="https://bell-sw.com/pages/downloads/#downloads">Java 11</a>,并在“(全局/实例特定) 游戏设置 → 游戏 Java”中将 Java 设置为 11.x 的版本。
game.crash.reason.jdk_9=当前游戏由于 Java 版本过高,无法继续运行。\n你需要下载安装 <a href="https://bell-sw.com/pages/downloads/#downloads">Java 8</a>,并在“(全局/实例特定) 游戏设置 → 游戏 Java”中将 Java 设置为 1.8.x 的版本。
game.crash.reason.jvm_32bit=当前游戏由于内存分配过大,超过了 32 位 Java 虚拟机内存限制,无法继续运行。\n如果你的电脑是 64 位系统,请下载安装并更换 64 位 Java。<a href="https://bell-sw.com/pages/downloads/#downloads">下载 Java</a>\n如果你的电脑是 32 位系统,你或许可以重新安装 64 位系统,或换一台新电脑。\n或者,你可以在“(全局/实例特定) 游戏设置 → 游戏内存”中关闭“自动分配内存”,并把内存分配值调整为 1024 MiB 或以下。
game.crash.reason.loading_crashed_forge=当前游戏由于模组“%1$s (%2$s)”错误,无法继续运行。\n你可以尝试删除或更新该模组以解决问题。
game.crash.reason.loading_crashed_fabric=当前游戏由于模组“%1$s”错误,无法继续运行。\n你可以尝试删除或更新该模组以解决问题。
game.crash.reason.memory_exceeded=当前游戏由于内存分配过大,无法继续运行。\n该问题是由于系统页面文件太小导致的。\n你需要在“(全局/版本特定) 游戏设置”中关闭“自动分配内存”,并将内存分配值调低至游戏能正常启动为止。\n你还可以尝试将虚拟内存设置调整为“自动管理所有驱动器分页文件大小”,<a href="https://docs.hmcl.net/assets/img/hmcl/自动管理所有驱动器分页文件大小.webp">详情</a>。
game.crash.reason.memory_exceeded=当前游戏由于内存分配过大,无法继续运行。\n该问题是由于系统页面文件太小导致的。\n你需要在“(全局/实例特定) 游戏设置”中关闭“自动分配内存”,并将内存分配值调低至游戏能正常启动为止。\n你还可以尝试将虚拟内存设置调整为“自动管理所有驱动器分页文件大小”,<a href="https://docs.hmcl.net/assets/img/hmcl/自动管理所有驱动器分页文件大小.webp">详情</a>。
game.crash.reason.mac_jdk_8u261=当前游戏由于你所使用的 Forge/OptiFine 与 Java 冲突而崩溃。\n请尝试更新 Forge/OptiFine,或使用 Java 8u251 及更早版本启动。
game.crash.reason.mod=当前游戏由于“%1$s”的问题,无法继续运行。\n你可以更新或删除已经安装的“%1$s”再试。
game.crash.reason.mod_resolution=当前游戏由于模组依赖问题,无法继续运行。Fabric 提供了如下信息:\n%1$s
@@ -459,37 +463,37 @@ game.crash.reason.mod_resolution_missing=当前游戏由于缺少前置模组,
game.crash.reason.mod_resolution_missing_minecraft=当前游戏由于模组和 Minecraft 游戏版本不匹配,无法继续运行。\n“%1$s”需要 Minecraft %2$s 才能运行。\n如果你要继续使用你已经安装的模组,你可以选择安装对应的 Minecraft 版本。如果你要继续使用当前 Minecraft 版本,你需要安装对应版本的模组。
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/HMCL-dev/HMCL/issues/1880">此为已知问题</a>\n建议将日志上传并反馈至 GitHub,以便我们找到更多线索并修复此问题。\n目前你可以在“版本管理 → 自动安装”中卸载 Forge 并重新安装。
game.crash.reason.optifine_repeat_installation=当前游戏由于 OptiFine 重复安装,无法继续运行。\n请删除模组文件夹下的 OptiFine 或前往“版本管理 → 自动安装”卸载安装的 OptiFine。
game.crash.reason.forge_repeat_installation=当前游戏由于 Forge 重复安装,无法继续运行。<a href="https://github.com/HMCL-dev/HMCL/issues/1880">此为已知问题</a>\n建议将日志上传并反馈至 GitHub,以便我们找到更多线索并修复此问题。\n目前你可以在“实例管理 → 自动安装”中卸载 Forge 并重新安装。
game.crash.reason.optifine_repeat_installation=当前游戏由于 OptiFine 重复安装,无法继续运行。\n请删除模组文件夹下的 OptiFine 或前往“实例管理 → 自动安装”卸载安装的 OptiFine。
game.crash.reason.forgemod_resolution=当前游戏由于模组依赖问题,无法继续运行。Forge/NeoForge 提供了如下信息:\n%1$s
game.crash.reason.forge_found_duplicate_mods=当前游戏由于模组重复安装,无法继续运行。Forge/NeoForge 提供了如下信息:\n%1$s
game.crash.reason.modmixin_failure=当前游戏由于某些模组注入失败,无法继续运行。\n这一般代表着该模组存在问题,或与当前环境不兼容。\n你可以查看日志寻找出错模组。
game.crash.reason.night_config_fixes=当前游戏由于 Night Config 库的一些问题,无法继续运行。\n你可以尝试安装 <a href="https://www.curseforge.com/minecraft/mc-mods/night-config-fixes">Night Config Fixes</a> 模组,这或许能帮助你解决这个问题。\n更多详情,可访问该模组的 <a href="https://github.com/Fuzss/nightconfigfixes">GitHub 仓库</a>。
game.crash.reason.forge_error=Forge/NeoForge 可能已经提供了错误信息。\n你可以查看日志,并根据错误报告中的日志信息进行对应处理。\n如果没有看到报错信息,可以查看错误报告了解错误具体是如何发生的。\n%1$s
game.crash.reason.mod_resolution0=当前游戏由于一些模组出现问题,无法继续运行。\n你可以查看日志寻找出错模组。
game.crash.reason.java_version_is_too_high=当前游戏由于 Java 虚拟机版本过高,无法继续运行。\n请在“(全局/版本特定) 游戏设置 → 游戏 Java”中改用较低版本的 Java,然后再启动游戏。\n如果没有,可以从 <a href="https://www.java.com/download/">java.com (Java 8)</a> 或 <a href="https://bell-sw.com/pages/downloads/#downloads">BellSoft Liberica Full JRE (Java 17)</a> 等平台下载、安装一个 (安装完后需重启启动器)。
game.crash.reason.java_version_is_too_high=当前游戏由于 Java 版本过高,无法继续运行。\n请在“(全局/实例特定) 游戏设置 → 游戏 Java”中改用较低版本的 Java,然后再启动游戏。\n如果没有,可以从 <a href="https://www.java.com/download/">java.com (Java 8)</a> 或 <a href="https://bell-sw.com/pages/downloads/#downloads">BellSoft Liberica Full JRE (Java 17)</a> 等平台下载、安装一个 (安装完后需重启启动器)。
game.crash.reason.mod_name=当前游戏由于模组文件名称问题,无法继续运行。\n模组文件名称应只使用半角的大小写字母 (Aa~Zz)、数字 (0~9)、横线 (-)、下划线 (_)和点 (.)。\n请到模组文件夹中将所有不合规的模组文件名修改为上述合规字符。
game.crash.reason.incomplete_forge_installation=当前游戏由于 Forge/NeoForge 安装不完整,无法继续运行。\n请在“版本管理 → 自动安装”中卸载 Forge 并重新安装。
game.crash.reason.optifine_is_not_compatible_with_forge=当前游戏由于 OptiFine 与当前版本的 Forge 不兼容,导致游戏崩溃。\n点击 <a href="https://zkitefly.github.io/optifine-forge-support-list">此处</a> 查看 OptiFine 所兼容的 Forge 版本,并严格按照对应版本重新安装游戏或在“版本管理 → 自动安装”中更换版本。\n经测试,Forge 版本过高或过低都可能导致崩溃。
game.crash.reason.incomplete_forge_installation=当前游戏由于 Forge/NeoForge 安装不完整,无法继续运行。\n请在“实例管理 → 自动安装”中卸载 Forge 并重新安装。
game.crash.reason.optifine_is_not_compatible_with_forge=当前游戏由于 OptiFine 与当前版本的 Forge 不兼容,导致游戏崩溃。\n点击 <a href="https://zkitefly.github.io/optifine-forge-support-list">此处</a> 查看 OptiFine 所兼容的 Forge 版本,并严格按照对应版本重新安装游戏或在“实例管理 → 自动安装”中更换版本。\n经测试,Forge 版本过高或过低都可能导致崩溃。
game.crash.reason.mod_files_are_decompressed=当前游戏由于模组文件被解压了,无法继续运行。\n请直接把整个模组文件放进模组文件夹中即可。\n解压模组文件会导致游戏出错。请删除模组文件夹中已被解压的模组,然后再启动游戏。
game.crash.reason.shaders_mod=当前游戏由于同时安装了 OptiFine 和 Shaders 模组,无法继续运行。\n由于 OptiFine 已集成 Shaders 模组的功能,所以只需删除 Shaders 模组即可。
game.crash.reason.rtss_forest_sodium=当前游戏由于 RivaTuner Statistics Server (RTSS) 与 Sodium 不兼容,导致游戏崩溃。\n点击 <a href="https://github.com/CaffeineMC/sodium-fabric/wiki/Known-Issues#rtss-incompatible">此处</a> 查看详情。
game.crash.reason.too_many_mods_lead_to_exceeding_the_id_limit=当前游戏由于你所安装的模组过多,超出了游戏的 ID 限制,无法继续运行。\n请尝试安装 <a href="https://www.curseforge.com/minecraft/mc-mods/jeid">JEID</a> 等修复模组,或删除部分大型模组。
game.crash.reason.optifine_causes_the_world_to_fail_to_load=当前游戏可能由于 OptiFine 而无法继续运行。\n该问题只在特定 OptiFine 版本中出现,你可以尝试在“版本管理 → 自动安装”中更换 OptiFine 的版本。
game.crash.reason.optifine_causes_the_world_to_fail_to_load=当前游戏可能由于 OptiFine 而无法继续运行。\n该问题只在特定 OptiFine 版本中出现,你可以尝试在“实例管理 → 自动安装”中更换 OptiFine 的版本。
game.crash.reason.modlauncher_8=当前游戏由于你所使用的 Forge 版本与当前使用的 Java 冲突而崩溃。请尝试更新 Forge 到 36.2.26 或更高版本或换用版本低于 1.8.0.320 的 Java。<a href="https://bell-sw.com/pages/downloads/?version=java-8&package=jre-full">Liberica JDK 8u312+7</a>
game.crash.reason.no_class_def_found_error=当前游戏由于代码不完整,无法继续运行。\n你的游戏可能缺失了某个模组,或者某些模组文件不完整,或者模组与游戏的版本不匹配。\n你可能需要重新安装游戏和模组,或寻求他人帮助。\n缺失:\n%1$s
game.crash.reason.no_such_method_error=当前游戏由于代码不完整,无法继续运行。\n你的游戏可能缺失了某个模组,或者某些模组文件不完整,或者模组与游戏的版本不匹配。\n你可能需要重新安装游戏和模组,或寻求他人帮助。
game.crash.reason.opengl_not_supported=当前游戏由于你的显卡驱动存在问题,无法继续运行。\n原因是 OpenGL 不受支持,你现在是否在远程桌面或者串流模式下?如果是,请直接使用原电脑启动游戏。\n或者尝试升级你的显卡驱动到最新版本后再尝试启动游戏。如果你的电脑同时存在独立显卡和集成显卡,你需要检查游戏是否使用集成显卡启动。如果是,请尝试使用独立显卡启动 HMCL 与游戏。如果仍有问题,你可能需要考虑换一个新显卡或新电脑。
game.crash.reason.openj9=当前游戏无法在 OpenJ9 虚拟机上运行。请你在“(全局/版本特定) 游戏设置 → 游戏 Java”中更换 Hotspot Java 虚拟机,并重新启动游戏。如果没有下载安装,你可以在网上自行下载。
game.crash.reason.out_of_memory=当前游戏由于内存不足,无法继续运行。\n这可能是内存分配过小,或者模组数量过多导致的。\n你可以在“(全局/版本特定) 游戏设置 → 游戏内存”中调整内存分配值以允许游戏在更大内存下运行。\n如果仍然出现该错误,你可能需要换一台更好的电脑。
game.crash.reason.openj9=当前游戏无法在 OpenJ9 虚拟机上运行。请你在“(全局/实例特定) 游戏设置 → 游戏 Java”中更换为使用 Hotspot 虚拟机的 Java,并重新启动游戏。如果没有下载安装,你可以在网上自行下载。
game.crash.reason.out_of_memory=当前游戏由于内存不足,无法继续运行。\n这可能是内存分配过小,或者模组数量过多导致的。\n你可以在“(全局/实例特定) 游戏设置 → 游戏内存”中调整内存分配值以允许游戏在更大内存下运行。\n如果仍然出现该错误,你可能需要换一台更好的电脑。
game.crash.reason.resolution_too_high=当前游戏由于资源包/纹理包分辨率过高,无法继续运行。\n你可以更换一个分辨率更低的资源包/纹理包,或者更换一个显存更大的显卡。
game.crash.reason.stacktrace=原因未知。请点击日志按钮查看详细信息。\n下面是一些关键词,其中可能包含模组名称,你可以通过搜索的方式查找有关信息。\n%s
game.crash.reason.too_old_java=当前游戏由于 Java 虚拟机版本过低,无法继续运行。\n你需要在“(全局/版本特定) 游戏设置 → 游戏 Java”中更换 Java %1$s 或更新版本的 Java 虚拟机,并重新启动游戏。如果没有下载安装,你可以点击 <a href="https://learn.microsoft.com/java/openjdk/download">此处</a> 下载 Microsoft JDK。
game.crash.reason.too_old_java=当前游戏由于 Java 版本过低,无法继续运行。\n你需要在“(全局/实例特定) 游戏设置 → 游戏 Java”中更换 Java %1$s 或更新版本的 Java,并重新启动游戏。如果没有下载安装,你可以点击 <a href="https://learn.microsoft.com/java/openjdk/download">此处</a> 下载 Microsoft JDK。
game.crash.reason.unknown=原因未知。请点击日志按钮查看详细信息。
game.crash.reason.unsatisfied_link_error=当前游戏由于缺少本地库,无法继续运行。\n这些本地库缺失:%1$s。\n如果你在“(全局/版本特定) 游戏设置 → 高级设置”中修改了本地库路径选项,请你修改回默认模式。\n如果你正在使用默认模式,请检查游戏文件夹路径是否只包含英文字母、数字和下划线。\n如果是,那么请检查是否为模组或 HMCL 导致了本地库缺失的问题。如果你确定是 HMCL 引起的,建议你向我们反馈。\n<b>对于 Windows 用户,你还可以尝试在“控制面板 → 时钟和区域 → 区域 → 管理 → 更改系统区域设置”中将“当前系统区域设置”修改为“中文(简体,中国大陆)”,并关闭“Beta 版:使用 Unicode UTF-8 提供全球语言支持”选项;</b>\n<b>或将游戏文件夹路径中的所有非英文字符的名称 (例如中文、空格等) 修改为英文字符。</b>\n如果你确实需要自定义本地库路径,你需要保证其中包含缺失的本地库!
game.crash.reason.unsatisfied_link_error=当前游戏由于缺少本地库,无法继续运行。\n这些本地库缺失:%1$s。\n如果你在“(全局/实例特定) 游戏设置 → 高级设置”中修改了本地库路径选项,请你修改回默认模式。\n如果你正在使用默认模式,请检查游戏文件夹路径是否只包含英文字母、数字和下划线。\n如果是,那么请检查是否为模组或 HMCL 导致了本地库缺失的问题。如果你确定是 HMCL 引起的,建议你向我们反馈。\n<b>对于 Windows 用户,你还可以尝试在“控制面板 → 时钟和区域 → 区域 → 管理 → 更改系统区域设置”中将“当前系统区域设置”修改为“中文(简体,中国大陆)”,并关闭“Beta 版:使用 Unicode UTF-8 提供全球语言支持”选项;</b>\n<b>或将游戏文件夹路径中的所有非英文字符的名称 (例如中文、空格等) 修改为英文字符。</b>\n如果你确实需要自定义本地库路径,你需要保证其中包含缺失的本地库!
game.crash.title=游戏意外退出
game.directory=游戏文件夹路径
game.version=游戏版本
game.version=游戏实例
help=帮助
help.doc=Hello Minecraft! Launcher 帮助文档
@@ -500,7 +504,7 @@ input.number=必须是数字
input.not_empty=必填项
input.url=必须是合法的链接
install=添加游戏
install=添加实例
install.change_version=更换版本
install.change_version.confirm=你确定要将 %s 从 %s 更换成 %s 吗?
install.change_version.process=更换版本
@@ -542,8 +546,8 @@ install.modpack=安装整合包
install.modpack.installation=安装整合包
install.name.invalid=名称中包含非 ASCII 字符(如 Emoji 表情或中文字符)。\n建议修改名称。名称建议仅包含英文字母、数字和下划线,以防启动游戏时出现问题。是否继续安装?
install.new_game=安装新游戏
install.new_game.already_exists=此版本已经存在,请换一个名字
install.new_game.current_game_version=当前游戏版本
install.new_game.already_exists=此实例已经存在,请换一个名字
install.new_game.current_game_version=当前游戏实例
install.new_game.installation=安装新游戏
install.new_game.malformed=名字不合法
install.select=请选择安装方式
@@ -585,10 +589,10 @@ lang.default=跟随系统语言
launch.advice=%s 是否继续启动?
launch.advice.multi=检测到以下问题:\n\n%s\n\n这些问题可能导致游戏无法正常启动或影响游戏体验,是否继续启动?\n你可以点击右上角帮助按钮进行求助。
launch.advice.java.auto=当前选择的 Java 虚拟机版本不满足游戏要求。\n点击“是”即可由 HMCL 来自动选取合适的 Java 虚拟机版本。\n或者你可以在“(全局/版本特定) 游戏设置 → 游戏 Java”中选择一个合适的 Java 虚拟机版本。
launch.advice.java.auto=当前选择的 Java 版本不满足游戏要求。\n点击“是”即可由 HMCL 来自动选取合适的 Java 版本。\n或者你可以在“(全局/实例特定) 游戏设置 → 游戏 Java”中选择一个合适的 Java 版本。
launch.advice.java.modded_java_7=Minecraft 1.7.2 及更低版本需要 Java 7 及更低版本。
launch.advice.corrected=我们已经修复了 Java 虚拟机版本问题。如果你确实希望使用你自定义的 Java 虚拟机,你可以在“(全局/版本特定) 游戏设置 → 高级设置”中往下滑,启用“不检查 JVM 与游戏的兼容性”。
launch.advice.uncorrected=如果你确实希望使用你自定义的 Java 虚拟机,你可以在“(全局/版本特定) 游戏设置 → 高级设置”中往下滑,启用“不检查 JVM 与游戏的兼容性”。
launch.advice.corrected=我们已经修复了 Java 版本问题。如果你确实希望使用你自定义的 Java,你可以在“(全局/实例特定) 游戏设置 → 高级设置”中往下滑,启用“不检查 Java 虚拟机与游戏的兼容性”。
launch.advice.uncorrected=如果你确实希望使用你自定义的 Java,你可以在“(全局/实例特定) 游戏设置 → 高级设置”中往下滑,启用“不检查 Java 虚拟机与游戏的兼容性”。
launch.advice.different_platform=你正在使用 32 位 Java 启动游戏。建议更换至 64 位 Java。
launch.advice.forge2760_liteloader=Forge 14.23.5.2760 与 LiteLoader 不兼容。请更新 Forge 至 14.23.5.2773 或更高版本。
launch.advice.forge28_2_2_optifine=Forge 28.2.2 及更高版本与 OptiFine 不兼容。请降级 Forge 至 28.2.1 或更低版本。
@@ -606,7 +610,7 @@ launch.advice.vanilla_linux_java_8=对于 Linux x86-64 平台,Minecraft 1.12.2
launch.advice.vanilla_x86.translation=Minecraft 尚未为你的平台提供完善支持,所以可能影响游戏体验或无法启动游戏。\n你可以在 <a href="https://learn.microsoft.com/java/openjdk/download">这里</a> 下载 <b>x86-64</b> 架构的 Java 以获得更完整的体验。
launch.advice.unknown=由于以下原因,无法继续启动游戏:
launch.failed=启动失败
launch.failed.cannot_create_jvm=无法创建 Java 虚拟机。可能是 Java 参数有问题,你可以在“(全局/版本特定) 游戏设置 → 高级设置 → Java 虚拟机设置”中移除所有 Java 虚拟机参数后,尝试再次启动游戏。
launch.failed.cannot_create_jvm=无法创建 Java 虚拟机。可能是虚拟机参数有问题。你可以在“(全局/实例特定) 游戏设置 → 高级设置 → Java 虚拟机设置”中移除所有虚拟机参数后,尝试再次启动游戏。
launch.failed.creating_process=启动失败。在创建新进程时发生错误,可能是 Java 路径错误。
launch.failed.command_too_long=命令长度超过限制,无法创建批处理脚本。请导出为 PowerShell 脚本。
launch.failed.decompressing_natives=未能解压游戏本地库。
@@ -617,7 +621,7 @@ launch.failed.execution_policy.failed_to_set=设置执行策略失败
launch.failed.execution_policy.hint=当前执行策略阻止你执行 PowerShell 脚本。\n点击“确定”允许当前用户执行本地 PowerShell 脚本,或点击“取消”保持现状。
launch.failed.exited_abnormally=游戏非正常退出。请查看日志文件,或联系他人寻求帮助。
launch.failed.java_version_too_low=你所指定的 Java 版本过低。请重新设置 Java 版本。
launch.failed.no_accepted_java=找不到适合当前游戏使用的 Java。是否使用默认 Java 启动游戏?点击“是”使用默认 Java 继续启动游戏,\n或者在“(全局/版本特定) 游戏设置 → 游戏 Java”中选择一个合适的 Java 虚拟机版本。\n你可以点击右上角帮助按钮进行求助。
launch.failed.no_accepted_java=找不到适合当前游戏使用的 Java。是否使用默认 Java 启动游戏?点击“是”使用默认 Java 继续启动游戏,\n或者在“(全局/实例特定) 游戏设置 → 游戏 Java”中选择一个合适的 Java 版本。\n你可以点击右上角帮助按钮进行求助。
launch.failed.sigkill=游戏被用户或系统强制终止。
launch.state.dependencies=处理游戏依赖
launch.state.done=启动完成
@@ -685,7 +689,7 @@ modpack.choose.local.detail=你可以直接将整合包文件拖入本页面以
modpack.choose.remote=从互联网下载整合包
modpack.choose.remote.detail=需要提供整合包的下载链接
modpack.choose.repository=从 CurseForge/Modrinth 下载整合包
modpack.choose.repository.detail=下载后记得回到这个界面,把整合包拖进来哦
modpack.choose.repository.detail=可直接在跳转到的页面安装整合包
modpack.choose.remote.tooltip=要下载的整合包的链接
modpack.completion=下载整合包相关文件
modpack.desc=描述你要制作的整合包,比如整合包注意事项和更新记录。支持 Markdown (图片请用网络链接)。
@@ -744,41 +748,58 @@ modpack.wizard.step.2=文件选择
modpack.wizard.step.2.title=选中你想添加到整合包中的文件或文件夹
modpack.wizard.step.3=整合包类型
modpack.wizard.step.3.title=选择整合包导出类型
modpack.wizard.step.initialization.exported_version=要导出的游戏版本
modpack.wizard.step.initialization.exported_version=要导出的游戏实例
modpack.wizard.step.initialization.force_update=强制升级整合包至最新版本 (需要自建服务器)
modpack.wizard.step.initialization.include_launcher=包含启动器
modpack.wizard.step.initialization.save=选择要导出到的游戏整合包位置
modpack.wizard.step.initialization.warning=在制作整合包前,请你确认你选择的版本可以正常启动,\n并保证你的 Minecraft 是正式版而非快照,\n而且不应当将不允许非官方途径传播的模组、资源 (纹理) 包等纳入整合包。\n整合包会保存你目前的下载源设置。\n如遇到问题,你可以点击右上角帮助按钮进行求助。
modpack.wizard.step.initialization.warning=在制作整合包前,请你确认你选择的实例可以正常启动,\n并保证你的 Minecraft 是正式版而非快照,\n而且不应当将不允许非官方途径传播的模组、资源 (纹理) 包等纳入整合包。\n整合包会保存你目前的下载源设置。\n如遇到问题,你可以点击右上角帮助按钮进行求助。
modpack.wizard.step.initialization.server=点击此处查看有关服务器自动更新整合包的制作教程
modrinth.category.adventure=冒险
modrinth.category.atmosphere=氛围
modrinth.category.audio=声音
modrinth.category.babric=Babric
modrinth.category.blocks=方块
modrinth.category.bloom=泛光
modrinth.category.bta-babric=BTA (Babric)
modrinth.category.bukkit=Bukkit
modrinth.category.bungeecord=BungeeCord
modrinth.category.canvas=Canvas
modrinth.category.cartoon=卡通
modrinth.category.challenging=高难度
modrinth.category.core-shaders=核心着色器
modrinth.category.colored-lighting=彩色光照
modrinth.category.combat=战斗
modrinth.category.core-shaders=核心着色器
modrinth.category.cursed=Cursed
modrinth.category.datapack=数据包
modrinth.category.decoration=装饰
modrinth.category.economy=经济
modrinth.category.entities=实体
modrinth.category.environment=环境
modrinth.category.equipment=装备
modrinth.category.fabric=Fabric
modrinth.category.fantasy=幻想
modrinth.category.folia=Folia
modrinth.category.foliage=植被
modrinth.category.fonts=字体
modrinth.category.food=食物
modrinth.category.forge=Forge
modrinth.category.game-mechanics=游戏机制
modrinth.category.gui=GUI
modrinth.category.high=高
modrinth.category.iris=Iris
modrinth.category.items=物品
modrinth.category.java-agent=Java Agent
modrinth.category.kitchen-sink=大杂烩
modrinth.category.legacy-fabric=Legacy Fabric
modrinth.category.library=支持库
modrinth.category.lightweight=轻量
modrinth.category.liteloader=LiteLoader
modrinth.category.locale=本地化
modrinth.category.low=低
modrinth.category.magic=魔法
modrinth.category.management=管理
modrinth.category.medium=中
modrinth.category.minecraft=Minecraft
modrinth.category.minigame=小游戏
modrinth.category.misc=其他
@@ -788,13 +809,23 @@ modrinth.category.models=模型
modrinth.category.modloader=Modloader
modrinth.category.multiplayer=多人
modrinth.category.neoforge=NeoForge
modrinth.category.nilloader=NilLoader
modrinth.category.optifine=OptiFine
modrinth.category.optimization=优化
modrinth.category.ornithe=Ornithe
modrinth.category.paper=Paper
modrinth.category.path-tracing=路径追踪
modrinth.category.pbr=PBR
modrinth.category.potato=极低
modrinth.category.purpur=Purpur
modrinth.category.quests=任务
modrinth.category.quilt=Quilt
modrinth.category.realistic=写实
modrinth.category.reflections=反射
modrinth.category.rift=Rift
modrinth.category.screenshot=极高
modrinth.category.semi-realistic=半写实
modrinth.category.shadows=阴影
modrinth.category.simplistic=简单
modrinth.category.social=社交
modrinth.category.spigot=Spigot
@@ -805,12 +836,11 @@ modrinth.category.themed=主题
modrinth.category.transportation=运输
modrinth.category.tweaks=优化
modrinth.category.utility=实用
modrinth.category.vanilla=原生
modrinth.category.vanilla-like=类原生
modrinth.category.velocity=Velocity
modrinth.category.waterfall=Waterfall
modrinth.category.worldgen=世界生成
modrinth.category.datapack=数据包
modrinth.category.folia=Folia
mods=模组
mods.add=添加模组
@@ -857,7 +887,7 @@ mods.not_modded=你需要先在“自动安装”页面安装 Forge、NeoForge
mods.restore=回退
mods.url=官方页面
mods.update_modpack_mod.warning=更新整合包中的模组可能导致整合包损坏,使整合包无法正常启动。该操作不可逆,确定要更新吗?
mods.install=安装到当前版本
mods.install=安装到当前实例
mods.save_as=下载到本地文件夹
nbt.entries=%s 个条目
@@ -873,20 +903,23 @@ datapack.title=世界 [%s] - 数据包
web.failed=加载页面失败
web.open_in_browser=是否要在浏览器中打开此链接:\n%s
web.view_in_browser=在浏览器中查看
web.view_in_browser=在浏览器中查看完整日志
world=世界
world.add=添加世界
world.backup=世界备份
world.backup=备份管理
world.backup.create.new_one=创建新备份
world.backup.create.failed=创建备份失败。\n%s
world.backup.create.locked=该世界正在使用中,请关闭游戏后重试。
world.backup.create.success=成功创建新备份:%s
world.backup.delete=删除此备份
world.backup.processing=正在备份中……
world.backup.title=世界 [%s] - 备份
world.datapack=管理数据包
world.datapack.1_13=仅 Minecraft 1.13 及之后的版本支持数据包
world.chunkbase=世界地图
world.chunkbase.end_city=末地城地图
world.chunkbase.seed_map=种子地图
world.chunkbase.stronghold=要塞地图
world.chunkbase.nether_fortress=下界要塞地图
world.datapack=数据包管理
world.datetime=上一次游戏时间: %s
world.download=下载世界
world.download.title=世界下载 - %1s
@@ -901,7 +934,6 @@ world.import.choose=选择要导入的存档压缩包
world.import.failed=无法导入此世界:%s
world.import.invalid=无法识别该存档压缩包
world.info=世界信息
world.info.title=世界 [%s] - 世界信息
world.info.basic=基本信息
world.info.allow_cheats=允许命令(作弊)
world.info.dimension.the_nether=下界
@@ -920,6 +952,7 @@ world.info.player.food_level=饥饿值
world.info.player.game_type=游戏模式
world.info.player.game_type.adventure=冒险
world.info.player.game_type.creative=创造
world.info.player.game_type.hardcore=极限
world.info.player.game_type.spectator=旁观者
world.info.player.game_type.survival=生存
world.info.player.health=生命值
@@ -931,7 +964,9 @@ world.info.random_seed=种子
world.info.time=游戏内时间
world.info.time.format=%s 天
world.locked=使用中
world.manage=世界/数据包
world.manage=世界管理
world.manage.button=世界管理
world.manage.title=世界管理 - %s
world.name=世界名称
world.name.enter=输入世界名称
world.show_all=显示全部
@@ -1005,9 +1040,9 @@ settings.advanced.modify=编辑高级设置
settings.advanced.title=高级设置 - %s
settings.advanced.custom_commands=自定义命令
settings.advanced.custom_commands.hint=自定义命令被调用时将包含如下的环境变量:\n\
\ · $INST_NAME: 版本名称;\n\
\ · $INST_ID: 版本名称;\n\
\ · $INST_DIR: 当前版本运行路径;\n\
\ · $INST_NAME: 实例名称;\n\
\ · $INST_ID: 实例名称;\n\
\ · $INST_DIR: 当前实例运行路径;\n\
\ · $INST_MC_DIR: 当前游戏文件夹路径;\n\
\ · $INST_JAVA: 游戏运行使用的 Java 路径;\n\
\ · $INST_FORGE: 若安装了 Forge,将会存在本环境变量;\n\
@@ -1017,18 +1052,18 @@ settings.advanced.custom_commands.hint=自定义命令被调用时将包含如
\ · $INST_FABRIC: 若安装了 Fabric,将会存在本环境变量;\n\
\ · $INST_QUILT: 若安装了 Quilt,将会存在本环境变量。
settings.advanced.dont_check_game_completeness=不检查游戏完整性
settings.advanced.dont_check_jvm_validity=不检查 JVM 与游戏的兼容性
settings.advanced.dont_check_jvm_validity=不检查 Java 虚拟机与游戏的兼容性
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.game_dir.independent=各实例独立 (存放在 ".minecraft/versions/<实例名>/",除 assets、libraries 外)
settings.advanced.java_permanent_generation_space=内存永久保存区域
settings.advanced.java_permanent_generation_space.prompt=单位 MiB
settings.advanced.jvm=Java 虚拟机设置
settings.advanced.jvm_args=Java 虚拟机参数
settings.advanced.jvm_args.prompt=\ · 若在“Java 虚拟机参数”输入的参数与默认参数相同,则不会添加;\n\
\ · 若在“Java 虚拟机参数”输入任何 GC 参数,默认参数的 G1 参数会被禁用;\n\
\ · 开启下方“不添加默认的 JVM 参数”可在启动游戏时不添加默认参数。
\ · 开启下方“不添加默认的 Java 虚拟机参数”可在启动游戏时不添加默认参数。
settings.advanced.launcher_visibility.close=游戏启动后结束启动器
settings.advanced.launcher_visibility.hide=游戏启动后隐藏启动器
settings.advanced.launcher_visibility.hide_and_reopen=隐藏启动器并在游戏结束后重新打开
@@ -1040,8 +1075,9 @@ settings.advanced.natives_directory=本地库路径 (LWJGL)
settings.advanced.natives_directory.choose=选择本地库路径
settings.advanced.natives_directory.custom=自定义 (由你提供游戏需要的本地库)
settings.advanced.natives_directory.default=默认 (由启动器提供游戏本地库)
settings.advanced.natives_directory.hint=本选项提供给 Apple M1 等未受游戏官方支持的平台来自定义游戏本地库。如果你不知道本选项的含义,请不要修改本选项,否则会导致游戏无法启动!\n\n如果你要修改本选项,你需要保证自定义文件夹下有游戏所需的本地库文件,如 lwjgl.dll (liblwjgl.so)、openal.dll (libopenal.so) 等文件。启动器不会帮你补全缺少的本地库文件!\n\n注意:指定的本地库文件路径建议只包含英文大小写字母、数字和下划线,否则可能会导致启动游戏失败。
settings.advanced.no_jvm_args=不添加默认的 JVM 参数
settings.advanced.natives_directory.default.version_id=<实例名称>
settings.advanced.natives_directory.hint=本选项提供给 Apple Silicon 等未受游戏官方支持的平台来自定义游戏本地库。如果你不知道本选项的含义,请不要修改本选项,否则会导致游戏无法启动!\n\n如果你要修改本选项,你需要保证自定义文件夹下有游戏所需的本地库文件,如 lwjgl.dll (liblwjgl.so)、openal.dll (libopenal.so) 等文件。启动器不会帮你补全缺少的本地库文件!\n\n注意:指定的本地库文件路径建议只包含英文大小写字母、数字和下划线,否则可能会导致启动游戏失败。
settings.advanced.no_jvm_args=不添加默认的 Java 虚拟机参数
settings.advanced.precall_command=游戏启动前执行命令
settings.advanced.precall_command.prompt=将在游戏启动前调用
settings.advanced.process_priority=进程优先级
@@ -1072,7 +1108,7 @@ settings.custom=自定义
settings.game=游戏设置
settings.game.copy_global=复制全局游戏设置
settings.game.copy_global.copy_all=复制全部
settings.game.copy_global.copy_all.confirm=你确定要覆盖当前版本特定游戏设置吗?此操作无法撤销!
settings.game.copy_global.copy_all.confirm=你确定要覆盖当前实例特定游戏设置吗?此操作无法撤销!
settings.game.current=游戏
settings.game.dimension=游戏窗口分辨率
settings.game.exploration=浏览
@@ -1086,9 +1122,9 @@ settings.game.java_directory.invalid=Java 路径不正确
settings.game.java_directory.version=指定 Java 版本
settings.game.java_directory.template=%s (%s)
settings.game.management=管理
settings.game.working_directory=版本隔离 (建议使用模组时选择“各版本独立”。改后需移动存档、模组等相关游戏文件)
settings.game.working_directory=版本隔离 (建议使用模组时选择“各实例独立”。改后需移动存档、模组等相关游戏文件)
settings.game.working_directory.choose=选择运行文件夹
settings.game.working_directory.hint=在“版本隔离”中选择“各版本独立”使当前版本独立存放设置、存档、模组等数据。使用模组时建议启用此选项以避免不同版本模组冲突。修改此选项后需自行移动存档等文件。
settings.game.working_directory.hint=在“版本隔离”中选择“各实例独立”使当前实例独立存放设置、存档、模组等数据。使用模组时建议启用此选项以避免不同实例模组冲突。修改此选项后需自行移动存档等文件。
settings.icon=游戏图标
@@ -1134,18 +1170,19 @@ settings.memory.allocate.manual=游戏分配 %1$.1f GiB
settings.memory.allocate.manual.exceeded=游戏分配 %1$.1f GiB (设备仅 %3$.1f GiB 可用)
settings.memory.auto_allocate=自动分配内存
settings.memory.lower_bound=最低内存分配
settings.memory.unit.mib=MiB
settings.memory.used_per_total=设备中已使用 %1$.1f GiB / 设备总内存 %2$.1f GiB
settings.physical_memory=物理内存大小
settings.show_log=查看日志
settings.tabs.installers=自动安装
settings.take_effect_after_restart=重启后生效
settings.type=版本游戏设置类型
settings.type.global=全局游戏设置 (未启用“版本特定游戏设置”的版本共用此设置)
settings.type=实例游戏设置类型
settings.type.global=全局游戏设置 (未启用“实例特定游戏设置”的实例共用此设置)
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.enable=启用实例特定游戏设置 (不影响其他游戏实例)
settings.type.special.edit=编辑实例特定游戏设置
settings.type.special.edit.hint=当前游戏实例“%s”启用了“实例特定游戏设置”,因此本页面选项不对该实例生效。点击链接前往该实例的“游戏设置”页。
sponsor=赞助
sponsor.bmclapi=国内下载源由 BMCLAPI 提供高速下载服务。BMCLAPI 为公益服务,赞助 BMCLAPI 可以帮助作者更好地提供稳定高速的下载服务。[点击此处查阅详细信息]
@@ -1182,11 +1219,11 @@ update.no_browser=无法打开浏览器。网址已经复制到剪贴板,你
update.tooltip=更新
version=游戏
version.name=游戏版本名称
version.cannot_read=读取游戏版本失败,无法进行自动安装
version.empty=没有游戏版本
version.name=游戏实例名称
version.cannot_read=读取游戏实例失败,无法进行自动安装
version.empty=没有游戏实例
version.empty.add=进入下载页安装游戏
version.empty.launch=没有可启动的游戏。点击“确定”将进入“下载”页面。\n你也可以点击 HMCL 主界面左侧的“下载”按钮安装游戏,或在“版本列表”切换游戏文件夹。
version.empty.launch=没有可启动的游戏。点击“确定”将进入“下载”页面。\n你也可以点击 HMCL 主界面左侧的“下载”按钮安装游戏,或在“实例列表”切换游戏文件夹。
version.empty.hint=没有已安装的游戏。\n你可以切换其他游戏文件夹,或者点击此处进入游戏下载页面。
version.game.old=远古版
version.game.release=正式版
@@ -1195,30 +1232,30 @@ version.game.snapshot=快照
version.game.snapshots=快照
version.launch=启动游戏
version.launch.test=测试游戏
version.switch=切换版本
version.switch=切换实例
version.launch_script=生成启动脚本
version.launch_script.failed=生成启动脚本失败
version.launch_script.save=保存启动脚本
version.launch_script.success=启动脚本已生成完毕:%s
version.manage=版本列表
version.manage=实例列表
version.manage.clean=清理游戏文件夹
version.manage.clean.tooltip=清理“logs”和“crash-reports”文件夹
version.manage.duplicate=复制游戏版本
version.manage.duplicate=复制游戏实例
version.manage.duplicate.duplicate_save=复制存档
version.manage.duplicate.prompt=请输入新游戏名称
version.manage.duplicate.confirm=新的游戏版本将复制该版本文件夹 (".minecraft/versions/<版本名>") 下的文件,并带有独立的运行文件夹和设置。
version.manage.manage=版本管理
version.manage.manage.title=版本管理 - %1s
version.manage.duplicate.confirm=新的游戏实例将复制该实例文件夹 (".minecraft/versions/<实例名>") 下的文件,并带有独立的运行文件夹和设置。
version.manage.manage=实例管理
version.manage.manage.title=实例管理 - %1s
version.manage.redownload_assets_index=更新游戏资源文件
version.manage.remove=删除该版本
version.manage.remove.confirm=真的要删除版本“%s”吗?你将无法找回被删除的文件!!!
version.manage.remove.confirm.trash=真的要删除版本“%s”吗?你可以在系统的回收站中还原“%s”文件夹来找回该版本。
version.manage.remove.confirm.independent=由于该游戏启用了“(全局/版本特定) 游戏设置 → 版本隔离 → 各版本独立”选项,删除该版本将导致该游戏的存档等数据一同被删除!真的要删除版本“%s”吗?
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=重命名该实例
version.manage.rename.message=请输入要修改的名称
version.manage.rename.fail=重命名版本失败,可能文件被占用或者名字有特殊字符。
version.manage.rename.fail=重命名实例失败,可能文件被占用或者名字有特殊字符。
version.settings=游戏设置
version.update=更新整合包
@@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<!DOCTYPE html>
<html lang="zh-CN">
<html lang="%lang%">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
File diff suppressed because it is too large. Load diff
+185 -41
View File
@@ -170,7 +170,7 @@ all-the-mods-6;164;;All the Mods 6;;ATM6
garden-of-glass-questbook-edition;165;;Garden of Glass (Questbook Edition);;
;166;;圣剑传奇;Sword Legendary;
;167;;FTB Presents Direwolf20 1.16;;
team-rustic;168;;Rusty Sink 1.20;;
team-rustic;168;;Rusty Sink 2;;
enigmatica2expertskyblock;169;;Enigmatica 2: Expert Skyblock;;E2ES
;170;;休闲-生存-2021版;Leisure Survival-2021;ls21
hexxit-updated;171;;Hexxit Updated;;
@@ -195,7 +195,7 @@ mineshafts-monsters;189;;矿井与怪物;Mineshafts & Monsters;
crazy-craft-remastered;190;;Crazy Craft Remastered;;
engineers-life-2;191;;Engineer's Life 2;;EL2
hr-new-beginnings-2;192;;HR: New Beginnings 2;;
minecolonies-testpack;193;;模拟殖民地;MineColonies Official;
minecolonies-official;193;;模拟殖民地;MineColonies Official;
tnp-limitless;194;;TNP Limitless;;
tnp-limitless-2;195;;TNP Limitless 2;;
tnp-limitless-3;196;;TNP Limitless 3;;
@@ -251,9 +251,9 @@ fabulously-optimized;248;;难以置信的优化;Fabulously Optimized;FO
better-minecraft-otv-edition;249;;Better Minecraft (OfflineTV Edition);;
evolution-reset;250;;进化:重启;Evolution: Reset;ER
the-nuclear-wasteland;251;;核荒原;The Nuclear Wasteland;
blessed-or-cursed-expedition-to-infinite-force;252;;祝福或诅咒:无限力量之征;Blessed or Cursed: Expedition to Infinite Force;BOC
blessed-or-cursed-expedition-to-infinite-force;252;;祝福或诅咒:无限力量之征;Blessed or Cursed: Expedition to Infinite Force;BoC
;253;;边界生存;Border Survival;BSV
medieval-mc-fabric;255;;Medieval Minecraft;;MMC
medieval-mc-forge-mmc1;255;;Medieval Minecraft;;MMC
heavens-of-sorcery;256;;巫术天堂;Heavens of Sorcery;
herodotus;257;;希罗多德;Herodotus-Authentic Edition:Official;HDS
;258;;重生世界;Rebirth world;RW
@@ -282,7 +282,7 @@ world-of-dragons-ii;282;;World of Dragons II;;
tensura-unleashed;283;;Tensura Unleashed;;
skyopolis-4;284;;Skyopolis 4;;
ftb-oceanblock;286;;FTB OceanBlock;;
tolkiencraft-iii-return-to-middle-earth;287;;TolkienCraft III - Return to Middle-earth;;
tolkiencraft-iii-return-to-middle-earth;287;;托尔金工艺3 - 重返中洲;TolkienCraft III - Return to Middle-earth;TC3
delivery-inc;288;;Delivery Inc.;;
poetica;289;;Poetica;;
divine-journey-2;290;;神圣之旅2;Divine Journey 2;DJ2
@@ -354,7 +354,7 @@ all-the-mods-expert-remastered;355;;All The Mods Expert: Remastered;;
all-the-mods-0;356;;All the Mods 0;;ATM0
all-the-mods-fabric;357;;All the Mods Fabric;;ATMF
the-pixelmon-modpack;358;;The Pixelmon Modpack;;
better-minecraft-skyblock;359;;更好的我的世界:空岛;Better Minecraft Skyblock;
better-mc-skyblock;359;;更好的我的世界空岛;Better MC Skyblock;BMCS
age-of-fate;360;;命运时代;Age of Fate;AOF
minecraft-kingdoms;361;;MC Kingdoms;;
;362;;幻想旅程;Imaginary Journey;
@@ -413,7 +413,6 @@ epoch-runner-formerly-nuclear-beyond-modpack;417;;时代领跑人;Epoch Runner;
;418;;星海征途;The Journey of Star Sea;TJoSS
roguelike-adventures-and-dungeons-2;419;;冒险与地牢2;Roguelike Adventures and Dungeons 2;RAD2
multiskyblock;420;;模块化空岛;MultiSkyBlock;MSB
;421;;人性化;Humanized;H
create-live-3;422;;Create Live 3;;
magnificia;423;;Magnificia;;
;424;;魔法金属-新手向导;ManaMetalMod - WithNewbHelper;
@@ -484,7 +483,7 @@ dimensionhopper;490;;Dimension Hopper:The Fall;;
technology-empty-island;491;;科技空岛;RxanGrben;
greed-the-resource-4-infinity;492;;Greed the Resource 4 - Infinity;;
caveopolis;493;;Caveopolis;;
;494;;硬核生存;HardCraft;HC
;494;;坚毅征途;HardCraft;HC
;495;;怀末韵云;Institutions mz;MZ1
feed-the-factory;496;;Feed the Factory;;FTF
fantasy-realm;497;;幻想领域;Fantasy Realm;
@@ -567,7 +566,6 @@ expert-of-magic-art-world;573;;魔艺世界;Expert of Magic Art World;EOMAW
nuclear-radiation-2;576;;核污染二;nuclear radiation 2;NR2
;577;;白狐铃的奇思妙想;Arctic Fox Ling Fantasy;AFLF
;578;;MineSpace;;MS
;579;;史诗战斗:兵团战争;EPIC Fight Corps War;EFC
;580;;月见清兰的个人优化;TskimiSeiran's Idea;TkSrI
;581;;兔头:真视世界;;
;582;;灾厄战争;;
@@ -584,7 +582,7 @@ the-winter-frontier;591;;冬境边域;The Winter Frontier;TWF
;593;;太阳机械厂;Create Sun;CS
dawn-craft;594;;破晓之界;DawnCraft: Echoes of Legends;DC
aof-presents-skylore;595;;AOF: Skylore;;
keletus-gregpack;596;;可乐兔的格雷整合包;Keletu's GregPack;
keletus-gregpack;596;;可乐兔的格雷整合包;Keletu's GregPack;GtMagic
ftb-skies;597;;FTB Skies;;
tnp-limitless-5;598;;TNP Limitless 5;;
create-back-on-track;599;;Create: Back on Track;;
@@ -614,8 +612,8 @@ eternal-night-modpacks;622;;永夜;Eternal Night;ETN
all-the-mods-9;623;;All the Mods 9;;ATM9
spoornpack;624;;Spoornpack;;
;625;;起源RPG;;ORPG
;626;;钓月织梦;Tsuri No Gatsu;TSNG
;627;;冰河物语;Ice Age Fairy;IAF
;626;;织彩钓月;Oriun Tagayasu;OTSU
;627;;冰河物语;;
hommage;628;;Hommage;;
days-to-craft;629;;Seven Days To Craft;;
emcworld;630;;质能世界;EMCWorld;
@@ -668,7 +666,7 @@ journey-watch-ytz;676;;征途 守望;Journey Watch;JW
;677;;Arcomua Lite 原版整合;Arcomua Lite;AL
incremental-industries;678;;Incremental Industries;;
;679;;海王星计划:重构;NeptunePlan:Reboot;NPR
;680;;中世纪生存日记;Medieval Survival Diary;MSD
;680;;求生日记:荒野;Survival Diary: Wilderness;SWD
a-new-technological-era;681;;A New Technological Era;;ANTE
;682;;超能力之旅;Meet Your Superpower;MYS
musketeer;683;;Musketeer - One for All;;
@@ -692,7 +690,7 @@ enigmatica9;699;;Enigmatica 9;;E9
create-arcane-engineering;701;;机械动力:奥术工程;Create: Arcane Engineering;CAE
chroma-endless-2;702;;Chroma Endless 2;;
techrevolution;703;;TechRevolution;;
cabin;704;;CABIN: Create Above & Beyond In Newer;;CABIN
cabin;704;;Create Above & Beyond In Newer;;CABIN
aoc;705;;All of Create;;
;706;;交错维度3;Among Dimensions 3;AD
gregfactory-sky;707;;GregFactory Sky;;GFS
@@ -735,8 +733,8 @@ life-in-the-village-3;740;;Life in the village 3;;LitV3
;745;;FTB Genesis;;
cook-in-skyblock;746;;天空厨房;Cook in skyland;CiS
;747;;被恐惧支配 1;Dominated by Fear 1;DoF
new-vanilla-modpack;748;;新的原版;New Vanilla;NVA
;749;;群峦救援非官方版;Terrafirma Rescue Unofficial;TFRU
new-vanilla-newhome;748;;新的原版;New Vanilla;NVA
;749;;峦屿梦星群;Terrafirma Rescue Unofficial;TFRU
stoneopolis;750;;Stoneopolis;;
ftb-skies-expert;751;;FTB Skies Expert;;
wmshd-pack-what-mojang-should-have-done;752;;What Mojang Should Have Done;;WMSHD
@@ -744,7 +742,7 @@ wmshd-pack-what-mojang-should-have-done;752;;What Mojang Should Have Done;;WMSHD
;754;;随心之旅;spontaneous journey;
oxmodpack;755;;OxMODPACK 8 +Online;;
gregtech-community-pack-modern;756;;GregTech Community Pack Modern;;GCPM
;757;;锻造大师3;Master Blacksmith3;MB3
;757;;锻造大师4;Master Blacksmith4;MB4
;758;;科技用于探索世界;Technology Exploration World;
;759;;幻梦闲笔;Aetherial Voyage;
fear-the-mist;760;;雾中的恐惧;Fear The Mist;
@@ -764,7 +762,7 @@ infernal-rpg;771;;Infernal Origins;;
;774;;AIR for JAVA;;AFJ
;775;;Create Extra;;
frs-optimisation-for-survival;776;;Fr的生存优化;Fr's Optimisation for Survival;FROS
;777;;Exile Magic Constructor;;EMC
exile-magic-constructor;777;;Exile Magic Constructor;;EMC
;778;;Landscapes Reimagined Genesis;;
al-nl;779;;冒险生活 - 新生活;Adventure Life - New Life;AL-NL
harvista;780;;拾穗;Harvista;
@@ -819,19 +817,19 @@ all-the-mods-9-to-the-sky;828;;All The Mod 9 - To The Sky;;ATM9S
fallenpetals;830;;落英;FallenPetals;FP
dungeon-heroes;831;;Dungeon Heroes (RPG Series);;
play-as-dragon-gothic-edition;832;;Play as Dragon: Gothic Edition;;
skyblock-burgeria;833;;空岛汉堡店;;
skyblock-burgeria;833;;空岛汉堡店;Skyblock Burgeria;
crafting-craft-2;834;;合成工艺2;Crafting Craft 2;
;835;;僵尸入侵100天;Zombie Invade 100 Days;ZI100D
compact-world-expert;836;;Compact World Expert;;CWE
a-fragment-of-peace;837;;宁然一隅;A Fragment of Peace;AFoP
;837;;宁然一隅;A Fragment of Peace;AFoP
easy-create-modpack;838;;Easy Create;;
;839;;流线鱼概念;Fish Spi Rally;FSRMP
meatballcraft;840;;肉丸工艺,次元提升;MeatballCraft, Dimensional Ascension;
meatballcraft;840;;肉丸工艺,次元飞升;MeatballCraft, Dimensional Ascension;MBC
seaopolis-submerged;841;;海之城: 潜没水中;Seaopolis: Submerged;
;842;;空中厕所;PoopSky;
compact-sky;843;;收缩空岛;Compact Sky;
back-to-back;844;;背靠背;Back to Back;BTB
adventure-exploration-part2;845;;冒险与探索;Adventure & Exploration;AnE
ane2;845;;冒险与探索;Adventure & Exploration;AnE
;846;;复兴;Renaissance;
live-like-a-castaway;847;;木筏求生2;Raftcraft2;RC2
mermaid-legend;848;;人鱼传说;Mermaid legend;
@@ -847,7 +845,6 @@ create-prepare-to-dye;856;;Create Prepare to Dye;;
create-leisurely-delight;858;;机械动力:悠然乐事;Create Delight;CD
;859;;蔚蓝悠悠;Azure Leisurely;AL
infinityevolved-reloaded;860;;Infinity Evolved: Reloaded;;
;861;;空中厕所:生产线;Poopsky:Assembly Line;PAL
;862;;佛罗伦萨迷思;Fantasy in Florence;FIF
craftoria;863;;Craftoria;;
mechanic-craft;864;;MechanicCraft;;
@@ -878,7 +875,7 @@ create-stellar;888;;Create Stellar;;
monifactory;889;;Monifactory;;
create-factory-builder;890;;Create: Factory Builder;;
;891;;群峦传说:宝宝快乐版;TerraFirma: Baby Happy Edition;TFBH
;892;;阶段: 残骸;Phase: the Debris;
;892;;阶段: 残骸;Phase: the Debris;PTD
;893;;AE管道;AE Pipes;APE
skyt-2;894;;SkyT 2;;
stagecraft;895;;StageCraft;;
@@ -909,7 +906,7 @@ strictly-medieval-3-forge;917;;Strictly Medival 3;;
easy-to-ascension;920;;易如飞升;Easy to Ascension;
;921;;幽匿末日;The Last Day of Sculk;TLDS
;922;;交错空岛;Crossisland;CrI
;923;;机械动力:魔法乐事;Create Magic's Delight;CRMD
;923;;机动工程:刀剑与魔法;Mechanical Engineering: SlashBlade and Magic;CRMD
minetech-evolution;924;;科技进化;Minetech Evolution;MTE
gregified-sky-edition;925;;Gregified: Sky Edition;;
;926;;原版扩展;Additonal Vanilla;AV
@@ -917,14 +914,14 @@ create-on-a-potato;927;;Create on a Potato PC: Ultimate;;CPU
;928;;现实主义;realism;RLM
;929;;晋升之路;path of advance;POA
;930;;永无止境:超越常新;Above and Beyond : Refresh;CABR
cosmic-frontiers;931;;Cosmic Frontiers;;
cosmic-frontiers;931;;星河边疆;Cosmic Frontiers;CF
integrated-minecraft;932;;Integrated MC;;
;933;;Rising Legends;;
;934;;城乡之旅;Travel Township;TT
;934;;城乡之旅;Harvest's Journey;HJ
;935;;龙鸣牢大;FarmerKobeBryant;FKB
setgamedifficulty-reika;936;;SetGameDifficulty Reika;;
epic-journey-gtceu;937;;Epic Journey GTCEu;;
;938;;飞渡群峦;TerraFirmaCraft:Leap The Top;TFC:LTT
terrafirmacraft-leap-the-top;938;;飞渡群峦;TerraFirmaCraft:Leap The Top;TFC:LTT
gregtech-skybound;939;;Gregtech: Skybound;;
simple-tfc;940;;简单群峦;SimpleTFC;
ftb-presents-direwolf20-s14;941;;FTB Presents Direwolf20 1.21;;
@@ -948,10 +945,10 @@ gregtania6-skyblock;959;;Gregtania6 Skyblock;;
harmonious-engineering;960;;Harmonious Engineering;;
valley-of-farming-and-ranching;961;;农牧物语;Valley of Farming and Ranching;
;962;;机械动力:火车;Create Train;
;963;;理想国冒险之旅;;AOIC
the-old-era-of-the-republic-fot-yifei;963;;理想国冒险之旅;;AOIC
;964;;生欲:求生2;Vivere The Zombie Apocalypse-II;VZA2
;965;;起源-世界;OriginCraft Pulses;
;966;;Legacies;;
legacies-mc;966;;Legacies;;
the-legend-of-2900;967;;海上机械师;The Legend Of 2900;TLOU
mirasites-100-days;968;;畸变:蛊之奇境;Mirasites-BrokenMemories;MBM
;969;;末日余晖:独自一人;;MRYH
@@ -970,7 +967,7 @@ venoms-craft;981;;Venom's Craft;;
;982;;慢生活冒险;;
;983;;多元工艺:绘镇曲;Painted Town Melody;PTM
;984;;遗忘之海;Leisurely Life;
;985;;幻想国度;F_Land;
;985;;幻想国度;Fancy_Land;
;986;;群峦传说:重制;TerraFirmaCraft Remake;TFCRe
;987;;未来;;
;988;;起源生物—崛起;;
@@ -988,12 +985,12 @@ industrial-machines;1000;;Industrial Machines;;
auto-terrafirmacraft-tfc-create;1001;;Auto-TerraFirmaCraft (TFC + Create);;ATFC
;1002;;时间之书:星的日记;The Book of Time: A Star's Journal;TASL
;1003;;特摄世界;Tokusatsu World;TW
journey-of-reincarnation;1004;;转世之旅;Journey of Reincarnation;
journey-of-reincarnation;1004;;转世之旅;Tensura: Journey of Reincarnation;
;1005;;冒险与休闲;Adventure and Leisure;AL
enigmatica10;1006;;Enigmatica 10;;E10
mc-chocolate-edition;1007;;The Chocolate Edition;;
mc-chocolate-edition;1007;;Chocolate Edition Minecraft;;
civilization-l;1008;;Civilization-L;;
;1009;;群峦:降临;TerraFirma:Advent;TFA
terrafirma-advent;1009;;群峦:降临;TerraFirma:Advent;TFA
valhelsia-7;1010;;Valhelsia 7;;
mechanical-mastery-plus;1011;;Mechanical Mastery Plus;;
ashes-to-ashes;1012;;尘归尘;Ashes To Ashes;ATA
@@ -1001,23 +998,23 @@ ashes-to-ashes;1012;;尘归尘;Ashes To Ashes;ATA
create-silence-and-prosperity;1014;;机械动力:寂静与繁荣;Create Silence and Prosperity;CSP
mif;1015;;MI:Foundation;;MIF
c2mse2;1016;;Create 2 Mekanism: Sky Edition 2;;C2MSE2
;1017;;Tsuri No Gatsu 5 - FDCraft Version;;TSNG5-FDC
;1017;;耕云钓月;Kumo O Tagayasu;KOTS
;1018;;无限战争4;Infinity War4;INF4
;1019;;群峦传说:工业长路;;
;1020;;自然冒险 初生;Nature Adventure Newborn;NAB
;1021;;羽舲的基础优化;Hanekmio's Basis Optimization;
;1020;;自然冒险 新生;Nature Adventure Newborn;NAB
hmbo;1021;;羽舲的基础优化;Hanekmio's Basis Optimization;
create-star-valley;1022;;Create: Gears of History;;
;1023;;阿尔法计划;AlphaPlan;AP
spell-dimension;1024;;咒次元;Spell Dimension;SD
skyfactory-5;1025;;天空工厂 5;SkyFactory 5;SF5
;1026;;破劫之刃;Break Disaster;
reminiscent-create;1027;;Reminiscent Create;;
techsert;1028;;Techsert;;TH
techsert;1028;;匿踪之城;Techsert;TH
prehistoric-world-dinosaurs-adventure;1029;;Prehistoric World - Dinosaurs & Adventure;;
terminal;1030;;终末;Terminal;
;1031;;异次元水晶 3;Isolated Crystal 3;
isolated-crystal-3;1031;;异次元水晶 3;Isolated Crystal 3;
;1032;;机械动力:无限总是小于零;Create:Infinity Always Smaller Than Zero;CIASZ
;1033;;踪灭I-开端;;
;1033;;踪灭I-起源;;
;1034;;踪灭II;;
rabbits-dawn-craft;1035;;某兔子的破晓之界;A Certain Rabbit's DawnCraft - Dawn of the Deep Soul;RDC
;1036;;铁砧天空;;ASKY
@@ -1027,7 +1024,7 @@ the-adventure-of-valkyriens;1038;;瓦尔基里大冒险;The Adventure of Valkyri
;1040;;群峦传说:天才版;;
compression;1041;;Compression;;
compact-sky-easy;1042;;Compact Sky: Easy;;CSE
;1043;;丰收与探索;;
;1043;;丰收与探索;Harvest and Exploration;
;1044;;脆骨症:黯光;No Flesh Within Chest-DIM;NFWC-DIM
;1045;;小雷的优化冒险;;
cobblemon-eternal-edition;1046;;Cobblemon Eternal;;
@@ -1035,3 +1032,150 @@ cobblemon-eternal-edition;1046;;Cobblemon Eternal;;
gregtech-expert;1048;;GregTech Expert;;
;1049;;Beta Horizons;;
minecraft-legendary-edition;1050;;Legendary Edition;;
;1051;;Origenceal LPackage;;OLP
;1052;;我的世界:未闻的故事;;mtus
;1053;;Versatile;;
;1054;;勇者之章 Ⅲ;Chapter of Yuusha Ⅲ;CY3
forge-frontier;1055;;Create - Forge Frontier;;
create-workshop-uwu;1056;;Create: Workshop UwU Edition;;
;1057;;古钠重铸;Oldium Optimized;OO
;1058;;越寰;Leaping across the world;LTW
ftb-oceanblock-2;1059;;FTB OceanBlock 2;;
;1060;;伊甸空天2;Eden Aerospace 2;EAS2
create-infinity-mechanism;1061;;机械动力:无限构件;Create:Infinity Mechanism;CIM
beyond-depth;1062;;Beyond Depth;;
;1063;;枪械交界地;TACZ Amid Crucible Zone;
;1064;;真幻世界;Realfantacy World;R.F.
minenufactory;1065;;创世工厂;Minenufactory;MF
craft-to-exile-2;1066;;放逐之路2;Craft to Exile 2 (VR Support);CTE2
;1067;;龙境III:菌土重来;Dragon Odyssey:Return of the Fungal Earth;DO3
the-culinary-journey;1068;;美食的旅途;The Culinary Journey;TCJ
;1069;;缥缈方舟;;PMFZ
;1070;;东方匠魂传;Touhou Tinker Fantasy;TTF
;1071;;亵渎;;MYBL
techopolis-3;1072;;Techopolis 3;;
sky1234;1074;;sky1234;;
star-technology;1075;;Star Technology;;
;1076;;Hyper World;;HW
;1077;;妖月的魔法金属整合包;yaoyaoDream's ManaMetalMod Modpack;YMM
techcraft-severwinter;1078;;科技工艺:严冬;TechCraft: SevereWinter;TCSW
randblock2-winterize;1079;;随机空岛 2:银装素裹;RandBlock2:Winterize;
create-the-brass-concerto;1080;;机械动力:黄铜协奏曲;Create: The Brass Concerto;CBC
explorers-eve;1081;;Explorer's Eve;;
all-the-magic-arcana;1082;;All the Magic :Arcana;;ATMAr
;1083;;创想新世界;Inspired Creations: New World;ICNW
farming-crossing-5-made-to-order;1084;;农业森友会 5;Farming Crossing 5: Made to Order!;
;1085;;Ben10 Craft;;BEN10
;1086;;渺小的世界;Small world;
mierno;1087;;迷厄路;Mierno;
;1088;;最后那些天;Those last day;
;1089;;泠月狐基础整合;LunarFox;
roguelike-adventures-and-dungeons-3;1090;;冒险与地牢3;Roguelike Adventures and Dungeons 3;RAD3
;1091;;羽舲界;Hanekmio Instance;HMI
;1092;;玄理2:洗剪版;EnigTech 2 - Dishwasher's Cut;ET2DC
wheelchair-gregtech;1093;;Wheelchair Gregtech;;
egged-up;1094;;Egged Up;;
;1095;;香草纪元:食旅纪行;VanillaEra: FaresChron;
;1096;;愚者;The Fool;TF
;1097;;时间之书2:星梦指南;BookofTime II Stardream Compass;BKSC
;1098;;群峦之上:无垠伊土;EndlessFirma;EnF
;1099;;血月低语:暗夜惊魂;Bloodmoon's Whisper;BW
;1100;;MC饭;Delicious in Minecraft;DIM
;1101;;美味的甜点总是有毒;;
delight-desert-chef;1102;;乐事:沙漠厨师;Delight: Desert Chef;DDC
;1103;;异界随行诗;Otherworld Traveling Poems;
;1104;;无边荒漠;;
;1105;;空中厕所2;PoopSky2;
fear-nightfall;1106;;Fear Nightfall;;
homestead-cozy;1107;;Homestead - A Cozy Survival Experience;;HD
madness-of-the-skies;1108;;Madness of the skies;;
;1109;;魔之逆鳞;Nemesis of Demons;NOD
emc-and-fe-plus2;1110;;EMC & FE ++;;E&F
;1111;;云之谷;CloudValley;
bm-exosphere;1112;;BM: Exosphere;;BME
;1113;;锻造大师:侠之器;Master Blacksmith:Chivalrous blade;MB:CB
;1114;;自然;Nature;NA
;1115;;农场物语;FarmingTales;FT
optimaxcraft;1116;;OptiMaxCraft;;OMC
;1117;;决战黎明;Decisive Battle at Dawn;DBAD
floaty-creatoria;1118;;天工浮生;Floaty Creatoria;
;1119;;简约生电优化;Simplicity Survival Optimization;SSO
;1120;;星辰之下;Under the Stars;UtS
madness-of-the-skies-2;1121;;Madness of the Skies 2;;
raspberry-flavoured;1122;;Raspberry Flavoured;;RF
skillet-man;1123;;平底锅侠;Skillet Man;
;1124;;格雷科技:新征程;GregTech Odyssey;GTO
;1125;;无中生无尽;Ex Nihilo Ad Avaritia;ENAA
;1126;;Undertale;;
create-creative-and-beyond;1127;;机械动力:创造与超越;Create Creative and Beyond;CCB
create-delight-remake;1128;;机械动力:齿轮盛宴;Create:Delight Remake;CDR
ftb-skies-2;1129;;FTB Skies 2;;
;1130;;星辰时代:科技魔法;Stellar Era: Technomagic;SETM
aircrashed-engineer;1131;;坠机工程师;Aircrashed Engineer;ACE
project-tfcreate;1132;;Project: TFCreate;;
;1133;;落幕曲;;
;1134;;超大陆:勇气;Supercontinent:Pluck;SUPL
create-chronicles-the-endventure;1135;;Create Chronicles: The Endventure;;
life-in-the-village-4;1136;;Life in the village 4;;LITV4
real-island-2-modular-craft;1137;;真实空岛2:模块化之旅;Real Island 2 - Modular Craft;RI2
;1138;;奥术咖啡馆;;
unyielding;1139;;Unyielding;;
;1140;;剑与王国;;SNK
the-fungal-infection-coop;1141;;The Fungal Infection | Co-Op Base Defense;;
mc-eternal-2;1142;;永恒的MC 2;MC Eternal 2;MCE2
solo-leveling-reawakening;1143;;Solo Leveling - Reawakening;;
create-ultimate-selection;1144;;Create: Ultimate Selection - QUESTS NOW!;;
;1145;;元素觉醒;Elemental Awakening;
endless-horror-the-ultimate-horror-modpack;1146;;Endless Horror - The ULTIMATE Horror Experience;;
;1147;;锻造之旅;;
picky;1148;;Picky;;
resource-factory;1149;;资源工厂;Resource Factory;
stacia-2-expert;1150;;Stacia 2 Expert;;S2E
;1151;;田园乐事;;
;1152;;世界之旅;Journey of the world;JOTW
;1153;;清新优化;Fresh optimization;FRO
;1154;;遗世之匠;Touhou:The Forgotten Swordsmith;
;1155;;千禧:蝉鸣之时;;
skyrealm-descent;1157;;Skyrealm: Descent;;
;1158;;远征:以太利亚;Expedition: Ætheria;
;1159;;快速优化;Rapid Optimization;RO
liminal-industries;1160;;阈限工业;Liminal Industries;
;1161;;史诗冒险;;EA
;1162;;根自日常;ROOTINE;
blue-archive-x-mineraft;1163;;我的世界x蔚蓝档案;BlueArchive x Minecraft;BACraft
;1164;;Fresh & Smooth;;
;1165;;机动重启工程:风之旅;The Caravan of Winds;TCW
;1166;;Furry Fandom Club Boss Rush Official Offline;;FFCBR
immersive-ultra;1167;;极致沉浸;Immersive Ultra;
;1168;;Polymania;;
survivearchive;1169;;SurviveArchive - A Blue Archive modpack;;
beloong;1170;;化龍;BeLoong;
;1171;;畅游自然;;SiF
;1172;;退休工艺;Retirement Craft;RmC
;1173;;随机枪械乱斗;NimShade's Random Gunfight;NRG
;1174;;雪狐的基础整合;SF Integrated Modpack;SFI
;1175;;硬核惊变;Hardcore Surprise;HS
society-sunlit-valley;1176;;Society: Sunlit Valley;;
ciscos-fantasy-medieval-rpg-dragonfyre;1177;;Cisco's Fantasy Medieval RPG [Dragonfyre];;
all-the-mods-10-sky;1178;;All the Mods 10: To the Sky;;ATM10S
;1179;;谐伴匠途;;
the-cursed-wanderer-reborn;1180;;咒游诸界:重生;The Cursed Wanderer: Reborn;TCWR
;1181;;Turbo Vulkan;;TuVk
uls-zsurvival-craft;1182;;Z-Liminex;;
;1183;;MITE 重生 3;MITE Reborn 3;MR
reclamation-reclaim-the-world;1184;;Reclamation - Reclaim the World!;;
;1185;;寰宇地球;Earthworld;EW
pokehaan-craft-2;1186;;Pokehaan Craft 2;;
project-nope-zone;1187;;Project Nope Zone;;PNZ
all-the-simple-zero;1188;;一切简易;All the Simple;ATS
;1189;;匠战妖域;Tinkers' Warfare Demon Realm;twdr
;1190;;空想生存;;
;1191;;东方起源鉴;Touhou Origins Annals;TOA
realm-of-destiny;1192;;生命之境;Realm of Destiny;ROD
;1193;;奇妙的世界;Special World;
;1194;;毁灭;DESTRUCTION;DES
pvzw;1195;;植物大战僵尸:世界;Plants VS Zombies : World;PVZW
;1196;;农场物语Forge版;FarmingTales Forge;FT-WT
new-century-adventure;1197;;新世纪之险;New Century Adventure;NCA
repalmon;1198;;Re帕鲁梦;;RPLM
structural-ingenuity;1199;;结构型奇思妙想;Structural Ingenuity;
@@ -4007,6 +4007,17 @@
"size": 27971214
}
}
},
"software-renderer-loader": {
"name": "org.glavo:llvmpipe-loader:1.0",
"downloads": {
"artifact": {
"path": "org/glavo/llvmpipe-loader/1.0/llvmpipe-loader-1.0.jar",
"url": "https://repo1.maven.org/maven2/org/glavo/llvmpipe-loader/1.0/llvmpipe-loader-1.0.jar",
"sha1": "ff255415e5c4b2a18970da0a8e552b557ca013ae",
"size": 12964773
}
}
}
},
"windows-x86": {
@@ -71,8 +71,7 @@ public final class BMCLAPIDownloadProvider implements DownloadProvider {
pair("http://files.minecraftforge.net/maven", apiRoot + "/maven"),
pair("https://files.minecraftforge.net/maven", apiRoot + "/maven"),
pair("https://maven.minecraftforge.net", apiRoot + "/maven"),
pair("https://maven.neoforged.net/releases/net/neoforged/forge", apiRoot + "/maven/net/neoforged/forge"),
pair("https://maven.neoforged.net/releases/net/neoforged/neoforge", apiRoot + "/maven/net/neoforged/neoforge"),
pair("https://maven.neoforged.net/releases/", apiRoot + "/maven/"),
pair("http://dl.liteloader.com/versions/versions.json", apiRoot + "/maven/com/mumfrey/liteloader/versions.json"),
pair("http://dl.liteloader.com/versions", apiRoot + "/maven"),
pair("https://meta.fabricmc.net", apiRoot + "/fabric-meta"),
@@ -24,6 +24,7 @@ import org.jackhuang.hmcl.game.ReleaseType;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
import java.time.Instant;
import java.util.List;
@@ -53,10 +54,16 @@ public final class GameRemoteVersion extends RemoteVersion {
@Override
public int compareTo(RemoteVersion o) {
if (!(o instanceof GameRemoteVersion))
if (!(o instanceof GameRemoteVersion)) {
return 0;
}
return o.getReleaseDate().compareTo(getReleaseDate());
int dateCompare = o.getReleaseDate().compareTo(getReleaseDate());
if (dateCompare != 0) {
return dateCompare;
}
return GameVersionNumber.compare(o.getSelfVersion(), getSelfVersion());
}
private static Type getReleaseType(ReleaseType type) {
@@ -41,6 +41,8 @@ public enum DiscoJavaDistribution implements JavaDistribution<DiscoJavaRemoteVer
pair(WINDOWS, EnumSet.of(X86_64, X86, ARM64)),
pair(LINUX, EnumSet.of(X86_64, X86, ARM64, ARM32, RISCV64, PPC64, PPC64LE, S390X, SPARCV9)),
pair(MACOS, EnumSet.of(X86_64, ARM64))),
// Fucking javac generates an anonymous inner class here, whose constructor takes no @SafeVarargs annotation, causing a compile-time warning.
@SuppressWarnings({"unchecked", "RedundantSuppression"})
LIBERICA("BellSoft Liberica", "liberica", "BellSoft",
EnumSet.of(JDK, JRE, JDKFX, JREFX),
pair(WINDOWS, EnumSet.of(X86_64, X86, ARM64)),
@@ -209,7 +209,7 @@ public final class CrashReportAnalyzer {
}
}
public static Set<Result> anaylze(String log) {
public static Set<Result> analyze(String log) {
Set<Result> results = new HashSet<>();
for (Rule rule : Rule.values()) {
Matcher matcher = rule.pattern.matcher(log);
@@ -24,6 +24,7 @@ import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import javafx.scene.image.Image;
import org.jackhuang.hmcl.util.io.*;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.InputStream;
@@ -31,6 +32,7 @@ import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.List;
@@ -41,7 +43,7 @@ import java.util.zip.GZIPOutputStream;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
public class World {
public final class World {
private final Path file;
private String fileName;
@@ -49,6 +51,8 @@ public class World {
private String gameVersion;
private long lastPlayed;
private Image icon;
private Long seed;
private boolean largeBiomes;
private boolean isLocked;
public World(Path file) throws IOException {
@@ -65,7 +69,7 @@ public class World {
private void loadFromDirectory() throws IOException {
fileName = FileUtils.getName(file);
Path levelDat = file.resolve("level.dat");
getWorldName(levelDat);
loadWorldInfo(levelDat);
isLocked = isLocked(getSessionLockFile());
Path iconFile = file.resolve("icon.png");
@@ -108,6 +112,14 @@ public class World {
return gameVersion;
}
public @Nullable Long getSeed() {
return seed;
}
public boolean isLargeBiomes() {
return largeBiomes;
}
public Image getIcon() {
return icon;
}
@@ -121,7 +133,7 @@ public class World {
if (!Files.exists(levelDat))
throw new IOException("Not a valid world zip file since level.dat cannot be found.");
getWorldName(levelDat);
loadWorldInfo(levelDat);
Path iconFile = root.resolve("icon.png");
if (Files.isRegularFile(iconFile)) {
@@ -154,7 +166,7 @@ public class World {
}
}
private void getWorldName(Path levelDat) throws IOException {
private void loadWorldInfo(Path levelDat) throws IOException {
CompoundTag nbt = parseLevelDat(levelDat);
CompoundTag data = nbt.get("Data");
@@ -178,6 +190,27 @@ public class World {
if (version.get("Name") instanceof StringTag)
gameVersion = version.<StringTag>get("Name").getValue();
}
Tag worldGenSettings = data.get("WorldGenSettings");
if (worldGenSettings instanceof CompoundTag) {
Tag seedTag = ((CompoundTag) worldGenSettings).get("seed");
if (seedTag instanceof LongTag) {
seed = ((LongTag) seedTag).getValue();
}
}
if (seed == null) {
Tag seedTag = data.get("RandomSeed");
if (seedTag instanceof LongTag) {
seed = ((LongTag) seedTag).getValue();
}
}
// FIXME: Only work for 1.15 and below
if (data.get("generatorName") instanceof StringTag) {
largeBiomes = "largeBiomes".equals(data.<StringTag>get("generatorName").getValue());
} else {
largeBiomes = false;
}
}
public void rename(String newName) throws IOException {
@@ -293,7 +326,7 @@ public class World {
private static boolean isLocked(Path sessionLockFile) {
try (FileChannel fileChannel = FileChannel.open(sessionLockFile, StandardOpenOption.WRITE)) {
return fileChannel.tryLock() == null;
} catch (AccessDeniedException accessDeniedException) {
} catch (AccessDeniedException | OverlappingFileLockException accessDeniedException) {
return true;
} catch (NoSuchFileException noSuchFileException) {
return false;
@@ -169,7 +169,7 @@ public class DefaultLauncher extends Launcher {
}
if (OperatingSystem.CURRENT_OS != OperatingSystem.WINDOWS)
res.addDefault("-Duser.home=", options.getGameDir().getParent());
res.addDefault("-Duser.home=", options.getGameDir().getAbsoluteFile().getParent());
Proxy.Type proxyType = options.getProxyType();
if (proxyType == null) {
@@ -132,7 +132,7 @@ public class Datapack {
try {
PackMcMeta pack = JsonUtils.fromNonNullJson(FileUtils.readText(mcmeta), PackMcMeta.class);
Platform.runLater(() -> info.add(new Pack(path, FileUtils.getNameWithoutExtension(path), pack.getPackInfo().getDescription(), this)));
} catch (IOException | JsonParseException e) {
} catch (Exception e) {
LOG.warning("Failed to read datapack " + path, e);
}
} else {
@@ -144,7 +144,7 @@ public class Datapack {
public void loadFromDir() {
try {
loadFromDir(path);
} catch (IOException e) {
} catch (Exception e) {
LOG.warning("Failed to read datapacks " + path, e);
}
}
@@ -302,11 +302,13 @@ public class RemoteMod {
public FileDownloadTask.IntegrityCheck getIntegrityCheck() {
if (hashes.containsKey("md5")) {
return new FileDownloadTask.IntegrityCheck("MD5", hashes.get("sha1"));
return new FileDownloadTask.IntegrityCheck("MD5", hashes.get("md5"));
} else if (hashes.containsKey("sha1")) {
return new FileDownloadTask.IntegrityCheck("SHA-1", hashes.get("sha1"));
} else if (hashes.containsKey("sha256")) {
return new FileDownloadTask.IntegrityCheck("SHA-256", hashes.get("sha256"));
} else if (hashes.containsKey("sha512")) {
return new FileDownloadTask.IntegrityCheck("SHA-256", hashes.get("sha1"));
return new FileDownloadTask.IntegrityCheck("SHA-512", hashes.get("sha512"));
} else {
return null;
}
@@ -46,6 +46,7 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
public static final ModrinthRemoteModRepository MODS = new ModrinthRemoteModRepository("mod");
public static final ModrinthRemoteModRepository MODPACKS = new ModrinthRemoteModRepository("modpack");
public static final ModrinthRemoteModRepository RESOURCE_PACKS = new ModrinthRemoteModRepository("resourcepack");
public static final ModrinthRemoteModRepository SHADER_PACKS = new ModrinthRemoteModRepository("shader");
private static final String PREFIX = "https://api.modrinth.com";
@@ -45,8 +45,8 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -96,7 +96,10 @@ public final class MultiMCInstancePatch {
@SerializedName("jarMods")
private final List<Library> jarMods;
public MultiMCInstancePatch(int formatVersion, String id, String version, AssetIndexInfo assetIndex, String minecraftArguments, List<String> jvmArgs, String mainClass, int[] javaMajors, Library mainJar, List<String> traits, List<String> tweakers, List<Library> libraries0, List<Library> libraries1, List<Library> mavenFiles, List<Library> jarMods) {
@SerializedName("requires")
private final List<MultiMCManifest.MultiMCManifestCachedRequires> requires;
public MultiMCInstancePatch(int formatVersion, String id, String version, AssetIndexInfo assetIndex, String minecraftArguments, List<String> jvmArgs, String mainClass, int[] javaMajors, Library mainJar, List<String> traits, List<String> tweakers, List<Library> libraries0, List<Library> libraries1, List<Library> mavenFiles, List<Library> jarMods, List<MultiMCManifest.MultiMCManifestCachedRequires> requires) {
this.formatVersion = formatVersion;
this.id = id;
this.version = version;
@@ -112,6 +115,7 @@ public final class MultiMCInstancePatch {
this.libraries1 = libraries1;
this.mavenFiles = mavenFiles;
this.jarMods = jarMods;
this.requires = requires;
}
public int getFormatVersion() {
@@ -177,16 +181,22 @@ public final class MultiMCInstancePatch {
return nonNullOrEmpty(jarMods);
}
public List<MultiMCManifest.MultiMCManifestCachedRequires> getRequires() {
return nonNullOrEmpty(requires);
}
private static <T> List<T> nonNullOrEmpty(List<T> value) {
return value != null && !value.isEmpty() ? value : Collections.emptyList();
}
private static <T, K> List<T> dropDuplicate(List<T> original, Function<T, K> mapper) {
Set<K> values = new HashSet<>();
private static <T> List<T> dropDuplicate(List<T> original) {
// TODO: Maybe new ArrayList(new LinkedHashSet(original)) ?
Set<T> values = new HashSet<>();
List<T> result = new ArrayList<>();
for (T item : original) {
if (values.add(mapper.apply(item))) {
if (values.add(item)) {
result.add(item);
}
}
@@ -257,6 +267,8 @@ public final class MultiMCInstancePatch {
}
for (MultiMCInstancePatch patch : patches) {
Objects.requireNonNull(patch, "patch");
if (patch.getFormatVersion() != 1) {
throw new UnsupportedOperationException(
String.format("Unsupported JSON-Patch[%s] format version: %d", patch.getID(), patch.getFormatVersion())
@@ -274,6 +286,8 @@ public final class MultiMCInstancePatch {
Library mainJar;
List<String> traits;
List<String> tweakers;
/* TODO: MultiMC use a slightly different way to store jars containing jni files.
Transforming them to Official Scheme might boost compatibility with other launchers. */
List<Library> libraries;
List<Library> mavenOnlyFiles;
List<String> jarModFileNames;
@@ -327,10 +341,9 @@ public final class MultiMCInstancePatch {
}
}
traits = dropDuplicate(traits, Function.identity());
tweakers = dropDuplicate(tweakers, Function.identity());
libraries = dropDuplicate(libraries, Library::getName);
jarModFileNames = dropDuplicate(jarModFileNames, Function.identity());
traits = dropDuplicate(traits);
tweakers = dropDuplicate(tweakers);
jarModFileNames = dropDuplicate(jarModFileNames);
for (String tweaker : tweakers) {
minecraftArguments.add("--tweakClass");
@@ -86,7 +86,7 @@ public final class MultiMCManifest {
return equalsVersion;
}
public String getUid() {
public String getID() {
return uid;
}
@@ -44,16 +44,26 @@ import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* <p>A task transforming MultiMC Modpack Scheme to Official Launcher Scheme.
* The transforming process contains 7 stage:
* General Setup, Load Components, Resolve Json-Patch, Build Artifact,
* Copy Embedded Files, Assemble Game, Download Game and Apply JAR mods.
* <ul>
* <li>General Setup</li>
* <li>Load Components</li>
* <li>Resolve Json-Patch</li>
* <li>Build Artifact</li>
* <li>Copy Embedded Files</li>
* <li>Assemble Game</li>
* <li>Download Game</li>
* <li>Apply JAR mods</li>
* </ul>
* See codes below for detailed implementation.
* </p>
*/
@@ -64,7 +74,6 @@ public final class MultiMCModpackInstallTask extends Task<MultiMCInstancePatch.R
private final MultiMCInstanceConfiguration manifest;
private final String name;
private final DefaultGameRepository repository;
private final List<Task<MultiMCInstancePatch>> patches = new ArrayList<>();
private final List<Task<?>> dependents = new ArrayList<>();
private final List<Task<?>> dependencies = new ArrayList<>();
private final DefaultDependencyManager dependencyManager;
@@ -125,29 +134,74 @@ public final class MultiMCModpackInstallTask extends Task<MultiMCInstancePatch.R
try (FileSystem fs = openModpack()) {
Path root = getRootPath(fs);
List<Task<MultiMCInstancePatch>> patches = new ArrayList<>();
for (MultiMCManifest.MultiMCManifestComponent component : Objects.requireNonNull(
Objects.requireNonNull(manifest.getMmcPack(), "mmc-pack.json").getComponents(), "components"
)) {
String componentID = Objects.requireNonNull(component.getUid(), "Component ID");
Path patchPath = root.resolve(String.format("patches/%s.json", componentID));
Task<String> task;
if (Files.exists(patchPath)) {
if (!Files.isRegularFile(patchPath)) {
throw new IllegalArgumentException("Json-Patch isn't a file: " + componentID);
}
// TODO: Task.completed has unclear compatibility issue.
String text = FileUtils.readText(patchPath, StandardCharsets.UTF_8);
task = Task.supplyAsync(() -> text);
MultiMCInstancePatch patch = MultiMCInstancePatch.read(componentID, FileUtils.readText(patchPath, StandardCharsets.UTF_8));
patches.add(Task.supplyAsync(() -> patch)); // TODO: Task.completed has unclear compatibility issue.
} else {
task = new GetTask(MultiMCComponents.getMetaURL(componentID, component.getVersion()));
patches.add(
new GetTask(MultiMCComponents.getMetaURL(componentID, component.getVersion()))
.thenApplyAsync(s -> MultiMCInstancePatch.read(componentID, s))
);
}
}
dependents.add(new MMCInstancePatchesAssembleTask(patches));
}
}
private static final class MMCInstancePatchesAssembleTask extends Task<List<MultiMCInstancePatch>> {
private final List<Task<MultiMCInstancePatch>> patches;
public MMCInstancePatchesAssembleTask(List<Task<MultiMCInstancePatch>> patches) {
this.patches = patches;
}
@Override
public Collection<? extends Task<?>> getDependents() {
return patches;
}
@Override
public void execute() throws Exception {
Map<String, MultiMCInstancePatch> existed = new HashMap<>();
for (Task<MultiMCInstancePatch> patch : patches) {
MultiMCInstancePatch result = patch.getResult();
existed.put(result.getID(), result);
}
checking:
while (true) {
for (MultiMCInstancePatch patch : existed.values()) {
for (MultiMCManifest.MultiMCManifestCachedRequires require : patch.getRequires()) {
String componentID = require.getID();
if (!existed.containsKey(componentID)) {
Task<MultiMCInstancePatch> task = new GetTask(MultiMCComponents.getMetaURL(
componentID, Lang.requireNonNullElse(require.getEqualsVersion(), require.getSuggests())
)).thenApplyAsync(s -> MultiMCInstancePatch.read(componentID, s));
task.run();
MultiMCInstancePatch result = Objects.requireNonNull(task.getResult());
existed.put(result.getID(), result);
continue checking;
}
}
}
Task<MultiMCInstancePatch> task2 = task.thenApplyAsync(s -> MultiMCInstancePatch.read(componentID, s));
patches.add(task2);
dependents.add(task2);
break;
}
setResult(new ArrayList<>(existed.values()));
}
}
@@ -160,10 +214,15 @@ public final class MultiMCModpackInstallTask extends Task<MultiMCInstancePatch.R
@Override
public void execute() throws Exception {
// Stage #3: Build Json-Patch artifact.
MultiMCInstancePatch.ResolvedInstance artifact = MultiMCInstancePatch.resolveArtifact(patches.stream()
.map(value -> Objects.requireNonNull(value.getResult(), "MultiMCInstancePatch"))
.collect(Collectors.toList()), name
);
MultiMCInstancePatch.ResolvedInstance artifact = null;
for (int i = dependents.size() - 1; i >= 0; i--) {
Task<?> task = dependents.get(i);
if (task instanceof MMCInstancePatchesAssembleTask) {
artifact = MultiMCInstancePatch.resolveArtifact(((MMCInstancePatchesAssembleTask) task).getResult(), name);
break;
}
}
Objects.requireNonNull(artifact, "artifact");
// Stage #4: Copy embedded files.
try (FileSystem fs = openModpack()) {
@@ -49,7 +49,7 @@ public final class MacOSHardwareDetector extends HardwareDetector {
try {
Map<String, String> values = SystemUtils.run(Arrays.asList("/usr/sbin/sysctl", "machdep.cpu"),
inputStream -> KeyValuePairUtils.loadProperties(
inputStream -> KeyValuePairUtils.loadPairs(
new BufferedReader(new InputStreamReader(inputStream, OperatingSystem.NATIVE_CHARSET))));
String brandString = values.get("machdep.cpu.brand_string");
@@ -813,4 +813,22 @@
1.21.5-rc1
1.21.5-rc2
1.21.5
25w14craftmine
25w14craftmine
25w15a
25w16a
25w17a
25w18a
25w19a
25w20a
25w21a
1.21.6-pre1
1.21.6-pre2
1.21.6-pre3
1.21.6-pre4
1.21.6-rc1
1.21.6
1.21.7-rc1
1.21.7-rc2
1.21.7
1.21.8-rc1
1.21.8
@@ -1,4 +1,4 @@
# https://gitlab.freedesktop.org/mesa/drm/-/blob/b65d6ede3ebe64dbde5805007ddef22f390f8050/data/amdgpu.ids
# https://gitlab.freedesktop.org/mesa/libdrm/-/blob/7d43d9b6fe5df4b234937fce9e51adb1b327ded1/data/amdgpu.ids
#
# List of AMDGPU IDs
#
@@ -591,7 +591,9 @@
74B6, 00, AMD Instinct MI308X
74BD, 00, AMD Instinct MI300X HF
7550, C0, AMD Radeon RX 9070 XT
7550, C2, AMD Radeon RX 9070 GRE
7550, C3, AMD Radeon RX 9070
7551, C0, AMD Radeon AI PRO R9700
9830, 00, AMD Radeon HD 8400 / R3 Series
9831, 00, AMD Radeon HD 8400E
9832, 00, AMD Radeon HD 8330
@@ -47,42 +47,42 @@ public class CrashReportAnalyzerTest {
@Test
public void jdk9() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/java9.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/java9.txt")),
CrashReportAnalyzer.Rule.JDK_9);
}
@Test
public void jadeForestOptifine() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/jade_forest_optifine.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/jade_forest_optifine.txt")),
CrashReportAnalyzer.Rule.JADE_FOREST_OPTIFINE);
}
@Test
public void rtssForestSodium() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/rtss_forest_sodium.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/rtss_forest_sodium.txt")),
CrashReportAnalyzer.Rule.RTSS_FOREST_SODIUM);
}
@Test
public void jvm32() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/jvm_32bit.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/jvm_32bit.txt")),
CrashReportAnalyzer.Rule.JVM_32BIT);
}
@Test
public void jvm321() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/jvm_32bit2.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/jvm_32bit2.txt")),
CrashReportAnalyzer.Rule.JVM_32BIT);
}
@Test
public void modResolution() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/mod_resolution.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/mod_resolution.txt")),
CrashReportAnalyzer.Rule.MOD_RESOLUTION);
assertEquals(("Errors were found!\n" +
" - Mod test depends on mod {fabricloader @ [>=0.11.3]}, which is missing!\n" +
@@ -94,7 +94,7 @@ public class CrashReportAnalyzerTest {
@Test
public void forgemodResolution() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/forgemod_resolution.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/forgemod_resolution.txt")),
CrashReportAnalyzer.Rule.FORGEMOD_RESOLUTION);
assertEquals(("\tMod ID: 'vampirism', Requested by: 'werewolves', Expected range: '[1.9.0-beta.1,)', Actual version: '[MISSING]'\n").replaceAll("\\s+", ""),
result.getMatcher().group("reason").replaceAll("\\s+", ""));
@@ -103,7 +103,7 @@ public class CrashReportAnalyzerTest {
@Test
public void forgeFoundDuplicateMods() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/forge_found_duplicate_mods.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/forge_found_duplicate_mods.txt")),
CrashReportAnalyzer.Rule.FORGE_FOUND_DUPLICATE_MODS);
assertEquals(("\tMod ID: 'jei' from mod files: REIPluginCompatibilities-forge-12.0.93.jar, jei-1.20.1-forge-15.2.0.27.jar\n").replaceAll("\\s+", ""),
result.getMatcher().group("reason").replaceAll("\\s+", ""));
@@ -112,7 +112,7 @@ public class CrashReportAnalyzerTest {
@Test
public void modResolutionCollection() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/mod_resolution_collection.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/mod_resolution_collection.txt")),
CrashReportAnalyzer.Rule.MOD_RESOLUTION_COLLECTION);
assertEquals("tabtps-fabric", result.getMatcher().group("sourcemod"));
assertEquals("{fabricloader @ [>=0.11.1]}", result.getMatcher().group("destmod"));
@@ -121,7 +121,7 @@ public class CrashReportAnalyzerTest {
@Test
public void forgeEroor() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/forge_error.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/forge_error.txt")),
CrashReportAnalyzer.Rule.FORGE_ERROR);
assertEquals(("\nnet.minecraftforge.fml.common.MissingModsException: Mod pixelmon (Pixelmon) requires [forge@[14.23.5.2860,)]\n" +
"\tat net.minecraftforge.fml.common.Loader.sortModList(Loader.java:264) ~[Loader.class:?]\n" +
@@ -148,14 +148,14 @@ public class CrashReportAnalyzerTest {
@Test
public void modResolution0() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/mod_resolution0.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/mod_resolution0.txt")),
CrashReportAnalyzer.Rule.MOD_RESOLUTION0);
}
@Test
public void tooOldJava() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/too_old_java.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/too_old_java.txt")),
CrashReportAnalyzer.Rule.TOO_OLD_JAVA);
assertEquals("60", result.getMatcher().group("expected"));
}
@@ -163,7 +163,7 @@ public class CrashReportAnalyzerTest {
@Test
public void tooOldJava1() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/too_old_java.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/too_old_java.txt")),
CrashReportAnalyzer.Rule.TOO_OLD_JAVA);
assertEquals("52", result.getMatcher().group("expected"));
}
@@ -171,7 +171,7 @@ public class CrashReportAnalyzerTest {
@Test
public void tooOldJava2() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/too_old_java2.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/too_old_java2.txt")),
CrashReportAnalyzer.Rule.TOO_OLD_JAVA);
assertEquals("52", result.getMatcher().group("expected"));
}
@@ -179,14 +179,14 @@ public class CrashReportAnalyzerTest {
@Test
public void javaVersionIsTooHigh() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/java_version_is_too_high.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/java_version_is_too_high.txt")),
CrashReportAnalyzer.Rule.JAVA_VERSION_IS_TOO_HIGH);
}
@Test
public void securityException() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/security.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/security.txt")),
CrashReportAnalyzer.Rule.FILE_CHANGED);
assertEquals("assets/minecraft/texts/splashes.txt", result.getMatcher().group("file"));
}
@@ -194,7 +194,7 @@ public class CrashReportAnalyzerTest {
@Test
public void noClassDefFoundError1() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/no_class_def_found_error.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/no_class_def_found_error.txt")),
CrashReportAnalyzer.Rule.NO_CLASS_DEF_FOUND_ERROR);
assertEquals("blk", result.getMatcher().group("class"));
}
@@ -202,7 +202,7 @@ public class CrashReportAnalyzerTest {
@Test
public void noClassDefFoundError2() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/no_class_def_found_error2.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/no_class_def_found_error2.txt")),
CrashReportAnalyzer.Rule.NO_CLASS_DEF_FOUND_ERROR);
assertEquals("cer", result.getMatcher().group("class"));
}
@@ -210,7 +210,7 @@ public class CrashReportAnalyzerTest {
@Test
public void fileAlreadyExists() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/file_already_exists.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/file_already_exists.txt")),
CrashReportAnalyzer.Rule.FILE_ALREADY_EXISTS);
assertEquals(
"D:\\Games\\Minecraft\\Minecraft Longtimeusing\\.minecraft\\versions\\1.12.2-forge1.12.2-14.23.5.2775\\config\\pvpsettings.txt",
@@ -220,7 +220,7 @@ public class CrashReportAnalyzerTest {
@Test
public void loaderExceptionModCrash() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/loader_exception_mod_crash.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/loader_exception_mod_crash.txt")),
CrashReportAnalyzer.Rule.LOADING_CRASHED_FORGE);
assertEquals("Better PvP", result.getMatcher().group("name"));
assertEquals("xaerobetterpvp", result.getMatcher().group("id"));
@@ -229,7 +229,7 @@ public class CrashReportAnalyzerTest {
@Test
public void loaderExceptionModCrash2() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/loader_exception_mod_crash2.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/loader_exception_mod_crash2.txt")),
CrashReportAnalyzer.Rule.LOADING_CRASHED_FORGE);
assertEquals("Inventory Sort", result.getMatcher().group("name"));
assertEquals("invsort", result.getMatcher().group("id"));
@@ -238,7 +238,7 @@ public class CrashReportAnalyzerTest {
@Test
public void loaderExceptionModCrash3() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/loader_exception_mod_crash3.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/loader_exception_mod_crash3.txt")),
CrashReportAnalyzer.Rule.LOADING_CRASHED_FORGE);
assertEquals("SuperOres", result.getMatcher().group("name"));
assertEquals("superores", result.getMatcher().group("id"));
@@ -247,7 +247,7 @@ public class CrashReportAnalyzerTest {
@Test
public void loaderExceptionModCrash4() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/loader_exception_mod_crash4.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/loader_exception_mod_crash4.txt")),
CrashReportAnalyzer.Rule.LOADING_CRASHED_FORGE);
assertEquals("Kathairis", result.getMatcher().group("name"));
assertEquals("kathairis", result.getMatcher().group("id"));
@@ -256,7 +256,7 @@ public class CrashReportAnalyzerTest {
@Test
public void loadingErrorFabric() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/loading_error_fabric.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/loading_error_fabric.txt")),
CrashReportAnalyzer.Rule.LOADING_CRASHED_FABRIC);
assertEquals("test", result.getMatcher().group("id"));
}
@@ -264,56 +264,56 @@ public class CrashReportAnalyzerTest {
@Test
public void graphicsDriver() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/graphics_driver.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/graphics_driver.txt")),
CrashReportAnalyzer.Rule.GRAPHICS_DRIVER);
}
@Test
public void graphicsDriverJVM() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/graphics_driver.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/graphics_driver.txt")),
CrashReportAnalyzer.Rule.GRAPHICS_DRIVER);
}
@Test
public void splashScreen() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/splashscreen.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/splashscreen.txt")),
CrashReportAnalyzer.Rule.GRAPHICS_DRIVER);
}
@Test
public void macosFailedToFindServicePortForDisplay() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/macos_failed_to_find_service_port_for_display.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/macos_failed_to_find_service_port_for_display.txt")),
CrashReportAnalyzer.Rule.MACOS_FAILED_TO_FIND_SERVICE_PORT_FOR_DISPLAY);
}
@Test
public void modName() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/mod_name.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/mod_name.txt")),
CrashReportAnalyzer.Rule.MOD_NAME);
}
@Test
public void openj9() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/openj9.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/openj9.txt")),
CrashReportAnalyzer.Rule.OPENJ9);
}
@Test
public void resolutionTooHigh() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/resourcepack_resolution.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/resourcepack_resolution.txt")),
CrashReportAnalyzer.Rule.RESOLUTION_TOO_HIGH);
}
@Test
public void bootstrapFailed() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/bootstrap.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/bootstrap.txt")),
CrashReportAnalyzer.Rule.BOOTSTRAP_FAILED);
assertEquals("prefab", result.getMatcher().group("id"));
}
@@ -321,7 +321,7 @@ public class CrashReportAnalyzerTest {
@Test
public void mixinApplyModFailed() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/mixin_apply_mod_failed.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/mixin_apply_mod_failed.txt")),
CrashReportAnalyzer.Rule.MIXIN_APPLY_MOD_FAILED);
assertEquals("enhancedblockentities", result.getMatcher().group("id"));
}
@@ -329,7 +329,7 @@ public class CrashReportAnalyzerTest {
@Test
public void unsatisfiedLinkError() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/unsatisfied_link_error.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/unsatisfied_link_error.txt")),
CrashReportAnalyzer.Rule.UNSATISFIED_LINK_ERROR);
assertEquals("lwjgl.dll", result.getMatcher().group("name"));
}
@@ -337,35 +337,35 @@ public class CrashReportAnalyzerTest {
@Test
public void outOfMemoryMC() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/out_of_memory.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/out_of_memory.txt")),
CrashReportAnalyzer.Rule.OUT_OF_MEMORY);
}
@Test
public void outOfMemoryJVM() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/out_of_memory.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/out_of_memory.txt")),
CrashReportAnalyzer.Rule.OUT_OF_MEMORY);
}
@Test
public void outOfMemoryJVM1() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/out_of_memory2.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/out_of_memory2.txt")),
CrashReportAnalyzer.Rule.OUT_OF_MEMORY);
}
@Test
public void memoryExceeded() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/memory_exceeded.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/memory_exceeded.txt")),
CrashReportAnalyzer.Rule.MEMORY_EXCEEDED);
}
@Test
public void config() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/config.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/config.txt")),
CrashReportAnalyzer.Rule.CONFIG);
assertEquals("jumbofurnace", result.getMatcher().group("id"));
assertEquals("jumbofurnace-server.toml", result.getMatcher().group("file"));
@@ -374,7 +374,7 @@ public class CrashReportAnalyzerTest {
@Test
public void fabricWarnings() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/fabric_warnings.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/fabric_warnings.txt")),
CrashReportAnalyzer.Rule.FABRIC_WARNINGS);
assertEquals((" - Conflicting versions found for fabric-api-base: used 0.3.0+a02b446313, also found 0.3.0+a02b44633d, 0.3.0+a02b446318\n" +
" - Conflicting versions found for fabric-rendering-data-attachment-v1: used 0.1.5+a02b446313, also found 0.1.5+a02b446318\n" +
@@ -390,7 +390,7 @@ public class CrashReportAnalyzerTest {
@Test
public void fabricWarnings1() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/fabric_warnings2.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/fabric_warnings2.txt")),
CrashReportAnalyzer.Rule.FABRIC_WARNINGS);
assertEquals(("net.fabricmc.loader.impl.FormattedException: Mod resolution encountered an incompatible mod set!\n" +
"A potential solution has been determined:\n" +
@@ -404,7 +404,7 @@ public class CrashReportAnalyzerTest {
@Test
public void fabricWarnings2() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/fabric_warnings3.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/fabric_warnings3.txt")),
CrashReportAnalyzer.Rule.FABRIC_WARNINGS);
assertEquals(("net.fabricmc.loader.impl.FormattedException: Some of your mods are incompatible with the game or each other!\n" +
"确定了一种可能的解决方法,这样做可能会解决你的问题:\n" +
@@ -420,7 +420,7 @@ public class CrashReportAnalyzerTest {
@Test
public void fabricConflicts() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/fabric-mod-conflict.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/fabric-mod-conflict.txt")),
CrashReportAnalyzer.Rule.MOD_RESOLUTION_CONFLICT);
assertEquals("phosphor", result.getMatcher().group("sourcemod"));
assertEquals("{starlight @ [*]}", result.getMatcher().group("destmod"));
@@ -429,7 +429,7 @@ public class CrashReportAnalyzerTest {
@Test
public void fabricMissing() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/fabric-mod-missing.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/fabric-mod-missing.txt")),
CrashReportAnalyzer.Rule.MOD_RESOLUTION_MISSING);
assertEquals("pca", result.getMatcher().group("sourcemod"));
assertEquals("{fabric @ [>=0.39.2]}", result.getMatcher().group("destmod"));
@@ -438,35 +438,35 @@ public class CrashReportAnalyzerTest {
@Test
public void fabric0_12() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/fabric-version-0.12.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/fabric-version-0.12.txt")),
CrashReportAnalyzer.Rule.FABRIC_VERSION_0_12);
}
@Test
public void twilightForestOptiFineIncompatible() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/mod/twilightforest_optifine_incompatibility.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/mod/twilightforest_optifine_incompatibility.txt")),
CrashReportAnalyzer.Rule.TWILIGHT_FOREST_OPTIFINE);
}
@Test
public void performantOptiFineIncompatible() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/mod/performant_optifine_incompatibility.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/mod/performant_optifine_incompatibility.txt")),
CrashReportAnalyzer.Rule.PERFORMANT_FOREST_OPTIFINE);
}
@Test
public void neoforgeForestOptiFineIncompatible() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/mod/neoforgeforest_optifine_incompatibility.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/mod/neoforgeforest_optifine_incompatibility.txt")),
CrashReportAnalyzer.Rule.NEOFORGE_FOREST_OPTIFINE);
}
@Test
public void fabricMissingMinecraft() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/fabric-minecraft.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/fabric-minecraft.txt")),
CrashReportAnalyzer.Rule.MOD_RESOLUTION_MISSING_MINECRAFT);
assertEquals("fabric", result.getMatcher().group("mod"));
assertEquals("[~1.16.2-alpha.20.28.a]", result.getMatcher().group("version"));
@@ -475,161 +475,161 @@ public class CrashReportAnalyzerTest {
@Test
public void optifineRepeatInstallation() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/optifine_repeat_installation.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/optifine_repeat_installation.txt")),
CrashReportAnalyzer.Rule.OPTIFINE_REPEAT_INSTALLATION);
}
@Test
public void incompleteForgeInstallation() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/incomplete_forge_installation.txt")),
CrashReportAnalyzer.analyze(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.analyze(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.analyze(loadLog("/logs/incomplete_forge_installation3.txt")),
CrashReportAnalyzer.Rule.INCOMPLETE_FORGE_INSTALLATION);
}
@Test
public void incompleteForgeInstallation4() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/incomplete_forge_installation4.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/incomplete_forge_installation4.txt")),
CrashReportAnalyzer.Rule.INCOMPLETE_FORGE_INSTALLATION);
}
@Test
public void incompleteForgeInstallation5() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/incomplete_forge_installation5.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/incomplete_forge_installation5.txt")),
CrashReportAnalyzer.Rule.INCOMPLETE_FORGE_INSTALLATION);
}
@Test
public void incompleteForgeInstallation6() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/incomplete_forge_installation6.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/incomplete_forge_installation6.txt")),
CrashReportAnalyzer.Rule.INCOMPLETE_FORGE_INSTALLATION);
}
@Test
public void incompleteForgeInstallation7() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/incomplete_forge_installation7.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/incomplete_forge_installation7.txt")),
CrashReportAnalyzer.Rule.INCOMPLETE_FORGE_INSTALLATION);
}
@Test
public void forgeRepeatInstallation() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/forge_repeat_installation.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/forge_repeat_installation.txt")),
CrashReportAnalyzer.Rule.FORGE_REPEAT_INSTALLATION);
}
@Test
public void forgeRepeatInstallation1() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/forge_repeat_installation2.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/forge_repeat_installation2.txt")),
CrashReportAnalyzer.Rule.FORGE_REPEAT_INSTALLATION);
}
@Test
public void needJDK11() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/need_jdk11.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/need_jdk11.txt")),
CrashReportAnalyzer.Rule.NEED_JDK11);
}
@Test
public void needJDK112() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/need_jdk112.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/need_jdk112.txt")),
CrashReportAnalyzer.Rule.NEED_JDK11);
}
@Test
public void needJDK113() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/need_jdk113.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/need_jdk113.txt")),
CrashReportAnalyzer.Rule.NEED_JDK11);
}
@Test
public void optifineIsNotCompatibleWithForge() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/optifine_is_not_compatible_with_forge.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/optifine_is_not_compatible_with_forge.txt")),
CrashReportAnalyzer.Rule.OPTIFINE_IS_NOT_COMPATIBLE_WITH_FORGE);
}
@Test
public void optifineIsNotCompatibleWithForge1() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/optifine_is_not_compatible_with_forge2.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/optifine_is_not_compatible_with_forge2.txt")),
CrashReportAnalyzer.Rule.OPTIFINE_IS_NOT_COMPATIBLE_WITH_FORGE);
}
@Test
public void optifineIsNotCompatibleWithForge2() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/optifine_is_not_compatible_with_forge3.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/optifine_is_not_compatible_with_forge3.txt")),
CrashReportAnalyzer.Rule.OPTIFINE_IS_NOT_COMPATIBLE_WITH_FORGE);
}
@Test
public void optifineIsNotCompatibleWithForge3() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/optifine_is_not_compatible_with_forge4.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/optifine_is_not_compatible_with_forge4.txt")),
CrashReportAnalyzer.Rule.OPTIFINE_IS_NOT_COMPATIBLE_WITH_FORGE);
}
@Test
public void optifineIsNotCompatibleWithForge4() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/optifine_is_not_compatible_with_forge5.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/optifine_is_not_compatible_with_forge5.txt")),
CrashReportAnalyzer.Rule.OPTIFINE_IS_NOT_COMPATIBLE_WITH_FORGE);
}
@Test
public void optifineIsNotCompatibleWithForge5() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/optifine_is_not_compatible_with_forge6.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/optifine_is_not_compatible_with_forge6.txt")),
CrashReportAnalyzer.Rule.OPTIFINE_IS_NOT_COMPATIBLE_WITH_FORGE);
}
@Test
public void shadersMod() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/shaders_mod.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/shaders_mod.txt")),
CrashReportAnalyzer.Rule.SHADERS_MOD);
}
@Test
public void installMixinbootstrap() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/logs/install_mixinbootstrap.txt")),
CrashReportAnalyzer.analyze(loadLog("/logs/install_mixinbootstrap.txt")),
CrashReportAnalyzer.Rule.INSTALL_MIXINBOOTSTRAP);
}
@Test
public void nightconfigfixes() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/night_config_fixes.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/night_config_fixes.txt")),
CrashReportAnalyzer.Rule.NIGHT_CONFIG_FIXES);
}
@Test
public void customNpc() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/mod/customnpc.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/mod/customnpc.txt")),
CrashReportAnalyzer.Rule.ENTITY);
assertEquals("customnpcs.CustomNpc (noppes.npcs.entity.EntityCustomNpc)",
result.getMatcher().group("type"));
@@ -644,7 +644,7 @@ public class CrashReportAnalyzerTest {
@Test
public void tconstruct() throws IOException {
CrashReportAnalyzer.Result result = findResultByRule(
CrashReportAnalyzer.anaylze(loadLog("/crash-report/mod/tconstruct.txt")),
CrashReportAnalyzer.analyze(loadLog("/crash-report/mod/tconstruct.txt")),
CrashReportAnalyzer.Rule.BLOCK);
assertEquals("Block{tconstruct:seared_drain}[active=true,facing=north]",
result.getMatcher().group("type"));
+4 -4
View File
@@ -4,13 +4,13 @@
| | Windows | Linux | macOS | FreeBSD |
|----------------------------|:--------------------------------------------------|:---------------------------|:------------------------------------------------------------------------|:---------------------------|
| x86-64 | ✅️ | ✅️ | ✅️ | 👌 (Minecraft 1.13~1.21.4) |
| x86-64 | ✅️ | ✅️ | ✅️ | 👌 (Minecraft 1.13~1.21.7) |
| x86 | ✅️ (~1.20.4) | ✅️ (~1.20.4) | / | / |
| ARM64 | 👌 (Minecraft 1.8~1.18.2)<br/>✅ (Minecraft 1.19+) | 👌 (Minecraft 1.8~1.21.4) | 👌 (Minecraft 1.6~1.18.2)<br/>✅ (Minecraft 1.19+)<br/>✅ (use Rosetta 2) | ❔ |
| ARM64 | 👌 (Minecraft 1.8~1.18.2)<br/>✅ (Minecraft 1.19+) | 👌 (Minecraft 1.8~1.21.7) | 👌 (Minecraft 1.6~1.18.2)<br/>✅ (Minecraft 1.19+)<br/>✅ (use Rosetta 2) | ❔ |
| ARM32 | /️ | 👌 (Minecraft 1.8~1.20.1) | / | / |
| MIPS64el | / | 👌 (Minecraft 1.8~1.20.1) | / | / |
| RISC-V 64 | / | 👌 (Minecraft 1.13~1.21.4) | / | / |
| LoongArch64 | / | 👌 (Minecraft 1.6~1.21.4) | / | / |
| RISC-V 64 | / | 👌 (Minecraft 1.13~1.21.7) | / | / |
| LoongArch64 | / | 👌 (Minecraft 1.6~1.21.7) | / | / |
| LoongArch64 (Old World) | / | 👌 (Minecraft 1.6~1.20.1) | / | / |
| PowerPC-64 (Little-Endian) | / | ❔ | / | / |
| S390x | / | ❔ | / | / |
+4 -4
View File
@@ -4,13 +4,13 @@
| | Windows | Linux | macOS | FreeBSD |
|----------------------------|:--------------------------------------------------|:---------------------------|:-----------------------------------------------------------------------|:--------------------------|
| x86-64 | ✅️ | ✅️ | ✅️ | 👌(Minecraft 1.13~1.21.4) |
| x86-64 | ✅️ | ✅️ | ✅️ | 👌(Minecraft 1.13~1.21.7) |
| x86 | ✅️ (~1.20.4) | ✅️ (~1.20.4) | / | / |
| ARM64 | 👌 (Minecraft 1.8~1.18.2)<br/>✅ (Minecraft 1.19+) | 👌 (Minecraft 1.8~1.21.4) | 👌 (Minecraft 1.6~1.18.2)<br/>✅ (Minecraft 1.19+)<br/>✅ (使用 Rosetta 2) | ❔ |
| ARM64 | 👌 (Minecraft 1.8~1.18.2)<br/>✅ (Minecraft 1.19+) | 👌 (Minecraft 1.8~1.21.7) | 👌 (Minecraft 1.6~1.18.2)<br/>✅ (Minecraft 1.19+)<br/>✅ (使用 Rosetta 2) | ❔ |
| ARM32 | /️ | 👌 (Minecraft 1.8~1.20.1) | / | / |
| MIPS64el | / | 👌 (Minecraft 1.8~1.20.1) | / | / |
| RISC-V 64 | / | 👌 (Minecraft 1.13~1.21.4) | / | / |
| LoongArch64 | / | 👌 (Minecraft 1.6~1.21.4) | / | / |
| RISC-V 64 | / | 👌 (Minecraft 1.13~1.21.7) | / | / |
| LoongArch64 | / | 👌 (Minecraft 1.6~1.21.7) | / | / |
| LoongArch64 (旧世界) | / | 👌 (Minecraft 1.6~1.20.1) | / | / |
| PowerPC-64 (Little-Endian) | / | ❔ | / | / |
| S390x | / | ❔ | / | / |
+4 -4
View File
@@ -4,13 +4,13 @@
| | Windows | Linux | macOS | FreeBSD |
|----------------------------|:--------------------------------------------------|:---------------------------|:-----------------------------------------------------------------------|:--------------------------|
| x86-64 | ✅️ | ✅️ | ✅️ | 👌(Minecraft 1.13~1.21.4) |
| x86-64 | ✅️ | ✅️ | ✅️ | 👌(Minecraft 1.13~1.21.7) |
| x86 | ✅️ (~1.20.4) | ✅️ (~1.20.4) | / | / |
| ARM64 | 👌 (Minecraft 1.8~1.18.2)<br/>✅ (Minecraft 1.19+) | 👌 (Minecraft 1.8~1.21.4) | 👌 (Minecraft 1.6~1.18.2)<br/>✅ (Minecraft 1.19+)<br/>✅ (使用 Rosetta 2) | ❔ |
| ARM64 | 👌 (Minecraft 1.8~1.18.2)<br/>✅ (Minecraft 1.19+) | 👌 (Minecraft 1.8~1.21.7) | 👌 (Minecraft 1.6~1.18.2)<br/>✅ (Minecraft 1.19+)<br/>✅ (使用 Rosetta 2) | ❔ |
| ARM32 | /️ | 👌 (Minecraft 1.8~1.20.1) | / | / |
| MIPS64el | / | 👌 (Minecraft 1.8~1.20.1) | / | / |
| RISC-V 64 | / | 👌 (Minecraft 1.13~1.21.4) | / | / |
| LoongArch64 | / | 👌 (Minecraft 1.6~1.21.4) | / | / |
| RISC-V 64 | / | 👌 (Minecraft 1.13~1.21.7) | / | / |
| LoongArch64 | / | 👌 (Minecraft 1.6~1.21.7) | / | / |
| LoongArch64 (舊世界) | / | 👌 (Minecraft 1.6~1.20.1) | / | / |
| PowerPC-64 (Little-Endian) | / | ❔ | / | / |
| S390x | / | ❔ | / | / |
@@ -1,3 +1,20 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2025 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.gradle.mod;
import com.google.gson.*;
@@ -24,6 +41,9 @@ import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Glavo
*/
public abstract class ParseModDataTask extends DefaultTask {
@InputFile
@@ -44,15 +64,12 @@ public abstract class ParseModDataTask extends DefaultTask {
Pattern.compile("^/projects/(?<modid>[\\w-]+)(/(.*?))?$"),
Pattern.compile("^/mc-mods/minecraft/(?<modid>[\\w-]+)(/(.*?))?$"),
Pattern.compile("^/legacy/mc-mods/minecraft/(\\d+)-(?<modid>[\\w-]+)"),
Pattern.compile("^//minecraft/mc-mods/(?<modid>eyes-mod)$") // Workaround for Eye Blocks
};
private static String parseCurseforge(String url) {
URI res = URI.create(url);
if (!"http".equals(res.getScheme())
&& !"https".equals(res.getScheme())
&& !"www.curseforge.com".equals(res.getHost())) { // Workaround for DakerACG
if (!"http".equals(res.getScheme()) && !"https".equals(res.getScheme())) {
return "";
}
@@ -77,10 +94,10 @@ public abstract class ParseModDataTask extends DefaultTask {
return "";
}
private static final Set<String> skip = new HashSet<>(Arrays.asList(
private static final Set<String> skip = Set.of(
"Minecraft",
"The Building Game"
));
);
@TaskAction
public void run() throws IOException {
+2
View File
@@ -1,4 +1,5 @@
[versions]
hmclauncher = "3.6.0.6"
jetbrains-annotations = "26.0.1"
kala-compress = "1.27.1-1"
simple-png-javafx = "0.3.0"
@@ -20,6 +21,7 @@ java-info = "1.0"
shadow = "8.3.6"
[libraries]
hmclauncher = { module = "org.glavo.hmcl:HMCLauncher", version.ref = "hmclauncher" }
jetbrains-annotations = { module = "org.jetbrains:annotations", version.ref = "jetbrains-annotations" }
kala-compress-zip = { module = "org.glavo.kala:kala-compress-archivers-zip", version.ref = "kala-compress" }
kala-compress-tar = { module = "org.glavo.kala:kala-compress-archivers-tar", version.ref = "kala-compress" }