mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2024-11-21 03:10:58 +08:00
parent
fb6538f042
commit
c1acd0b0b5
@ -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")
|
||||
|
@ -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);
|
||||
|
@ -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/";
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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<>();
|
||||
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
||||
|
@ -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;
|
@ -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;
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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. |
|
||||
|
@ -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>` | 覆盖原生编码. |
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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"}}}}
|
Loading…
Reference in New Issue
Block a user