Compare commits
53
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7a5c7a310 | ||
|
|
633e8f8fe2 | ||
|
|
440ea1b21d | ||
|
|
f9dd7a1e64 | ||
|
|
cbb83d4f15 | ||
|
|
bab863287d | ||
|
|
e4c479754b | ||
|
|
8ebf58e8fa | ||
|
|
064b39d753 | ||
|
|
c669026312 | ||
|
|
d9b95b28b2 | ||
|
|
d492884927 | ||
|
|
cf4fde5592 | ||
|
|
fd959c41c3 | ||
|
|
e34037c055 | ||
|
|
417c804f48 | ||
|
|
ea6eced136 | ||
|
|
432465ab46 | ||
|
|
03c95cd726 | ||
|
|
730febca8c | ||
|
|
bc41499d1c | ||
|
|
60fae5cc88 | ||
|
|
c11837baa4 | ||
|
|
aaf1218536 | ||
|
|
88637b33bc | ||
|
|
462eea3539 | ||
|
|
d624086594 | ||
|
|
fa8b3a952b | ||
|
|
c70a6c3372 | ||
|
|
0616c37de7 | ||
|
|
b72056f7fe | ||
|
|
3ebe8dce44 | ||
|
|
a9c28573b8 | ||
|
|
9bead7e34e | ||
|
|
d0b36ffa15 | ||
|
|
ea081333e7 | ||
|
|
2b99da430f | ||
|
|
f1ea316bad | ||
|
|
9565c3bdfe | ||
|
|
710fd0f075 | ||
|
|
7d7dbb9eb5 | ||
|
|
88a7243b32 | ||
|
|
a0e9a20782 | ||
|
|
8889cca878 | ||
|
|
bc911b95a0 | ||
|
|
a46991bd95 | ||
|
|
395823c2b5 | ||
|
|
9769cf3996 | ||
|
|
ee652f6f31 | ||
|
|
d317844b28 | ||
|
|
d9d32eab1d | ||
|
|
24bb630afa | ||
|
|
2359ad9ed7 |
No files matched your search
@@ -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
|
||||
@@ -41,9 +41,10 @@ dependencies {
|
||||
implementation(project(":HMCLCore"))
|
||||
implementation("libs:JFoenix")
|
||||
implementation(libs.twelvemonkeys.imageio.webp)
|
||||
implementation(libs.java.info)
|
||||
|
||||
if (launcherExe == null) {
|
||||
implementation("org.glavo.hmcl:HMCLauncher:3.6.0.2")
|
||||
implementation(libs.hmclauncher)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import javafx.scene.input.DataFormat;
|
||||
import javafx.stage.Stage;
|
||||
import org.jackhuang.hmcl.setting.ConfigHolder;
|
||||
import org.jackhuang.hmcl.setting.SambaException;
|
||||
import org.jackhuang.hmcl.util.FileSaver;
|
||||
import org.jackhuang.hmcl.task.AsyncTaskExecutor;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
@@ -214,6 +215,7 @@ public final class Launcher extends Application {
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
Controllers.onApplicationStop();
|
||||
FileSaver.shutdown();
|
||||
LOG.shutdown();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jackhuang.hmcl;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.control.Alert;
|
||||
import org.jackhuang.hmcl.util.FileSaver;
|
||||
import org.jackhuang.hmcl.ui.AwtUtils;
|
||||
import org.jackhuang.hmcl.util.ModuleHelper;
|
||||
import org.jackhuang.hmcl.util.SelfDependencyPatcher;
|
||||
@@ -79,6 +80,7 @@ public final class Main {
|
||||
}
|
||||
|
||||
public static void exit(int exitCode) {
|
||||
FileSaver.shutdown();
|
||||
LOG.shutdown();
|
||||
System.exit(exitCode);
|
||||
}
|
||||
|
||||
@@ -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 = 11;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import org.jackhuang.hmcl.mod.Modpack;
|
||||
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||
import org.jackhuang.hmcl.mod.ModpackProvider;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.util.FileSaver;
|
||||
import org.jackhuang.hmcl.setting.VersionIconType;
|
||||
import org.jackhuang.hmcl.setting.VersionSetting;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
@@ -329,22 +330,17 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean saveVersionSetting(String id) {
|
||||
public void saveVersionSetting(String id) {
|
||||
if (!localVersionSettings.containsKey(id))
|
||||
return false;
|
||||
File file = getLocalVersionSettingFile(id);
|
||||
if (!FileUtils.makeDirectory(file.getAbsoluteFile().getParentFile()))
|
||||
return false;
|
||||
|
||||
LOG.info("Saving version setting: " + id);
|
||||
|
||||
return;
|
||||
Path file = getLocalVersionSettingFile(id).toPath().toAbsolutePath().normalize();
|
||||
try {
|
||||
FileUtils.writeText(file, GSON.toJson(localVersionSettings.get(id)));
|
||||
return true;
|
||||
Files.createDirectories(file.getParent());
|
||||
} catch (IOException e) {
|
||||
LOG.error("Unable to save version setting of " + id, e);
|
||||
return false;
|
||||
LOG.warning("Failed to create directory: " + file.getParent(), e);
|
||||
}
|
||||
|
||||
FileSaver.save(file, GSON.toJson(localVersionSettings.get(id)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -168,7 +168,7 @@ public final class HMCLJavaRepository implements JavaRepository {
|
||||
|
||||
JavaInfo info;
|
||||
if (JavaManager.isCompatible(platform))
|
||||
info = JavaInfo.fromExecutable(executable, false);
|
||||
info = JavaInfoUtils.fromExecutable(executable, false);
|
||||
else
|
||||
info = new JavaInfo(platform, result.download.getVersion().getName(), null);
|
||||
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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.java;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.io.JarUtils;
|
||||
import org.jackhuang.hmcl.util.platform.Architecture;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.platform.Platform;
|
||||
import org.jackhuang.hmcl.util.platform.SystemUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* @author Glavo
|
||||
* @see <a href="https://github.com/Glavo/java-info">Glavo/java-info</a>
|
||||
*/
|
||||
public final class JavaInfoUtils {
|
||||
|
||||
private JavaInfoUtils() {
|
||||
}
|
||||
|
||||
private static Path tryFindReleaseFile(Path executable) {
|
||||
Path parent = executable.getParent();
|
||||
if (parent != null && parent.getFileName() != null && parent.getFileName().toString().equals("bin")) {
|
||||
Path javaHome = parent.getParent();
|
||||
if (javaHome != null && javaHome.getFileName() != null) {
|
||||
Path releaseFile = javaHome.resolve("release");
|
||||
String javaHomeName = javaHome.getFileName().toString();
|
||||
if ((javaHomeName.contains("jre") || javaHomeName.contains("jdk") || javaHomeName.contains("openj9"))
|
||||
&& Files.isRegularFile(releaseFile)) {
|
||||
return releaseFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static @NotNull JavaInfo fromExecutable(Path executable, boolean tryFindReleaseFile) throws IOException {
|
||||
assert executable.isAbsolute();
|
||||
|
||||
Path releaseFile;
|
||||
if (tryFindReleaseFile && (releaseFile = tryFindReleaseFile(executable)) != null) {
|
||||
try {
|
||||
return JavaInfo.fromReleaseFile(releaseFile);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
Path thisPath = JarUtils.thisJarPath();
|
||||
|
||||
if (thisPath == null) {
|
||||
throw new IOException("Failed to find current HMCL location");
|
||||
}
|
||||
|
||||
try {
|
||||
Result result = JsonUtils.GSON.fromJson(SystemUtils.run(
|
||||
executable.toString(),
|
||||
"-classpath",
|
||||
thisPath.toString(),
|
||||
org.glavo.info.Main.class.getName()
|
||||
), Result.class);
|
||||
|
||||
if (result == null) {
|
||||
throw new IOException("Failed to get Java info from " + executable);
|
||||
}
|
||||
|
||||
if (result.javaVersion == null) {
|
||||
throw new IOException("Failed to get Java version from " + executable);
|
||||
}
|
||||
|
||||
Architecture architecture = Architecture.parseArchName(result.osArch);
|
||||
Platform platform = Platform.getPlatform(OperatingSystem.CURRENT_OS,
|
||||
architecture != Architecture.UNKNOWN
|
||||
? architecture
|
||||
: Architecture.SYSTEM_ARCH);
|
||||
|
||||
|
||||
return new JavaInfo(platform, result.javaVersion, result.javaVendor);
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Result {
|
||||
@SerializedName("os.name")
|
||||
public String osName;
|
||||
@SerializedName("os.arch")
|
||||
public String osArch;
|
||||
@SerializedName("java.version")
|
||||
public String javaVersion;
|
||||
@SerializedName("java.vendor")
|
||||
public String javaVendor;
|
||||
}
|
||||
}
|
||||
@@ -32,10 +32,7 @@ import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.util.CacheRepository;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.platform.Architecture;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.platform.Platform;
|
||||
import org.jackhuang.hmcl.util.platform.UnsupportedPlatformException;
|
||||
import org.jackhuang.hmcl.util.platform.*;
|
||||
import org.jackhuang.hmcl.util.platform.windows.WinReg;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -162,7 +159,7 @@ public final class JavaManager {
|
||||
return javaRuntime;
|
||||
}
|
||||
|
||||
JavaInfo info = JavaInfo.fromExecutable(executable);
|
||||
JavaInfo info = JavaInfoUtils.fromExecutable(executable, true);
|
||||
return JavaRuntime.of(executable, info, false);
|
||||
}
|
||||
|
||||
@@ -484,7 +481,7 @@ public final class JavaManager {
|
||||
info = JavaInfo.fromReleaseFile(releaseFile);
|
||||
} catch (IOException e) {
|
||||
try {
|
||||
info = JavaInfo.fromExecutable(executable, false);
|
||||
info = JavaInfoUtils.fromExecutable(executable, false);
|
||||
} catch (IOException e2) {
|
||||
e2.addSuppressed(e);
|
||||
LOG.warning("Failed to lookup Java executable at " + executable, e2);
|
||||
@@ -509,7 +506,7 @@ public final class JavaManager {
|
||||
|
||||
JavaInfo info = null;
|
||||
try {
|
||||
info = JavaInfo.fromExecutable(executable);
|
||||
info = JavaInfoUtils.fromExecutable(executable, true);
|
||||
} catch (IOException e) {
|
||||
LOG.warning("Failed to lookup Java executable at " + executable, e);
|
||||
}
|
||||
|
||||
@@ -35,9 +35,7 @@ import org.jackhuang.hmcl.auth.offline.OfflineAccountFactory;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.RemoteAuthenticationException;
|
||||
import org.jackhuang.hmcl.game.OAuthServer;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.util.InvocationDispatcher;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.FileSaver;
|
||||
import org.jackhuang.hmcl.util.skin.InvalidSkinException;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
@@ -184,21 +182,8 @@ public final class Accounts {
|
||||
}
|
||||
}
|
||||
|
||||
InvocationDispatcher<String> dispatcher = InvocationDispatcher.runOn(Lang::thread, json -> {
|
||||
LOG.info("Saving global accounts");
|
||||
synchronized (globalAccountsFile) {
|
||||
try {
|
||||
synchronized (globalAccountsFile) {
|
||||
FileUtils.saveSafely(globalAccountsFile, json);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to save global accounts", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
globalAccountStorages.addListener(onInvalidating(() ->
|
||||
dispatcher.accept(Config.CONFIG_GSON.toJson(globalAccountStorages))));
|
||||
FileSaver.save(globalAccountsFile, Config.CONFIG_GSON.toJson(globalAccountStorages))));
|
||||
}
|
||||
|
||||
private static Account parseAccount(Map<Object, Object> storage) {
|
||||
|
||||
@@ -19,8 +19,7 @@ package org.jackhuang.hmcl.setting;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.util.InvocationDispatcher;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.FileSaver;
|
||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.io.JarUtils;
|
||||
@@ -83,18 +82,20 @@ public final class ConfigHolder {
|
||||
LOG.info("Config location: " + configLocation);
|
||||
|
||||
configInstance = loadConfig();
|
||||
configInstance.addListener(source -> markConfigDirty());
|
||||
configInstance.addListener(source -> FileSaver.save(configLocation, configInstance.toJson()));
|
||||
|
||||
globalConfigInstance = loadGlobalConfig();
|
||||
globalConfigInstance.addListener(source -> markGlobalConfigDirty());
|
||||
globalConfigInstance.addListener(source -> FileSaver.save(GLOBAL_CONFIG_PATH, globalConfigInstance.toJson()));
|
||||
|
||||
Locale.setDefault(config().getLocalization().getLocale());
|
||||
I18n.setLocale(configInstance.getLocalization());
|
||||
LOG.setLogRetention(globalConfig().getLogRetention());
|
||||
Settings.init();
|
||||
|
||||
if (newlyCreated)
|
||||
saveConfigSync();
|
||||
if (newlyCreated) {
|
||||
LOG.info("Creating config file " + configLocation);
|
||||
FileUtils.saveSafely(configLocation, configInstance.toJson());
|
||||
}
|
||||
|
||||
if (!Files.isWritable(configLocation)) {
|
||||
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS
|
||||
@@ -169,34 +170,10 @@ public final class ConfigHolder {
|
||||
}
|
||||
}
|
||||
|
||||
LOG.info("Creating an empty config");
|
||||
newlyCreated = true;
|
||||
return new Config();
|
||||
}
|
||||
|
||||
private static final InvocationDispatcher<String> configWriter = InvocationDispatcher.runOn(Lang::thread, content -> {
|
||||
try {
|
||||
writeToConfig(content);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to save config", e);
|
||||
}
|
||||
});
|
||||
|
||||
private static void writeToConfig(String content) throws IOException {
|
||||
LOG.info("Saving config");
|
||||
synchronized (configLocation) {
|
||||
FileUtils.saveSafely(configLocation, content);
|
||||
}
|
||||
}
|
||||
|
||||
private static void markConfigDirty() {
|
||||
configWriter.accept(configInstance.toJson());
|
||||
}
|
||||
|
||||
private static void saveConfigSync() throws IOException {
|
||||
writeToConfig(configInstance.toJson());
|
||||
}
|
||||
|
||||
// Global Config
|
||||
|
||||
private static GlobalConfig loadGlobalConfig() throws IOException {
|
||||
@@ -218,22 +195,4 @@ public final class ConfigHolder {
|
||||
return new GlobalConfig();
|
||||
}
|
||||
|
||||
private static final InvocationDispatcher<String> globalConfigWriter = InvocationDispatcher.runOn(Lang::thread, content -> {
|
||||
try {
|
||||
writeToGlobalConfig(content);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to save config", e);
|
||||
}
|
||||
});
|
||||
|
||||
private static void writeToGlobalConfig(String content) throws IOException {
|
||||
LOG.info("Saving global config");
|
||||
synchronized (GLOBAL_CONFIG_PATH) {
|
||||
FileUtils.saveSafely(GLOBAL_CONFIG_PATH, content);
|
||||
}
|
||||
}
|
||||
|
||||
private static void markGlobalConfigDirty() {
|
||||
globalConfigWriter.accept(globalConfigInstance.toJson());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -312,47 +311,21 @@ public final class Controllers {
|
||||
}
|
||||
|
||||
if (JavaRuntime.CURRENT_VERSION < 10) {
|
||||
Integer shownTipVersion = null;
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -281,7 +281,13 @@ public class InstallerItem extends Control {
|
||||
|
||||
for (InstallerItem item : all) {
|
||||
if (!item.resolvedStateProperty.isBound()) {
|
||||
item.resolvedStateProperty.bind(item.versionProperty);
|
||||
item.resolvedStateProperty.bind(Bindings.createObjectBinding(() -> {
|
||||
InstalledState itemVersion = item.versionProperty.get();
|
||||
if (itemVersion != null) {
|
||||
return itemVersion;
|
||||
}
|
||||
return InstallableState.INSTANCE;
|
||||
}, item.versionProperty));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,7 +391,10 @@ public class InstallerItem extends Control {
|
||||
if (control.id.equals(MINECRAFT.getPatchId())) {
|
||||
removeButton.setVisible(false);
|
||||
} else {
|
||||
removeButton.visibleProperty().bind(Bindings.createBooleanBinding(() -> control.resolvedStateProperty.get() instanceof InstalledState, control.resolvedStateProperty));
|
||||
removeButton.visibleProperty().bind(Bindings.createBooleanBinding(() -> {
|
||||
State state = control.resolvedStateProperty.get();
|
||||
return state instanceof InstalledState && !((InstalledState) state).external;
|
||||
}, control.resolvedStateProperty));
|
||||
}
|
||||
removeButton.managedProperty().bind(removeButton.visibleProperty());
|
||||
removeButton.setOnAction(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);
|
||||
|
||||
@@ -49,6 +49,7 @@ public class PromptDialogPane extends DialogPane {
|
||||
public PromptDialogPane(Builder builder) {
|
||||
this.builder = builder;
|
||||
setTitle(builder.title);
|
||||
setPrefWidth(560);
|
||||
|
||||
GridPane body = new GridPane();
|
||||
body.setVgap(8);
|
||||
@@ -72,13 +73,13 @@ public class PromptDialogPane extends DialogPane {
|
||||
if (StringUtils.isNotBlank(question.question.get())) {
|
||||
body.addRow(rowIndex++, new Label(question.question.get()), textField);
|
||||
} else {
|
||||
GridPane.setColumnSpan(textField, 2);
|
||||
GridPane.setColumnSpan(textField, 1);
|
||||
body.addRow(rowIndex++, textField);
|
||||
}
|
||||
GridPane.setMargin(textField, new Insets(0, 0, 20, 0));
|
||||
} else if (question instanceof Builder.BooleanQuestion) {
|
||||
HBox hBox = new HBox();
|
||||
GridPane.setColumnSpan(hBox, 2);
|
||||
GridPane.setColumnSpan(hBox, 1);
|
||||
JFXCheckBox checkBox = new JFXCheckBox();
|
||||
hBox.getChildren().setAll(checkBox);
|
||||
HBox.setMargin(checkBox, new Insets(0, 0, 0, -10));
|
||||
@@ -95,12 +96,12 @@ public class PromptDialogPane extends DialogPane {
|
||||
if (StringUtils.isNotBlank(question.question.get())) {
|
||||
body.addRow(rowIndex++, new Label(question.question.get()), comboBox);
|
||||
} else {
|
||||
GridPane.setColumnSpan(comboBox, 2);
|
||||
GridPane.setColumnSpan(comboBox, 1);
|
||||
body.addRow(rowIndex++, comboBox);
|
||||
}
|
||||
} else if (question instanceof Builder.HintQuestion) {
|
||||
HintPane pane = new HintPane();
|
||||
GridPane.setColumnSpan(pane, 2);
|
||||
GridPane.setColumnSpan(pane, 1);
|
||||
pane.textProperty().bind(question.question);
|
||||
body.addRow(rowIndex++, pane);
|
||||
}
|
||||
|
||||
@@ -311,8 +311,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);
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ public final class JavaManagementPage extends ListPageBase<JavaManagementPage.Ja
|
||||
revealButton.getStyleClass().add("toggle-icon4");
|
||||
revealButton.setGraphic(FXUtils.limitingSize(SVG.FOLDER_OPEN.createIcon(Theme.blackFill(), 24), 24, 24));
|
||||
revealButton.setOnAction(e -> control.onReveal());
|
||||
FXUtils.installFastTooltip(revealButton, i18n("java.reveal"));
|
||||
FXUtils.installFastTooltip(revealButton, i18n("reveal.in_file_manager"));
|
||||
|
||||
JFXButton removeButton = new JFXButton();
|
||||
removeButton.getStyleClass().add("toggle-icon4");
|
||||
|
||||
@@ -162,7 +162,7 @@ public final class JavaRestorePage extends ListPageBase<JavaRestorePage.Disabled
|
||||
revealButton.getStyleClass().add("toggle-icon4");
|
||||
revealButton.setGraphic(FXUtils.limitingSize(SVG.FOLDER_OPEN.createIcon(Theme.blackFill(), 24), 24, 24));
|
||||
revealButton.setOnAction(e -> skinnable.onReveal());
|
||||
FXUtils.installFastTooltip(revealButton, i18n("java.reveal"));
|
||||
FXUtils.installFastTooltip(revealButton, i18n("reveal.in_file_manager"));
|
||||
|
||||
if (skinnable.realPath == null) {
|
||||
revealButton.setDisable(true);
|
||||
|
||||
@@ -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("");
|
||||
|
||||
|
||||
@@ -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() {
|
||||
@@ -356,11 +363,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 +440,15 @@ 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);
|
||||
};
|
||||
|
||||
FXUtils.onChange(control.pageCount, pageCountN -> {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -579,7 +581,7 @@ public final class SchematicsPage extends ListPageBase<SchematicsPage.Item> impl
|
||||
right.setAlignment(Pos.CENTER_RIGHT);
|
||||
|
||||
JFXButton btnReveal = new JFXButton();
|
||||
FXUtils.installFastTooltip(btnReveal, i18n("reveal.in_explorer"));
|
||||
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 -> item.onReveal());
|
||||
|
||||
@@ -299,6 +299,7 @@ public class VersionPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
new IconedMenuItem(SVG.SETTINGS, i18n("folder.config"), () -> control.onBrowse("config"), browsePopup),
|
||||
new IconedMenuItem(SVG.TEXTURE, i18n("folder.resourcepacks"), () -> control.onBrowse("resourcepacks"), browsePopup),
|
||||
new IconedMenuItem(SVG.WB_SUNNY, i18n("folder.shaderpacks"), () -> control.onBrowse("shaderpacks"), browsePopup),
|
||||
new IconedMenuItem(SVG.SCHEMA, i18n("folder.schematics"), () -> control.onBrowse("schematics"), browsePopup),
|
||||
new IconedMenuItem(SVG.SCREENSHOT_MONITOR, i18n("folder.screenshots"), () -> control.onBrowse("screenshots"), browsePopup),
|
||||
new IconedMenuItem(SVG.PUBLIC, i18n("folder.saves"), () -> control.onBrowse("saves"), browsePopup),
|
||||
new IconedMenuItem(SVG.SCRIPT, i18n("folder.logs"), () -> control.onBrowse("logs"), browsePopup)
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,7 +255,7 @@ public final class WorldBackupsPage extends ListPageBase<WorldBackupsPage.Backup
|
||||
|
||||
JFXButton btnReveal = new JFXButton();
|
||||
right.getChildren().add(btnReveal);
|
||||
FXUtils.installFastTooltip(btnReveal, i18n("world.reveal"));
|
||||
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.onReveal());
|
||||
|
||||
@@ -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,7 +356,7 @@ 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);
|
||||
|
||||
@@ -397,7 +387,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 +421,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 +455,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 +500,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"));
|
||||
|
||||
@@ -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("world.reveal"));
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -111,6 +111,7 @@ public final class CrashReporter implements Thread.UncaughtExceptionHandler {
|
||||
LOG.error("Unable to handle uncaught exception", handlingException);
|
||||
}
|
||||
|
||||
FileSaver.shutdown();
|
||||
LOG.shutdown();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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.util.io.FileUtils;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
/**
|
||||
* @author Glavo
|
||||
*/
|
||||
public final class FileSaver extends Thread {
|
||||
|
||||
private static final Pair<Path, String> SHUTDOWN = Pair.pair(null, null);
|
||||
|
||||
private static final BlockingQueue<Pair<Path, String>> queue = new LinkedBlockingQueue<>();
|
||||
private static final AtomicBoolean running = new AtomicBoolean(false);
|
||||
private static final ReentrantLock runningLock = new ReentrantLock();
|
||||
private static volatile boolean shutdown = false;
|
||||
|
||||
private static void doSave(Map<Path, String> map) {
|
||||
for (Map.Entry<Path, String> entry : map.entrySet()) {
|
||||
saveSync(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public static void save(Path file, String content) {
|
||||
Objects.requireNonNull(file);
|
||||
Objects.requireNonNull(content);
|
||||
|
||||
ShutdownHook.ensureInstalled();
|
||||
|
||||
queue.add(Pair.pair(file, content));
|
||||
if (running.compareAndSet(false, true)) {
|
||||
new FileSaver().start();
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveSync(Path file, String content) {
|
||||
LOG.info("Saving file " + file);
|
||||
try {
|
||||
FileUtils.saveSafely(file, content);
|
||||
} catch (Throwable e) {
|
||||
LOG.warning("Failed to save " + file, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void shutdown() {
|
||||
shutdown = true;
|
||||
queue.add(SHUTDOWN);
|
||||
}
|
||||
|
||||
private FileSaver() {
|
||||
super("FileSaver");
|
||||
}
|
||||
|
||||
private boolean stopped = false;
|
||||
|
||||
private void stopCurrentSaver() {
|
||||
// Ensure that each saver calls `running.set(false)` at most once
|
||||
if (!stopped) {
|
||||
stopped = true;
|
||||
running.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
runningLock.lock();
|
||||
try {
|
||||
HashMap<Path, String> map = new HashMap<>();
|
||||
ArrayList<Pair<Path, String>> buffer = new ArrayList<>();
|
||||
|
||||
while (!stopped) {
|
||||
if (shutdown) {
|
||||
stopCurrentSaver();
|
||||
} else {
|
||||
Pair<Path, String> head = queue.poll(30, TimeUnit.SECONDS);
|
||||
if (head == null || head == SHUTDOWN) {
|
||||
stopCurrentSaver();
|
||||
} else {
|
||||
map.put(head.getKey(), head.getValue());
|
||||
//noinspection BusyWait
|
||||
Thread.sleep(200); // Waiting for more changes
|
||||
}
|
||||
}
|
||||
|
||||
while (queue.drainTo(buffer) > 0) {
|
||||
for (Pair<Path, String> pair : buffer) {
|
||||
if (pair == SHUTDOWN)
|
||||
stopCurrentSaver();
|
||||
else
|
||||
map.put(pair.getKey(), pair.getValue());
|
||||
}
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
doSave(map);
|
||||
map.clear();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new AssertionError("This thread cannot be interrupted", e);
|
||||
} finally {
|
||||
runningLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ShutdownHook extends Thread {
|
||||
|
||||
static {
|
||||
Runtime.getRuntime().addShutdownHook(new ShutdownHook());
|
||||
}
|
||||
|
||||
static void ensureInstalled() {
|
||||
// Ensure the shutdown hook is installed
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
shutdown();
|
||||
runningLock.lock();
|
||||
try {
|
||||
HashMap<Path, String> map = new HashMap<>();
|
||||
for (Pair<Path, String> pair : queue) {
|
||||
if (pair != SHUTDOWN)
|
||||
map.put(pair.getKey(), pair.getValue());
|
||||
}
|
||||
doSave(map);
|
||||
} finally {
|
||||
runningLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,7 +106,7 @@ account.methods.microsoft.error.add_family_probably=Please check if the age indi
|
||||
account.methods.microsoft.error.country_unavailable=Xbox Live is not available in your current country/region.
|
||||
account.methods.microsoft.error.missing_xbox_account=Your Microsoft account does not have a linked Xbox account yet. Please click "Create Profile / Edit Profile Name" to create one before continuing.
|
||||
account.methods.microsoft.error.no_character=Please ensure you have purchased Minecraft: Java Edition.\nIf you have already purchased the game, a profile may not have been created yet.\nClick "Create Profile / Edit Profile Name" to create it.
|
||||
account.methods.microsoft.error.banned=Your account may have been banned by Xbox Live.\nYou can click on "Ban Query" above for more details.
|
||||
account.methods.microsoft.error.banned=Your account may have been banned by Xbox Live.\nYou can click "Ban Query" for more details.
|
||||
account.methods.microsoft.error.unknown=Failed to log in, error code: %d.
|
||||
account.methods.microsoft.error.wrong_verify_method=Please log in using your password on the Microsoft account login page, and do not use a verification code to log in.
|
||||
account.methods.microsoft.logging_in=Logging in...
|
||||
@@ -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
|
||||
@@ -402,6 +403,10 @@ fatal.deprecated_java_version=HMCL will require Java 11 or later to run in the f
|
||||
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 11 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.
|
||||
@@ -435,7 +440,9 @@ folder.mod=Mods
|
||||
folder.resourcepacks=Resource Packs
|
||||
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.
|
||||
@@ -754,7 +761,6 @@ java.install.failed.unsupported_platform=This Java is not compatible with the cu
|
||||
java.install.name=Name
|
||||
java.install.warning.invalid_character=Illegal character in name
|
||||
java.installing=Installing Java
|
||||
java.reveal=Reveal the Java directory
|
||||
java.uninstall=Uninstall Java
|
||||
java.uninstall.confirm=Are you sure you want to uninstall this Java? This action cannot be undone!
|
||||
|
||||
@@ -932,11 +938,16 @@ 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.blocks=Blocks
|
||||
modrinth.category.bloom=Bloom
|
||||
modrinth.category.bukkit=Bukkit
|
||||
modrinth.category.bungeecord=BungeeCord
|
||||
modrinth.category.canvas=Canvas
|
||||
modrinth.category.cartoon=Cartoon
|
||||
modrinth.category.challenging=Challenging
|
||||
modrinth.category.colored-lighting=Colored Lighting
|
||||
modrinth.category.core-shaders=Core Shaders
|
||||
modrinth.category.combat=Combat
|
||||
modrinth.category.cursed=Cursed
|
||||
@@ -946,19 +957,25 @@ modrinth.category.entities=Entities
|
||||
modrinth.category.environment=Environment
|
||||
modrinth.category.equipment=Equipment
|
||||
modrinth.category.fabric=Fabric
|
||||
modrinth.category.fantasy=Fantasy
|
||||
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.kitchen-sink=Kitchen-Sink
|
||||
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 +985,21 @@ modrinth.category.models=Models
|
||||
modrinth.category.modloader=Modloader
|
||||
modrinth.category.multiplayer=Multiplayer
|
||||
modrinth.category.neoforge=NeoForge
|
||||
modrinth.category.optifine=OptiFine
|
||||
modrinth.category.optimization=Optimization
|
||||
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,6 +1010,7 @@ 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
|
||||
@@ -1061,7 +1087,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,10 +1098,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.reveal=Show in folder
|
||||
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
|
||||
@@ -1090,7 +1118,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
|
||||
@@ -1121,9 +1148,10 @@ 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.reveal=Reveal in Explorer
|
||||
world.show_all=Show All
|
||||
|
||||
profile=Game Directories
|
||||
@@ -1152,7 +1180,7 @@ repositories.chooser.title=Choose download source for JavaFX
|
||||
resourcepack=Resource Packs
|
||||
resourcepack.download.title=Download Resource Pack - %1s
|
||||
|
||||
reveal.in_explorer=Reveal in File Manager
|
||||
reveal.in_file_manager=Reveal in File Manager
|
||||
|
||||
schematics=Schematics
|
||||
schematics.add=Add Schematic Files
|
||||
@@ -1175,7 +1203,7 @@ schematics.info.total_blocks=Total Blocks
|
||||
schematics.info.total_volume=Total Volume
|
||||
schematics.info.version=Schematic Version
|
||||
schematics.manage=Schematics
|
||||
schematics.sub_items=%d sub-items
|
||||
schematics.sub_items=%d sub-item(s)
|
||||
|
||||
search=Search
|
||||
search.hint.chinese=Search in English and Chinese
|
||||
@@ -1306,9 +1334,9 @@ settings.launcher.font=Font
|
||||
settings.launcher.general=General
|
||||
settings.launcher.language=Language (Applies After Restart)
|
||||
settings.launcher.launcher_log.export=Export Launcher Logs
|
||||
settings.launcher.launcher_log.reveal=Reveal Logs in File Manager
|
||||
settings.launcher.launcher_log.export.failed=Failed to export logs.
|
||||
settings.launcher.launcher_log.export.success=Logs have been exported to "%s".
|
||||
settings.launcher.launcher_log.reveal=Reveal Logs in File Manager
|
||||
settings.launcher.log=Logging
|
||||
settings.launcher.log.font=Font
|
||||
settings.launcher.proxy=Proxy
|
||||
@@ -1333,6 +1361,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
|
||||
|
||||
@@ -107,6 +107,7 @@ account.methods.microsoft.error.add_family_probably=Por favor, compruebe si la e
|
||||
account.methods.microsoft.error.country_unavailable=Xbox Live no está disponible en tu país/región actual.
|
||||
account.methods.microsoft.error.missing_xbox_account=Tu cuenta Microsoft aún no tiene una cuenta Xbox vinculada. Haga clic en «Crear perfil / Editar nombre de perfil» para crear una antes de continuar.
|
||||
account.methods.microsoft.error.no_character=Por favor, asegúrese de que ha comprado Minecraft: Java Edition. \nSi ya lo has comprado, es posible que no hayas creado un perfil de juego.\nPor favor, haga clic en «Crear perfil / Editar nombre de perfil» para crearlo.
|
||||
account.methods.microsoft.error.banned=Es posible que Xbox Live haya bloqueado tu cuenta.\nPuede hacer clic en «Consulta de bloqueo de cuentas» para obtener más detalles.
|
||||
account.methods.microsoft.error.unknown=No se ha podido iniciar sesión, error: %d.
|
||||
account.methods.microsoft.error.wrong_verify_method=Inicie sesión con su contraseña en la página de inicio de sesión de la cuenta Microsoft y no utilice un código de verificación para iniciar sesión.
|
||||
account.methods.microsoft.logging_in=Iniciando sesión...
|
||||
@@ -137,6 +138,7 @@ account.methods.offline.uuid.hint=UUID es un identificador único para los jugad
|
||||
Esta opción es sólo para usuarios avanzados. No recomendamos editar esta opción a menos que sepas lo que estás haciendo.
|
||||
account.methods.offline.uuid.malformed=Formato no válido.
|
||||
account.methods.forgot_password=Olvidé mi contraseña
|
||||
account.methods.ban_query=Consulta de bloqueo de cuentas
|
||||
account.missing=No hay cuentas
|
||||
account.missing.add=Haga clic aquí para añadir una.
|
||||
account.move_to_global=Convertir en cuenta global\nLa información de la cuenta se guardará en un archivo de configuración del directorio de usuario actual del sistema.
|
||||
@@ -331,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
|
||||
@@ -401,7 +404,12 @@ fatal.deprecated_java_version=HMCL requerirá Java 11 o posterior para funcionar
|
||||
\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 11 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\
|
||||
@@ -436,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.
|
||||
@@ -757,7 +767,6 @@ java.install.failed.unsupported_platform=Este Java no es compatible con la plata
|
||||
java.install.name=Nombre
|
||||
java.install.warning.invalid_character=Carácter ilegal en el nombre
|
||||
java.installing=Instalando Java
|
||||
java.reveal=Abrir el directorio Java
|
||||
java.uninstall=Desinstalar Java
|
||||
java.uninstall.confirm=¿Está seguro de que desea desinstalar este Java? ¡Esta acción no se puede deshacer!
|
||||
|
||||
@@ -935,11 +944,16 @@ 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.blocks=Bloqueos
|
||||
modrinth.category.bloom=Resplandor
|
||||
modrinth.category.bukkit=Bukkit
|
||||
modrinth.category.bungeecord=BungeeCord
|
||||
modrinth.category.canvas=Canvas
|
||||
modrinth.category.cartoon=Caricatura
|
||||
modrinth.category.challenging=Desafío
|
||||
modrinth.category.colored-lighting=Iluminación de color
|
||||
modrinth.category.core-shaders=Sombreadores de núcleo
|
||||
modrinth.category.combat=Combate
|
||||
modrinth.category.cursed=Maldición
|
||||
@@ -949,19 +963,25 @@ modrinth.category.entities=Entidades
|
||||
modrinth.category.environment=Medio ambiente
|
||||
modrinth.category.equipment=Equipo
|
||||
modrinth.category.fabric=Fabric
|
||||
modrinth.category.fantasy=Fantasía
|
||||
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.kitchen-sink=Fregadero de cocina
|
||||
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
|
||||
@@ -971,13 +991,21 @@ modrinth.category.models=Modelos
|
||||
modrinth.category.modloader=Cargador de mods
|
||||
modrinth.category.multiplayer=Multijugador
|
||||
modrinth.category.neoforge=NeoForge
|
||||
modrinth.category.optifine=OptiFine
|
||||
modrinth.category.optimization=Optimización
|
||||
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
|
||||
@@ -988,6 +1016,7 @@ 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
|
||||
@@ -1075,10 +1104,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.reveal=Abrir el directorio
|
||||
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 +1124,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,9 +1154,10 @@ 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.reveal=Abrir el directorio
|
||||
world.show_all=Mostrar todo
|
||||
|
||||
profile=Directorios del juego
|
||||
@@ -1155,6 +1186,31 @@ repositories.chooser.title=Elija la fuente de descarga de JavaFX
|
||||
resourcepack=Paquetes de recursos
|
||||
resourcepack.download.title=Descargar paquete de recursos - %1s
|
||||
|
||||
reveal.in_file_manager=Mostrar en el administrador de archivos
|
||||
|
||||
schematics=Esquemas
|
||||
schematics.add=Añadir archivos de esquema
|
||||
schematics.add.failed=No se han podido añadir archivos de esquema
|
||||
schematics.back_to=Atrás a «%s»
|
||||
schematics.create_directory=Crear directorio
|
||||
schematics.create_directory.prompt=Introduzca el nuevo nombre del directorio
|
||||
schematics.create_directory.failed=No se ha podido crear el directorio
|
||||
schematics.create_directory.failed.already_exists=El directorio ya existe
|
||||
schematics.create_directory.failed.empty_name=El nombre no puede estar vacío
|
||||
schematics.create_directory.failed.invalid_name=El nombre contiene caracteres no válidos
|
||||
schematics.info.description=Descripción
|
||||
schematics.info.enclosing_size=Tamaño de Encierro
|
||||
schematics.info.name=Nombre
|
||||
schematics.info.region_count=Regiones
|
||||
schematics.info.schematic_author=Autor
|
||||
schematics.info.time_created=Creado
|
||||
schematics.info.time_modified=Modificado
|
||||
schematics.info.total_blocks=Bloques totales
|
||||
schematics.info.total_volume=Volumen total
|
||||
schematics.info.version=Versión de esquema
|
||||
schematics.manage=Esquemas
|
||||
schematics.sub_items=%d elemento(s)
|
||||
|
||||
search=Búsqueda
|
||||
search.hint.chinese=Buscar en inglés y chino
|
||||
search.hint.english=Buscar sólo en inglés
|
||||
@@ -1285,7 +1341,7 @@ settings.launcher.language=Idioma
|
||||
settings.launcher.launcher_log.export=Exportar registros del launcher
|
||||
settings.launcher.launcher_log.export.failed=No se han podido exportar los registros
|
||||
settings.launcher.launcher_log.export.success=Los registros se han exportado a «%s»
|
||||
settings.launcher.launcher_log.reveal=Abrir el directorio
|
||||
settings.launcher.launcher_log.reveal=Abrir directorio de registro
|
||||
settings.launcher.log=Registro
|
||||
settings.launcher.log.font=Fuente
|
||||
settings.launcher.proxy=Proxy
|
||||
@@ -1310,6 +1366,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
|
||||
@@ -488,7 +489,6 @@ java.install.failed.invalid=このアーカイブは有効なJavaインストー
|
||||
java.install.failed.unsupported_platform=このJavaは現在のプラットフォームと互換性がないため、インストールできません。
|
||||
java.install.name=名前
|
||||
java.install.warning.invalid_character=名前に不正な文字が含まれています
|
||||
java.reveal=Javaディレクトリを表示する
|
||||
java.uninstall=このJavaをアンインストールする
|
||||
java.uninstall.confirm=本当にこのJavaをアンインストールしますか?この操作は元に戻せません!
|
||||
|
||||
@@ -701,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=このマップをエクスポートする
|
||||
@@ -714,10 +713,10 @@ 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.reveal=フォルダを表示
|
||||
world.show_all=すべて表示
|
||||
|
||||
profile=ゲームディレクトリ
|
||||
@@ -741,6 +740,8 @@ repositories.chooser.title=ダウンロードソースを選択してJavaFXを
|
||||
|
||||
resourcepack=リソースパック
|
||||
|
||||
reveal.in_file_manager=ファイルマネージャーで表示する
|
||||
|
||||
search=検索
|
||||
search.hint.chinese=中国語と英語での検索をサポート
|
||||
search.hint.english=英語でのみ検索をサポート
|
||||
@@ -849,6 +850,7 @@ settings.launcher.language=言語
|
||||
settings.launcher.launcher_log.export=ランチャーログのエクスポート
|
||||
settings.launcher.launcher_log.export.failed=ログのエクスポートに失敗しました
|
||||
settings.launcher.launcher_log.export.success=ログが %s にエクスポートされました
|
||||
settings.launcher.launcher_log.reveal=ファイルマネージャーでログを表示する
|
||||
settings.launcher.log=ログ
|
||||
settings.launcher.log.font=ログフォント
|
||||
settings.launcher.proxy=プロキシ
|
||||
@@ -866,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
|
||||
|
||||
@@ -109,6 +109,7 @@ account.methods.microsoft.error.missing_xbox_account=Ваш аккаунт Micro
|
||||
account.methods.microsoft.error.no_character=Убедитесь, что вы приобрели Minecraft: Java Edition.\n\
|
||||
Если вы уже приобрели игру, профиль может быть еще не создан.\n\
|
||||
Нажмите «Создать / Редактировать профиль», чтобы создать его.
|
||||
account.methods.microsoft.error.banned=Возможно ваш аккаунт был заблокирован Xbox Live.\nВы можете нажать «Просмотреть аккаунт заблокирован» для получения более подробной информации.
|
||||
account.methods.microsoft.error.unknown=Не удалось войти. Microsoft отвечает с кодом ошибки %d.
|
||||
account.methods.microsoft.error.wrong_verify_method=Пожалуйста, войдите в систему, используя свой пароль на странице входа в аккаунт Microsoft, и не используйте проверочный код для авторизации.
|
||||
account.methods.microsoft.logging_in=Авторизация...
|
||||
@@ -138,6 +139,7 @@ account.methods.offline.uuid=UUID
|
||||
account.methods.offline.uuid.hint=UUID - это уникальный идентификатор игрового персонажа в Minecraft. Способ генерации UUID различается в разных лаунчерах игр. Изменение UUID на тот, который генерируется другим лаунчером гарантирует, что игровые блоки/предметы в рюкзаке вашего офлайн аккаунта останутся. Этот параметр предназначен для экспертов. Если вы не знаете что делаете, мы не советуем вам изменять этот параметр. Эта опция не требуется для присоединения к серверам.
|
||||
account.methods.offline.uuid.malformed=Недопустимый формат.
|
||||
account.methods.forgot_password=Забыли пароль
|
||||
account.methods.ban_query=Просмотреть аккаунт заблокирован
|
||||
account.missing=Нет аккаунтов
|
||||
account.missing.add=Нажмите здесь, чтобы добавить.
|
||||
account.move_to_global=Конвертировать в глобальный аккаунт\nИнформация об аккаунте будет сохранена в файле конфигурации в папке текущего пользователя системы.
|
||||
@@ -332,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=Открыть сайт
|
||||
@@ -400,6 +403,11 @@ fatal.deprecated_java_version=В будущем для работы HMCL пот
|
||||
Рекомендуется установить последнюю версию Java, чтобы обеспечить правильную работу HMCL.\n\
|
||||
\n\
|
||||
Вы можете продолжать использовать старую версию Java. HMCL может распознавать и управлять несколькими версиями Java и автоматически выбирать подходящую Java в зависимости от версии игры.
|
||||
fatal.deprecated_java_version.update=В будущем для работы HMCL потребуется Java 11 или более поздняя версия. Пожалуйста, установить последнюю версию Java, чтобы убедиться, что приложение сможет завершить обновление.\n\
|
||||
\n\
|
||||
Вы можете продолжать использовать старую версию Java.\
|
||||
HMCL может распознавать и управлять несколькими версиями Java \
|
||||
и автоматически выбирать подходящую Java в зависимости от версии игры.
|
||||
fatal.deprecated_java_version.download_link=Скачать Java %d
|
||||
fatal.samba=Если вы пытаетесь открыть лаунчер в общей папке Samba, он может не работать, попробуйте обновить Java или запустить лаунчер из локальной папки.
|
||||
fatal.illegal_char=Недопустимый символ «=» в пути к папке пользователя. Лаунчер может работать, но некоторые функции не будут работать.\n\
|
||||
@@ -434,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> и отправьте экспортированный файл другим для анализа.
|
||||
@@ -532,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\
|
||||
Вы можете попробовать удалить или обновить его.
|
||||
@@ -753,7 +763,6 @@ java.install.failed.unsupported_platform=Эта Java несовместима с
|
||||
java.install.name=Название
|
||||
java.install.warning.invalid_character=Неправильный символ в названии
|
||||
java.installing=Установка Java
|
||||
java.reveal=Откройте каталог Java
|
||||
java.uninstall=Удалить Java
|
||||
java.uninstall.confirm=Вы уверены, что хотите удалить эту Java? Это действие нельзя отменить!
|
||||
|
||||
@@ -775,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\
|
||||
@@ -934,11 +943,16 @@ modpack.wizard.step.initialization.warning=Перед созданием мод
|
||||
modpack.wizard.step.initialization.server=Нажмите здесь для получения дополнительных руководств по созданию модпака на сервере, который может автообновляться.
|
||||
|
||||
modrinth.category.adventure=Приключение
|
||||
modrinth.category.atmosphere=Атмосфера
|
||||
modrinth.category.audio=Аудио
|
||||
modrinth.category.blocks=Блоки
|
||||
modrinth.category.bloom=Свечение
|
||||
modrinth.category.bukkit=Bukkit
|
||||
modrinth.category.bungeecord=BungeeCord
|
||||
modrinth.category.canvas=Canvas
|
||||
modrinth.category.cartoon=Мультяшный
|
||||
modrinth.category.challenging=Вызов
|
||||
modrinth.category.colored-lighting=Цветное освещение
|
||||
modrinth.category.core-shaders=Ядро шейдеров
|
||||
modrinth.category.combat=Бой
|
||||
modrinth.category.cursed=Cursed
|
||||
@@ -948,19 +962,25 @@ modrinth.category.entities=Сущности
|
||||
modrinth.category.environment=Окружающая среда
|
||||
modrinth.category.equipment=Оборудование
|
||||
modrinth.category.fabric=Fabric
|
||||
modrinth.category.fantasy=Фэнтези
|
||||
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.kitchen-sink=Kitchen-Sink
|
||||
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=Разное
|
||||
@@ -970,13 +990,21 @@ modrinth.category.models=Модели
|
||||
modrinth.category.modloader=Мод-загрузчик
|
||||
modrinth.category.multiplayer=Сетевая игра
|
||||
modrinth.category.neoforge=NeoForge
|
||||
modrinth.category.optifine=OptiFine
|
||||
modrinth.category.optimization=Оптимизация
|
||||
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
|
||||
@@ -987,6 +1015,7 @@ 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
|
||||
@@ -1074,10 +1103,12 @@ world.backup.create.locked=В настоящее время мир находи
|
||||
world.backup.create.success=Успешно создано новое резервное копирование: %s
|
||||
world.backup.delete=Удалить эту резервную копию
|
||||
world.backup.processing=Создание новой резервной копии ...
|
||||
world.backup.reveal=Показать в диспетчере файлов
|
||||
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 +1123,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,9 +1153,10 @@ 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.reveal=Показать в диспетчере файлов
|
||||
world.show_all=Показать все
|
||||
|
||||
profile=Папки с играми
|
||||
@@ -1154,6 +1185,31 @@ repositories.chooser.title=Выберите зеркало для скачива
|
||||
resourcepack=Пакеты ресурсов
|
||||
resourcepack.download.title=Скачать пакет ресурсов - %1s
|
||||
|
||||
reveal.in_file_manager=Открыть в файловый менеджер
|
||||
|
||||
schematics=Схемы
|
||||
schematics.add=Добавить файлы схем
|
||||
schematics.add.failed=Не удалось добавить файлы схем
|
||||
schematics.back_to=Назад на «%s»
|
||||
schematics.create_directory=Создать папку
|
||||
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.info.description=Описание
|
||||
schematics.info.enclosing_size=Суммарный размер
|
||||
schematics.info.name=Название
|
||||
schematics.info.region_count=Регионов
|
||||
schematics.info.schematic_author=Автор
|
||||
schematics.info.time_created=Создан
|
||||
schematics.info.time_modified=Изменен
|
||||
schematics.info.total_blocks=Всего блоков
|
||||
schematics.info.total_volume=Общий объём
|
||||
schematics.info.version=Версия схемы
|
||||
schematics.manage=Схемы
|
||||
schematics.sub_items=%d элемент(ов)
|
||||
|
||||
search=Поиск
|
||||
search.hint.chinese=Поиск на китайском и английском языках
|
||||
search.hint.english=Поиск только на английском языке
|
||||
@@ -1194,7 +1250,7 @@ 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.java_permanent_generation_space.prompt=в МиБ
|
||||
settings.advanced.jvm=Настройки Java VM
|
||||
settings.advanced.jvm_args=Аргументы Java VM
|
||||
settings.advanced.jvm_args.prompt=\ · Если параметр, введённый в «Аргументы Java VM», совпадает с параметром по умолчанию, он не будет добавлен.\n\
|
||||
@@ -1282,10 +1338,10 @@ settings.launcher.enable_game_list=Показывать список сборо
|
||||
settings.launcher.font=Шрифт
|
||||
settings.launcher.general=Общие
|
||||
settings.launcher.language=Язык (Применится после перезапуска)
|
||||
settings.launcher.launcher_log.reveal=Расположение
|
||||
settings.launcher.launcher_log.export=Экспорт логов лаунчера
|
||||
settings.launcher.launcher_log.export.failed=Не удалось экспортировать логи
|
||||
settings.launcher.launcher_log.export.success=Логи экспортированы в %s
|
||||
settings.launcher.launcher_log.reveal=Открыть папку журнала
|
||||
settings.launcher.log=Запись логов
|
||||
settings.launcher.log.font=Шрифт
|
||||
settings.launcher.proxy=Прокси
|
||||
@@ -1304,13 +1360,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=Погрузчики
|
||||
|
||||
@@ -53,33 +53,33 @@ account.cape=披風
|
||||
account.character=角色
|
||||
account.choose=請選取角色
|
||||
account.create=建立帳戶
|
||||
account.create.microsoft=加入 Microsoft 帳戶
|
||||
account.create.offline=加入離線模式帳戶
|
||||
account.create.authlibInjector=加入 authlib-injector 帳戶
|
||||
account.create.microsoft=新增 Microsoft 帳戶
|
||||
account.create.offline=新增離線模式帳戶
|
||||
account.create.authlibInjector=新增 authlib-injector 帳戶
|
||||
account.email=電子信箱
|
||||
account.failed=帳戶重新整理失敗
|
||||
account.failed.character_deleted=已刪除此角色
|
||||
account.failed.connect_authentication_server=無法連線至認證伺服器,可能是網路問題,請檢查裝置能否正常上網或使用代理服務。
|
||||
account.failed.connect_injector_server=無法連線至認證伺服器,可能是網路故障,請檢查裝置能否正常上網、檢查網址是否輸入錯誤或使用代理服務。
|
||||
account.failed.injector_download_failure=無法下載 authlib-injector,請檢查網路或嘗試切換下載來源。
|
||||
account.failed.invalid_credentials=你的使用者名稱或密碼錯誤,或者登入次數過多被暫時禁止登入,請稍後再試。
|
||||
account.failed.connect_authentication_server=無法連線至認證伺服器。可能是網路問題,請檢查裝置能否正常上網或使用代理服務。
|
||||
account.failed.connect_injector_server=無法連線至認證伺服器。可能是網路故障,請檢查裝置能否正常上網、檢查網址是否輸入錯誤或使用代理服務。
|
||||
account.failed.injector_download_failure=無法下載 authlib-injector。請檢查網路或嘗試切換下載來源。
|
||||
account.failed.invalid_credentials=你的使用者名稱或密碼錯誤,或者登入次數過多被暫時禁止登入。請稍後再試。
|
||||
account.failed.invalid_password=密碼無效
|
||||
account.failed.invalid_token=請嘗試登出並重新輸入密碼登入。
|
||||
account.failed.migration=你的帳戶需要遷移至 Microsoft 帳戶。如果你已經遷移,你需要使用遷移後的 Microsoft 帳戶登入。
|
||||
account.failed.no_character=該帳戶沒有角色。
|
||||
account.failed.server_disconnected=無法訪問登入伺服器,帳戶資訊重新整理失敗。\n\
|
||||
account.failed.server_disconnected=無法訪問登入伺服器。帳戶資訊重新整理失敗。\n\
|
||||
你可以選取「再次重新整理帳戶」重新嘗試。\n\
|
||||
你也可以選取「跳過帳戶重新整理」繼續啟動遊戲,但可能會導致帳戶資訊未同步更新。\n\
|
||||
若最近沒有重新整理帳戶資訊,則可能導致帳戶資訊過期失效。\n\
|
||||
若使用 Microsoft/authlib-injector 帳戶啟動遊戲,帳戶資訊過期失效可能將無法進入需線上驗證的伺服器。\n\
|
||||
若嘗試多次無法重新整理,可嘗試重新加入該帳戶,或許可以解決該問題。
|
||||
若嘗試多次無法重新整理,可嘗試重新增加該帳戶,或許可以解決該問題。
|
||||
account.failed.server_response_malformed=無法解析認證伺服器回應,可能是伺服器故障。
|
||||
account.failed.ssl=連線伺服器時發生了 SSL 錯誤,可能網站證書已過期或你使用的 Java 版本過低,請嘗試更新 Java。
|
||||
account.failed.ssl=連線伺服器時發生了 SSL 錯誤。可能網站證書已過期或你使用的 Java 版本過低。請嘗試更新 Java。
|
||||
account.failed.wrong_account=登入了錯誤的帳戶
|
||||
account.hmcl.hint=你需要點擊「登入」按鈕,並在打開的網頁中完成登入
|
||||
account.hmcl.hint=你需要點擊「登入」按鈕,並在開啟的網頁中完成登入
|
||||
account.injector.add=新增認證伺服器
|
||||
account.injector.empty=無 (按一下右側 + 加入)
|
||||
account.injector.http=警告: 此伺服器使用不安全的 HTTP 協定,你的密碼在登入時會被明文傳輸。
|
||||
account.injector.empty=無 (按一下右側 + 新增)
|
||||
account.injector.http=警告: 此伺服器使用不安全的 HTTP 協定。你的密碼在登入時會被明文傳輸。
|
||||
account.injector.link.homepage=首頁
|
||||
account.injector.link.register=註冊
|
||||
account.injector.server=認證伺服器
|
||||
@@ -90,7 +90,7 @@ account.login.hint=我們不會儲存你的密碼
|
||||
account.login.skip=跳過重新整理帳戶
|
||||
account.login.retry=再次重新整理帳戶
|
||||
account.login.refresh=重新登入
|
||||
account.login.refresh.microsoft.hint=由於帳戶授權失效,你需要重新加入 Microsoft 帳戶
|
||||
account.login.refresh.microsoft.hint=由於帳戶授權失效,你需要重新增加 Microsoft 帳戶
|
||||
account.login.restricted=登入微軟帳戶以啟用此功能
|
||||
account.logout=登出
|
||||
account.register=註冊
|
||||
@@ -101,25 +101,25 @@ account.methods.authlib_injector=外置登入
|
||||
account.methods.microsoft=Microsoft 帳戶
|
||||
account.methods.microsoft.birth=如何變更帳戶出生日期
|
||||
account.methods.microsoft.deauthorize=移除應用存取權
|
||||
account.methods.microsoft.close_page=已完成 Microsoft 帳戶授權,接下來啟動器還需要完成其餘登入步驟。你現在可以關閉本頁面了。
|
||||
account.methods.microsoft.close_page=已完成 Microsoft 帳戶授權。啟動器將自動執行後續步驟。你現在可以關閉本頁面了。
|
||||
account.methods.microsoft.error.add_family=由於你未滿 18 歲,你的帳戶必須被加入到家庭中才能登入遊戲。你也可以點擊上方「編輯帳戶配置檔」更改你的帳戶出生日期,使年齡滿 18 歲以上以繼續登入。
|
||||
account.methods.microsoft.error.add_family_probably=請檢查你的帳戶設定,如果你未滿 18 歲,你的帳戶必須被加入到家庭中才能登入遊戲。你也可以點擊上方「編輯帳戶配置檔」更改你的帳戶出生日期,使年齡滿 18 歲以上以繼續登入。
|
||||
account.methods.microsoft.error.country_unavailable=你所在的國家或地區不受 Xbox Live 的支援。
|
||||
account.methods.microsoft.error.missing_xbox_account=你的 Microsoft 帳戶尚未關聯 Xbox 帳戶,你必須先建立 Xbox 帳戶,才能登入遊戲。
|
||||
account.methods.microsoft.error.no_character=該帳戶未包含 Minecraft: Java 版購買記錄。\n若已購買,則可能未建立遊戲檔案,請點擊上方連結建立。
|
||||
account.methods.microsoft.error.no_character=該帳戶未包含 Minecraft: Java 版購買記錄。\n若已購買,則可能未建立遊戲檔案。請點擊上方連結建立。
|
||||
account.methods.microsoft.error.banned=你的帳戶可能被 Xbox Live 封禁。\n你可以點擊上方「查詢帳戶是否被封禁」查看帳戶狀態。
|
||||
account.methods.microsoft.error.unknown=登入失敗,錯誤碼:%d
|
||||
account.methods.microsoft.error.wrong_verify_method=請在 Microsoft 帳戶登入頁面使用密碼登入,不要使用驗證碼登入。
|
||||
account.methods.microsoft.logging_in=登入中……
|
||||
account.methods.microsoft.makegameidsettings=建立檔案 / 編輯檔案名稱
|
||||
account.methods.microsoft.hint=請點擊「登入」按鈕,稍後複製此處顯示的代碼,在打開的登入網頁中完成登入過程。\n\
|
||||
account.methods.microsoft.hint=請點擊「登入」按鈕,稍後複製此處顯示的代碼,在開啟的登入網頁中完成登入過程。\n\
|
||||
\n\
|
||||
如果登入 Microsoft 帳戶的令牌洩露,可點擊下方「移除應用存取權」,然後等待令牌過期。\n\
|
||||
\n\
|
||||
如遇到問題,你可以點擊右上角幫助按鈕進行求助。
|
||||
account.methods.microsoft.manual=你的代碼為 <b>%1$s</b>,請點擊此處複製。\n\
|
||||
account.methods.microsoft.manual=你的代碼為 <b>%1$s</b>。請點擊此處複製。\n\
|
||||
\n\
|
||||
點擊「登入」按鈕後,你應該在打開的登入網頁中完成登入過程。如果網頁沒有打開,你可以自行在瀏覽器中訪問 %2$s\n\
|
||||
點擊「登入」按鈕後,你應該在開啟的登入網頁中完成登入過程。如果網頁沒有開啟,你可以自行在瀏覽器中訪問 %2$s\n\
|
||||
\n\
|
||||
如果登入 Microsoft 帳戶的令牌洩露,可點擊下方「移除應用存取權」,然後等待令牌過期。\n\
|
||||
\n\
|
||||
@@ -128,7 +128,7 @@ account.methods.microsoft.profile=編輯帳戶配置檔
|
||||
account.methods.microsoft.purchase=購買 Minecraft
|
||||
account.methods.forgot_password=忘記密碼
|
||||
account.methods.ban_query=查詢帳戶是否被封禁
|
||||
account.methods.microsoft.snapshot=你正在使用第三方提供的 HMCL,請下載官方版本進行登入。
|
||||
account.methods.microsoft.snapshot=你正在使用第三方提供的 HMCL。請下載官方版本進行登入。
|
||||
account.methods.microsoft.snapshot.website=官方網站
|
||||
account.methods.offline=離線模式
|
||||
account.methods.offline.name.special_characters=建議使用英文字母、數字以及底線命名
|
||||
@@ -137,12 +137,12 @@ account.methods.offline.name.invalid=遊戲使用者名稱建議僅使用英文
|
||||
\ · 一些有效的使用者名稱:HuangYu、huang_Yu、Huang_Yu_123;\n\
|
||||
\ · 一些無效的使用者名稱:黃魚,Huang Yu、Huang-Yu_%%%、Huang_Yu_hello_world_hello_world。\n\
|
||||
\n\
|
||||
使用非法使用者名稱會導致你無法加入大部分伺服器,並可能與部分模組衝突而使遊戲崩潰。
|
||||
使用非法使用者名稱會導致你無法進入大部分伺服器,並可能與部分模組衝突而使遊戲崩潰。
|
||||
account.methods.offline.uuid=UUID
|
||||
account.methods.offline.uuid.hint=UUID 是 Minecraft 玩家的唯一標識符,每個啟動器生成 UUID 的方式可能不同。\n透過修改 UUID 選項至原啟動器所生成的 UUID,你可以保證在切換啟動器後,遊戲還能將你的遊戲角色識別為給定 UUID 所對應的角色,從而保留原來角色的背包物品。\nUUID 選項為進階選項,除非你知道你在做什麼,否則你不需要調整該選項。
|
||||
account.methods.offline.uuid.hint=UUID 是 Minecraft 玩家的唯一標識符。每個啟動器生成 UUID 的方式可能不同。\n透過修改 UUID 選項至原啟動器所生成的 UUID,你可以保證在切換啟動器後,遊戲還能將你的遊戲角色識別為給定 UUID 所對應的角色,從而保留原來角色的背包物品。\nUUID 選項為進階選項。除非你知道你在做什麼,否則你不需要調整該選項。
|
||||
account.methods.offline.uuid.malformed=格式錯誤
|
||||
account.missing=沒有遊戲帳戶
|
||||
account.missing.add=按一下此處加入帳戶
|
||||
account.missing.add=按一下此處新增帳戶
|
||||
account.move_to_global=轉換為全域帳戶\n該帳戶的資訊會儲存至系統目前使用者目錄的配置檔案中
|
||||
account.move_to_portable=轉換為可攜式帳戶\n該帳戶的資訊會儲存至與 HMCL 同目錄的配置檔案中
|
||||
account.not_logged_in=未登入
|
||||
@@ -158,7 +158,7 @@ account.skin.type.csl_api=Blessing Skin 伺服器
|
||||
account.skin.type.csl_api.location=伺服器位址
|
||||
account.skin.type.csl_api.location.hint=CustomSkinAPI 位址
|
||||
account.skin.type.little_skin=LittleSkin 皮膚站
|
||||
account.skin.type.little_skin.hint=你需要在皮膚站中加入並使用和該離線帳戶同名角色,此時離線帳戶外觀將為皮膚站上對應角色所設定的外觀。
|
||||
account.skin.type.little_skin.hint=你需要在皮膚站中新增並使用和該離線帳戶同名角色。此時離線帳戶外觀將為皮膚站上對應角色所設定的外觀。
|
||||
account.skin.type.local_file=本機外觀圖片檔案
|
||||
account.skin.type.steve=Steve
|
||||
account.skin.upload=上傳/編輯外觀
|
||||
@@ -173,7 +173,7 @@ archive.version=版本
|
||||
|
||||
assets.download=下載資源
|
||||
assets.download_all=驗證資源檔案完整性
|
||||
assets.index.malformed=資源檔案的索引檔案損壞,你可以在相應實例的「實例管理」頁面中,點擊頁面左下角的「設定 → 更新遊戲資源檔案」以修復該問題。
|
||||
assets.index.malformed=資源檔案的索引檔案損壞。你可以在相應實例的「實例管理」頁面中,點擊頁面左下角的「設定 → 更新遊戲資源檔案」以修復該問題。
|
||||
|
||||
button.cancel=取消
|
||||
button.change_source=切換下載源
|
||||
@@ -334,9 +334,10 @@ download=下載
|
||||
download.hint=安裝遊戲和模組包或下載模組、資源包和地圖
|
||||
download.code.404=遠端伺服器沒有需要下載的檔案:%s
|
||||
download.content=遊戲內容
|
||||
download.shader=光影
|
||||
download.curseforge.unavailable=這個 HMCL 版本不支援訪問 CurseForge。請使用官方版本進行下載。
|
||||
download.existing=檔案已存在,無法儲存。你可以將檔案儲存至其他地方。
|
||||
download.external_link=打開下載網站
|
||||
download.external_link=開啟下載網站
|
||||
download.failed=下載失敗:%1$s\n錯誤碼:%2$d
|
||||
download.failed.empty=沒有可供安裝的版本,按一下此處返回。
|
||||
download.failed.no_code=下載失敗
|
||||
@@ -351,15 +352,15 @@ download.java=下載 Java
|
||||
download.java.override=此 Java 版本已經存在,是否移除並重新安裝?
|
||||
download.java.process=下載 Java
|
||||
download.javafx=正在下載必要的執行時元件
|
||||
download.javafx.notes=正在透過網路下載 HMCL 必要的執行時元件。\n點擊「切換下載源」按鈕查看詳情以及選取下載源,點擊「取消」按鈕停止並退出。\n注意:如果下載速度過慢,請嘗試切換下載源。
|
||||
download.javafx.notes=正在透過網路下載 HMCL 必要的執行時元件。\n點擊「切換下載源」按鈕查看詳情以及選取下載源。點擊「取消」按鈕停止並退出。\n注意:如果下載速度過慢,請嘗試切換下載源。
|
||||
download.javafx.component=正在下載元件「%s」
|
||||
download.javafx.prepare=準備開始下載
|
||||
|
||||
exception.access_denied=無法存取檔案「%s」,因為 HMCL 沒有對該檔案的訪問權限,或者該檔案已被其他程式打開。\n\
|
||||
exception.access_denied=無法存取檔案「%s」。因為 HMCL 沒有對該檔案的訪問權限,或者該檔案已被其他程式開啟。\n\
|
||||
請你檢查目前作業系統帳戶是否能訪存取檔案,比如非管理員使用者可能不能訪問其他帳戶的個人目錄內的檔案。\n\
|
||||
對於 Windows 使用者,你還可以嘗試透過資源監視器查看是否有程式占用了該檔案,如果是,你可以關閉占用此檔案的程式,或者重啟電腦再試。
|
||||
對於 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=模組檔案
|
||||
@@ -368,24 +369,25 @@ extension.ps1=PowerShell 指令碼
|
||||
extension.sh=Bash 指令碼
|
||||
|
||||
fatal.create_hmcl_current_directory_failure=Hello Minecraft! Launcher 無法建立 HMCL 資料夾 (%s),請將 HMCL 移動至其他位置再開啟。
|
||||
fatal.javafx.incompatible=缺少 JavaFX 執行環境。\nHMCL 無法在低於 Java 11 的 Java 環境上自行補全 JavaFX 執行環境,請更新到 Java 11 或更高版本。
|
||||
fatal.javafx.incomplete=JavaFX 執行環境不完整,請嘗試更換你的 Java 或者重新安裝 OpenJFX。
|
||||
fatal.javafx.missing=缺少 JavaFX 執行環境,請使用包含 OpenJFX 的 Java 執行環境開啟 Hello Minecraft! Launcher。
|
||||
fatal.javafx.incompatible=缺少 JavaFX 執行環境。\nHMCL 無法在低於 Java 11 的 Java 環境上自行補全 JavaFX 執行環境。請更新到 Java 11 或更高版本。
|
||||
fatal.javafx.incomplete=JavaFX 執行環境不完整。請嘗試更換你的 Java 或者重新安裝 OpenJFX。
|
||||
fatal.javafx.missing=缺少 JavaFX 執行環境。請使用包含 OpenJFX 的 Java 執行環境開啟 Hello Minecraft! Launcher。
|
||||
fatal.config_change_owner_root=你正在使用 root 帳戶開啟 Hello Minecraft! Launcher,這可能導致你未來無法使用其他帳戶正常開啟 HMCL。\n是否繼續開啟?
|
||||
fatal.config_in_temp_dir=你正在暫存目錄中開啟 Hello Minecraft! Launcher,你的設定和遊戲資料可能會遺失,建議將 HMCL 移動至其他位置再開啟。\n是否繼續開啟?
|
||||
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.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.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.update=更高版本的 HMCL 需要 Java 11 或更高版本才能執行,請安裝最新版本的 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=由於您的使用者目錄路徑中存在無效字元『=』,您將無法使用外部登入帳戶以及離線登入更換外觀功能。
|
||||
fatal.unsupported_platform=Minecraft 尚未你您的平臺提供完善支援,所以可能影響遊戲體驗或無法啟動遊戲。\n若無法啟動 Minecraft 1.17 及更高版本,可以嘗試在「(全域/實例特定) 遊戲設定 → 進階設定 → 除錯選項」中將「繪製器」切換為「軟繪製器」,以獲得更好的相容性。
|
||||
fatal.unsupported_platform.loongarch=Hello Minecraft! Launcher 已為龍芯提供支援。\n如果遇到問題,你可以點擊右上角幫助按鈕進行求助。
|
||||
fatal.unsupported_platform.macos_arm64=Hello Minecraft! Launcher 已為 Apple Silicon 平臺提供支援,使用 ARM 原生 Java 啟動遊戲以獲得更流暢的遊戲體驗。\n如果你在遊戲中遭遇問題,使用 x86-64 架構的 Java 啟動遊戲可能有更好的相容性。
|
||||
fatal.unsupported_platform.macos_arm64=Hello Minecraft! Launcher 已為 Apple Silicon 平臺提供支援。使用 ARM 原生 Java 啟動遊戲以獲得更流暢的遊戲體驗。\n如果你在遊戲中遭遇問題,使用 x86-64 架構的 Java 啟動遊戲可能有更好的相容性。
|
||||
fatal.unsupported_platform.windows_arm64=Hello Minecraft! Launcher 已為 Windows on Arm 平臺提供原生支援。如果你在遊戲中遭遇問題,請嘗試使用 x86 架構的 Java 啟動遊戲。\n\n如果你正在使用<b>高通</b>平臺,你可能需要安裝 <a href="ms-windows-store://pdp/?productid=9NQPSL29BFFF">OpenGL 相容包</a>後才能進行遊戲。點擊連結前往 Microsoft Store 安裝相容包。
|
||||
|
||||
feedback=回報
|
||||
@@ -406,7 +408,9 @@ folder.mod=模組目錄
|
||||
folder.resourcepacks=資源包目錄
|
||||
folder.shaderpacks=著色器包目錄
|
||||
folder.saves=遊戲存檔目錄
|
||||
folder.schematics=原理圖目錄
|
||||
folder.screenshots=截圖目錄
|
||||
folder.world=世界目錄
|
||||
|
||||
game=遊戲
|
||||
game.crash.feedback=<b>請不要將本介面截圖給他人!</b>如果你要求助他人,請你點擊左下角「匯出遊戲崩潰資訊」後將匯出的檔案發送給他人以供分析。\n你可以點擊下方的「幫助」前往社群尋求幫助。
|
||||
@@ -414,68 +418,68 @@ game.crash.info=遊戲訊息
|
||||
game.crash.reason=崩潰原因
|
||||
game.crash.reason.analyzing=分析中……
|
||||
game.crash.reason.multiple=檢測到多個原因:\n\n
|
||||
game.crash.reason.block=目前遊戲由於某個方塊不能正常工作,無法繼續執行。\n你可以嘗試透過 MCEdit 工具編輯存檔刪除該方塊,或者直接刪除相應的模組。\n方塊類型:%1$s\n方塊坐標:%2$s
|
||||
game.crash.reason.block=目前遊戲由於某個方塊不能正常工作,無法繼續執行。\n你可以嘗試使用 MCEdit 工具編輯存檔刪除該方塊,或者直接刪除對應的模組。\n方塊類型:%1$s\n方塊坐標:%2$s
|
||||
game.crash.reason.bootstrap_failed=目前遊戲由於模組「%1$s」出現問題,無法繼續執行。\n你可以嘗試刪除或更新該模組以解決問題。
|
||||
game.crash.reason.mixin_apply_mod_failed=目前遊戲由於 Mixin 無法應用於「%1$s」模組,無法繼續執行。\n你可以嘗試刪除或更新該模組以解決問題。
|
||||
game.crash.reason.config=目前遊戲由於無法解析模組配置檔案,無法繼續執行\n模組「%1$s」的配置檔案「%2$s」無法被解析。
|
||||
game.crash.reason.debug_crash=目前遊戲由於手動觸發崩潰,無法繼續執行。\n事實上遊戲並沒有問題,問題都是你造成的!
|
||||
game.crash.reason.duplicated_mod=目前遊戲由於模組重複安裝,無法繼續執行。\n%s\n每種模組只能安裝一個,請你刪除多餘的模組再試。
|
||||
game.crash.reason.entity=目前遊戲由於某個實體不能正常工作,無法繼續執行。\n你可以嘗試透過 MCEdit 工具編輯存檔刪除該實體,或者直接刪除相應的模組。\n實體類型:%1$s\n實體坐標:%2$s
|
||||
game.crash.reason.entity=目前遊戲由於某個實體不能正常工作,無法繼續執行。\n你可以嘗試使用 MCEdit 工具編輯存檔刪除該實體,或者直接刪除對應的模組。\n實體類型:%1$s\n實體坐標:%2$s
|
||||
game.crash.reason.fabric_version_0_12=Fabric Loader 0.12 及更高版本與目前已經安裝的模組可能不相容,你需要將 Fabric Loader 降級至 0.11.7。
|
||||
game.crash.reason.fabric_warnings=Fabric 提供了一些警告訊息:\n%1$s
|
||||
game.crash.reason.modmixin_failure=目前遊戲由於某些模組注入失敗,無法繼續執行。\n這一般代表著該模組存在問題,或與目前環境不相容。\n你可以查看日誌尋找出錯模組。
|
||||
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 開頭的版本。
|
||||
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.graphics_driver=目前遊戲由於你的顯示卡驅動存在問題崩潰了,請嘗試升級你的顯示卡驅動到最新版本後再嘗試啟動遊戲。\n\
|
||||
如果你的電腦存在獨立顯示卡,你需要檢查遊戲是否使用整合/核芯顯示卡啟動。如果是,請嘗試使用獨立顯示卡開啟 HMCL 與遊戲。如果仍有問題,你可能需要考慮換一個新顯示卡或新電腦。\n\
|
||||
如果你確實需要使用核芯顯示卡,請檢查你電腦的 CPU 是否為 Intel(R) Core(TM) 3000 系列或更舊的處理器,如果是,對於 Minecraft 1.16.5 及更低版本,請你將遊戲所使用的 Java 版本降級至 1.8.0_51 及更低版本,否則你需要更換獨立顯示卡或新電腦。
|
||||
game.crash.reason.macos_failed_to_find_service_port_for_display=目前遊戲由於 Apple Silicon 平台下初始化 OpenGL 視窗失敗,無法繼續執行。\n對於該問題,HMCL 暫無直接性的解決方案。請你嘗試任意打開一個瀏覽器並切換為全螢幕,然後再回到 HMCL 啟動遊戲,在彈出遊戲視窗前<b>迅速切回瀏覽器頁面</b>,等待遊戲視窗出現後再切回遊戲視窗。
|
||||
game.crash.reason.graphics_driver=目前遊戲由於你的顯示卡驅動存在問題崩潰了。請嘗試升級你的顯示卡驅動到最新版本後再嘗試啟動遊戲。\n\
|
||||
如果你的電腦同時存在獨立顯示卡與整合式顯示卡,你需要檢查遊戲是否使用整合式顯示卡啟動。如果是,請嘗試使用獨立顯示卡開啟 HMCL 與遊戲。如果仍有問題,你可能需要考慮換一個新顯示卡或新電腦。\n\
|
||||
如果你確實需要使用整合式顯示卡,請檢查你電腦的 CPU 是否為 Intel(R) Core(TM) 3000 系列或更舊的處理器。如果是,對於 Minecraft 1.16.5 及更低版本,請你將遊戲所使用的 Java 版本降級至 1.8.0_51 及更低版本;否則你需要更換獨立顯示卡或新電腦。
|
||||
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.jdk_9=目前遊戲由於 Java 版本過高,無法繼續執行。\n你需要下載安裝 Java 8,並在「(全域/實例特定) 遊戲設定 → 遊戲 Java」中將 Java 設定為 1.8 的版本。
|
||||
game.crash.reason.jvm_32bit=目前遊戲由於記憶體分配過大,超過了 32 位 Java 記憶體限制,無法繼續執行。\n如果你的電腦是 64 位系統,請下載安裝並更換 64 位 Java。\n如果你的電腦是 32 位系統,你或許可以重新安裝 64 位系統,或換一台新電腦。\n或者,你可以關閉「(全域/實例特定) 遊戲設定 → 遊戲記憶體」中的「自動分配」,並且把記憶體限制調節為 1024 MiB 或以下。
|
||||
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.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 及更早版本啟動。
|
||||
game.crash.reason.memory_exceeded=目前遊戲由於分配的記憶體過大,無法繼續執行。\n該問題是由於系統頁面檔案太小導致的。\n你需要在「(全域/實例特定) 遊戲設定 → 遊戲記憶體」中關閉「自動分配」,並將遊戲記憶體調低至遊戲能正常啟動為止。\n你還可以嘗試調大系統的頁面大小。
|
||||
game.crash.reason.memory_exceeded=目前遊戲由於記憶體分配過大,無法繼續執行。\n該問題是由於系統分頁檔太小導致的。\n你需要在「(全域/實例特定) 遊戲設定 → 遊戲記憶體」中關閉「自動分配」,並將記憶體分配值調低至遊戲能正常啟動為止。\n你還可以嘗試調大系統的分頁檔大小。
|
||||
game.crash.reason.mod=目前遊戲由於 %1$s 的問題,無法繼續執行。\n你可以更新或刪除已經安裝的「%1$s」再試。
|
||||
game.crash.reason.mod_resolution=目前遊戲由於相依模組問題,無法繼續執行。Fabric 提供了如下訊息:\n%1$s
|
||||
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.mod_resolution_collection=目前遊戲由於相依模組版本不匹配,無法繼續執行。\n「%1$s」需要相依模組「%2$s」才能繼續執行。\n這表示你需要更新或降級相依模組。你可以到「下載 → 模組」頁面下載,或到網路上下載「%3$s」。
|
||||
game.crash.reason.mod_resolution_collection=目前遊戲由於相依模組版本不匹配,無法繼續執行。\n「%1$s」需要相依模組「%2$s」才能繼續執行。\n這表示你需要更新或降級相依模組。你可以到「下載 → 模組」頁面或網路上下載「%3$s」。
|
||||
game.crash.reason.mod_resolution_conflict=目前遊戲由於模組衝突,無法繼續執行。\n「%1$s」與「%2$s」不能相容。
|
||||
game.crash.reason.mod_resolution_missing=目前遊戲由於缺少相依模組,無法繼續執行。\n「%1$s」需要相依模組「%2$s」才能繼續執行。\n這表示你少安裝了模組,或該模組版本不夠。你可以到「下載 → 模組」頁面下載,或到網路上下載「%2$s」。
|
||||
game.crash.reason.mod_resolution_missing_minecraft=目前遊戲由於模組和 Minecraft 遊戲版本不匹配,無法繼續執行。\n「%1$s」需要 Minecraft %2$s 才能執行。\n如果你要繼續使用你已經安裝的模組,你可以選取安裝對應的 Minecraft 版本;如果你要繼續使用目前的 Minecraft 版本,你需要安裝對應版本的模組。
|
||||
game.crash.reason.mod_resolution_missing=目前遊戲由於缺少相依模組,無法繼續執行。\n「%1$s」需要相依模組「%2$s」才能繼續執行。\n這表示你有一些必需的模組沒有安裝,或該模組版本不相配。你可以在「下載 → 模組」頁面或網路上下載「%2$s」。
|
||||
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.optifine_is_not_compatible_with_forge=目前遊戲由於 OptiFine 與目前版本的 Forge 不相容,導致了遊戲崩潰。\n請前往 <a href="https://optifine.net/downloads">OptiFine 官網</a>查看 OptiFine 所相容的 Forge 版本,並嚴格按照對應版本重新安裝遊戲,或在「實例管理 → 自動安裝」中更換版本。\n經測試,Forge 版本過高或過低都可能導致崩潰。
|
||||
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.mod_files_are_decompressed=目前遊戲由於模組檔案被解壓了,無法繼續執行。\n請直接把整個模組檔案放進模組目錄中即可。\n若解壓就會導致遊戲出錯,請刪除模組目錄中已被解壓的模組,然後再啟動遊戲。
|
||||
game.crash.reason.mod_files_are_decompressed=目前遊戲由於模組檔案被解壓了,無法繼續執行。\n請直接把整個模組檔案放進模組目錄中即可。\n解壓模組檔案會導致遊戲出錯。請刪除模組目錄中已被解壓的模組,然後再啟動遊戲。
|
||||
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.modlauncher_8=目前遊戲由於你所使用的 Forge 版本與目前使用的 Java 衝突崩潰,請嘗試更新 Forge。
|
||||
game.crash.reason.modlauncher_8=目前遊戲由於你所使用的 Forge 版本與目前使用的 Java 衝突崩潰。請嘗試更新 Forge。
|
||||
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.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.out_of_memory=目前遊戲由於記憶體不足,無法繼續執行。\n這可能是記憶體分配太小,或者模組數量過多導致的。\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.resolution_too_high=目前遊戲由於資源包/紋理包解析度過高,無法繼續執行。\n你可以更換一個解析度更低的資源包/紋理包,或者更換一個視訊記憶體更大的顯示卡。
|
||||
game.crash.reason.stacktrace=原因未知,請點擊日誌按鈕查看詳細訊息。\n下面是一些關鍵字,其中可能包含模組名稱,你可以透過搜尋的方式尋找有關訊息。\n%s
|
||||
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.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.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=遊戲意外退出
|
||||
game.directory=遊戲目錄路徑
|
||||
game.version=遊戲實例
|
||||
@@ -508,7 +512,7 @@ install.installer.depend=需要先安裝 %s
|
||||
install.installer.do_not_install=不安裝
|
||||
install.installer.fabric=Fabric
|
||||
install.installer.fabric-api=Fabric API
|
||||
install.installer.fabric-api.warning=警告:Fabric API 是模組,將會被安裝到新遊戲的模組目錄,請你在安裝遊戲後不要修改目前遊戲的「執行路徑」設定,如果你在之後修改了相關設定,則需要重新安裝 Fabric API。
|
||||
install.installer.fabric-api.warning=警告:Fabric API 是一個模組,將會被安裝到新遊戲的模組目錄。請你在安裝遊戲後不要修改目前遊戲的「執行路徑」設定。如果你在之後修改了相關設定,則需要重新安裝 Fabric API。
|
||||
install.installer.forge=Forge
|
||||
install.installer.neoforge=NeoForge
|
||||
install.installer.game=Minecraft
|
||||
@@ -529,7 +533,7 @@ install.installer.external_version=%s [由外部安裝的版本,無法解除
|
||||
install.installing=安裝
|
||||
install.modpack=安裝模組包
|
||||
install.modpack.installation=安裝模組包
|
||||
install.name.invalid=名稱中包含非 ASCII 字元(如 Emoji 表情或中文字元)。\n建議修改名稱,名稱建議僅包含英文字母、數字和底線,以防啟動遊戲時出現問題。是否繼續安裝?
|
||||
install.name.invalid=名稱中包含非 ASCII 字元(如 Emoji 表情或中文字元)。\n建議修改名稱。名稱建議僅包含英文字母、數字和底線,以防啟動遊戲時出現問題。是否繼續安裝?
|
||||
install.new_game=安裝新實例
|
||||
install.new_game.already_exists=此實例已經存在,請重新命名
|
||||
install.new_game.current_game_version=目前遊戲實例
|
||||
@@ -566,7 +570,6 @@ java.install.failed.unsupported_platform=此 Java 與目前平臺不相容,無
|
||||
java.install.name=名稱
|
||||
java.install.warning.invalid_character=名稱中包含無效字元
|
||||
java.installing=安裝 Java
|
||||
java.reveal=瀏覽 Java 目錄
|
||||
java.uninstall=移除此 Java
|
||||
java.uninstall.confirm=你確定要移除此 Java 嗎?此操作無法復原!
|
||||
|
||||
@@ -579,19 +582,19 @@ launch.advice.java.auto=目前選取的 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.different_platform=你正在使用 32 位元 Java 啟動遊戲,建議更換至 64 位元 Java。
|
||||
launch.advice.forge2760_liteloader=Forge 14.23.5.2760 與 LiteLoader 不相容,請更新 Forge 至 14.23.5.2773 或更高版本。
|
||||
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 或更低版本。
|
||||
launch.advice.forge37_0_60=Forge 37.0.59 及更低版本與 Java 17 不相容。請更新 Forge 到 37.0.60 或更高版本,或者使用 Java 16 啟動遊戲。
|
||||
launch.advice.java8_1_13=Minecraft 1.13 及更高版本僅支援 Java 8 或更高版本,請使用 Java 8 或最新版本。
|
||||
launch.advice.java8_1_13=Minecraft 1.13 及更高版本僅支援 Java 8 或更高版本。請使用 Java 8 或最新版本。
|
||||
launch.advice.java8_51_1_13=低於 1.8.0_51 的 Java 版本可能會導致 Minecraft 1.13 崩潰。建議你到 https://java.com 安裝最新版的 Java 8。
|
||||
launch.advice.java9=低於 (包含) 1.13 的有安裝模組的 Minecraft 版本不支援 Java 9 或更高版本,請使用 Java 8。
|
||||
launch.advice.modded_java=部分模組可能與高版本 Java 不相容,建議使用 Java %s 啟動 Minecraft %s。
|
||||
launch.advice.java9=低於 (包含) 1.13 的有安裝模組的 Minecraft 版本不支援 Java 9 或更高版本。請使用 Java 8。
|
||||
launch.advice.modded_java=部分模組可能與高版本 Java 不相容。建議使用 Java %s 啟動 Minecraft %s。
|
||||
launch.advice.modlauncher8=你所使用的 Forge 版本與目前使用的 Java 不相容。請更新 Forge。
|
||||
launch.advice.newer_java=偵測到你正在使用舊版本 Java 啟動遊戲,這可能導致部分模組引發遊戲崩潰,建議更新至 Java 8 後再次啟動。
|
||||
launch.advice.not_enough_space=你設定的記憶體大小過大,由於超過了系統記憶體大小 %d MiB,所以可能影響遊戲體驗或無法啟動遊戲。
|
||||
launch.advice.require_newer_java_version=目前遊戲版本需要 Java %s,但 HMCL 未能找到該 Java 版本,你可以點擊「是」,HMCL 會自動下載他,是否下載?
|
||||
launch.advice.too_large_memory_for_32bit=你設定的記憶體大小過大,由於可能超過了 32 位元 Java 的記憶體分配限制,所以可能無法啟動遊戲,請將記憶體調至低於 1024 MiB 的值。
|
||||
launch.advice.newer_java=偵測到你正在使用舊版本 Java 啟動遊戲,這可能導致部分模組引發遊戲崩潰。建議更新至 Java 8 後再次啟動。
|
||||
launch.advice.not_enough_space=你設定的記憶體分配值過大,超過了系統記憶體大小 %d MiB,可能影響遊戲體驗或無法啟動遊戲。
|
||||
launch.advice.require_newer_java_version=目前遊戲版本需要 Java %s,但 HMCL 未能找到該 Java 版本。你可以點擊「是」,HMCL 會自動下載它。是否下載?
|
||||
launch.advice.too_large_memory_for_32bit=你設定的記憶體分配值過大,由於可能超過了 32 位元 Java 的記憶體分配限制,所以可能無法啟動遊戲。請將記憶體分配值調至低於 1024 MiB 的值。
|
||||
launch.advice.vanilla_linux_java_8=對於 Linux x86-64 平台,Minecraft 1.12.2 及更低版本與 Java 9+ 不相容,請使用 Java 8 啟動遊戲。
|
||||
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=由於以下原因,無法繼續啟動遊戲:
|
||||
@@ -605,9 +608,9 @@ launch.failed.executable_permission=無法為啟動檔案新增執行權限。
|
||||
launch.failed.execution_policy=設定執行策略
|
||||
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.exited_abnormally=遊戲非正常退出。請查看日誌檔案,或聯絡他人尋求幫助。
|
||||
launch.failed.java_version_too_low=你所指定的 Java 版本過低。請重新設定 Java 版本。
|
||||
launch.failed.no_accepted_java=找不到適合目前遊戲使用的 Java。是否使用預設 Java 啟動遊戲?點擊「是」使用預設 Java 繼續啟動遊戲,\n或者請到「(全域/實例特定) 遊戲設定 → 遊戲 Java」中選取一個合適的 Java 虛擬機版本。
|
||||
launch.failed.sigkill=遊戲被使用者或系統強制終止。
|
||||
launch.state.dependencies=處理遊戲相依元件
|
||||
launch.state.done=啟動完成
|
||||
@@ -634,11 +637,11 @@ launcher.cache_directory.clean=清理
|
||||
launcher.cache_directory.choose=選取檔案下載快取目錄
|
||||
launcher.cache_directory.default=預設 ("%APPDATA%/.minecraft" 或 "~/.minecraft")
|
||||
launcher.cache_directory.disabled=停用
|
||||
launcher.cache_directory.invalid=無法建立自訂的快取目錄,還原至預設設定
|
||||
launcher.cache_directory.invalid=無法建立自訂的快取目錄。已還原至預設設定。
|
||||
launcher.contact=聯絡我們
|
||||
launcher.crash=Hello Minecraft! Launcher 遇到了無法處理的錯誤,請複製下列內容並透過 GitHub、Discord 或 HMCL QQ 群回報問題。
|
||||
launcher.crash.java_internal_error=Hello Minecraft! Launcher 由於目前 Java 損壞而無法繼續執行,請移除目前 Java,點擊 <a href="https://bell-sw.com/pages/downloads/#downloads">此處</a> 安裝合適的 Java 版本。
|
||||
launcher.crash.hmcl_out_dated=Hello Minecraft! Launcher 遇到了無法處理的錯誤,已偵測到你的啟動器不是最新版本,請更新後重試!
|
||||
launcher.crash=Hello Minecraft! Launcher 遇到了無法處理的錯誤。請複製下列內容並透過 GitHub、Discord 或 HMCL QQ 群回報問題。
|
||||
launcher.crash.java_internal_error=Hello Minecraft! Launcher 由於目前 Java 損壞而無法繼續執行。請移除目前 Java,點擊 <a href="https://bell-sw.com/pages/downloads/#downloads">此處</a> 安裝合適的 Java 版本。
|
||||
launcher.crash.hmcl_out_dated=Hello Minecraft! Launcher 遇到了無法處理的錯誤。已偵測到你的啟動器不是最新版本,請更新後重試!
|
||||
launcher.update_java=請更新你的 Java
|
||||
|
||||
login.empty_username=你還未設定使用者名稱!
|
||||
@@ -678,7 +681,7 @@ modpack.choose.repository=從 CurseForge/Modrinth 下載模組包
|
||||
modpack.choose.repository.detail=下載後記得回到這個介面,把模組包拖進來哦
|
||||
modpack.choose.remote.tooltip=要下載的模組包的連結
|
||||
modpack.completion=下載模組包相關檔案
|
||||
modpack.desc=描述你要製作的模組包,比如模組包注意事項和更新紀錄,支援 Markdown (圖片請上傳至網路)。
|
||||
modpack.desc=描述你要製作的模組包,比如模組包注意事項和更新紀錄。支援 Markdown (圖片請上傳至網路)。
|
||||
modpack.description=模組包描述
|
||||
modpack.download=下載模組包
|
||||
modpack.download.title=模組包下載 - %1s
|
||||
@@ -702,8 +705,8 @@ modpack.files.servers_dat=多人遊戲伺服器清單
|
||||
modpack.installing=安裝模組包
|
||||
modpack.installing.given=安裝 %s 模組包
|
||||
modpack.introduction=支援 Curse、Modrinth、MultiMC、MCBBS 模組包。
|
||||
modpack.invalid=無效的模組包升級檔案,可能是下載時出現問題。
|
||||
modpack.mismatched_type=模組包類型不符,目前遊戲是「%s」模組包,但是提供的模組包更新檔案是「%s」模組包。
|
||||
modpack.invalid=無效的模組包升級檔案。可能是下載時出現問題。
|
||||
modpack.mismatched_type=模組包類型不符。目前遊戲是「%s」模組包,但是提供的模組包更新檔案是「%s」模組包。
|
||||
modpack.name=模組包名稱
|
||||
modpack.not_a_valid_name=模組包名稱無效
|
||||
modpack.origin=來源
|
||||
@@ -715,8 +718,8 @@ modpack.task.install=匯入模組包
|
||||
modpack.task.install.error=無法識別該模組包,目前僅支援匯入 Curse、Modrinth、MultiMC 和 MCBBS 模組包。
|
||||
modpack.type.curse=Curse
|
||||
modpack.type.curse.error=無法完成該模組包所需的相依元件下載,請多次重試或設定代理……
|
||||
modpack.type.curse.not_found=部分必需檔案已經從網路中被刪除並且再也無法下載,請嘗試該模組包的最新版本或者安裝其他模組包。
|
||||
modpack.type.manual.warning=該模組包由發佈者手動打包,其中可能已經包含啟動器,建議嘗試解壓後使用其內建的啟動器執行遊戲。\nHMCL 可以嘗試匯入該模組包,但不保證可用性,是否繼續?
|
||||
modpack.type.curse.not_found=部分必需檔案已經從網路中被刪除並且再也無法下載。請嘗試該模組包的最新版本或者安裝其他模組包。
|
||||
modpack.type.manual.warning=該模組包由發佈者手動打包,其中可能已經包含啟動器。建議嘗試解壓後使用其內建的啟動器執行遊戲。\nHMCL 可以嘗試匯入該模組包,但不保證可用性。是否繼續?
|
||||
modpack.type.mcbbs=MCBBS 模組包
|
||||
modpack.type.mcbbs.export=可以被 Hello Minecraft! Launcher 匯入
|
||||
modpack.type.modrinth=Modrinth
|
||||
@@ -742,11 +745,16 @@ modpack.wizard.step.initialization.warning=在製作模組包前,請你確認
|
||||
modpack.wizard.step.initialization.server=點選此處查看有關伺服器自動更新模組包的製作教學
|
||||
|
||||
modrinth.category.adventure=冒險
|
||||
modrinth.category.atmosphere=氛圍
|
||||
modrinth.category.audio=聲音
|
||||
modrinth.category.blocks=方塊
|
||||
modrinth.category.bloom=泛光
|
||||
modrinth.category.bukkit=Bukkit
|
||||
modrinth.category.bungeecord=BungeeCord
|
||||
modrinth.category.canvas=Canvas
|
||||
modrinth.category.cartoon=卡通
|
||||
modrinth.category.challenging=高難度
|
||||
modrinth.category.colored-lighting=彩色光照
|
||||
modrinth.category.core-shaders=核心著色器
|
||||
modrinth.category.combat=戰鬥
|
||||
modrinth.category.cursed=Cursed
|
||||
@@ -756,19 +764,25 @@ modrinth.category.entities=實體
|
||||
modrinth.category.environment=環境
|
||||
modrinth.category.equipment=裝備
|
||||
modrinth.category.fabric=Fabric
|
||||
modrinth.category.fantasy=幻想
|
||||
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.kitchen-sink=大雜燴
|
||||
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 +792,21 @@ modrinth.category.models=模型
|
||||
modrinth.category.modloader=ModLoader
|
||||
modrinth.category.multiplayer=多人
|
||||
modrinth.category.neoforge=NeoForge
|
||||
modrinth.category.optifine=OptiFine
|
||||
modrinth.category.optimization=最佳化
|
||||
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,6 +817,7 @@ 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
|
||||
@@ -851,22 +874,22 @@ mods.install=安裝到目前實例
|
||||
mods.save_as=下載到本機目錄
|
||||
|
||||
nbt.entries=%s 個條目
|
||||
nbt.open.failed=打開檔案失敗
|
||||
nbt.open.failed=開啟檔案失敗
|
||||
nbt.save.failed=儲存檔案失敗
|
||||
nbt.title=查看檔案 - %s
|
||||
|
||||
datapack=資料包
|
||||
datapack.add=加入資料包
|
||||
datapack.add=新增資料包
|
||||
datapack.choose_datapack=選取要匯入的資料包壓縮檔
|
||||
datapack.extension=資料包
|
||||
datapack.title=世界 [%s] - 資料包
|
||||
|
||||
web.failed=加載頁面失敗
|
||||
web.open_in_browser=是否要在瀏覽器中打開此連結:\n%s
|
||||
web.view_in_browser=在瀏覽器中查看
|
||||
web.open_in_browser=是否要在瀏覽器中開啟此連結:\n%s
|
||||
web.view_in_browser=在瀏覽器中查看完整日誌
|
||||
|
||||
world=世界
|
||||
world.add=加入世界
|
||||
world.add=新增世界
|
||||
world.backup=世界備份
|
||||
world.backup.create.new_one=建立新備份
|
||||
world.backup.create.failed=創建備份失敗。\n%s
|
||||
@@ -874,9 +897,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=世界地圖
|
||||
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 +916,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=地獄
|
||||
@@ -921,10 +946,11 @@ 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.reveal=開啟目錄
|
||||
world.show_all=全部顯示
|
||||
|
||||
profile=遊戲目錄
|
||||
@@ -943,13 +969,13 @@ profile.use_relative_path=如可行,則對於遊戲目錄使用相對路徑
|
||||
repositories.custom=自訂 Maven 倉庫 (%s)
|
||||
repositories.maven_central=全球 (Maven Central)
|
||||
repositories.tencentcloud_mirror=中國大陸 (騰訊雲 Maven 倉庫)
|
||||
repositories.chooser=缺少 JavaFX 執行環境,HMCL 需要 JavaFX 才能正常執行。\n點擊「確認」從指定下載源下載 JavaFX 執行時元件並開啟 HMCL,點擊「取消」退出程式。\n選取下載源:
|
||||
repositories.chooser=缺少 JavaFX 執行環境。HMCL 需要 JavaFX 才能正常執行。\n點擊「確認」從指定下載源下載 JavaFX 執行時元件並開啟 HMCL。點擊「取消」退出程式。\n選取下載源:
|
||||
repositories.chooser.title=選取 JavaFX 下載源
|
||||
|
||||
resourcepack=資源包
|
||||
resourcepack.download.title=資源包下載 - %1s
|
||||
|
||||
reveal.in_explorer=在檔案管理員中查看
|
||||
reveal.in_file_manager=在檔案管理員中查看
|
||||
|
||||
schematics=原理圖
|
||||
schematics.add=添加原理圖
|
||||
@@ -1017,9 +1043,9 @@ 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 參數」可在啟動遊戲時不加入預設參數。
|
||||
settings.advanced.jvm_args.prompt=\ · 若在「Java 虛擬機參數」中輸入的參數與預設參數相同,則不會新增;\n\
|
||||
\ · 若在「Java 虛擬機參數」輸入任何 GC 參數,預設參數的 G1 參數會被禁用;\n\
|
||||
\ · 啟用下方「不新增預設的 JVM 參數」可在啟動遊戲時不新增預設參數。
|
||||
settings.advanced.launcher_visibility.close=遊戲啟動後結束啟動器
|
||||
settings.advanced.launcher_visibility.hide=遊戲啟動後隱藏啟動器
|
||||
settings.advanced.launcher_visibility.hide_and_reopen=隱藏啟動器並在遊戲結束後重新開啟
|
||||
@@ -1031,8 +1057,8 @@ 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如果你要修改本選項,你需要保證自訂目錄下有遊戲所需的本機庫檔案,如 lwjgl.dll (liblwjgl.so), openal.dll (libopenal.so) 等檔案。啟動器不會幫你補全缺少的本機庫檔案。\n注意:建議指定的本機庫檔案路徑使用全英文字元,否則可能導致遊戲啟動失敗。
|
||||
settings.advanced.no_jvm_args=不加入預設的 JVM 參數
|
||||
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.precall_command=遊戲啟動前執行指令
|
||||
settings.advanced.precall_command.prompt=將在遊戲啟動前呼叫使用
|
||||
settings.advanced.process_priority=處理程式優先度
|
||||
@@ -1077,20 +1103,20 @@ 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=遊戲圖示
|
||||
|
||||
settings.launcher=啟動器設定
|
||||
settings.launcher.appearance=外觀
|
||||
settings.launcher.common_path.tooltip=啟動器將所有遊戲資源及相依元件庫檔案放於此集中管理,如果遊戲目錄內有現成的將不會使用公共庫檔案。
|
||||
settings.launcher.common_path.tooltip=啟動器將所有遊戲資源及相依元件庫檔案放於此集中管理。如果遊戲目錄內有現成的將不會使用公共庫檔案。
|
||||
settings.launcher.debug=除錯
|
||||
settings.launcher.download=下載
|
||||
settings.launcher.download.threads=執行緒數
|
||||
settings.launcher.download.threads.auto=自動選取執行緒數
|
||||
settings.launcher.download.threads.hint=執行緒數過高可能導致系統卡頓。你的下載速度會受到網際網路運營商、下載來源伺服器等方面的影響,調高下載執行緒數不一定能大幅提升總下載速度。
|
||||
settings.launcher.download.threads.hint=執行緒數過高可能導致系統卡頓。你的下載速度會受到網際網路運營商、下載來源伺服器等方面的影響。調高下載執行緒數不一定能大幅提升總下載速度。
|
||||
settings.launcher.download_source=下載來源
|
||||
settings.launcher.download_source.auto=自動選取下載來源
|
||||
settings.launcher.enable_game_list=在首頁內顯示遊戲清單
|
||||
@@ -1098,9 +1124,9 @@ settings.launcher.font=字體
|
||||
settings.launcher.general=一般
|
||||
settings.launcher.language=語言 (重啟後生效)
|
||||
settings.launcher.launcher_log.export=匯出啟動器日誌
|
||||
settings.launcher.launcher_log.reveal=打開日誌目錄
|
||||
settings.launcher.launcher_log.export.failed=無法匯出日誌。
|
||||
settings.launcher.launcher_log.export.success=日誌已儲存到「%s」。
|
||||
settings.launcher.launcher_log.reveal=開啟日誌目錄
|
||||
settings.launcher.log=日誌
|
||||
settings.launcher.log.font=日誌字體
|
||||
settings.launcher.proxy=代理
|
||||
@@ -1125,6 +1151,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=查看日誌
|
||||
@@ -1145,7 +1172,7 @@ sponsor.hmcl=Hello Minecraft! Launcher 是一個免費、自由、開源的 Mine
|
||||
system.architecture=架構
|
||||
system.operating_system=作業系統
|
||||
|
||||
unofficial.hint=你正在使用第三方提供的 HMCL,我們無法保證其安全性,請注意甄別。
|
||||
unofficial.hint=你正在使用第三方提供的 HMCL。我們無法保證其安全性,請注意甄別。
|
||||
|
||||
update=啟動器更新
|
||||
update.accept=更新
|
||||
@@ -1169,13 +1196,13 @@ update.bubble.title=發現更新:%s
|
||||
update.bubble.subtitle=點選此處進行升級
|
||||
update.note=開發版與預覽版包含更多的功能以及錯誤修復,但也可能會包含其他的問題。
|
||||
update.latest=目前版本為最新版本
|
||||
update.no_browser=無法開啟瀏覽器,網址已經複製到剪貼簿了,你可以手動複製網址開啟頁面。
|
||||
update.no_browser=無法開啟瀏覽器。網址已經複製到剪貼簿了,你可以手動複製網址開啟頁面。
|
||||
update.tooltip=更新
|
||||
|
||||
version=遊戲
|
||||
version.name=遊戲實例名稱
|
||||
version.cannot_read=讀取遊戲版本失敗,無法進行自動安裝
|
||||
version.empty=沒有遊戲版本
|
||||
version.cannot_read=讀取遊戲實例失敗,無法進行自動安裝
|
||||
version.empty=沒有遊戲實例
|
||||
version.empty.add=進入下載頁安裝遊戲
|
||||
version.empty.launch=沒有可啟動的遊戲。點擊「確定」將進入「下載」頁面。\n你也可以點擊 HMCL 主介面左側的「下載」按鈕安裝遊戲,或在「實例清單」切換遊戲目錄。
|
||||
version.empty.hint=沒有已安裝的遊戲。\n你可以切換其他遊戲目錄,或者點擊此處進入遊戲下載頁面。
|
||||
@@ -1204,7 +1231,7 @@ 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.confirm.independent=由於該實例啟用了「(全域/實例特定) 遊戲設定 → 執行路徑 → 各實例獨立」設定,刪除該實例將導致該遊戲的存檔等資料一同被刪除!真的要刪除實例「%s」嗎?
|
||||
version.manage.remove_assets=刪除所有遊戲資源檔案
|
||||
version.manage.remove_libraries=刪除所有支援庫檔案
|
||||
version.manage.rename=重新命名該實例
|
||||
|
||||
@@ -59,28 +59,28 @@ account.create.authlibInjector=添加外置登录账户 (authlib-injector)
|
||||
account.email=邮箱
|
||||
account.failed=账户刷新失败
|
||||
account.failed.character_deleted=此角色已被删除
|
||||
account.failed.connect_authentication_server=无法连接认证服务器,可能是网络问题,请检查设备能否正常上网或使用代理服务。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.failed.connect_injector_server=无法连接认证服务器,可能是网络问题,请检查设备是否能正常上网、URL 是否输入错误,或使用代理服务。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.failed.injector_download_failure=无法下载 authlib-injector,可能是网络问题,请检查设备是否能正常上网、尝试切换下载源或使用代理服务。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.failed.invalid_credentials=你的用户名或密码错误,或登录次数过多被暂时禁止登录,请稍后再试。
|
||||
account.failed.connect_authentication_server=无法连接认证服务器。可能是网络问题,请检查设备能否正常上网或使用代理服务。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.failed.connect_injector_server=无法连接认证服务器。可能是网络问题,请检查设备是否能正常上网、URL 是否输入错误,或使用代理服务。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.failed.injector_download_failure=无法下载 authlib-injector。可能是网络问题,请检查设备是否能正常上网、尝试切换下载源或使用代理服务。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.failed.invalid_credentials=你的用户名或密码错误,或登录次数过多被暂时禁止登录。请稍后再试。
|
||||
account.failed.invalid_password=无效的密码
|
||||
account.failed.invalid_token=请尝试登出并重新输入密码登录
|
||||
account.failed.migration=你的账户需要迁移至微软账户。如果你已经迁移,你需要使用迁移后的微软账户登录。
|
||||
account.failed.no_character=该账户没有角色
|
||||
account.failed.server_disconnected=无法访问登录服务器,账户信息刷新失败。\n\
|
||||
account.failed.server_disconnected=无法访问登录服务器。账户信息刷新失败。\n\
|
||||
你可以选择“再次刷新账户”重新尝试。\n\
|
||||
你也可以选择“跳过账户刷新”继续启动游戏,但可能会导致账户信息未同步更新。\n\
|
||||
若最近没有刷新账户信息,则可能导致账户信息过期失效。\n\
|
||||
若使用微软账户或外置登录账户启动游戏,账户信息过期失效可能将无法进入需在线验证的服务器。\n\
|
||||
若尝试多次无法成功刷新,可尝试重新添加该账户,或许会解决该问题。\n\
|
||||
你可以点击右上角帮助按钮进行求助。
|
||||
account.failed.server_response_malformed=无法解析认证服务器响应,可能是服务器故障。
|
||||
account.failed.ssl=连接服务器时发生了 SSL 错误,可能网站证书已过期或你使用的 Java 版本过低,请尝试更新 Java,或关闭网络代理后再试。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.failed.server_response_malformed=无法解析认证服务器响应。可能是服务器故障。
|
||||
account.failed.ssl=连接服务器时发生了 SSL 错误。可能网站证书已过期或你使用的 Java 版本过低。请尝试更新 Java,或关闭网络代理后再试。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.failed.wrong_account=登录了错误的账户
|
||||
account.hmcl.hint=你需要点击“登录”按钮,并在弹出的网页中完成登录。
|
||||
account.injector.add=添加认证服务器
|
||||
account.injector.empty=无 (点击右侧加号添加)
|
||||
account.injector.http=警告:此服务器使用不安全的 HTTP 协议,你的密码在登录时会被明文传输。
|
||||
account.injector.http=警告:此服务器使用不安全的 HTTP 协议。你的密码在登录时会被明文传输。
|
||||
account.injector.link.homepage=主页
|
||||
account.injector.link.register=注册
|
||||
account.injector.server=认证服务器
|
||||
@@ -101,13 +101,13 @@ account.methods=登录方式
|
||||
account.methods.authlib_injector=外置登录
|
||||
account.methods.microsoft=微软账户
|
||||
account.methods.microsoft.birth=如何更改账户出生日期
|
||||
account.methods.microsoft.close_page=已完成微软账户授权,接下来启动器还需要完成其余登录步骤。你现在可以关闭本页面了。
|
||||
account.methods.microsoft.close_page=已完成微软账户授权。其余登录步骤将由启动器自动执行。你现在可以关闭本页面了。
|
||||
account.methods.microsoft.deauthorize=解除账户授权
|
||||
account.methods.microsoft.error.add_family=请点击上方“编辑账户个人信息”更改你的账户出生日期,使年龄满 18 岁以上,或将账户加入到家庭中。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.methods.microsoft.error.add_family_probably=请点击上方“编辑账户个人信息”更改你的账户出生日期,使年龄满 18 岁以上,或将账户加入到家庭中。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.methods.microsoft.error.country_unavailable=你所在的国家或地区不受 Xbox Live 的支持。
|
||||
account.methods.microsoft.error.missing_xbox_account=请点击上方“创建档案”关联 Xbox 账户。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.methods.microsoft.error.no_character=请确认你已经购买了 Minecraft: Java 版。\n若已购买,则可能未创建游戏档案,请点击上方“创建档案”以创建游戏档案。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.methods.microsoft.error.no_character=请确认你已经购买了 Minecraft: Java 版。\n若已购买,则可能未创建游戏档案。请点击上方“创建档案”以创建游戏档案。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.methods.microsoft.error.banned=你的账户可能被 Xbox Live 封禁。\n你可以点击上方“检测账户是否被封禁”查看账户状态。
|
||||
account.methods.microsoft.error.unknown=未知问题。错误码:%d。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.methods.microsoft.error.wrong_verify_method=请在微软账户登录页面使用密码登录,不要使用验证码登录。\n你可以点击右上角帮助按钮进行求助。
|
||||
@@ -120,7 +120,7 @@ account.methods.microsoft.hint=你需要按照以下步骤添加账户:\n\
|
||||
\ 4. 当网站提示“是否允许此应用访问你的信息?”时,请点击“接受”;\n\
|
||||
\ 5. 在网站提示“大功告成”后,等待账户完成添加即可。\n\
|
||||
若网站提示“出现错误”或账户添加失败时,请按照以上步骤重新添加。\n\
|
||||
<b>若设备网络环境不佳,可能会导致网页加载缓慢甚至无法加载,请使用网络代理并重试。</b>\n\
|
||||
<b>若设备网络环境不佳,可能会导致网页加载缓慢甚至无法加载。请使用网络代理并重试。</b>\n\
|
||||
如遇到问题,你可以点击右上角帮助按钮进行求助。
|
||||
account.methods.microsoft.manual=你需要按照以下步骤添加:\n\
|
||||
\ 1. 点击“登录”按钮;\n\
|
||||
@@ -130,13 +130,13 @@ account.methods.microsoft.manual=你需要按照以下步骤添加:\n\
|
||||
\ 5. 在网站提示“大功告成”后,等待账户完成添加即可。\n\
|
||||
若网站未能显示,请手动在浏览器中打开:%2$s\n\
|
||||
若网站提示“出现错误”或账户添加失败时,请按照以上步骤重新添加。\n\
|
||||
<b>若设备网络环境不佳,可能会导致网页加载缓慢甚至无法加载,请使用网络代理并重试。</b>\n\
|
||||
<b>若设备网络环境不佳,可能会导致网页加载缓慢甚至无法加载。请使用网络代理并重试。</b>\n\
|
||||
如遇到问题,你可以点击右上角帮助按钮进行求助。
|
||||
account.methods.microsoft.profile=编辑账户个人信息
|
||||
account.methods.microsoft.purchase=购买 Minecraft
|
||||
account.methods.forgot_password=忘记密码
|
||||
account.methods.ban_query=检测账户是否被封禁
|
||||
account.methods.microsoft.snapshot=你正在使用第三方提供的 HMCL,请下载官方版本来登录微软账户。
|
||||
account.methods.microsoft.snapshot=你正在使用第三方提供的 HMCL。请下载官方版本来登录微软账户。
|
||||
account.methods.microsoft.snapshot.website=官方网站
|
||||
account.methods.offline=离线模式
|
||||
account.methods.offline.name.special_characters=建议使用英文字符、数字以及下划线命名
|
||||
@@ -147,7 +147,7 @@ account.methods.offline.name.invalid=游戏用户名建议仅使用英文字母
|
||||
\n\
|
||||
使用非法用户名会导致你无法加入大部分服务器,并可能与部分模组冲突而使游戏崩溃。
|
||||
account.methods.offline.uuid=UUID
|
||||
account.methods.offline.uuid.hint=UUID 是 Minecraft 玩家的唯一标识符,每个启动器生成 UUID 的方式可能不同。通过将 UUID 修改为原启动器所生成的 UUID,你可以保证在切换启动器后,游戏还能将你的游戏角色识别为给定 UUID 所对应的角色,从而保留原角色的背包物品。UUID 选项为高级选项,除非你知道你在做什么,否则你不需要调整该选项。
|
||||
account.methods.offline.uuid.hint=UUID 是 Minecraft 玩家的唯一标识符。每个启动器生成 UUID 的方式可能不同。通过将 UUID 修改为原启动器所生成的 UUID,你可以保证在切换启动器后,游戏还能将你的游戏角色识别为给定 UUID 所对应的角色,从而保留原角色的背包物品。UUID 选项为高级选项。除非你知道你在做什么,否则你不需要调整该选项。
|
||||
account.methods.offline.uuid.malformed=格式错误
|
||||
account.missing=没有游戏账户
|
||||
account.missing.add=点击此处添加账户
|
||||
@@ -166,7 +166,7 @@ account.skin.type.csl_api=Blessing Skin 服务器
|
||||
account.skin.type.csl_api.location=服务器地址
|
||||
account.skin.type.csl_api.location.hint=CustomSkinAPI 地址
|
||||
account.skin.type.little_skin=LittleSkin 皮肤站
|
||||
account.skin.type.little_skin.hint=你需要在皮肤站中创建并使用和该离线账户同名的角色,此时离线账户皮肤将显示为皮肤站上对应角色所设置的皮肤。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.skin.type.little_skin.hint=你需要在皮肤站中创建并使用和该离线账户同名的角色。此时离线账户皮肤将显示为皮肤站上对应角色所设置的皮肤。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.skin.type.local_file=本地皮肤图片文件
|
||||
account.skin.type.steve=Steve
|
||||
account.skin.upload=上传/编辑皮肤
|
||||
@@ -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=打开下载网站
|
||||
@@ -359,16 +360,16 @@ download.java=下载 Java
|
||||
download.java.override=此 Java 版本已经存在,是否卸载并重新安装?
|
||||
download.java.process=下载 Java
|
||||
download.javafx=正在下载必要的运行时组件……
|
||||
download.javafx.notes=正在通过网络下载 HMCL 必要的运行时组件。\n点击“切换下载源”按钮查看详情以及选择下载源,点击“取消”按钮停止并退出。\n注意:若下载速度过慢,请尝试切换下载源。
|
||||
download.javafx.notes=正在通过网络下载 HMCL 必要的运行时组件。\n点击“切换下载源”按钮查看详情以及选择下载源。点击“取消”按钮停止并退出。\n注意:若下载速度过慢,请尝试切换下载源。
|
||||
download.javafx.component=正在下载模块“%s”
|
||||
download.javafx.prepare=准备开始下载
|
||||
|
||||
exception.access_denied=无法访问文件“%s”,因为 HMCL 没有对该文件的访问权限,或者该文件已被其他程序打开。\n\
|
||||
exception.access_denied=无法访问文件“%s”。HMCL 没有对该文件的访问权限,或者该文件已被其他程序打开。\n\
|
||||
请你检查当前操作系统账户是否能访问该文件,比如非管理员用户可能无法访问其他账户的个人文件夹内的文件。\n\
|
||||
对于 Windows 用户,你还可以尝试通过资源监视器查看是否有程序占用了该文件,如果是,请关闭占用该文件的程序,或者重启电脑再试。\n\
|
||||
对于 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=模组文件
|
||||
@@ -377,24 +378,25 @@ extension.ps1=PowerShell 脚本
|
||||
extension.sh=Bash 脚本
|
||||
|
||||
fatal.create_hmcl_current_directory_failure=Hello Minecraft! Launcher 无法创建 HMCL 文件夹 (%s),请将 HMCL 移动至其他位置再启动。\n如遇到问题,你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
|
||||
fatal.javafx.incompatible=缺少 JavaFX 运行环境。\nHello Minecraft! Launcher 无法在低于 Java 11 的 Java 环境上自行补全 JavaFX 运行环境,请更新到 Java 11 或更高版本。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
|
||||
fatal.javafx.incomplete=JavaFX 运行环境不完整,请尝试更换你的 Java 或者重新安装 OpenJFX。
|
||||
fatal.javafx.missing=缺少 JavaFX 运行环境,请使用包含 OpenJFX 的 Java 运行环境启动 Hello Minecraft! Launcher。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
|
||||
fatal.config_change_owner_root=你正在使用 root 账户启动 Hello Minecraft! Launcher, 这可能导致你未来无法正常使用其他账户正常启动 HMCL。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。\n是否继续启动?
|
||||
fatal.config_in_temp_dir=你正在临时文件夹中启动 Hello Minecraft! Launcher, 你的设置和游戏数据可能会丢失,建议将 HMCL 移动至其他位置再启动。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。\n是否继续启动?
|
||||
fatal.javafx.incompatible=缺少 JavaFX 运行环境。\nHello Minecraft! Launcher 无法在低于 Java 11 的 Java 环境上自行补全 JavaFX 运行环境。请更新到 Java 11 或更高版本。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
|
||||
fatal.javafx.incomplete=JavaFX 运行环境不完整。请尝试更换你的 Java 或者重新安装 OpenJFX。
|
||||
fatal.javafx.missing=缺少 JavaFX 运行环境。请使用包含 OpenJFX 的 Java 运行环境启动 Hello Minecraft! Launcher。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
|
||||
fatal.config_change_owner_root=你正在使用 root 账户启动 Hello Minecraft! Launcher,这可能导致你未来无法正常使用其他账户正常启动 HMCL。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。\n是否继续启动?
|
||||
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.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.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.update=更高版本的 HMCL 需要 Java 11 或更高版本才能运行,请安装最新版本的 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.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.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如遇到问题,你可以点击右上角帮助按钮进行求助。
|
||||
|
||||
feedback=反馈
|
||||
@@ -415,47 +417,49 @@ folder.mod=模组文件夹
|
||||
folder.resourcepacks=资源包文件夹
|
||||
folder.shaderpacks=光影包文件夹
|
||||
folder.saves=存档文件夹
|
||||
folder.schematics=原理图文件夹
|
||||
folder.screenshots=截图文件夹
|
||||
folder.world=世界文件夹
|
||||
|
||||
game=游戏
|
||||
game.crash.feedback=<b>请不要将本界面截图给他人!</b>如果你要向他人求助,请你点击左下角<b>“导出游戏崩溃信息”</b>后将导出的文件发送给他人以供分析。\n你可以点击下方的<b>“帮助”</b>前往交流群寻求帮助。
|
||||
game.crash.info=游戏信息
|
||||
game.crash.reason=崩溃原因
|
||||
game.crash.reason.analyzing=分析中……
|
||||
game.crash.reason.block=当前游戏由于某个方块不能正常工作,无法继续运行。\n你可以尝试通过 <a href="https://www.mcedit.net">MCEdit</a> 工具编辑存档删除该方块,或者直接删除相应的模组。\n方块类型:%1$s\n方块坐标:%2$s
|
||||
game.crash.reason.block=当前游戏由于某个方块不能正常工作,无法继续运行。\n你可以尝试使用 <a href="https://www.mcedit.net">MCEdit</a> 工具编辑存档删除该方块,或者直接删除对应的模组。\n方块类型:%1$s\n方块坐标:%2$s
|
||||
game.crash.reason.bootstrap_failed=当前游戏由于模组“%1$s”出现问题,无法继续运行。\n你可以尝试删除或更新该模组以解决问题。
|
||||
game.crash.reason.mixin_apply_mod_failed=当前游戏由于 Mixin 无法应用于“%1$s”模组,无法继续运行。\n你可以尝试删除或更新该模组以解决问题。
|
||||
game.crash.reason.config=当前游戏由于无法解析模组配置文件,无法继续运行。\n无法解析模组“%1$s”的配置文件“%2$s”。
|
||||
game.crash.reason.multiple=检测到多个原因:\n\n
|
||||
game.crash.reason.debug_crash=当前游戏由于手动触发崩溃,无法继续运行。\n事实上游戏并没有问题,问题都是你造成的!
|
||||
game.crash.reason.duplicated_mod=当前游戏由于模组“%1$s”重复安装,无法继续运行。\n%2$s\n每种模组只能安装一个,请你删除多余的模组再试。
|
||||
game.crash.reason.entity=当前游戏由于某个实体不能正常工作,无法继续运行。\n你可以尝试通过 <a href="https://www.mcedit.net">MCEdit</a> 工具编辑存档删除该实体,或者直接删除相应的模组。\n实体类型:%1$s\n实体坐标:%2$s
|
||||
game.crash.reason.entity=当前游戏由于某个实体不能正常工作,无法继续运行。\n你可以尝试使用 <a href="https://www.mcedit.net">MCEdit</a> 工具编辑存档删除该实体,或者直接删除对应的模组。\n实体类型:%1$s\n实体坐标:%2$s
|
||||
game.crash.reason.fabric_version_0_12=Fabric Loader 0.12 及更高版本与当前已经安装的模组可能不兼容,你需要将 Fabric Loader 降级至 0.11.7。
|
||||
game.crash.reason.fabric_warnings=Fabric 提供了一些警告信息:\n%1$s
|
||||
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.graphics_driver=当前游戏由于显卡驱动问题而崩溃,请尝试以下操作:\n\
|
||||
\ · 如果你的电脑存在独立显卡,请尝试使用独立显卡而非 Intel 核显启动 HMCL 与游戏;<a href="https://www.bing.com/search?q=使用独立显卡运行Minecraft">详情</a>\n\
|
||||
\ · <a href="https://minecrafthopper.net/help/pixel-format-not-accelerated/">尝试升级你的显卡驱动到最新版本</a>,或回退到出厂版本;\n\
|
||||
\ · 如果你确实需要使用核芯显卡,请检查你电脑的 CPU 是否为 Intel(R) Core(TM) 3000 系列或更旧的处理器。如果是,对于 Minecraft 1.16.5 及更旧版本,请你将游戏所使用的 Java 降级至 1.8.0_51 及更低版本,否则请跳过;<a href="https://github.com/frekele/oracle-java/releases/8u51-b16">Java 1.8.0 历史版本</a>\n\
|
||||
\ · 如果你的电脑同时存在独立显卡和集成显卡,请尝试使用独立显卡启动 HMCL 与游戏。<a href="https://www.bing.com/search?q=使用独立显卡运行Minecraft">详情</a>\n\
|
||||
\ · <a href="https://minecrafthopper.net/help/pixel-format-not-accelerated/">尝试升级你的显卡驱动到最新版本</a>,或回退到出厂版本。\n\
|
||||
\ · 如果你确实需要使用集成显卡,请检查你电脑的 CPU 是否为 Intel(R) Core(TM) 3000 系列或更旧的处理器。如果是,对于 Minecraft 1.16.5 及更旧版本,请你将游戏所使用的 Java 降级至 1.8.0_51 及更低版本,否则请跳过。<a href="https://github.com/frekele/oracle-java/releases/8u51-b16">Java 1.8.0 历史版本</a>\n\
|
||||
如果仍有问题,你可能需要考虑换一张新显卡或一台新电脑。
|
||||
game.crash.reason.macos_failed_to_find_service_port_for_display=当前游戏由于 Apple Silicon 平台下初始化 OpenGL 窗口失败,无法继续运行。\n对于该问题,HMCL 暂无直接性的解决方案。请你尝试任意打开一个浏览器并全屏,然后再回到 HMCL 启动游戏,在弹出游戏窗口前<b>迅速切回浏览器页面</b>,等待游戏窗口出现后再切回游戏窗口。
|
||||
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 开头的版本。
|
||||
game.crash.reason.jdk_9=当前游戏由于 Java 版本过高,无法继续运行。\n你需要下载安装 <a href="https://bell-sw.com/pages/downloads/#downloads">Java 8</a>,并在“(全局/版本特定) 游戏设置 → 游戏 Java”中将 Java 设置为 1.8 的版本。
|
||||
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.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.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
|
||||
game.crash.reason.mod_resolution_collection=当前游戏由于前置模组版本不匹配,无法继续运行。\n“%1$s”需要前置模组“%2$s”才能继续运行。\n这表示你需要更新或降级前置。你可以前往“下载 → 模组”页面下载,或到网上下载“%3$s”。
|
||||
game.crash.reason.mod_resolution_collection=当前游戏由于前置模组版本不匹配,无法继续运行。\n“%1$s”需要前置模组“%2$s”才能继续运行。\n这表示你需要更新或降级前置模组。你可以前往“下载 → 模组”页面或网上下载“%3$s”。
|
||||
game.crash.reason.mod_resolution_conflict=当前游戏由于模组冲突,无法继续运行。\n“%1$s”与“%2$s”不兼容。
|
||||
game.crash.reason.mod_resolution_missing=当前游戏由于缺少前置模组,无法继续运行。\n“%1$s”需要前置模组“%2$s”才能继续运行。\n这表示你少安装了模组,或该模组版本不够。你可以前往“下载 → 模组”页面下载,或到网上下载“%3$s”。
|
||||
game.crash.reason.mod_resolution_missing_minecraft=当前游戏由于模组和 Minecraft 游戏版本不匹配,无法继续运行。\n“%1$s”需要 Minecraft %2$s 才能运行。\n如果你要继续使用你已经安装的模组,你可以选择安装对应的 Minecraft 版本;如果你要继续使用当前 Minecraft 版本,你需要安装对应版本的模组。
|
||||
game.crash.reason.mod_resolution_missing=当前游戏由于缺少前置模组,无法继续运行。\n“%1$s”需要前置模组“%2$s”才能继续运行。\n这表示有一些必需的模组没有安装,或该模组版本不匹配。你可以前往“下载 → 模组”页面或网上下载“%3$s”。
|
||||
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 并重新安装。
|
||||
@@ -470,22 +474,22 @@ game.crash.reason.java_version_is_too_high=当前游戏由于 Java 虚拟机版
|
||||
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.mod_files_are_decompressed=当前游戏由于模组文件被解压了,无法继续运行。\n请直接把整个模组文件放进模组文件夹中即可。\n解压模组会导致游戏出错,请删除模组文件夹中已被解压的模组,然后再启动游戏。
|
||||
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.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.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.resolution_too_high=当前游戏由于资源包/纹理包分辨率过高,无法继续运行\n你可以更换一个分辨率更低的资源包/纹理包,或者更换一个显存更大的显卡。
|
||||
game.crash.reason.stacktrace=原因未知,请点击日志按钮查看详细信息。\n下面是一些关键词,其中可能包含模组名称,你可以通过搜索的方式查找有关信息。\n%s
|
||||
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.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.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.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=游戏意外退出
|
||||
game.directory=游戏文件夹路径
|
||||
game.version=游戏版本
|
||||
@@ -518,7 +522,7 @@ install.installer.depend=需要先安装 %s
|
||||
install.installer.do_not_install=不安装
|
||||
install.installer.fabric=Fabric
|
||||
install.installer.fabric-api=Fabric API
|
||||
install.installer.fabric-api.warning=警告:Fabric API 是一个模组,将会被安装到新游戏的模组文件夹,请你在安装游戏后不要修改当前游戏的“运行路径”设置,如果你在之后修改了相关设置,则需要重新安装 Fabric API。
|
||||
install.installer.fabric-api.warning=警告:Fabric API 是一个模组,将会被安装到新游戏的模组文件夹。请你在安装游戏后不要修改当前游戏的“运行路径”设置。如果你在之后修改了相关设置,则需要重新安装 Fabric API。
|
||||
install.installer.forge=Forge
|
||||
install.installer.neoforge=NeoForge
|
||||
install.installer.game=Minecraft
|
||||
@@ -539,7 +543,7 @@ install.installer.external_version=%s (由外部安装的版本,无法卸载
|
||||
install.installing=安装
|
||||
install.modpack=安装整合包
|
||||
install.modpack.installation=安装整合包
|
||||
install.name.invalid=名称中包含非 ASCII 字符(如 Emoji 表情或中文字符)。\n建议修改名称,名称建议仅包含英文字母、数字和下划线,以防启动游戏时出现问题。是否继续安装?
|
||||
install.name.invalid=名称中包含非 ASCII 字符(如 Emoji 表情或中文字符)。\n建议修改名称。名称建议仅包含英文字母、数字和下划线,以防启动游戏时出现问题。是否继续安装?
|
||||
install.new_game=安装新游戏
|
||||
install.new_game.already_exists=此版本已经存在,请换一个名字
|
||||
install.new_game.current_game_version=当前游戏版本
|
||||
@@ -576,7 +580,6 @@ java.install.failed.unsupported_platform=此 Java 与当前平台不兼容,无
|
||||
java.install.name=名称
|
||||
java.install.warning.invalid_character=名称中包含非法字符
|
||||
java.installing=安装 Java
|
||||
java.reveal=浏览 Java 文件夹
|
||||
java.uninstall=卸载此 Java
|
||||
java.uninstall.confirm=你确定要卸载此 Java 吗?此操作无法撤销!
|
||||
|
||||
@@ -589,35 +592,35 @@ launch.advice.java.auto=当前选择的 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.different_platform=你正在使用 32 位 Java 启动游戏,建议更换至 64 位 Java。
|
||||
launch.advice.forge2760_liteloader=Forge 14.23.5.2760 与 LiteLoader 不兼容,请更新 Forge 至 14.23.5.2773 或更高版本。
|
||||
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 或更低版本。
|
||||
launch.advice.forge37_0_60=Forge 37.0.59 及更低版本与 Java 17 不兼容。请更新 Forge 至 37.0.60 或更高版本,或使用 Java 16 启动游戏。
|
||||
launch.advice.java8_1_13=Minecraft 1.13 及更高版本只能在 Java 8 或更高版本上运行,请使用 Java 8 或最新版本。
|
||||
launch.advice.java8_51_1_13=低于 1.8.0_51 的 Java 版本可能会导致 Minecraft 1.13 崩溃,建议更新 Java 至 1.8.0_51 或更高版本后再次启动。
|
||||
launch.advice.java9=低于 1.13 的有安装模组的 Minecraft 版本与 Java 9 或更高版本不兼容,请使用 Java 8。
|
||||
launch.advice.modded_java=部分模组可能与高版本 Java 不兼容,建议使用 Java %s 启动 Minecraft %s。
|
||||
launch.advice.modlauncher8=你所使用的 Forge 版本与当前使用的 Java 不兼容,请更新 Forge。
|
||||
launch.advice.newer_java=检测到你正在使用旧版本 Java 启动游戏,这可能导致部分模组引发游戏崩溃,建议更新至 Java 8 后再次启动。
|
||||
launch.advice.not_enough_space=你设置的内存大小过大,超过了系统内存容量 %d MiB,可能导致游戏无法启动。
|
||||
launch.advice.require_newer_java_version=当前游戏版本需要 Java %s,但 HMCL 未能找到该 Java 版本,你可以点击“是”,HMCL 会自动下载他,是否下载?\n如遇到问题,你可以点击右上角帮助按钮进行求助。
|
||||
launch.advice.too_large_memory_for_32bit=你设置的内存大小过大,由于可能超过了 32 位 Java 的内存分配限制,所以可能无法启动游戏,请将内存调至 1024 MiB 或更小。\n如遇到问题,你可以点击右上角帮助按钮进行求助。
|
||||
launch.advice.java8_1_13=Minecraft 1.13 及更高版本只能在 Java 8 或更高版本上运行。请使用 Java 8 或最新版本。
|
||||
launch.advice.java8_51_1_13=低于 1.8.0_51 的 Java 版本可能会导致 Minecraft 1.13 崩溃。建议更新 Java 至 1.8.0_51 或更高版本后再次启动。
|
||||
launch.advice.java9=低于 1.13 的有安装模组的 Minecraft 版本与 Java 9 或更高版本不兼容。请使用 Java 8。
|
||||
launch.advice.modded_java=部分模组可能与高版本 Java 不兼容。建议使用 Java %s 启动 Minecraft %s。
|
||||
launch.advice.modlauncher8=你所使用的 Forge 版本与当前使用的 Java 不兼容。请更新 Forge。
|
||||
launch.advice.newer_java=检测到你正在使用旧版本 Java 启动游戏,这可能导致部分模组引发游戏崩溃。建议更新至 Java 8 后再次启动。
|
||||
launch.advice.not_enough_space=你设置的内存分配值过大,超过了系统内存容量 %d MiB,可能导致游戏无法启动。
|
||||
launch.advice.require_newer_java_version=当前游戏版本需要 Java %s,但 HMCL 未能找到该 Java 版本。你可以点击“是”,HMCL 会自动下载它。是否下载?\n如遇到问题,你可以点击右上角帮助按钮进行求助。
|
||||
launch.advice.too_large_memory_for_32bit=你设置的内存分配值过大,由于可能超过了 32 位 Java 的内存分配限制,所以可能无法启动游戏。请将内存分配值调至 1024 MiB 或更小。\n如遇到问题,你可以点击右上角帮助按钮进行求助。
|
||||
launch.advice.vanilla_linux_java_8=对于 Linux x86-64 平台,Minecraft 1.12.2 及更低版本与 Java 9+ 不兼容,请使用 Java 8 启动游戏。\n如遇到问题,你可以点击右上角帮助按钮进行求助。
|
||||
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.creating_process=启动失败,在创建新进程时发生错误,可能是 Java 路径错误。
|
||||
launch.failed.command_too_long=命令长度超过限制,无法创建批处理脚本,请导出为 PowerShell 脚本。
|
||||
launch.failed.cannot_create_jvm=无法创建 Java 虚拟机。可能是 Java 参数有问题,你可以在“(全局/版本特定) 游戏设置 → 高级设置 → Java 虚拟机设置”中移除所有 Java 虚拟机参数后,尝试再次启动游戏。
|
||||
launch.failed.creating_process=启动失败。在创建新进程时发生错误,可能是 Java 路径错误。
|
||||
launch.failed.command_too_long=命令长度超过限制,无法创建批处理脚本。请导出为 PowerShell 脚本。
|
||||
launch.failed.decompressing_natives=未能解压游戏本地库。
|
||||
launch.failed.download_library=未能下载游戏依赖“%s”
|
||||
launch.failed.executable_permission=未能为启动文件添加执行权限。
|
||||
launch.failed.execution_policy=设置执行策略
|
||||
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.exited_abnormally=游戏非正常退出。请查看日志文件,或联系他人寻求帮助。
|
||||
launch.failed.java_version_too_low=你所指定的 Java 版本过低。请重新设置 Java 版本。
|
||||
launch.failed.no_accepted_java=找不到适合当前游戏使用的 Java。是否使用默认 Java 启动游戏?点击“是”使用默认 Java 继续启动游戏,\n或者在“(全局/版本特定) 游戏设置 → 游戏 Java”中选择一个合适的 Java 虚拟机版本。\n你可以点击右上角帮助按钮进行求助。
|
||||
launch.failed.sigkill=游戏被用户或系统强制终止。
|
||||
launch.state.dependencies=处理游戏依赖
|
||||
launch.state.done=启动完成
|
||||
@@ -644,11 +647,11 @@ launcher.cache_directory.clean=清理缓存
|
||||
launcher.cache_directory.choose=选择文件下载缓存文件夹
|
||||
launcher.cache_directory.default=默认 ("%APPDATA%/.minecraft" 或 "~/.minecraft")
|
||||
launcher.cache_directory.disabled=禁用 (总是使用游戏文件夹路径)
|
||||
launcher.cache_directory.invalid=无法创建自定义的缓存文件夹,恢复默认设置
|
||||
launcher.cache_directory.invalid=无法创建自定义的缓存文件夹。已经恢复到默认设置。
|
||||
launcher.contact=联系我们
|
||||
launcher.crash=Hello Minecraft! Launcher 遇到了无法处理的错误,请复制下列内容并点击右下角的按钮反馈问题。
|
||||
launcher.crash.java_internal_error=Hello Minecraft! Launcher 由于当前 Java 损坏而无法继续运行,请卸载当前 Java,点击 <a href="https://bell-sw.com/pages/downloads/#downloads">此处</a> 安装合适的 Java 版本。
|
||||
launcher.crash.hmcl_out_dated=Hello Minecraft! Launcher 遇到了无法处理的错误,已检测到你的启动器不是最新版本,请更新后再试。
|
||||
launcher.crash=Hello Minecraft! Launcher 遇到了无法处理的错误。请复制下列内容并点击右下角的按钮反馈问题。
|
||||
launcher.crash.java_internal_error=Hello Minecraft! Launcher 由于当前 Java 损坏而无法继续运行。请卸载当前 Java,点击 <a href="https://bell-sw.com/pages/downloads/#downloads">此处</a> 安装合适的 Java 版本。
|
||||
launcher.crash.hmcl_out_dated=Hello Minecraft! Launcher 遇到了无法处理的错误。已检测到你的启动器不是最新版本,请更新后再试。
|
||||
launcher.update_java=请更新你的 Java。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
|
||||
|
||||
login.empty_username=你还未设置用户名!
|
||||
@@ -688,7 +691,7 @@ modpack.choose.repository=从 CurseForge/Modrinth 下载整合包
|
||||
modpack.choose.repository.detail=下载后记得回到这个界面,把整合包拖进来哦
|
||||
modpack.choose.remote.tooltip=要下载的整合包的链接
|
||||
modpack.completion=下载整合包相关文件
|
||||
modpack.desc=描述你要制作的整合包,比如整合包注意事项和更新记录,支持 Markdown (图片请用网络链接)
|
||||
modpack.desc=描述你要制作的整合包,比如整合包注意事项和更新记录。支持 Markdown (图片请用网络链接)。
|
||||
modpack.description=整合包描述
|
||||
modpack.download=下载整合包
|
||||
modpack.download.title=整合包下载 - %1s
|
||||
@@ -712,8 +715,8 @@ modpack.files.servers_dat=多人游戏服务器列表
|
||||
modpack.installing=安装整合包
|
||||
modpack.installing.given=安装 %s 整合包
|
||||
modpack.introduction=支持 Curse、Modrinth、MultiMC、MCBBS 整合包。
|
||||
modpack.invalid=无效的整合包升级文件,可能是下载时出现问题。
|
||||
modpack.mismatched_type=整合包类型不匹配,当前游戏为“%s”整合包,但是提供的整合包更新文件为“%s”整合包。\n如遇到问题,你可以点击右上角帮助按钮进行求助。
|
||||
modpack.invalid=无效的整合包升级文件。可能是下载时出现问题。
|
||||
modpack.mismatched_type=整合包类型不匹配。当前游戏为“%s”整合包,但是提供的整合包更新文件为“%s”整合包。\n如遇到问题,你可以点击右上角帮助按钮进行求助。
|
||||
modpack.name=整合包名称
|
||||
modpack.not_a_valid_name=不是一个有效的整合包名称
|
||||
modpack.origin=来源
|
||||
@@ -725,8 +728,8 @@ modpack.task.install=导入整合包
|
||||
modpack.task.install.error=无法识别该整合包,目前仅支持导入 Curse、Modrinth、MultiMC、MCBBS 整合包。\n你可以点击右上角帮助按钮进行求助。
|
||||
modpack.type.curse=Curse
|
||||
modpack.type.curse.error=未能完成该整合包所需的依赖下载,请再次尝试或设置代理。\n你可以点击右上角帮助按钮进行求助。
|
||||
modpack.type.curse.not_found=部分必需文件已经在网络中被删除并且再也无法下载,请尝试该整合包的最新版本或者安装其他整合包。\n如遇到问题,你可以点击右上角帮助按钮进行求助。
|
||||
modpack.type.manual.warning=该整合包由发布者手动打包,其中可能已经包含启动器,建议尝试解压后使用其自带的启动器运行游戏。\n如遇到问题,你可以点击右上角帮助按钮进行求助。\nHMCL 可以尝试导入该整合包,但不保证可用性,是否继续?
|
||||
modpack.type.curse.not_found=部分必需文件已经在网络中被删除并且再也无法下载。请尝试该整合包的最新版本或者安装其他整合包。\n如遇到问题,你可以点击右上角帮助按钮进行求助。
|
||||
modpack.type.manual.warning=该整合包由发布者手动打包,其中可能已经包含启动器。建议尝试解压后使用其自带的启动器运行游戏。\n如遇到问题,你可以点击右上角帮助按钮进行求助。\nHMCL 可以尝试导入该整合包,但不保证可用性。是否继续?
|
||||
modpack.type.mcbbs=MCBBS 整合包 (推荐)
|
||||
modpack.type.mcbbs.export=可以被 Hello Minecraft! Launcher 导入
|
||||
modpack.type.modrinth=Modrinth
|
||||
@@ -752,11 +755,16 @@ modpack.wizard.step.initialization.warning=在制作整合包前,请你确认
|
||||
modpack.wizard.step.initialization.server=点击此处查看有关服务器自动更新整合包的制作教程
|
||||
|
||||
modrinth.category.adventure=冒险
|
||||
modrinth.category.atmosphere=氛围
|
||||
modrinth.category.audio=声音
|
||||
modrinth.category.blocks=方块
|
||||
modrinth.category.bloom=泛光
|
||||
modrinth.category.bukkit=Bukkit
|
||||
modrinth.category.bungeecord=BungeeCord
|
||||
modrinth.category.canvas=Canvas
|
||||
modrinth.category.cartoon=卡通
|
||||
modrinth.category.challenging=高难度
|
||||
modrinth.category.colored-lighting=彩色光照
|
||||
modrinth.category.core-shaders=核心着色器
|
||||
modrinth.category.combat=战斗
|
||||
modrinth.category.cursed=Cursed
|
||||
@@ -766,19 +774,25 @@ modrinth.category.entities=实体
|
||||
modrinth.category.environment=环境
|
||||
modrinth.category.equipment=装备
|
||||
modrinth.category.fabric=Fabric
|
||||
modrinth.category.fantasy=幻想
|
||||
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.kitchen-sink=大杂烩
|
||||
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 +802,21 @@ modrinth.category.models=模型
|
||||
modrinth.category.modloader=Modloader
|
||||
modrinth.category.multiplayer=多人
|
||||
modrinth.category.neoforge=NeoForge
|
||||
modrinth.category.optifine=OptiFine
|
||||
modrinth.category.optimization=优化
|
||||
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,6 +827,7 @@ 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
|
||||
@@ -873,20 +896,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 +927,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=下界
|
||||
@@ -931,10 +956,11 @@ 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.reveal=打开文件夹
|
||||
world.show_all=显示全部
|
||||
|
||||
profile=游戏文件夹
|
||||
@@ -953,13 +979,13 @@ profile.use_relative_path=若可能,游戏文件夹使用相对路径
|
||||
repositories.custom=自定义 Maven 仓库 (%s)
|
||||
repositories.maven_central=全球 (Maven Central)
|
||||
repositories.tencentcloud_mirror=中国大陆 (腾讯云镜像源 Maven 仓库)
|
||||
repositories.chooser=缺少 JavaFX 运行环境,HMCL 需要 JavaFX 才能正常运行。\n点击“确认”从指定下载源下载 JavaFX 运行时组件并启动 HMCL,点击“取消”退出程序。\n选择下载源:
|
||||
repositories.chooser=缺少 JavaFX 运行环境。HMCL 需要 JavaFX 才能正常运行。\n点击“确认”从指定源下载 JavaFX 运行时组件并启动 HMCL。点击“取消”退出程序。\n选择下载源:
|
||||
repositories.chooser.title=选择 JavaFX 下载源
|
||||
|
||||
resourcepack=资源包
|
||||
resourcepack.download.title=资源包下载 - %1s
|
||||
|
||||
reveal.in_explorer=在文件管理器中查看
|
||||
reveal.in_file_manager=在文件管理器中查看
|
||||
|
||||
schematics=原理图
|
||||
schematics.add=添加原理图
|
||||
@@ -1028,8 +1054,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\
|
||||
\ · 开启下方“不添加默认的 JVM 参数”可在启动游戏时不添加默认参数。
|
||||
settings.advanced.launcher_visibility.close=游戏启动后结束启动器
|
||||
settings.advanced.launcher_visibility.hide=游戏启动后隐藏启动器
|
||||
settings.advanced.launcher_visibility.hide_and_reopen=隐藏启动器并在游戏结束后重新打开
|
||||
@@ -1041,7 +1067,7 @@ 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如果你要修改本选项,你需要保证自定义文件夹下有游戏所需的本地库文件,如 lwjgl.dll (liblwjgl.so)、openal.dll (libopenal.so) 等文件。启动器不会帮你补全缺少的本地库文件!\n注意:指定的本地库文件路径建议只包含英文大小写字母、数字和下划线,否则可能会导致启动游戏失败。
|
||||
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.precall_command=游戏启动前执行命令
|
||||
settings.advanced.precall_command.prompt=将在游戏启动前调用
|
||||
@@ -1087,20 +1113,20 @@ 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=游戏图标
|
||||
|
||||
settings.launcher=启动器设置
|
||||
settings.launcher.appearance=外观
|
||||
settings.launcher.common_path.tooltip=启动器将所有游戏资源及依赖库文件存放于此集中管理,如果游戏文件夹内有现成的将不会使用公共库文件。
|
||||
settings.launcher.common_path.tooltip=启动器将所有游戏资源及依赖库文件存放于此集中管理。如果游戏文件夹内有现成的将不会使用公共库文件。
|
||||
settings.launcher.debug=调试
|
||||
settings.launcher.download=下载
|
||||
settings.launcher.download.threads=线程数
|
||||
settings.launcher.download.threads.auto=自动选择线程数
|
||||
settings.launcher.download.threads.hint=线程数过高可能导致系统卡顿。你的下载速度会受到互联网运营商、下载源服务器等方面的影响,调高下载线程数不一定能大幅提升总下载速度。
|
||||
settings.launcher.download.threads.hint=线程数过高可能导致系统卡顿。你的下载速度会受到互联网运营商、下载源服务器等方面的影响。调高下载线程数不一定能大幅提升总下载速度。
|
||||
settings.launcher.download_source=下载源
|
||||
settings.launcher.download_source.auto=自动选择下载源
|
||||
settings.launcher.enable_game_list=在主页内显示版本列表
|
||||
@@ -1108,9 +1134,9 @@ settings.launcher.font=字体
|
||||
settings.launcher.general=通用
|
||||
settings.launcher.language=语言 (重启后生效)
|
||||
settings.launcher.launcher_log.export=导出启动器日志
|
||||
settings.launcher.launcher_log.reveal=打开日志文件夹
|
||||
settings.launcher.launcher_log.export.failed=无法导出日志
|
||||
settings.launcher.launcher_log.export.success=日志已保存到“%s”
|
||||
settings.launcher.launcher_log.reveal=打开日志文件夹
|
||||
settings.launcher.log=日志
|
||||
settings.launcher.log.font=日志字体
|
||||
settings.launcher.proxy=代理
|
||||
@@ -1135,6 +1161,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=查看日志
|
||||
@@ -1155,7 +1182,7 @@ sponsor.hmcl=Hello Minecraft! Launcher 是一个免费、自由、开放源代
|
||||
system.architecture=架构
|
||||
system.operating_system=操作系统
|
||||
|
||||
unofficial.hint=你正在使用非官方构建的 HMCL,我们无法保证其安全性,请注意甄别。
|
||||
unofficial.hint=你正在使用非官方构建的 HMCL。我们无法保证其安全性,请注意甄别。
|
||||
|
||||
update=启动器更新
|
||||
update.accept=更新
|
||||
@@ -1179,7 +1206,7 @@ update.bubble.title=发现更新:%s
|
||||
update.bubble.subtitle=点击此处进行升级
|
||||
update.note=开发版与预览版包含更多的功能以及错误修复,但也可能会包含其他的问题。
|
||||
update.latest=当前版本为最新版本
|
||||
update.no_browser=无法打开浏览器,网址已经复制到剪贴板,你可以手动粘贴网址打开页面。
|
||||
update.no_browser=无法打开浏览器。网址已经复制到剪贴板,你可以手动粘贴网址打开页面。
|
||||
update.tooltip=更新
|
||||
|
||||
version=游戏
|
||||
@@ -1214,7 +1241,7 @@ 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.confirm.independent=由于该游戏启用了“(全局/版本特定) 游戏设置 → 版本隔离 → 各版本独立”选项,删除该版本将导致该游戏的存档等数据一同被删除!真的要删除版本“%s”吗?
|
||||
version.manage.remove_assets=删除所有游戏资源文件
|
||||
version.manage.remove_libraries=删除所有库文件
|
||||
version.manage.rename=重命名该版本
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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"),
|
||||
|
||||
@@ -124,7 +124,7 @@ public final class FabricInstallTask extends Task<Version> {
|
||||
libraries.add(new Library(Artifact.fromDescriptor(fabricInfo.intermediary.maven), "https://maven.fabricmc.net/", null));
|
||||
libraries.add(new Library(Artifact.fromDescriptor(fabricInfo.loader.maven), "https://maven.fabricmc.net/", null));
|
||||
|
||||
return new Version(LibraryAnalyzer.LibraryType.FABRIC.getPatchId(), loaderVersion, 30000, arguments, mainClass, libraries);
|
||||
return new Version(LibraryAnalyzer.LibraryType.FABRIC.getPatchId(), loaderVersion, Version.PRIORITY_LOADER, arguments, mainClass, libraries);
|
||||
}
|
||||
|
||||
public static class FabricInfo {
|
||||
|
||||
@@ -410,7 +410,7 @@ public class ForgeNewInstallTask extends Task<Version> {
|
||||
dependencyManager.checkLibraryCompletionAsync(forgeVersion, true)));
|
||||
|
||||
setResult(forgeVersion
|
||||
.setPriority(30000)
|
||||
.setPriority(Version.PRIORITY_LOADER)
|
||||
.setId(LibraryAnalyzer.LibraryType.FORGE.getPatchId())
|
||||
.setVersion(selfVersion));
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ public class ForgeOldInstallTask extends Task<Version> {
|
||||
}
|
||||
|
||||
setResult(installProfile.getVersionInfo()
|
||||
.setPriority(30000)
|
||||
.setPriority(Version.PRIORITY_LOADER)
|
||||
.setId(LibraryAnalyzer.LibraryType.FORGE.getPatchId())
|
||||
.setVersion(selfVersion));
|
||||
dependencies.add(dependencyManager.checkLibraryCompletionAsync(installProfile.getVersionInfo(), true));
|
||||
|
||||
@@ -65,7 +65,7 @@ public class GameInstallTask extends Task<Version> {
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
Version patch = JsonUtils.fromNonNullJson(downloadTask.getResult(), Version.class)
|
||||
.setId(MINECRAFT.getPatchId()).setVersion(remote.getGameVersion()).setJar(null).setPriority(0);
|
||||
.setId(MINECRAFT.getPatchId()).setVersion(remote.getGameVersion()).setJar(null).setPriority(Version.PRIORITY_MC);
|
||||
setResult(patch);
|
||||
|
||||
Version version = new Version(this.version.getId()).addPatch(patch);
|
||||
|
||||
+2
@@ -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)),
|
||||
|
||||
+1
-1
@@ -406,7 +406,7 @@ public class NeoForgeOldInstallTask extends Task<Version> {
|
||||
dependencyManager.checkLibraryCompletionAsync(neoForgeVersion, true)));
|
||||
|
||||
setResult(neoForgeVersion
|
||||
.setPriority(30000)
|
||||
.setPriority(Version.PRIORITY_LOADER)
|
||||
.setId(LibraryAnalyzer.LibraryType.NEO_FORGE.getPatchId())
|
||||
.setVersion(selfVersion));
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ public final class QuiltInstallTask extends Task<Version> {
|
||||
libraries.add(new Library(Artifact.fromDescriptor(quiltInfo.intermediary.maven), getMavenRepositoryByGroup(quiltInfo.intermediary.maven), null));
|
||||
libraries.add(new Library(Artifact.fromDescriptor(quiltInfo.loader.maven), getMavenRepositoryByGroup(quiltInfo.loader.maven), null));
|
||||
|
||||
return new Version(LibraryAnalyzer.LibraryType.QUILT.getPatchId(), loaderVersion, 30000, arguments, mainClass, libraries);
|
||||
return new Version(LibraryAnalyzer.LibraryType.QUILT.getPatchId(), loaderVersion, Version.PRIORITY_LOADER, arguments, mainClass, libraries);
|
||||
}
|
||||
|
||||
private static String getMavenRepositoryByGroup(String maven) {
|
||||
|
||||
@@ -79,8 +79,12 @@ public final class Arguments {
|
||||
}
|
||||
|
||||
public Arguments addJVMArguments(List<String> jvmArguments) {
|
||||
List<Argument> list = jvmArguments.stream().map(StringArgument::new).collect(Collectors.toList());
|
||||
return new Arguments(getGame(), Lang.merge(getJvm(), list));
|
||||
return addJVMArgumentsDirect(jvmArguments.stream().map(StringArgument::new).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
// TODO: How to distinguish addJVMArgumentsDirect from addJVMArguments? Naming is hard :)
|
||||
public Arguments addJVMArgumentsDirect(List<Argument> jvmArguments) {
|
||||
return new Arguments(getGame(), Lang.merge(getJvm(), jvmArguments));
|
||||
}
|
||||
|
||||
public static Arguments merge(Arguments a, Arguments b) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -95,10 +95,15 @@ public class DefaultGameRepository implements GameRepository {
|
||||
|
||||
@Override
|
||||
public File getLibraryFile(Version version, Library lib) {
|
||||
if ("local".equals(lib.getHint()) && lib.getFileName() != null)
|
||||
return new File(getVersionRoot(version.getId()), "libraries/" + lib.getFileName());
|
||||
else
|
||||
return new File(getLibrariesDirectory(version), lib.getPath());
|
||||
if ("local".equals(lib.getHint())) {
|
||||
if (lib.getFileName() != null) {
|
||||
return new File(getVersionRoot(version.getId()), "libraries/" + lib.getFileName());
|
||||
}
|
||||
|
||||
return new File(getVersionRoot(version.getId()), "libraries/" + lib.getArtifact().getFileName());
|
||||
}
|
||||
|
||||
return new File(getLibrariesDirectory(version), lib.getPath());
|
||||
}
|
||||
|
||||
public Path getArtifactFile(Version version, Artifact artifact) {
|
||||
|
||||
@@ -34,62 +34,18 @@ import static org.jackhuang.hmcl.download.LibraryAnalyzer.LAUNCH_WRAPPER_MAIN;
|
||||
|
||||
public enum JavaVersionConstraint {
|
||||
VANILLA(true, VersionRange.all(), VersionRange.all()) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version, @Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) {
|
||||
// Give priority to the Java version requirements specified in the version JSON
|
||||
return version == null || version.getJavaVersion() == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkJava(GameVersionNumber gameVersionNumber, Version version, JavaRuntime java) {
|
||||
GameJavaVersion minimumJavaVersion = GameJavaVersion.getMinimumJavaVersion(gameVersionNumber);
|
||||
return minimumJavaVersion == null || java.getParsedVersion() >= minimumJavaVersion.getMajorVersion();
|
||||
}
|
||||
},
|
||||
// Minecraft<=1.7.2+Forge requires Java<=7, But LegacyModFixer may fix that problem. So only suggest user using Java 7.
|
||||
MODDED_JAVA_7(false, GameVersionNumber.atMost("1.7.2"), VersionNumber.atMost("1.7.999")) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) {
|
||||
return version != null && analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE);
|
||||
}
|
||||
},
|
||||
MODDED_JAVA_8(false, GameVersionNumber.between("1.7.10", "1.16.999"), VersionNumber.between("1.8", "1.8.999")) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) {
|
||||
return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE);
|
||||
}
|
||||
},
|
||||
MODDED_JAVA_16(false, GameVersionNumber.between("1.17", "1.17.999"), VersionNumber.between("16", "16.999")) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) {
|
||||
return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE);
|
||||
}
|
||||
},
|
||||
MODDED_JAVA_17(false, GameVersionNumber.between("1.18", "1.20.4"), VersionNumber.between("17", "17.999")) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) {
|
||||
return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE);
|
||||
}
|
||||
},
|
||||
MODDED_JAVA_21(false, GameVersionNumber.atLeast("1.20.5"), VersionNumber.between("21", "21.999")) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) {
|
||||
return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE);
|
||||
}
|
||||
},
|
||||
// LaunchWrapper<=1.12 will crash because LaunchWrapper assumes the system class loader is an instance of URLClassLoader (Java 8)
|
||||
LAUNCH_WRAPPER(true, GameVersionNumber.atMost("1.12.999"), VersionNumber.atMost("1.8.999")) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) {
|
||||
if (version == null) return false;
|
||||
return LAUNCH_WRAPPER_MAIN.equals(version.getMainClass()) &&
|
||||
version.getLibraries().stream()
|
||||
.filter(library -> "launchwrapper".equals(library.getArtifactId()))
|
||||
.anyMatch(library -> VersionNumber.asVersion(library.getVersion()).compareTo(VersionNumber.asVersion("1.13")) < 0);
|
||||
}
|
||||
},
|
||||
// Minecraft>=1.13 may crash when generating world on Java [1.8,1.8.0_51)
|
||||
VANILLA_JAVA_8_51(false, GameVersionNumber.atLeast("1.13"), VersionNumber.atLeast("1.8.0_51")),
|
||||
// Minecraft with suggested java version recorded in game json is restrictedly constrained.
|
||||
GAME_JSON(true, VersionRange.all(), VersionRange.all()) {
|
||||
@Override
|
||||
@@ -111,6 +67,61 @@ public enum JavaVersionConstraint {
|
||||
return VersionNumber.atLeast(javaVersion);
|
||||
}
|
||||
},
|
||||
// Minecraft<=1.7.2+Forge requires Java<=7, But LegacyModFixer may fix that problem. So only suggest user using Java 7.
|
||||
MODDED_JAVA_7(false, GameVersionNumber.atMost("1.7.2"), VersionNumber.atMost("1.7.999")) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) {
|
||||
return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE)
|
||||
&& super.appliesToVersionImpl(gameVersionNumber, version, java, analyzer);
|
||||
}
|
||||
},
|
||||
MODDED_JAVA_8(false, GameVersionNumber.between("1.7.10", "1.16.999"), VersionNumber.between("1.8", "1.8.999")) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) {
|
||||
return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE)
|
||||
&& super.appliesToVersionImpl(gameVersionNumber, version, java, analyzer);
|
||||
}
|
||||
},
|
||||
MODDED_JAVA_16(false, GameVersionNumber.between("1.17", "1.17.999"), VersionNumber.between("16", "16.999")) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) {
|
||||
return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE)
|
||||
&& super.appliesToVersionImpl(gameVersionNumber, version, java, analyzer);
|
||||
}
|
||||
},
|
||||
MODDED_JAVA_17(false, GameVersionNumber.between("1.18", "1.20.4"), VersionNumber.between("17", "17.999")) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) {
|
||||
return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE)
|
||||
&& super.appliesToVersionImpl(gameVersionNumber, version, java, analyzer);
|
||||
}
|
||||
},
|
||||
MODDED_JAVA_21(false, GameVersionNumber.atLeast("1.20.5"), VersionNumber.between("21", "21.999")) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) {
|
||||
return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE)
|
||||
&& super.appliesToVersionImpl(gameVersionNumber, version, java, analyzer);
|
||||
}
|
||||
},
|
||||
// LaunchWrapper<=1.12 will crash because LaunchWrapper assumes the system class loader is an instance of URLClassLoader (Java 8)
|
||||
LAUNCH_WRAPPER(true, GameVersionNumber.atMost("1.12.999"), VersionNumber.atMost("1.8.999")) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) {
|
||||
if (version == null) return false;
|
||||
return super.appliesToVersionImpl(gameVersionNumber, version, java, analyzer) && LAUNCH_WRAPPER_MAIN.equals(version.getMainClass()) &&
|
||||
version.getLibraries().stream()
|
||||
.filter(library -> "launchwrapper".equals(library.getArtifactId()))
|
||||
.anyMatch(library -> VersionNumber.asVersion(library.getVersion()).compareTo(VersionNumber.asVersion("1.13")) < 0);
|
||||
}
|
||||
},
|
||||
// Minecraft>=1.13 may crash when generating world on Java [1.8,1.8.0_51)
|
||||
VANILLA_JAVA_8_51(false, GameVersionNumber.atLeast("1.13"), VersionNumber.atLeast("1.8.0_51")),
|
||||
// On Linux, JDK 9+ cannot launch Minecraft<=1.12.2, since JDK 9+ does not accept loading native library built in different arch.
|
||||
// For example, JDK 9+ 64-bit cannot load 32-bit lwjgl native library.
|
||||
VANILLA_LINUX_JAVA_8(true, GameVersionNumber.atMost("1.12.999"), VersionNumber.atMost("1.8.999")) {
|
||||
@@ -151,7 +162,8 @@ public enum JavaVersionConstraint {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) {
|
||||
if (version == null || java == null || analyzer == null) return false;
|
||||
if (version == null || java == null || analyzer == null || !super.appliesToVersionImpl(gameVersionNumber, version, java, analyzer))
|
||||
return false;
|
||||
VersionNumber forgePatchVersion = analyzer.getVersion(LibraryAnalyzer.LibraryType.FORGE)
|
||||
.map(VersionNumber::asVersion)
|
||||
.orElse(null);
|
||||
@@ -221,7 +233,19 @@ public enum JavaVersionConstraint {
|
||||
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) {
|
||||
return true;
|
||||
GameJavaVersion gameJavaVersion;
|
||||
if (version == null || (gameJavaVersion = version.getJavaVersion()) == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String versionNumber = gameJavaVersion.getMajorVersion() >= 9
|
||||
? String.valueOf(gameJavaVersion.getMajorVersion())
|
||||
: "1." + gameJavaVersion.getMajorVersion();
|
||||
|
||||
VersionRange<VersionNumber> range = getJavaVersionRange(version);
|
||||
VersionNumber maximum = range.getMaximum();
|
||||
|
||||
return maximum == null || maximum.compareTo(versionNumber) >= 0;
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
|
||||
@@ -17,10 +17,11 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import org.jackhuang.hmcl.util.Constants;
|
||||
import org.jackhuang.hmcl.util.Immutable;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.ToStringBuilder;
|
||||
import org.jackhuang.hmcl.util.gson.TolerableValidationException;
|
||||
import org.jackhuang.hmcl.util.gson.Validation;
|
||||
@@ -28,10 +29,7 @@ import org.jackhuang.hmcl.util.platform.Architecture;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A class that describes a Minecraft dependency.
|
||||
@@ -40,6 +38,40 @@ import java.util.Optional;
|
||||
*/
|
||||
@Immutable
|
||||
public class Library implements Comparable<Library>, Validation {
|
||||
/**
|
||||
* <p>A possible native descriptors can be: [variant-]os[-key]</p>
|
||||
*
|
||||
* <p>
|
||||
* Variant can be empty string, 'native', or 'natives'.
|
||||
* Key can be empty string, system arch, or system arch bit count.
|
||||
* </p>
|
||||
*/
|
||||
private static final String[] POSSIBLE_NATIVE_DESCRIPTORS;
|
||||
|
||||
static {
|
||||
String[] keys = {
|
||||
"",
|
||||
Architecture.SYSTEM_ARCH.name().toLowerCase(Locale.ROOT),
|
||||
Architecture.SYSTEM_ARCH.getBits().getBit()
|
||||
}, variants = {"", "native", "natives"};
|
||||
|
||||
POSSIBLE_NATIVE_DESCRIPTORS = new String[keys.length * variants.length];
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
for (int j = 0; j < variants.length; j++) {
|
||||
if (!variants[j].isEmpty()) {
|
||||
builder.append(variants[j]).append('-');
|
||||
}
|
||||
builder.append(OperatingSystem.CURRENT_OS.getMojangName());
|
||||
if (!keys[i].isEmpty()) {
|
||||
builder.append('-').append(keys[i]);
|
||||
}
|
||||
|
||||
POSSIBLE_NATIVE_DESCRIPTORS[i * variants.length + j] = builder.toString();
|
||||
builder.setLength(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SerializedName("name")
|
||||
private final Artifact artifact;
|
||||
@@ -93,13 +125,27 @@ public class Library implements Comparable<Library>, Validation {
|
||||
}
|
||||
|
||||
public String getClassifier() {
|
||||
if (artifact.getClassifier() == null)
|
||||
if (natives != null && natives.containsKey(OperatingSystem.CURRENT_OS.getMojangName()))
|
||||
return natives.get(OperatingSystem.CURRENT_OS.getMojangName()).replace("${arch}", Architecture.SYSTEM_ARCH.getBits().getBit());
|
||||
else
|
||||
return null;
|
||||
else
|
||||
if (artifact.getClassifier() == null) {
|
||||
if (natives != null) {
|
||||
for (String nativeDescriptor : POSSIBLE_NATIVE_DESCRIPTORS) {
|
||||
String nd = natives.get(nativeDescriptor);
|
||||
if (nd != null) {
|
||||
return nd.replace("${arch}", Architecture.SYSTEM_ARCH.getBits().getBit());
|
||||
}
|
||||
}
|
||||
} else if (downloads != null && downloads.getClassifiers() != null) {
|
||||
for (String nativeDescriptor : POSSIBLE_NATIVE_DESCRIPTORS) {
|
||||
LibraryDownloadInfo info = downloads.getClassifiers().get(nativeDescriptor);
|
||||
if (info != null) {
|
||||
return nativeDescriptor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
} else {
|
||||
return artifact.getClassifier();
|
||||
}
|
||||
}
|
||||
|
||||
public ExtractRules getExtract() {
|
||||
@@ -111,10 +157,17 @@ public class Library implements Comparable<Library>, Validation {
|
||||
}
|
||||
|
||||
public boolean isNative() {
|
||||
return natives != null && appliesToCurrentEnvironment();
|
||||
if (!appliesToCurrentEnvironment()) {
|
||||
return false;
|
||||
}
|
||||
if (natives != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return downloads != null && downloads.getClassifiers().keySet().stream().anyMatch(s -> s.startsWith("native"));
|
||||
}
|
||||
|
||||
protected LibraryDownloadInfo getRawDownloadInfo() {
|
||||
public LibraryDownloadInfo getRawDownloadInfo() {
|
||||
if (downloads != null) {
|
||||
if (isNative())
|
||||
return downloads.getClassifiers().get(getClassifier());
|
||||
@@ -125,6 +178,10 @@ public class Library implements Comparable<Library>, Validation {
|
||||
}
|
||||
}
|
||||
|
||||
public Artifact getArtifact() {
|
||||
return artifact;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
LibraryDownloadInfo temp = getRawDownloadInfo();
|
||||
if (temp != null && temp.getPath() != null)
|
||||
@@ -137,12 +194,28 @@ public class Library implements Comparable<Library>, Validation {
|
||||
LibraryDownloadInfo temp = getRawDownloadInfo();
|
||||
String path = getPath();
|
||||
return new LibraryDownloadInfo(path,
|
||||
Optional.ofNullable(temp).map(LibraryDownloadInfo::getUrl).orElse(Optional.ofNullable(url).orElse(Constants.DEFAULT_LIBRARY_URL) + path),
|
||||
computePath(temp, path),
|
||||
temp != null ? temp.getSha1() : null,
|
||||
temp != null ? temp.getSize() : 0
|
||||
);
|
||||
}
|
||||
|
||||
private String computePath(LibraryDownloadInfo raw, String path) {
|
||||
if (raw != null) {
|
||||
String url = raw.getUrl();
|
||||
if (url != null) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
String repo = Lang.requireNonNullElse(url, Constants.DEFAULT_LIBRARY_URL);
|
||||
if (!repo.endsWith("/")) {
|
||||
repo += '/';
|
||||
}
|
||||
|
||||
return repo + path;
|
||||
}
|
||||
|
||||
public boolean hasDownloadURL() {
|
||||
LibraryDownloadInfo temp = getRawDownloadInfo();
|
||||
if (temp != null) return temp.getUrl() != null;
|
||||
@@ -159,6 +232,7 @@ public class Library implements Comparable<Library>, Validation {
|
||||
|
||||
/**
|
||||
* Hint for how to locate the library file.
|
||||
*
|
||||
* @return null for default, "local" for location in version/<version>/libraries/filename
|
||||
*/
|
||||
@Nullable
|
||||
@@ -168,6 +242,7 @@ public class Library implements Comparable<Library>, Validation {
|
||||
|
||||
/**
|
||||
* Available when hint is "local"
|
||||
*
|
||||
* @return the filename of the local library in version/<version>/libraries/$filename
|
||||
*/
|
||||
@Nullable
|
||||
|
||||
@@ -24,7 +24,6 @@ import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class OSRestriction {
|
||||
@@ -57,19 +56,28 @@ public final class OSRestriction {
|
||||
this.arch = arch;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public String getArch() {
|
||||
return arch;
|
||||
}
|
||||
|
||||
public boolean allow() {
|
||||
// Some modpacks directly use { name: "win-x86" }
|
||||
if (name != null) {
|
||||
String[] parts = name.split("-", 3);
|
||||
if (parts.length == 2) {
|
||||
OperatingSystem os = OperatingSystem.parseOSName(parts[0]);
|
||||
Architecture arch = Architecture.parseArchName(parts[1]);
|
||||
|
||||
if (os != OperatingSystem.UNKNOWN && arch != Architecture.UNKNOWN) {
|
||||
if (os != OperatingSystem.CURRENT_OS && !(os == OperatingSystem.LINUX && OperatingSystem.CURRENT_OS.isLinuxOrBSD())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arch != Architecture.SYSTEM_ARCH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OperatingSystem os = OperatingSystem.parseOSName(name);
|
||||
if (os != OperatingSystem.UNKNOWN
|
||||
&& os != OperatingSystem.CURRENT_OS
|
||||
@@ -85,5 +93,4 @@ public final class OSRestriction {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,6 +36,11 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
@Immutable
|
||||
public class Version implements Comparable<Version>, Validation {
|
||||
|
||||
/**
|
||||
* Patches with higher priority can override info from other patches, such as mainClass.
|
||||
*/
|
||||
public static final int PRIORITY_MC = 0, PRIORITY_LOADER = 30000;
|
||||
|
||||
private final String id;
|
||||
private final String version;
|
||||
private final Integer priority;
|
||||
@@ -302,7 +307,8 @@ public class Version implements Comparable<Version>, Validation {
|
||||
|
||||
if (inheritsFrom == null) {
|
||||
if (isRoot()) {
|
||||
thisVersion = new Version(id).setPatches(patches);
|
||||
// TODO: Breaking change, require much testing on versions installed with external installer, other launchers, and all kinds of versions.
|
||||
thisVersion = patches != null ? new Version(id).setPatches(patches) : this;
|
||||
} else {
|
||||
thisVersion = this;
|
||||
}
|
||||
@@ -368,7 +374,7 @@ public class Version implements Comparable<Version>, Validation {
|
||||
return thisVersion.setId(id).setJar(resolve(provider).getJar());
|
||||
}
|
||||
|
||||
private Version markAsResolved() {
|
||||
public Version markAsResolved() {
|
||||
return new Version(true, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
@@ -380,6 +386,10 @@ public class Version implements Comparable<Version>, Validation {
|
||||
return new Version(true, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
public Version setRoot(Boolean root) {
|
||||
return new Version(true, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
public Version setId(String id) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
@@ -396,6 +406,10 @@ public class Version implements Comparable<Version>, Validation {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
public Version setJavaVersion(GameJavaVersion javaVersion) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
public Version setArguments(Arguments arguments) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
@@ -412,10 +426,18 @@ public class Version implements Comparable<Version>, Validation {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
public Version setAssetIndex(AssetIndexInfo assetIndex) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
public Version setLibraries(List<Library> libraries) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
public Version setDownload(JsonMap<DownloadType, DownloadInfo> downloads) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
public Version setLogging(Map<DownloadType, LoggingInfo> logging) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -18,7 +18,12 @@
|
||||
package org.jackhuang.hmcl.game.tlauncher;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import org.jackhuang.hmcl.game.*;
|
||||
import org.jackhuang.hmcl.game.Artifact;
|
||||
import org.jackhuang.hmcl.game.CompatibilityRule;
|
||||
import org.jackhuang.hmcl.game.ExtractRules;
|
||||
import org.jackhuang.hmcl.game.LibrariesDownloadInfo;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.LibraryDownloadInfo;
|
||||
import org.jackhuang.hmcl.util.Immutable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -136,108 +136,6 @@ public final class JavaInfo {
|
||||
}
|
||||
}
|
||||
|
||||
private static final String OS_ARCH = "os.arch = ";
|
||||
private static final String JAVA_VERSION = "java.version = ";
|
||||
private static final String JAVA_VENDOR = "java.vendor = ";
|
||||
private static final String VERSION_PREFIX = "version \"";
|
||||
|
||||
public static JavaInfo fromExecutable(Path executable) throws IOException {
|
||||
return fromExecutable(executable, true);
|
||||
}
|
||||
|
||||
public static JavaInfo fromExecutable(Path executable, boolean tryFindReleaseFile) throws IOException {
|
||||
assert executable.isAbsolute();
|
||||
Path parent = executable.getParent();
|
||||
if (tryFindReleaseFile && parent != null && parent.getFileName() != null && parent.getFileName().toString().equals("bin")) {
|
||||
Path javaHome = parent.getParent();
|
||||
if (javaHome != null && javaHome.getFileName() != null) {
|
||||
Path releaseFile = javaHome.resolve("release");
|
||||
String javaHomeName = javaHome.getFileName().toString();
|
||||
if ((javaHomeName.contains("jre") || javaHomeName.contains("jdk") || javaHomeName.contains("openj9")) && Files.isRegularFile(releaseFile)) {
|
||||
try {
|
||||
return fromReleaseFile(releaseFile);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String osArch = null;
|
||||
String version = null;
|
||||
String vendor = null;
|
||||
Platform platform = null;
|
||||
|
||||
String executablePath = executable.toString();
|
||||
|
||||
Process process = new ProcessBuilder(executablePath, "-XshowSettings:properties", "-version").start();
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream(), OperatingSystem.NATIVE_CHARSET))) {
|
||||
for (String line; (line = reader.readLine()) != null; ) {
|
||||
|
||||
int idx = line.indexOf(OS_ARCH);
|
||||
if (idx >= 0) {
|
||||
osArch = line.substring(idx + OS_ARCH.length()).trim();
|
||||
if (version != null && vendor != null)
|
||||
break;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
idx = line.indexOf(JAVA_VERSION);
|
||||
if (idx >= 0) {
|
||||
version = line.substring(idx + JAVA_VERSION.length()).trim();
|
||||
if (osArch != null && vendor != null)
|
||||
break;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
idx = line.indexOf(JAVA_VENDOR);
|
||||
if (idx >= 0) {
|
||||
vendor = line.substring(idx + JAVA_VENDOR.length()).trim();
|
||||
if (osArch != null && version != null)
|
||||
break;
|
||||
else
|
||||
//noinspection UnnecessaryContinue
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (osArch != null)
|
||||
platform = Platform.getPlatform(OperatingSystem.CURRENT_OS, Architecture.parseArchName(osArch));
|
||||
|
||||
// Java 6
|
||||
if (version == null) {
|
||||
boolean is64Bit = false;
|
||||
process = new ProcessBuilder(executablePath, "-version").start();
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream(), OperatingSystem.NATIVE_CHARSET))) {
|
||||
for (String line; (line = reader.readLine()) != null; ) {
|
||||
if (version == null) {
|
||||
int idx = line.indexOf(VERSION_PREFIX);
|
||||
if (idx >= 0) {
|
||||
int begin = idx + VERSION_PREFIX.length();
|
||||
int end = line.indexOf('"', begin);
|
||||
if (end >= 0) {
|
||||
version = line.substring(begin, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (line.contains("64-Bit"))
|
||||
is64Bit = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (platform == null)
|
||||
platform = Platform.getPlatform(OperatingSystem.CURRENT_OS, is64Bit ? Architecture.X86_64 : Architecture.X86);
|
||||
|
||||
if (version == null)
|
||||
throw new IOException("Cannot determine version");
|
||||
}
|
||||
|
||||
return new JavaInfo(platform, version, vendor);
|
||||
}
|
||||
|
||||
public static final JavaInfo CURRENT_ENVIRONMENT = new JavaInfo(Platform.CURRENT_PLATFORM, System.getProperty("java.version"), System.getProperty("java.vendor"));
|
||||
|
||||
private final Platform platform;
|
||||
|
||||
+1
@@ -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";
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.jackhuang.hmcl.mod.multimc;
|
||||
|
||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class MultiMCComponents {
|
||||
|
||||
private MultiMCComponents() {
|
||||
}
|
||||
|
||||
private static final Map<String, LibraryAnalyzer.LibraryType> ID_TYPE = new HashMap<>();
|
||||
|
||||
static {
|
||||
ID_TYPE.put("net.minecraft", LibraryAnalyzer.LibraryType.MINECRAFT);
|
||||
ID_TYPE.put("net.minecraftforge", LibraryAnalyzer.LibraryType.FORGE);
|
||||
ID_TYPE.put("net.neoforged", LibraryAnalyzer.LibraryType.NEO_FORGE);
|
||||
ID_TYPE.put("com.mumfrey.liteloader", LibraryAnalyzer.LibraryType.LITELOADER);
|
||||
ID_TYPE.put("net.fabricmc.fabric-loader", LibraryAnalyzer.LibraryType.FABRIC);
|
||||
ID_TYPE.put("org.quiltmc.quilt-loader", LibraryAnalyzer.LibraryType.QUILT);
|
||||
}
|
||||
|
||||
private static final Map<LibraryAnalyzer.LibraryType, String> TYPE_ID =
|
||||
ID_TYPE.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
|
||||
|
||||
private static final Collection<Map.Entry<String, LibraryAnalyzer.LibraryType>> PAIRS = Collections.unmodifiableCollection(ID_TYPE.entrySet());
|
||||
|
||||
static {
|
||||
if (TYPE_ID.isEmpty()) {
|
||||
throw new AssertionError("Please make sure TYPE_ID and PAIRS is initialized after ID_TYPE!");
|
||||
}
|
||||
}
|
||||
|
||||
public static String getComponent(LibraryAnalyzer.LibraryType type) {
|
||||
return TYPE_ID.get(type);
|
||||
}
|
||||
|
||||
public static LibraryAnalyzer.LibraryType getComponent(String type) {
|
||||
return ID_TYPE.get(type);
|
||||
}
|
||||
|
||||
public static Collection<Map.Entry<String, LibraryAnalyzer.LibraryType>> getPairs() {
|
||||
return PAIRS;
|
||||
}
|
||||
|
||||
public static URL getMetaURL(String componentID, String version) {
|
||||
return NetworkUtils.toURL(String.format("https://meta.multimc.org/v1/%s/%s.json", componentID, version));
|
||||
}
|
||||
}
|
||||
@@ -17,80 +17,421 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.mod.multimc;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||
import org.jackhuang.hmcl.game.Argument;
|
||||
import org.jackhuang.hmcl.game.Arguments;
|
||||
import org.jackhuang.hmcl.game.Artifact;
|
||||
import org.jackhuang.hmcl.game.AssetIndexInfo;
|
||||
import org.jackhuang.hmcl.game.CompatibilityRule;
|
||||
import org.jackhuang.hmcl.game.DownloadType;
|
||||
import org.jackhuang.hmcl.game.GameJavaVersion;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.OSRestriction;
|
||||
import org.jackhuang.hmcl.game.RuledArgument;
|
||||
import org.jackhuang.hmcl.game.StringArgument;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.util.Immutable;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.gson.JsonMap;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.logging.Logger;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
@Immutable
|
||||
public final class MultiMCInstancePatch {
|
||||
private final int formatVersion;
|
||||
|
||||
private final String name;
|
||||
@SerializedName("uid")
|
||||
private final String id;
|
||||
|
||||
@SerializedName("version")
|
||||
private final String version;
|
||||
|
||||
@SerializedName("mcVersion")
|
||||
private final String gameVersion;
|
||||
@SerializedName("assetIndex")
|
||||
private final AssetIndexInfo assetIndex;
|
||||
|
||||
@SerializedName("minecraftArguments")
|
||||
private final String minecraftArguments;
|
||||
|
||||
@SerializedName("+jvmArgs")
|
||||
private final List<String> jvmArgs;
|
||||
|
||||
@SerializedName("mainClass")
|
||||
private final String mainClass;
|
||||
private final String fileId;
|
||||
|
||||
@SerializedName("compatibleJavaMajors")
|
||||
private final int[] javaMajors;
|
||||
|
||||
@SerializedName("mainJar")
|
||||
private final Library mainJar;
|
||||
|
||||
@SerializedName("+traits")
|
||||
private final List<String> traits;
|
||||
|
||||
@SerializedName("+tweakers")
|
||||
private final List<String> tweakers;
|
||||
|
||||
@SerializedName("+libraries")
|
||||
private final List<Library> _libraries;
|
||||
@SerializedName(value = "+libraries")
|
||||
private final List<Library> libraries0;
|
||||
@SerializedName(value = "libraries")
|
||||
private final List<Library> libraries1;
|
||||
@SerializedName(value = "mavenFiles")
|
||||
private final List<Library> mavenFiles;
|
||||
|
||||
@SerializedName("libraries")
|
||||
private final List<Library> libraries;
|
||||
@SerializedName("jarMods")
|
||||
private final List<Library> jarMods;
|
||||
|
||||
public MultiMCInstancePatch() {
|
||||
this("", "", "", "", "", Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
|
||||
}
|
||||
@SerializedName("requires")
|
||||
private final List<MultiMCManifest.MultiMCManifestCachedRequires> requires;
|
||||
|
||||
public MultiMCInstancePatch(String name, String version, String gameVersion, String mainClass, String fileId, List<String> tweakers, List<Library> _libraries, List<Library> libraries) {
|
||||
this.name = name;
|
||||
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;
|
||||
this.gameVersion = gameVersion;
|
||||
this.assetIndex = assetIndex;
|
||||
this.minecraftArguments = minecraftArguments;
|
||||
this.jvmArgs = jvmArgs;
|
||||
this.mainClass = mainClass;
|
||||
this.fileId = fileId;
|
||||
this.tweakers = new ArrayList<>(tweakers);
|
||||
this._libraries = new ArrayList<>(_libraries);
|
||||
this.libraries = new ArrayList<>(libraries);
|
||||
this.javaMajors = javaMajors;
|
||||
this.mainJar = mainJar;
|
||||
this.traits = traits;
|
||||
this.tweakers = tweakers;
|
||||
this.libraries0 = libraries0;
|
||||
this.libraries1 = libraries1;
|
||||
this.mavenFiles = mavenFiles;
|
||||
this.jarMods = jarMods;
|
||||
this.requires = requires;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
public int getFormatVersion() {
|
||||
return formatVersion;
|
||||
}
|
||||
|
||||
public String getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public String getGameVersion() {
|
||||
return gameVersion;
|
||||
public AssetIndexInfo getAssetIndex() {
|
||||
return assetIndex;
|
||||
}
|
||||
|
||||
public String getMinecraftArguments() {
|
||||
return minecraftArguments;
|
||||
}
|
||||
|
||||
public List<String> getJvmArgs() {
|
||||
return nonNullOrEmpty(jvmArgs);
|
||||
}
|
||||
|
||||
public String getMainClass() {
|
||||
return mainClass;
|
||||
}
|
||||
|
||||
public String getFileId() {
|
||||
return fileId;
|
||||
public int[] getJavaMajors() {
|
||||
return javaMajors;
|
||||
}
|
||||
|
||||
public Library getMainJar() {
|
||||
return mainJar;
|
||||
}
|
||||
|
||||
public List<String> getTraits() {
|
||||
return nonNullOrEmpty(traits);
|
||||
}
|
||||
|
||||
public List<String> getTweakers() {
|
||||
return Collections.unmodifiableList(tweakers);
|
||||
return nonNullOrEmpty(tweakers);
|
||||
}
|
||||
|
||||
public List<Library> getLibraries() {
|
||||
return Lang.merge(_libraries, libraries);
|
||||
List<Library> list = new ArrayList<>();
|
||||
if (libraries0 != null) {
|
||||
list.addAll(libraries0);
|
||||
}
|
||||
if (libraries1 != null) {
|
||||
list.addAll(libraries1);
|
||||
}
|
||||
return nonNullOrEmpty(list);
|
||||
}
|
||||
|
||||
public List<Library> getMavenOnlyFiles() {
|
||||
return nonNullOrEmpty(mavenFiles);
|
||||
}
|
||||
|
||||
public List<Library> getJarMods() {
|
||||
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> 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(item)) {
|
||||
result.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static MultiMCInstancePatch read(String componentID, String text) {
|
||||
try {
|
||||
return JsonUtils.fromNonNullJson(text, MultiMCInstancePatch.class);
|
||||
} catch (JsonParseException e) {
|
||||
throw new IllegalArgumentException("Illegal Json-Patch: " + componentID);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ResolvedInstance {
|
||||
private final Version version;
|
||||
|
||||
private final String gameVersion;
|
||||
|
||||
private final Library mainJar;
|
||||
|
||||
private final List<String> jarModFileNames;
|
||||
private final List<Library> mavenOnlyFiles;
|
||||
|
||||
public ResolvedInstance(Version version, String gameVersion, Library mainJar, List<String> jarModFileNames, List<Library> mavenOnlyFiles) {
|
||||
this.version = version;
|
||||
this.gameVersion = gameVersion;
|
||||
this.mainJar = mainJar;
|
||||
this.jarModFileNames = jarModFileNames;
|
||||
this.mavenOnlyFiles = mavenOnlyFiles;
|
||||
}
|
||||
|
||||
public Version getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public String getGameVersion() {
|
||||
return gameVersion;
|
||||
}
|
||||
|
||||
public Library getMainJar() {
|
||||
return mainJar;
|
||||
}
|
||||
|
||||
public List<String> getJarModFileNames() {
|
||||
return jarModFileNames;
|
||||
}
|
||||
|
||||
public List<Library> getMavenOnlyFiles() {
|
||||
return mavenOnlyFiles;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Core methods transforming MultiMCModpack to Official Version Scheme.</p>
|
||||
*
|
||||
* <p>Mose of the information can be transformed in a lossless manner, except for some inputs.
|
||||
* See to do marks below for more information</p>
|
||||
*
|
||||
* @param patches List of all Json-Patch.
|
||||
* @param versionID the version ID. Used when constructing a Version.
|
||||
* @return The resolved instance.
|
||||
*/
|
||||
public static ResolvedInstance resolveArtifact(List<MultiMCInstancePatch> patches, String versionID) {
|
||||
if (patches.isEmpty()) {
|
||||
throw new IllegalArgumentException("Empty components.");
|
||||
}
|
||||
|
||||
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())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder message = new StringBuilder();
|
||||
|
||||
List<String> minecraftArguments;
|
||||
ArrayList<Argument> jvmArguments = new ArrayList<>(Arguments.DEFAULT_JVM_ARGUMENTS);
|
||||
String mainClass;
|
||||
AssetIndexInfo assetIndex;
|
||||
int[] javaMajors;
|
||||
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;
|
||||
|
||||
{
|
||||
MultiMCInstancePatch last = patches.get(patches.size() - 1);
|
||||
minecraftArguments = last.getMinecraftArguments() == null ? null : StringUtils.tokenize(last.getMinecraftArguments());
|
||||
mainClass = last.getMainClass();
|
||||
assetIndex = last.getAssetIndex();
|
||||
javaMajors = last.getJavaMajors();
|
||||
mainJar = last.getMainJar();
|
||||
traits = last.getTraits();
|
||||
tweakers = last.getTweakers();
|
||||
libraries = last.getLibraries();
|
||||
mavenOnlyFiles = last.getMavenOnlyFiles();
|
||||
jarModFileNames = last.getJarMods().stream().map(Library::getFileName).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
for (int i = patches.size() - 2; i >= 0; i--) {
|
||||
MultiMCInstancePatch patch = patches.get(i);
|
||||
if (minecraftArguments == null & patch.getMinecraftArguments() != null) {
|
||||
minecraftArguments = StringUtils.tokenize(patch.getMinecraftArguments());
|
||||
}
|
||||
for (String jvmArg : patch.getJvmArgs()) {
|
||||
jvmArguments.add(new StringArgument(jvmArg));
|
||||
}
|
||||
mainClass = Lang.requireNonNullElse(mainClass, patch.getMainClass());
|
||||
assetIndex = Lang.requireNonNullElse(patch.getAssetIndex(), assetIndex);
|
||||
javaMajors = Lang.requireNonNullElse(patch.getJavaMajors(), javaMajors);
|
||||
mainJar = Lang.requireNonNullElse(patch.getMainJar(), mainJar);
|
||||
traits = Lang.merge(patch.getTraits(), traits);
|
||||
tweakers = Lang.merge(patch.getTweakers(), tweakers);
|
||||
libraries = Lang.merge(patch.getLibraries(), libraries);
|
||||
mavenOnlyFiles = Lang.merge(patch.getMavenOnlyFiles(), mavenOnlyFiles);
|
||||
jarModFileNames = Lang.merge(patch.getJarMods().stream().map(Library::getFileName).collect(Collectors.toList()), jarModFileNames);
|
||||
}
|
||||
|
||||
mainClass = Lang.requireNonNullElse(mainClass, "net.minecraft.client.Minecraft");
|
||||
|
||||
if (minecraftArguments == null) {
|
||||
minecraftArguments = new ArrayList<>();
|
||||
}
|
||||
|
||||
// '--tweakClass' can't be the last argument.
|
||||
for (int i = minecraftArguments.size() - 2; i >= 0; i--) {
|
||||
if ("--tweakClass".equals(minecraftArguments.get(i))) {
|
||||
tweakers.add(minecraftArguments.get(i + 1));
|
||||
|
||||
minecraftArguments.remove(i);
|
||||
minecraftArguments.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
traits = dropDuplicate(traits);
|
||||
tweakers = dropDuplicate(tweakers);
|
||||
jarModFileNames = dropDuplicate(jarModFileNames);
|
||||
|
||||
for (String tweaker : tweakers) {
|
||||
minecraftArguments.add("--tweakClass");
|
||||
minecraftArguments.add(tweaker);
|
||||
}
|
||||
|
||||
for (String trait : traits) {
|
||||
switch (trait) {
|
||||
case "FirstThreadOnMacOS": {
|
||||
jvmArguments.add(new RuledArgument(
|
||||
Collections.singletonList(
|
||||
new CompatibilityRule(CompatibilityRule.Action.ALLOW, new OSRestriction(OperatingSystem.MACOS))
|
||||
),
|
||||
Collections.singletonList("-XstartOnFirstThread")
|
||||
));
|
||||
break;
|
||||
}
|
||||
case "XR:Initial": // Flag for chat report. See https://discord.com/channels/132965178051526656/134843027553255425/1380885829702127616
|
||||
case "texturepacks": // HMCL hasn't support checking whether a game version supports texture packs.
|
||||
case "no-texturepacks": {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
message.append(" - Trait: ").append(trait).append('\n');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Library library : libraries) {
|
||||
Artifact artifact = library.getArtifact();
|
||||
if ("io.github.zekerzhayard".equals(artifact.getGroup()) && "ForgeWrapper".equals(artifact.getName())) {
|
||||
jvmArguments.add(new StringArgument("-Dforgewrapper.librariesDir=${library_directory}"));
|
||||
jvmArguments.add(new StringArgument("-Dforgewrapper.minecraft=${primary_jar}"));
|
||||
|
||||
for (Library lib : libraries) {
|
||||
Artifact ar = lib.getArtifact();
|
||||
if ("net.neoforged".equals(ar.getGroup()) && "neoforge".equals(ar.getName()) && "installer".equals(ar.getClassifier()) ||
|
||||
"net.minecraftforge".equals(ar.getGroup()) && "forge".equals(ar.getName()) && "installer".equals(ar.getClassifier())
|
||||
) {
|
||||
jvmArguments.add(new StringArgument("-Dforgewrapper.installer=${library_directory}/" + ar.getPath()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Version version = new Version(versionID)
|
||||
.setArguments(new Arguments().addGameArguments(minecraftArguments).addJVMArgumentsDirect(jvmArguments))
|
||||
.setMainClass(mainClass)
|
||||
.setLibraries(libraries)
|
||||
.setAssetIndex(assetIndex)
|
||||
.setDownload(new JsonMap<>(Collections.singletonMap(DownloadType.CLIENT, mainJar.getRawDownloadInfo())));
|
||||
|
||||
/* TODO: Official Version-Json can only store one pre-defined GameJavaVersion, including 8, 11, 16, 17 and 21.
|
||||
An array of all suitable java versions are NOT supported.
|
||||
For compatibility with official launcher and any other launchers, a transform is made between int[] and GameJavaVersion. */
|
||||
javaMajors:
|
||||
if (javaMajors != null) {
|
||||
javaMajors = javaMajors.clone();
|
||||
Arrays.sort(javaMajors);
|
||||
|
||||
for (int i = javaMajors.length - 1; i >= 0; i--) {
|
||||
GameJavaVersion jv = GameJavaVersion.get(javaMajors[i]);
|
||||
if (jv != null) {
|
||||
version = version.setJavaVersion(jv);
|
||||
break javaMajors;
|
||||
}
|
||||
}
|
||||
|
||||
message.append(" - Java Version Range: ").append(Arrays.toString(javaMajors)).append('\n');
|
||||
}
|
||||
|
||||
version = version.markAsResolved();
|
||||
|
||||
String gameVersion = null;
|
||||
for (MultiMCInstancePatch patch : patches) {
|
||||
if (MultiMCComponents.getComponent(patch.getID()) == LibraryAnalyzer.LibraryType.MINECRAFT) {
|
||||
gameVersion = patch.getVersion();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (message.length() != 0) {
|
||||
if (message.charAt(message.length() - 1) == '\n') {
|
||||
message.setLength(message.length() - 1);
|
||||
}
|
||||
Logger.LOG.warning("Cannot fully parse MultiMC modpack with following unsupported features: \n" + message);
|
||||
}
|
||||
return new ResolvedInstance(version, gameVersion, mainJar, jarModFileNames, mavenOnlyFiles);
|
||||
}
|
||||
}
|
||||
@@ -86,7 +86,7 @@ public final class MultiMCManifest {
|
||||
return equalsVersion;
|
||||
}
|
||||
|
||||
public String getUid() {
|
||||
public String getID() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
|
||||
+11
-11
@@ -31,6 +31,7 @@ import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
@@ -74,17 +75,16 @@ public class MultiMCModpackExportTask extends Task<Void> {
|
||||
.orElseThrow(() -> new IOException("Cannot parse the version of " + versionId));
|
||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(versionId), gameVersion);
|
||||
List<MultiMCManifest.MultiMCManifestComponent> components = new ArrayList<>();
|
||||
components.add(new MultiMCManifest.MultiMCManifestComponent(true, false, "net.minecraft", gameVersion));
|
||||
analyzer.getVersion(FORGE).ifPresent(forgeVersion ->
|
||||
components.add(new MultiMCManifest.MultiMCManifestComponent(false, false, "net.minecraftforge", forgeVersion)));
|
||||
analyzer.getVersion(NEO_FORGE).ifPresent(neoForgeVersion ->
|
||||
components.add(new MultiMCManifest.MultiMCManifestComponent(false, false, "net.neoforged", neoForgeVersion)));
|
||||
analyzer.getVersion(LITELOADER).ifPresent(liteLoaderVersion ->
|
||||
components.add(new MultiMCManifest.MultiMCManifestComponent(false, false, "com.mumfrey.liteloader", liteLoaderVersion)));
|
||||
analyzer.getVersion(FABRIC).ifPresent(fabricVersion ->
|
||||
components.add(new MultiMCManifest.MultiMCManifestComponent(false, false, "net.fabricmc.fabric-loader", fabricVersion)));
|
||||
analyzer.getVersion(QUILT).ifPresent(quiltVersion ->
|
||||
components.add(new MultiMCManifest.MultiMCManifestComponent(false, false, "org.quiltmc.quilt-loader", quiltVersion)));
|
||||
components.add(new MultiMCManifest.MultiMCManifestComponent(true, false, MultiMCComponents.getComponent(MINECRAFT), gameVersion));
|
||||
|
||||
for (Map.Entry<String, LibraryAnalyzer.LibraryType> pair : MultiMCComponents.getPairs()) {
|
||||
if (pair.getValue().isModLoader()) {
|
||||
analyzer.getVersion(pair.getValue()).ifPresent(
|
||||
v -> components.add(new MultiMCManifest.MultiMCManifestComponent(false, false, pair.getKey(), v))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
MultiMCManifest mmcPack = new MultiMCManifest(1, components);
|
||||
zip.putTextFile(JsonUtils.GSON.toJson(mmcPack), "mmc-pack.json");
|
||||
|
||||
|
||||
+225
-104
@@ -19,101 +19,83 @@ package org.jackhuang.hmcl.mod.multimc;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.download.GameBuilder;
|
||||
import org.jackhuang.hmcl.game.Arguments;
|
||||
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
|
||||
import org.jackhuang.hmcl.download.game.GameDownloadTask;
|
||||
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
|
||||
import org.jackhuang.hmcl.game.Artifact;
|
||||
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.mod.MinecraftInstanceTask;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||
import org.jackhuang.hmcl.mod.ModpackInstallTask;
|
||||
import org.jackhuang.hmcl.task.GetTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.DirectoryStream;
|
||||
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.Optional;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
* <p>A task transforming MultiMC Modpack Scheme to Official Launcher Scheme.
|
||||
* The transforming process contains 7 stage:
|
||||
* <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>
|
||||
*/
|
||||
public final class MultiMCModpackInstallTask extends Task<Void> {
|
||||
public final class MultiMCModpackInstallTask extends Task<MultiMCInstancePatch.ResolvedInstance> {
|
||||
|
||||
private final File zipFile;
|
||||
private final Modpack modpack;
|
||||
private final MultiMCInstanceConfiguration manifest;
|
||||
private final String name;
|
||||
private final DefaultGameRepository repository;
|
||||
private final List<Task<?>> dependencies = new ArrayList<>(1);
|
||||
private final List<Task<?>> dependents = new ArrayList<>(4);
|
||||
private final List<Task<?>> dependents = new ArrayList<>();
|
||||
private final List<Task<?>> dependencies = new ArrayList<>();
|
||||
private final DefaultDependencyManager dependencyManager;
|
||||
|
||||
public MultiMCModpackInstallTask(DefaultDependencyManager dependencyManager, File zipFile, Modpack modpack, MultiMCInstanceConfiguration manifest, String name) {
|
||||
this.zipFile = zipFile;
|
||||
this.modpack = modpack;
|
||||
this.manifest = manifest;
|
||||
this.name = name;
|
||||
this.dependencyManager = dependencyManager;
|
||||
this.repository = dependencyManager.getGameRepository();
|
||||
|
||||
File json = repository.getModpackConfiguration(name);
|
||||
if (repository.hasVersion(name) && !json.exists())
|
||||
throw new IllegalArgumentException("Version " + name + " already exists.");
|
||||
|
||||
GameBuilder builder = dependencyManager.gameBuilder().name(name).gameVersion(manifest.getGameVersion());
|
||||
|
||||
if (manifest.getMmcPack() != null) {
|
||||
Optional<MultiMCManifest.MultiMCManifestComponent> forge = manifest.getMmcPack().getComponents().stream().filter(e -> e.getUid().equals("net.minecraftforge")).findAny();
|
||||
forge.ifPresent(c -> {
|
||||
if (c.getVersion() != null)
|
||||
builder.version("forge", c.getVersion());
|
||||
});
|
||||
|
||||
Optional<MultiMCManifest.MultiMCManifestComponent> neoForge = manifest.getMmcPack().getComponents().stream().filter(e -> e.getUid().equals("net.neoforged")).findAny();
|
||||
neoForge.ifPresent(c -> {
|
||||
if (c.getVersion() != null)
|
||||
builder.version("neoforge", c.getVersion());
|
||||
});
|
||||
|
||||
Optional<MultiMCManifest.MultiMCManifestComponent> liteLoader = manifest.getMmcPack().getComponents().stream().filter(e -> e.getUid().equals("com.mumfrey.liteloader")).findAny();
|
||||
liteLoader.ifPresent(c -> {
|
||||
if (c.getVersion() != null)
|
||||
builder.version("liteloader", c.getVersion());
|
||||
});
|
||||
|
||||
Optional<MultiMCManifest.MultiMCManifestComponent> fabric = manifest.getMmcPack().getComponents().stream().filter(e -> e.getUid().equals("net.fabricmc.fabric-loader")).findAny();
|
||||
fabric.ifPresent(c -> {
|
||||
if (c.getVersion() != null)
|
||||
builder.version("fabric", c.getVersion());
|
||||
});
|
||||
|
||||
Optional<MultiMCManifest.MultiMCManifestComponent> quilt = manifest.getMmcPack().getComponents().stream().filter(e -> e.getUid().equals("org.quiltmc.quilt-loader")).findAny();
|
||||
quilt.ifPresent(c -> {
|
||||
if (c.getVersion() != null)
|
||||
builder.version("quilt", c.getVersion());
|
||||
});
|
||||
}
|
||||
|
||||
dependents.add(builder.buildAsync());
|
||||
onDone().register(event -> {
|
||||
if (event.isFailed())
|
||||
repository.removeVersionFromDisk(name);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Task<?>> getDependencies() {
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doPreExecute() {
|
||||
return true;
|
||||
@@ -121,85 +103,135 @@ public final class MultiMCModpackInstallTask extends Task<Void> {
|
||||
|
||||
@Override
|
||||
public void preExecute() throws Exception {
|
||||
File run = repository.getRunDirectory(name);
|
||||
File json = repository.getModpackConfiguration(name);
|
||||
// Stage #0: General Setup
|
||||
{
|
||||
File run = repository.getRunDirectory(name);
|
||||
File json = repository.getModpackConfiguration(name);
|
||||
|
||||
ModpackConfiguration<MultiMCInstanceConfiguration> config = null;
|
||||
try {
|
||||
if (json.exists()) {
|
||||
config = JsonUtils.GSON.fromJson(FileUtils.readText(json), ModpackConfiguration.typeOf(MultiMCInstanceConfiguration.class));
|
||||
ModpackConfiguration<MultiMCInstanceConfiguration> config = null;
|
||||
try {
|
||||
if (json.exists()) {
|
||||
config = JsonUtils.GSON.fromJson(FileUtils.readText(json), ModpackConfiguration.typeOf(MultiMCInstanceConfiguration.class));
|
||||
|
||||
if (!MultiMCModpackProvider.INSTANCE.getName().equals(config.getType()))
|
||||
throw new IllegalArgumentException("Version " + name + " is not a MultiMC modpack. Cannot update this version.");
|
||||
if (!MultiMCModpackProvider.INSTANCE.getName().equals(config.getType()))
|
||||
throw new IllegalArgumentException("Version " + name + " is not a MultiMC modpack. Cannot update this version.");
|
||||
}
|
||||
} catch (JsonParseException | IOException ignore) {
|
||||
}
|
||||
} catch (JsonParseException | IOException ignore) {
|
||||
|
||||
String mcDirectory;
|
||||
try (FileSystem fs = openModpack()) {
|
||||
mcDirectory = getRootPath(fs).resolve(".minecraft").toAbsolutePath().normalize().toString();
|
||||
}
|
||||
|
||||
// TODO: Optimize unbearably slow ModpackInstallTask
|
||||
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), Collections.singletonList(mcDirectory), any -> true, config).withStage("hmcl.modpack"));
|
||||
dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList(mcDirectory), manifest, MultiMCModpackProvider.INSTANCE, manifest.getName(), null, repository.getModpackConfiguration(name)).withStage("hmcl.modpack"));
|
||||
}
|
||||
|
||||
String subDirectory;
|
||||
// Stage #1: Load all related Json-Patch from meta maven or local mod pack.
|
||||
|
||||
try (FileSystem fs = CompressingUtils.readonly(zipFile.toPath()).setEncoding(modpack.getEncoding()).build()) {
|
||||
// /.minecraft
|
||||
if (Files.exists(fs.getPath("/.minecraft"))) {
|
||||
subDirectory = "/.minecraft";
|
||||
// /minecraft
|
||||
} else if (Files.exists(fs.getPath("/minecraft"))) {
|
||||
subDirectory = "/minecraft";
|
||||
// /[name]/.minecraft
|
||||
} else if (Files.exists(fs.getPath("/" + manifest.getName() + "/.minecraft"))) {
|
||||
subDirectory = "/" + manifest.getName() + "/.minecraft";
|
||||
// /[name]/minecraft
|
||||
} else if (Files.exists(fs.getPath("/" + manifest.getName() + "/minecraft"))) {
|
||||
subDirectory = "/" + manifest.getName() + "/minecraft";
|
||||
} else {
|
||||
subDirectory = "/" + manifest.getName() + "/.minecraft";
|
||||
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));
|
||||
|
||||
if (Files.exists(patchPath)) {
|
||||
if (!Files.isRegularFile(patchPath)) {
|
||||
throw new IllegalArgumentException("Json-Patch isn't a file: " + componentID);
|
||||
}
|
||||
|
||||
MultiMCInstancePatch patch = MultiMCInstancePatch.read(componentID, FileUtils.readText(patchPath, StandardCharsets.UTF_8));
|
||||
patches.add(Task.supplyAsync(() -> patch)); // TODO: Task.completed has unclear compatibility issue.
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), Collections.singletonList(subDirectory), any -> true, config).withStage("hmcl.modpack"));
|
||||
dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList(subDirectory), manifest, MultiMCModpackProvider.INSTANCE, manifest.getName(), null, repository.getModpackConfiguration(name)).withStage("hmcl.modpack"));
|
||||
@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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
setResult(new ArrayList<>(existed.values()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Task<?>> getDependents() {
|
||||
// Stage #2: Resolve all Json-Patch
|
||||
return dependents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
Version version = repository.readVersionJson(name);
|
||||
|
||||
try (FileSystem fs = CompressingUtils.readonly(zipFile.toPath()).setAutoDetectEncoding(true).build()) {
|
||||
Path root = MultiMCModpackProvider.getRootPath(fs.getPath("/"));
|
||||
Path patches = root.resolve("patches");
|
||||
|
||||
if (Files.exists(patches)) {
|
||||
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(patches)) {
|
||||
for (Path patchJson : directoryStream) {
|
||||
if (patchJson.toString().endsWith(".json")) {
|
||||
// If json is malformed, we should stop installing this modpack instead of skipping it.
|
||||
MultiMCInstancePatch multiMCPatch = JsonUtils.GSON.fromJson(FileUtils.readText(patchJson), MultiMCInstancePatch.class);
|
||||
|
||||
List<String> arguments = new ArrayList<>();
|
||||
for (String arg : multiMCPatch.getTweakers()) {
|
||||
arguments.add("--tweakClass");
|
||||
arguments.add(arg);
|
||||
}
|
||||
|
||||
Version patch = new Version(multiMCPatch.getName(), multiMCPatch.getVersion(), 1, new Arguments().addGameArguments(arguments), multiMCPatch.getMainClass(), multiMCPatch.getLibraries());
|
||||
version = version.addPatch(patch);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Stage #3: Build Json-Patch artifact.
|
||||
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()) {
|
||||
Path root = getRootPath(fs);
|
||||
|
||||
Path libraries = root.resolve("libraries");
|
||||
if (Files.exists(libraries))
|
||||
FileUtils.copyDirectory(libraries, repository.getVersionRoot(name).toPath().resolve("libraries"));
|
||||
|
||||
Path jarmods = root.resolve("jarmods");
|
||||
if (Files.exists(jarmods))
|
||||
FileUtils.copyDirectory(jarmods, repository.getVersionRoot(name).toPath().resolve("jarmods"));
|
||||
|
||||
String iconKey = this.manifest.getIconKey();
|
||||
if (iconKey != null) {
|
||||
Path iconFile = root.resolve(iconKey + ".png");
|
||||
@@ -209,6 +241,95 @@ public final class MultiMCModpackInstallTask extends Task<Void> {
|
||||
}
|
||||
}
|
||||
|
||||
dependencies.add(repository.saveAsync(version));
|
||||
// Stage #5: Assemble game files.
|
||||
{
|
||||
Version version = artifact.getVersion();
|
||||
|
||||
dependencies.add(repository.saveAsync(artifact.getVersion()));
|
||||
dependencies.add(new GameAssetDownloadTask(dependencyManager, version, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY, true));
|
||||
dependencies.add(new GameLibrariesTask(
|
||||
dependencyManager,
|
||||
// TODO: check integrity of maven-only files when launching games?
|
||||
version.setLibraries(Lang.merge(version.getLibraries(), artifact.getMavenOnlyFiles())),
|
||||
true
|
||||
));
|
||||
|
||||
Artifact mainJarArtifact = artifact.getMainJar().getArtifact();
|
||||
String gameVersion = artifact.getGameVersion();
|
||||
if (gameVersion != null &&
|
||||
"com.mojang".equals(mainJarArtifact.getGroup()) &&
|
||||
"minecraft".equals(mainJarArtifact.getName()) &&
|
||||
Objects.equals(gameVersion, mainJarArtifact.getVersion()) &&
|
||||
"client".equals(mainJarArtifact.getClassifier())
|
||||
) {
|
||||
dependencies.add(new GameDownloadTask(dependencyManager, gameVersion, version));
|
||||
} else {
|
||||
dependencies.add(new GameDownloadTask(dependencyManager, null, version));
|
||||
}
|
||||
}
|
||||
|
||||
setResult(artifact);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Task<?>> getDependencies() {
|
||||
// Stage #6: Download game files.
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doPostExecute() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postExecute() throws Exception {
|
||||
MultiMCInstancePatch.ResolvedInstance artifact = Objects.requireNonNull(getResult(), "ResolvedInstance");
|
||||
|
||||
List<String> files = artifact.getJarModFileNames();
|
||||
if (!isDependenciesSucceeded() || files.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Stage #7: Apply jar mods.
|
||||
try (FileSystem fs = openModpack()) {
|
||||
Path root = getRootPath(fs).resolve("jarmods");
|
||||
|
||||
try (FileSystem mc = CompressingUtils.writable(
|
||||
repository.getVersionRoot(name).toPath().resolve(name + ".jar")
|
||||
).setAutoDetectEncoding(true).build()) {
|
||||
for (String fileName : files) {
|
||||
try (FileSystem jm = CompressingUtils.readonly(root.resolve(fileName)).setAutoDetectEncoding(true).build()) {
|
||||
FileUtils.copyDirectory(jm.getPath("/"), mc.getPath("/"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private FileSystem openModpack() throws IOException {
|
||||
return CompressingUtils.readonly(zipFile.toPath()).setAutoDetectEncoding(true).setEncoding(modpack.getEncoding()).build();
|
||||
}
|
||||
|
||||
private static boolean testPath(Path root) {
|
||||
return Files.exists(root.resolve("instance.cfg"));
|
||||
}
|
||||
|
||||
private static Path getRootPath(FileSystem fs) throws IOException {
|
||||
Path root = fs.getPath("/");
|
||||
|
||||
if (testPath(root)) {
|
||||
return root;
|
||||
}
|
||||
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(root)) {
|
||||
for (Path candidate : stream) {
|
||||
if (testPath(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new IOException("Not a valid MultiMC modpack");
|
||||
}
|
||||
}
|
||||
@@ -31,9 +31,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class MultiMCModpackProvider implements ModpackProvider {
|
||||
public static final MultiMCModpackProvider INSTANCE = new MultiMCModpackProvider();
|
||||
@@ -56,20 +54,6 @@ public final class MultiMCModpackProvider implements ModpackProvider {
|
||||
return new ModpackUpdateTask(dependencyManager.getGameRepository(), name, new MultiMCModpackInstallTask(dependencyManager, zipFile, modpack, (MultiMCInstanceConfiguration) modpack.getManifest(), name));
|
||||
}
|
||||
|
||||
private static boolean testPath(Path root) {
|
||||
return Files.exists(root.resolve("instance.cfg"));
|
||||
}
|
||||
|
||||
public static Path getRootPath(Path root) throws IOException {
|
||||
if (testPath(root)) return root;
|
||||
try (Stream<Path> stream = Files.list(root)) {
|
||||
Path candidate = stream.filter(Files::isDirectory).findAny()
|
||||
.orElseThrow(() -> new IOException("Not a valid MultiMC modpack"));
|
||||
if (testPath(candidate)) return candidate;
|
||||
throw new IOException("Not a valid MultiMC modpack");
|
||||
}
|
||||
}
|
||||
|
||||
private static String getRootEntryName(ZipArchiveReader file) throws IOException {
|
||||
final String instanceFileName = "instance.cfg";
|
||||
|
||||
|
||||
@@ -64,83 +64,65 @@ public final class LitematicFile {
|
||||
else if (!(versionTag instanceof IntTag))
|
||||
throw new IOException("Version tag is not an integer");
|
||||
|
||||
|
||||
Tag metadataTag = root.get("Metadata");
|
||||
if (metadataTag == null)
|
||||
throw new IOException("Metadata tag not found");
|
||||
else if (!(metadataTag instanceof CompoundTag))
|
||||
throw new IOException("Metadata tag is not a compound tag");
|
||||
|
||||
return new LitematicFile(file, root);
|
||||
int regions = 0;
|
||||
Tag regionsTag = root.get("Regions");
|
||||
if (regionsTag instanceof CompoundTag)
|
||||
regions = ((CompoundTag) regionsTag).size();
|
||||
|
||||
return new LitematicFile(file, (CompoundTag) metadataTag,
|
||||
((IntTag) versionTag).getValue(),
|
||||
tryGetInt(root.get("SubVersion")),
|
||||
tryGetInt(root.get("MinecraftDataVersion")),
|
||||
regions
|
||||
);
|
||||
}
|
||||
|
||||
private final @NotNull Path file;
|
||||
private final @NotNull CompoundTag root;
|
||||
|
||||
private LitematicFile(@NotNull Path file, @NotNull CompoundTag root) {
|
||||
private final int version;
|
||||
private final int subVersion;
|
||||
private final int minecraftDataVersion;
|
||||
private final int regionCount;
|
||||
private final int[] previewImageData;
|
||||
private final String name;
|
||||
private final String author;
|
||||
private final String description;
|
||||
private final Instant timeCreated;
|
||||
private final Instant timeModified;
|
||||
private final int totalBlocks;
|
||||
private final int totalVolume;
|
||||
private final Point3D enclosingSize;
|
||||
|
||||
private LitematicFile(@NotNull Path file, @NotNull CompoundTag metadata,
|
||||
int version, int subVersion, int minecraftDataVersion, int regionCount) {
|
||||
this.file = file;
|
||||
this.root = root;
|
||||
}
|
||||
this.version = version;
|
||||
this.subVersion = subVersion;
|
||||
this.minecraftDataVersion = minecraftDataVersion;
|
||||
this.regionCount = regionCount;
|
||||
|
||||
private @NotNull CompoundTag getMetadata() {
|
||||
return root.get("Metadata");
|
||||
}
|
||||
Tag previewImageData = metadata.get("PreviewImageData");
|
||||
this.previewImageData = previewImageData instanceof IntArrayTag
|
||||
? ((IntArrayTag) previewImageData).getValue()
|
||||
: null;
|
||||
|
||||
public @NotNull Path getFile() {
|
||||
return file;
|
||||
}
|
||||
this.name = tryGetString(metadata.get("Name"));
|
||||
this.author = tryGetString(metadata.get("Author"));
|
||||
this.description = tryGetString(metadata.get("Description"));
|
||||
this.timeCreated = tryGetLongTimestamp(metadata.get("TimeCreated"));
|
||||
this.timeModified = tryGetLongTimestamp(metadata.get("TimeModified"));
|
||||
this.totalBlocks = tryGetInt(metadata.get("TotalBlocks"));
|
||||
this.totalVolume = tryGetInt(metadata.get("TotalVolume"));
|
||||
|
||||
public int getVersion() {
|
||||
return root.<IntTag>get("Version").getValue();
|
||||
}
|
||||
|
||||
public int getSubVersion() {
|
||||
return tryGetInt(root.get("SubVersion"));
|
||||
}
|
||||
|
||||
public int getMinecraftDataVersion() {
|
||||
return tryGetInt(root.get("MinecraftDataVersion"));
|
||||
}
|
||||
|
||||
public int[] getPreviewImageData() {
|
||||
Tag previewImageData = getMetadata().get("PreviewImageData");
|
||||
if (previewImageData instanceof IntArrayTag) {
|
||||
return ((IntArrayTag) previewImageData).getValue().clone();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return tryGetString(getMetadata().get("Name"));
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return tryGetString(getMetadata().get("Author"));
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return tryGetString(getMetadata().get("Description"));
|
||||
}
|
||||
|
||||
public Instant getTimeCreated() {
|
||||
return tryGetLongTimestamp(getMetadata().get("TimeCreated"));
|
||||
}
|
||||
|
||||
public Instant getTimeModified() {
|
||||
return tryGetLongTimestamp(getMetadata().get("TimeModified"));
|
||||
}
|
||||
|
||||
public int getTotalBlocks() {
|
||||
return tryGetInt(getMetadata().get("TotalBlocks"));
|
||||
}
|
||||
|
||||
public int getTotalVolume() {
|
||||
return tryGetInt(getMetadata().get("TotalVolume"));
|
||||
}
|
||||
|
||||
public Point3D getEnclosingSize() {
|
||||
Tag enclosingSizeTag = getMetadata().get("EnclosingSize");
|
||||
Point3D enclosingSize = null;
|
||||
Tag enclosingSizeTag = metadata.get("EnclosingSize");
|
||||
if (enclosingSizeTag instanceof CompoundTag) {
|
||||
CompoundTag list = (CompoundTag) enclosingSizeTag;
|
||||
int x = tryGetInt(list.get("x"));
|
||||
@@ -148,16 +130,65 @@ public final class LitematicFile {
|
||||
int z = tryGetInt(list.get("z"));
|
||||
|
||||
if (x >= 0 && y >= 0 && z >= 0)
|
||||
return new Point3D(x, y, z);
|
||||
enclosingSize = new Point3D(x, y, z);
|
||||
}
|
||||
this.enclosingSize = enclosingSize;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public @NotNull Path getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public int getSubVersion() {
|
||||
return subVersion;
|
||||
}
|
||||
|
||||
public int getMinecraftDataVersion() {
|
||||
return minecraftDataVersion;
|
||||
}
|
||||
|
||||
public int[] getPreviewImageData() {
|
||||
return previewImageData != null ? previewImageData.clone() : null;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public Instant getTimeCreated() {
|
||||
return timeCreated;
|
||||
}
|
||||
|
||||
public Instant getTimeModified() {
|
||||
return timeModified;
|
||||
}
|
||||
|
||||
public int getTotalBlocks() {
|
||||
return totalBlocks;
|
||||
}
|
||||
|
||||
public int getTotalVolume() {
|
||||
return totalVolume;
|
||||
}
|
||||
|
||||
public Point3D getEnclosingSize() {
|
||||
return enclosingSize;
|
||||
}
|
||||
|
||||
public int getRegionCount() {
|
||||
Tag regions = root.get("Regions");
|
||||
if (regions instanceof CompoundTag)
|
||||
return ((CompoundTag) regions).size();
|
||||
else return 0;
|
||||
return regionCount;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
@@ -14,11 +15,13 @@ chardet = "2.5.0"
|
||||
twelvemonkeys = "3.12.0"
|
||||
jna = "5.17.0"
|
||||
pci-ids = "0.4.0"
|
||||
java-info = "1.0"
|
||||
|
||||
# plugins
|
||||
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" }
|
||||
@@ -36,6 +39,7 @@ twelvemonkeys-imageio-webp = { module = "com.twelvemonkeys.imageio:imageio-webp"
|
||||
jna = { module = "net.java.dev.jna:jna", version.ref = "jna" }
|
||||
jna-platform = { module = "net.java.dev.jna:jna-platform", version.ref = "jna" }
|
||||
pci-ids = { module = "org.glavo:pci-ids", version.ref = "pci-ids" }
|
||||
java-info = { module = "org.glavo:java-info", version.ref = "java-info" }
|
||||
|
||||
[plugins]
|
||||
shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }
|
||||
Reference in new issue
Block a user