删除 RemoteResourceManager (#2761)

* 删除 RemoteResourceManager

* update
This commit is contained in:
Glavo 2024-02-08 19:19:01 +08:00 committed by GitHub
parent fb6538f042
commit c1acd0b0b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 38 additions and 359 deletions

View File

@ -1,6 +1,3 @@
import com.google.gson.Gson
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import java.net.URI
import java.nio.file.FileSystems
import java.nio.file.Files
@ -10,16 +7,6 @@ import java.security.Signature
import java.security.spec.PKCS8EncodedKeySpec
import java.util.zip.ZipFile
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("com.google.code.gson:gson:2.10.1")
}
}
plugins {
id("com.github.johnrengelman.shadow") version "7.1.2"
}
@ -94,59 +81,6 @@ fun attachSignature(jar: File) {
}
}
tasks.getByName<JavaCompile>("compileJava") {
dependsOn(tasks.create("computeDynamicResources") {
this@create.inputs.file(rootProject.rootDir.toPath().resolve("data-json/dynamic-remote-resources-raw.json"))
this@create.outputs.file(rootProject.rootDir.toPath().resolve("data-json/dynamic-remote-resources.json"))
doLast {
Gson().also { gsonInstance ->
rootProject.rootDir.resolve("data-json/dynamic-remote-resources-raw.json").bufferedReader().use { br ->
(gsonInstance.fromJson(br, JsonElement::class.java) as JsonObject)
}.also { data ->
data.asMap().forEach { (namespace, namespaceData) ->
(namespaceData as JsonObject).asMap().forEach { (name, nameData) ->
(nameData as JsonObject).asMap().forEach { (version, versionData) ->
require(versionData is JsonObject)
val localPath =
(versionData.get("local_path") as com.google.gson.JsonPrimitive).asString
val sha1 = (versionData.get("sha1") as com.google.gson.JsonPrimitive).asString
val currentSha1 = digest(
"SHA-1",
rootProject.rootDir.resolve(localPath).readBytes()
).joinToString(separator = "") { "%02x".format(it) }
if (!sha1.equals(currentSha1, ignoreCase = true)) {
throw IllegalStateException("Mismatched SHA-1 in $.${namespace}.${name}.${version} of dynamic remote resources detected. Require ${currentSha1}, but found $sha1")
}
}
}
}
rootProject.rootDir.resolve("data-json/dynamic-remote-resources.json").also { zippedPath ->
gsonInstance.toJson(data).also { expectedData ->
if (zippedPath.exists()) {
zippedPath.readText().also { rawData ->
if (rawData != expectedData) {
if (System.getenv("GITHUB_SHA") == null) {
zippedPath.writeText(expectedData)
} else {
throw IllegalStateException("Mismatched zipped dynamic-remote-resources json file!")
}
}
}
} else {
zippedPath.writeText(expectedData)
}
}
}
}
}
}
})
}
val java11 = sourceSets.create("java11") {
java {
srcDir("src/main/java11")

View File

@ -33,12 +33,10 @@ import org.jackhuang.hmcl.setting.SambaException;
import org.jackhuang.hmcl.task.AsyncTaskExecutor;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.upgrade.hmcl.UpdateChecker;
import org.jackhuang.hmcl.upgrade.hmcl.UpdateHandler;
import org.jackhuang.hmcl.upgrade.resource.RemoteResourceManager;
import org.jackhuang.hmcl.upgrade.UpdateChecker;
import org.jackhuang.hmcl.upgrade.UpdateHandler;
import org.jackhuang.hmcl.util.CrashReporter;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.io.JarUtils;
import org.jackhuang.hmcl.util.platform.Architecture;
@ -123,10 +121,6 @@ public final class Launcher extends Application {
UpdateChecker.init();
RemoteResourceManager.init();
RemoteResourceManager.register();
primaryStage.show();
});
} catch (Throwable e) {
@ -293,10 +287,6 @@ public final class Launcher extends Application {
if (OperatingSystem.CURRENT_OS.isLinuxOrBSD())
LOG.info("XDG Session Type: " + System.getenv("XDG_SESSION_TYPE"));
if (System.getProperty("hmcl.update_source.override") != null) {
Logging.LOG.log(Level.WARNING, "'hmcl.update_source.override' is deprecated! Please use 'hmcl.hmcl_update_source.override' instead");
}
launch(Launcher.class, args);
} catch (Throwable e) { // Fucking JavaFX will suppress the exception and will break our crash reporter.
CRASH_REPORTER.uncaughtException(Thread.currentThread(), e);

View File

@ -37,9 +37,7 @@ public final class Metadata {
public static final String TITLE = NAME + " " + VERSION;
public static final String FULL_TITLE = FULL_NAME + " v" + VERSION;
// hmcl.update_source.override is deprecated. If it is used, a warning message will be printed in org.jackhuang.hmcl.Launcher.main .
public static final String HMCL_UPDATE_URL = System.getProperty("hmcl.hmcl_update_source.override", System.getProperty("hmcl.update_source.override", "https://hmcl.huangyuhui.net/api/update_link"));
public static final String RESOURCE_UPDATE_URL = System.getProperty("hmcl.resource_update_source.override", "https://hmcl.huangyuhui.net/api/dynamic_remote_resource/update_link");
public static final String HMCL_UPDATE_URL = System.getProperty("hmcl.update_source.override", "https://hmcl.huangyuhui.net/api/update_link");
public static final String CONTACT_URL = "https://docs.hmcl.net/help.html";
public static final String HELP_URL = "https://docs.hmcl.net";
public static final String CHANGELOG_URL = "https://docs.hmcl.net/changelog/";

View File

@ -28,7 +28,7 @@ import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.countly.CrashReport;
import org.jackhuang.hmcl.upgrade.hmcl.UpdateChecker;
import org.jackhuang.hmcl.upgrade.UpdateChecker;
import static org.jackhuang.hmcl.ui.FXUtils.newBuiltinImage;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;

View File

@ -25,7 +25,7 @@ import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
import org.jackhuang.hmcl.upgrade.hmcl.RemoteVersion;
import org.jackhuang.hmcl.upgrade.RemoteVersion;
import java.util.logging.Level;

View File

@ -58,7 +58,7 @@ import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.WeakListenerHolder;
import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.upgrade.hmcl.IntegrityChecker;
import org.jackhuang.hmcl.upgrade.IntegrityChecker;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.gson.UUIDTypeAdapter;
import org.jackhuang.hmcl.util.javafx.BindingMapping;

View File

@ -53,9 +53,9 @@ import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.ui.versions.GameItem;
import org.jackhuang.hmcl.ui.versions.Versions;
import org.jackhuang.hmcl.upgrade.hmcl.RemoteVersion;
import org.jackhuang.hmcl.upgrade.hmcl.UpdateChecker;
import org.jackhuang.hmcl.upgrade.hmcl.UpdateHandler;
import org.jackhuang.hmcl.upgrade.RemoteVersion;
import org.jackhuang.hmcl.upgrade.UpdateChecker;
import org.jackhuang.hmcl.upgrade.UpdateHandler;
import org.jackhuang.hmcl.util.javafx.BindingMapping;
import org.jackhuang.hmcl.util.javafx.MappedObservableList;

View File

@ -43,7 +43,7 @@ import org.jackhuang.hmcl.ui.nbt.NBTEditorPage;
import org.jackhuang.hmcl.ui.nbt.NBTHelper;
import org.jackhuang.hmcl.ui.versions.GameAdvancedListItem;
import org.jackhuang.hmcl.ui.versions.Versions;
import org.jackhuang.hmcl.upgrade.hmcl.UpdateChecker;
import org.jackhuang.hmcl.upgrade.UpdateChecker;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.TaskCancellationAction;
import org.jackhuang.hmcl.util.io.CompressingUtils;

View File

@ -27,10 +27,10 @@ import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
import org.jackhuang.hmcl.upgrade.hmcl.RemoteVersion;
import org.jackhuang.hmcl.upgrade.hmcl.UpdateChannel;
import org.jackhuang.hmcl.upgrade.hmcl.UpdateChecker;
import org.jackhuang.hmcl.upgrade.hmcl.UpdateHandler;
import org.jackhuang.hmcl.upgrade.RemoteVersion;
import org.jackhuang.hmcl.upgrade.UpdateChannel;
import org.jackhuang.hmcl.upgrade.UpdateChecker;
import org.jackhuang.hmcl.upgrade.UpdateHandler;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.i18n.Locales;
import org.jackhuang.hmcl.util.io.FileUtils;

View File

@ -18,13 +18,11 @@
package org.jackhuang.hmcl.ui.versions;
import org.jackhuang.hmcl.mod.RemoteModRepository;
import org.jackhuang.hmcl.upgrade.resource.RemoteResourceManager;
import org.jackhuang.hmcl.util.Pair;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.io.IOUtils;
import org.jetbrains.annotations.Nullable;
import java.io.InputStream;
import java.util.*;
import java.util.logging.Level;
import java.util.stream.Collectors;
@ -38,19 +36,19 @@ import static org.jackhuang.hmcl.util.Pair.pair;
* @see <a href="https://www.mcmod.cn">mcmod.cn</a>
*/
public enum ModTranslations {
MOD("/assets/mod_data.txt", "translation", "mod_data", "1") {
MOD("/assets/mod_data.txt") {
@Override
public String getMcmodUrl(Mod mod) {
return String.format("https://www.mcmod.cn/class/%s.html", mod.getMcmod());
}
},
MODPACK("/assets/modpack_data.txt", "translation", "modpack_data", "1") {
MODPACK("/assets/modpack_data.txt") {
@Override
public String getMcmodUrl(Mod mod) {
return String.format("https://www.mcmod.cn/modpack/%s.html", mod.getMcmod());
}
},
EMPTY("", "", "", "") {
EMPTY("") {
@Override
public String getMcmodUrl(Mod mod) {
return "";
@ -68,18 +66,15 @@ public enum ModTranslations {
}
}
private final String defaultResourceName;
private final RemoteResourceManager.RemoteResourceKey remoteResourceKey;
private final String resourceName;
private List<Mod> mods;
private Map<String, Mod> modIdMap; // mod id -> mod
private Map<String, Mod> curseForgeMap; // curseforge id -> mod
private List<Pair<String, Mod>> keywords;
private int maxKeywordLength = -1;
ModTranslations(String defaultResourceName, String namespace, String name, String version) {
this.defaultResourceName = defaultResourceName;
remoteResourceKey = RemoteResourceManager.get(namespace, name, version, () -> ModTranslations.class.getResourceAsStream(defaultResourceName));
ModTranslations(String resourceName) {
this.resourceName = resourceName;
}
@Nullable
@ -120,24 +115,19 @@ public enum ModTranslations {
.collect(Collectors.toList());
}
private boolean loaded() {
private boolean loadFromResource() {
if (mods != null) return true;
if (StringUtils.isBlank(defaultResourceName)) {
if (StringUtils.isBlank(resourceName)) {
mods = Collections.emptyList();
return true;
}
try {
InputStream inputStream = remoteResourceKey.getResource();
if (inputStream == null) {
return false;
}
String modData = IOUtils.readFullyAsString(inputStream);
String modData = IOUtils.readFullyAsString(ModTranslations.class.getResourceAsStream(resourceName));
mods = Arrays.stream(modData.split("\n")).filter(line -> !line.startsWith("#")).map(Mod::new).collect(Collectors.toList());
return true;
} catch (Exception e) {
LOG.log(Level.WARNING, "Failed to load " + defaultResourceName, e);
LOG.log(Level.WARNING, "Failed to load " + resourceName, e);
return false;
}
}
@ -148,7 +138,7 @@ public enum ModTranslations {
}
if (mods == null) {
if (!loaded()) return false;
if (!loadFromResource()) return false;
}
curseForgeMap = new HashMap<>();
@ -166,7 +156,7 @@ public enum ModTranslations {
}
if (mods == null) {
if (!loaded()) return false;
if (!loadFromResource()) return false;
}
modIdMap = new HashMap<>();
@ -186,7 +176,7 @@ public enum ModTranslations {
}
if (mods == null) {
if (!loaded()) return false;
if (!loadFromResource()) return false;
}
keywords = new ArrayList<>();

View File

@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.upgrade.hmcl;
package org.jackhuang.hmcl.upgrade;
import java.io.IOException;
import java.io.InputStream;

View File

@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.upgrade.hmcl;
package org.jackhuang.hmcl.upgrade;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.util.Pack200Utils;

View File

@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.upgrade.hmcl;
package org.jackhuang.hmcl.upgrade;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.util.DigestUtils;

View File

@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.upgrade.hmcl;
package org.jackhuang.hmcl.upgrade;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

View File

@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.upgrade.hmcl;
package org.jackhuang.hmcl.upgrade;
import org.jackhuang.hmcl.Metadata;

View File

@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.upgrade.hmcl;
package org.jackhuang.hmcl.upgrade;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;

View File

@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.upgrade.hmcl;
package org.jackhuang.hmcl.upgrade;
import com.google.gson.Gson;
import com.google.gson.JsonParseException;

View File

@ -1,204 +0,0 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.upgrade.resource;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.versions.ModTranslations;
import org.jackhuang.hmcl.upgrade.hmcl.IntegrityChecker;
import org.jackhuang.hmcl.util.DigestUtils;
import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
import org.jackhuang.hmcl.util.io.HttpRequest;
import org.jackhuang.hmcl.util.io.IOUtils;
import org.jackhuang.hmcl.util.io.NetworkUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
public final class RemoteResourceManager {
private RemoteResourceManager() {
}
private static final class RemoteResource {
@SerializedName("sha1")
private final String sha1;
@SerializedName("urls")
private final String[] urls;
private transient byte[] data = null;
private RemoteResource(String sha1, String[] urls) {
this.sha1 = sha1;
this.urls = urls;
}
public void download(Path path, Runnable callback) {
if (data != null) {
return;
}
new FileDownloadTask(Arrays.stream(urls).map(NetworkUtils::toURL).collect(Collectors.toList()), path.toFile(), new FileDownloadTask.IntegrityCheck("SHA-1", sha1))
.whenComplete(Schedulers.defaultScheduler(), (result, exception) -> {
if (exception != null) {
data = Files.readAllBytes(path);
callback.run();
}
}).start();
}
}
public static final class RemoteResourceKey {
private final String namespace;
private final String name;
private final String version;
private final Path cachePath;
private final ExceptionalSupplier<InputStream, IOException> localResourceSupplier;
private String localResourceSha1 = null;
public RemoteResourceKey(String namespace, String name, String version, ExceptionalSupplier<InputStream, IOException> localResourceSupplier) {
this.namespace = namespace;
this.name = name;
this.version = version;
this.localResourceSupplier = localResourceSupplier;
this.cachePath = Metadata.HMCL_DIRECTORY.resolve("remoteResources").resolve(namespace).resolve(name).resolve(version).resolve(String.format("%s-%s-%s.resource", namespace, name, version));
}
private InputStream getLocalResource() throws IOException {
if (Files.isReadable(cachePath)) {
return Files.newInputStream(cachePath);
}
return localResourceSupplier.get();
}
private String getLocalResourceSha1() throws IOException {
if (localResourceSha1 == null) {
localResourceSha1 = DigestUtils.digestToString("SHA-1", IOUtils.readFullyAsByteArray(getLocalResource()));
}
return localResourceSha1;
}
@Nullable
private RemoteResource getRemoteResource() {
return Optional.ofNullable(remoteResources.get(namespace)).map(map -> map.get(name)).map(map -> map.get(version)).orElse(null);
}
@Nullable
public InputStream getResource() throws IOException {
RemoteResource remoteResource = getRemoteResource();
if (remoteResource == null) {
return getLocalResource();
}
if (remoteResource.sha1.equals(getLocalResourceSha1())) {
return getLocalResource();
}
if (remoteResource.data == null) {
return null;
}
return new ByteArrayInputStream(remoteResource.data);
}
public void downloadRemoteResourceIfNecessary() throws IOException {
RemoteResource remoteResource = getRemoteResource();
if (remoteResource == null) {
return;
}
if (remoteResource.sha1.equals(getLocalResourceSha1())) {
return;
}
remoteResource.download(cachePath, () -> localResourceSha1 = null);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RemoteResourceKey that = (RemoteResourceKey) o;
if (!namespace.equals(that.namespace)) return false;
if (!name.equals(that.name)) return false;
return version.equals(that.version);
}
@Override
public int hashCode() {
int result = namespace.hashCode();
result = 31 * result + name.hashCode();
result = 31 * result + version.hashCode();
return result;
}
}
private static final Map<String, Map<String, Map<String, RemoteResource>>> remoteResources = new ConcurrentHashMap<>();
private static final Map<String, RemoteResourceKey> keys = new ConcurrentHashMap<>();
public static void init() {
Task.<Map<String, Map<String, Map<String, RemoteResource>>>>supplyAsync(() ->
IntegrityChecker.isSelfVerified() ? HttpRequest.GET(Metadata.RESOURCE_UPDATE_URL).getJson(
new TypeToken<Map<String, Map<String, Map<String, RemoteResource>>>>() {
}.getType()
) : null
).whenComplete(Schedulers.defaultScheduler(), (result, exception) -> {
if (exception == null && result != null) {
remoteResources.clear();
remoteResources.putAll(result);
for (RemoteResourceKey key : keys.values()) {
key.downloadRemoteResourceIfNecessary();
}
}
}).start();
}
public static void register() {
ModTranslations.values();
}
public static RemoteResourceKey get(@NotNull String namespace, @NotNull String name, @NotNull String version, ExceptionalSupplier<InputStream, IOException> defaultSupplier) {
String stringKey = String.format("%s:%s:%s", namespace, name, version);
RemoteResourceKey key = keys.containsKey(stringKey) ? keys.get(stringKey) : new RemoteResourceKey(namespace, name, version, defaultSupplier);
Task.runAsync(key::downloadRemoteResourceIfNecessary).start();
keys.put(stringKey, key);
return key;
}
}

View File

@ -23,8 +23,8 @@ import javafx.scene.control.Alert.AlertType;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.countly.CrashReport;
import org.jackhuang.hmcl.ui.CrashWindow;
import org.jackhuang.hmcl.upgrade.hmcl.IntegrityChecker;
import org.jackhuang.hmcl.upgrade.hmcl.UpdateChecker;
import org.jackhuang.hmcl.upgrade.IntegrityChecker;
import org.jackhuang.hmcl.upgrade.UpdateChecker;
import org.jackhuang.hmcl.util.io.NetworkUtils;
import java.io.IOException;

View File

@ -66,9 +66,7 @@ Make sure you have Java installed with JavaFX 8 at least. Liberica Full JDK 8 or
| `-Dhmcl.bmclapi.override=<version>` | Override API Root of BMCLAPI download provider, defaults to `https://bmclapi2.bangbang93.com`. e.g. `https://download.mcbbs.net`. |
| `-Dhmcl.font.override=<font family>` | Override font family. |
| `-Dhmcl.version.override=<version>` | Override the version number. |
| ~~`-Dhmcl.update_source.override=<url>`~~ | Override the update source for HMCL itself. (Deprecated, please use `hmcl.hmcl_update_source.override` instead.) |
| `-Dhmcl.hmcl_update_source.override=<url>` | Override the update source for HMCL itself. |
| `-Dhmcl.resource_update_source.override=<url>` | Override the update source for dynamic remote resources. |
| `-Dhmcl.update_source.override=<url>` | Override the update source for HMCL itself. |
| `-Dhmcl.authlibinjector.location=<path>` | Use specified authlib-injector (instead of downloading one). |
| `-Dhmcl.openjfx.repo=<maven repository url>` | Add custom Maven repository for download OpenJFX. |
| `-Dhmcl.native.encoding=<encoding>` | Override the native encoding. |

View File

@ -64,9 +64,7 @@ HMCL 有着强大的跨平台能力. 它不仅支持 Windows、Linux、macOS 等
| `-Dhmcl.bmclapi.override=<version>` | 覆盖 BMCLAPI 的 API Root, 默认值为 `https://bmclapi2.bangbang93.com`. 例如 `https://download.mcbbs.net`. |
| `-Dhmcl.font.override=<font family>` | 覆盖字族. |
| `-Dhmcl.version.override=<version>` | 覆盖版本号. |
| ~~`-Dhmcl.update_source.override=<url>`~~ | 覆盖 HMCL 更新源(已弃用,请使用 `hmcl.hmcl_update_source.override`. |
| `-Dhmcl.hmcl_update_source.override=<url>` | 覆盖 HMCL 更新源. |
| `-Dhmcl.resource_update_source.override=<url>` | 覆盖动态远程资源更新源. |
| `-Dhmcl.update_source.override=<url>` | 覆盖 HMCL 更新源. |
| `-Dhmcl.authlibinjector.location=<path>` | 使用指定的 authlib-injector (而非下载一个). |
| `-Dhmcl.openjfx.repo=<maven repository url>` | 添加用于下载 OpenJFX 的自定义 Maven 仓库 |
| `-Dhmcl.native.encoding=<encoding>` | 覆盖原生编码. |

View File

@ -1,24 +0,0 @@
{
"translation": {
"mod_data": {
"1": {
"urls": [
"https://github.com/huanghongxun/HMCL/raw/javafx/HMCL/src/main/resources/assets/mod_data.txt",
"https://rgp.zkitefly.repl.co/https://github.com/huanghongxun/HMCL/raw/javafx/HMCL/src/main/resources/assets/mod_data.txt"
],
"local_path": "HMCL/src/main/resources/assets/mod_data.txt",
"sha1": "0ae36a65a00b00176358bd6b0d3c8787b3668c23"
}
},
"modpack_data": {
"1": {
"urls": [
"https://github.com/huanghongxun/HMCL/blob/javafx/HMCL/src/main/resources/assets/modpack_data.txt",
"https://rgp.zkitefly.repl.co/https://github.com/huanghongxun/HMCL/blob/javafx/HMCL/src/main/resources/assets/modpack_data.txt"
],
"local_path": "HMCL/src/main/resources/assets/modpack_data.txt",
"sha1": "b0e771db170835e1154da4c21b7417a688836162"
}
}
}
}

View File

@ -1 +0,0 @@
{"translation":{"mod_data":{"1":{"urls":["https://github.com/huanghongxun/HMCL/raw/javafx/HMCL/src/main/resources/assets/mod_data.txt","https://rgp.zkitefly.repl.co/https://github.com/huanghongxun/HMCL/raw/javafx/HMCL/src/main/resources/assets/mod_data.txt"],"local_path":"HMCL/src/main/resources/assets/mod_data.txt","sha1":"0ae36a65a00b00176358bd6b0d3c8787b3668c23"}},"modpack_data":{"1":{"urls":["https://github.com/huanghongxun/HMCL/blob/javafx/HMCL/src/main/resources/assets/modpack_data.txt","https://rgp.zkitefly.repl.co/https://github.com/huanghongxun/HMCL/blob/javafx/HMCL/src/main/resources/assets/modpack_data.txt"],"local_path":"HMCL/src/main/resources/assets/modpack_data.txt","sha1":"b0e771db170835e1154da4c21b7417a688836162"}}}}