mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-08 03:15:17 -04: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.net.URI
|
||||||
import java.nio.file.FileSystems
|
import java.nio.file.FileSystems
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
@ -10,16 +7,6 @@ import java.security.Signature
|
|||||||
import java.security.spec.PKCS8EncodedKeySpec
|
import java.security.spec.PKCS8EncodedKeySpec
|
||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipFile
|
||||||
|
|
||||||
buildscript {
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
classpath("com.google.code.gson:gson:2.10.1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.github.johnrengelman.shadow") version "7.1.2"
|
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") {
|
val java11 = sourceSets.create("java11") {
|
||||||
java {
|
java {
|
||||||
srcDir("src/main/java11")
|
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.AsyncTaskExecutor;
|
||||||
import org.jackhuang.hmcl.task.Schedulers;
|
import org.jackhuang.hmcl.task.Schedulers;
|
||||||
import org.jackhuang.hmcl.ui.Controllers;
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
import org.jackhuang.hmcl.upgrade.hmcl.UpdateChecker;
|
import org.jackhuang.hmcl.upgrade.UpdateChecker;
|
||||||
import org.jackhuang.hmcl.upgrade.hmcl.UpdateHandler;
|
import org.jackhuang.hmcl.upgrade.UpdateHandler;
|
||||||
import org.jackhuang.hmcl.upgrade.resource.RemoteResourceManager;
|
|
||||||
import org.jackhuang.hmcl.util.CrashReporter;
|
import org.jackhuang.hmcl.util.CrashReporter;
|
||||||
import org.jackhuang.hmcl.util.Lang;
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
import org.jackhuang.hmcl.util.Logging;
|
|
||||||
import org.jackhuang.hmcl.util.StringUtils;
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
import org.jackhuang.hmcl.util.io.JarUtils;
|
import org.jackhuang.hmcl.util.io.JarUtils;
|
||||||
import org.jackhuang.hmcl.util.platform.Architecture;
|
import org.jackhuang.hmcl.util.platform.Architecture;
|
||||||
@ -123,10 +121,6 @@ public final class Launcher extends Application {
|
|||||||
|
|
||||||
UpdateChecker.init();
|
UpdateChecker.init();
|
||||||
|
|
||||||
RemoteResourceManager.init();
|
|
||||||
|
|
||||||
RemoteResourceManager.register();
|
|
||||||
|
|
||||||
primaryStage.show();
|
primaryStage.show();
|
||||||
});
|
});
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
@ -293,10 +287,6 @@ public final class Launcher extends Application {
|
|||||||
if (OperatingSystem.CURRENT_OS.isLinuxOrBSD())
|
if (OperatingSystem.CURRENT_OS.isLinuxOrBSD())
|
||||||
LOG.info("XDG Session Type: " + System.getenv("XDG_SESSION_TYPE"));
|
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);
|
launch(Launcher.class, args);
|
||||||
} catch (Throwable e) { // Fucking JavaFX will suppress the exception and will break our crash reporter.
|
} catch (Throwable e) { // Fucking JavaFX will suppress the exception and will break our crash reporter.
|
||||||
CRASH_REPORTER.uncaughtException(Thread.currentThread(), e);
|
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 TITLE = NAME + " " + VERSION;
|
||||||
public static final String FULL_TITLE = FULL_NAME + " v" + 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.update_source.override", "https://hmcl.huangyuhui.net/api/update_link");
|
||||||
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 CONTACT_URL = "https://docs.hmcl.net/help.html";
|
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 HELP_URL = "https://docs.hmcl.net";
|
||||||
public static final String CHANGELOG_URL = "https://docs.hmcl.net/changelog/";
|
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 javafx.stage.Stage;
|
||||||
import org.jackhuang.hmcl.Metadata;
|
import org.jackhuang.hmcl.Metadata;
|
||||||
import org.jackhuang.hmcl.countly.CrashReport;
|
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.ui.FXUtils.newBuiltinImage;
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
@ -25,7 +25,7 @@ import javafx.scene.web.WebEngine;
|
|||||||
import javafx.scene.web.WebView;
|
import javafx.scene.web.WebView;
|
||||||
import org.jackhuang.hmcl.Metadata;
|
import org.jackhuang.hmcl.Metadata;
|
||||||
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
|
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;
|
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.SVG;
|
||||||
import org.jackhuang.hmcl.ui.WeakListenerHolder;
|
import org.jackhuang.hmcl.ui.WeakListenerHolder;
|
||||||
import org.jackhuang.hmcl.ui.construct.*;
|
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.StringUtils;
|
||||||
import org.jackhuang.hmcl.util.gson.UUIDTypeAdapter;
|
import org.jackhuang.hmcl.util.gson.UUIDTypeAdapter;
|
||||||
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
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.decorator.DecoratorPage;
|
||||||
import org.jackhuang.hmcl.ui.versions.GameItem;
|
import org.jackhuang.hmcl.ui.versions.GameItem;
|
||||||
import org.jackhuang.hmcl.ui.versions.Versions;
|
import org.jackhuang.hmcl.ui.versions.Versions;
|
||||||
import org.jackhuang.hmcl.upgrade.hmcl.RemoteVersion;
|
import org.jackhuang.hmcl.upgrade.RemoteVersion;
|
||||||
import org.jackhuang.hmcl.upgrade.hmcl.UpdateChecker;
|
import org.jackhuang.hmcl.upgrade.UpdateChecker;
|
||||||
import org.jackhuang.hmcl.upgrade.hmcl.UpdateHandler;
|
import org.jackhuang.hmcl.upgrade.UpdateHandler;
|
||||||
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||||
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
|
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.nbt.NBTHelper;
|
||||||
import org.jackhuang.hmcl.ui.versions.GameAdvancedListItem;
|
import org.jackhuang.hmcl.ui.versions.GameAdvancedListItem;
|
||||||
import org.jackhuang.hmcl.ui.versions.Versions;
|
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.StringUtils;
|
||||||
import org.jackhuang.hmcl.util.TaskCancellationAction;
|
import org.jackhuang.hmcl.util.TaskCancellationAction;
|
||||||
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
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.Controllers;
|
||||||
import org.jackhuang.hmcl.ui.FXUtils;
|
import org.jackhuang.hmcl.ui.FXUtils;
|
||||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
|
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
|
||||||
import org.jackhuang.hmcl.upgrade.hmcl.RemoteVersion;
|
import org.jackhuang.hmcl.upgrade.RemoteVersion;
|
||||||
import org.jackhuang.hmcl.upgrade.hmcl.UpdateChannel;
|
import org.jackhuang.hmcl.upgrade.UpdateChannel;
|
||||||
import org.jackhuang.hmcl.upgrade.hmcl.UpdateChecker;
|
import org.jackhuang.hmcl.upgrade.UpdateChecker;
|
||||||
import org.jackhuang.hmcl.upgrade.hmcl.UpdateHandler;
|
import org.jackhuang.hmcl.upgrade.UpdateHandler;
|
||||||
import org.jackhuang.hmcl.util.Logging;
|
import org.jackhuang.hmcl.util.Logging;
|
||||||
import org.jackhuang.hmcl.util.i18n.Locales;
|
import org.jackhuang.hmcl.util.i18n.Locales;
|
||||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
|
@ -18,13 +18,11 @@
|
|||||||
package org.jackhuang.hmcl.ui.versions;
|
package org.jackhuang.hmcl.ui.versions;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.mod.RemoteModRepository;
|
import org.jackhuang.hmcl.mod.RemoteModRepository;
|
||||||
import org.jackhuang.hmcl.upgrade.resource.RemoteResourceManager;
|
|
||||||
import org.jackhuang.hmcl.util.Pair;
|
import org.jackhuang.hmcl.util.Pair;
|
||||||
import org.jackhuang.hmcl.util.StringUtils;
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
import org.jackhuang.hmcl.util.io.IOUtils;
|
import org.jackhuang.hmcl.util.io.IOUtils;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
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>
|
* @see <a href="https://www.mcmod.cn">mcmod.cn</a>
|
||||||
*/
|
*/
|
||||||
public enum ModTranslations {
|
public enum ModTranslations {
|
||||||
MOD("/assets/mod_data.txt", "translation", "mod_data", "1") {
|
MOD("/assets/mod_data.txt") {
|
||||||
@Override
|
@Override
|
||||||
public String getMcmodUrl(Mod mod) {
|
public String getMcmodUrl(Mod mod) {
|
||||||
return String.format("https://www.mcmod.cn/class/%s.html", mod.getMcmod());
|
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
|
@Override
|
||||||
public String getMcmodUrl(Mod mod) {
|
public String getMcmodUrl(Mod mod) {
|
||||||
return String.format("https://www.mcmod.cn/modpack/%s.html", mod.getMcmod());
|
return String.format("https://www.mcmod.cn/modpack/%s.html", mod.getMcmod());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
EMPTY("", "", "", "") {
|
EMPTY("") {
|
||||||
@Override
|
@Override
|
||||||
public String getMcmodUrl(Mod mod) {
|
public String getMcmodUrl(Mod mod) {
|
||||||
return "";
|
return "";
|
||||||
@ -68,18 +66,15 @@ public enum ModTranslations {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String defaultResourceName;
|
private final String resourceName;
|
||||||
private final RemoteResourceManager.RemoteResourceKey remoteResourceKey;
|
|
||||||
private List<Mod> mods;
|
private List<Mod> mods;
|
||||||
private Map<String, Mod> modIdMap; // mod id -> mod
|
private Map<String, Mod> modIdMap; // mod id -> mod
|
||||||
private Map<String, Mod> curseForgeMap; // curseforge id -> mod
|
private Map<String, Mod> curseForgeMap; // curseforge id -> mod
|
||||||
private List<Pair<String, Mod>> keywords;
|
private List<Pair<String, Mod>> keywords;
|
||||||
private int maxKeywordLength = -1;
|
private int maxKeywordLength = -1;
|
||||||
|
|
||||||
ModTranslations(String defaultResourceName, String namespace, String name, String version) {
|
ModTranslations(String resourceName) {
|
||||||
this.defaultResourceName = defaultResourceName;
|
this.resourceName = resourceName;
|
||||||
|
|
||||||
remoteResourceKey = RemoteResourceManager.get(namespace, name, version, () -> ModTranslations.class.getResourceAsStream(defaultResourceName));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -120,24 +115,19 @@ public enum ModTranslations {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean loaded() {
|
private boolean loadFromResource() {
|
||||||
if (mods != null) return true;
|
if (mods != null) return true;
|
||||||
if (StringUtils.isBlank(defaultResourceName)) {
|
if (StringUtils.isBlank(resourceName)) {
|
||||||
mods = Collections.emptyList();
|
mods = Collections.emptyList();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
InputStream inputStream = remoteResourceKey.getResource();
|
String modData = IOUtils.readFullyAsString(ModTranslations.class.getResourceAsStream(resourceName));
|
||||||
if (inputStream == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
String modData = IOUtils.readFullyAsString(inputStream);
|
|
||||||
mods = Arrays.stream(modData.split("\n")).filter(line -> !line.startsWith("#")).map(Mod::new).collect(Collectors.toList());
|
mods = Arrays.stream(modData.split("\n")).filter(line -> !line.startsWith("#")).map(Mod::new).collect(Collectors.toList());
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.log(Level.WARNING, "Failed to load " + defaultResourceName, e);
|
LOG.log(Level.WARNING, "Failed to load " + resourceName, e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,7 +138,7 @@ public enum ModTranslations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mods == null) {
|
if (mods == null) {
|
||||||
if (!loaded()) return false;
|
if (!loadFromResource()) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
curseForgeMap = new HashMap<>();
|
curseForgeMap = new HashMap<>();
|
||||||
@ -166,7 +156,7 @@ public enum ModTranslations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mods == null) {
|
if (mods == null) {
|
||||||
if (!loaded()) return false;
|
if (!loadFromResource()) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
modIdMap = new HashMap<>();
|
modIdMap = new HashMap<>();
|
||||||
@ -186,7 +176,7 @@ public enum ModTranslations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mods == null) {
|
if (mods == null) {
|
||||||
if (!loaded()) return false;
|
if (!loadFromResource()) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
keywords = new ArrayList<>();
|
keywords = new ArrayList<>();
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
@ -15,7 +15,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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.task.FileDownloadTask;
|
||||||
import org.jackhuang.hmcl.util.Pack200Utils;
|
import org.jackhuang.hmcl.util.Pack200Utils;
|
@ -15,7 +15,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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.Metadata;
|
||||||
import org.jackhuang.hmcl.util.DigestUtils;
|
import org.jackhuang.hmcl.util.DigestUtils;
|
@ -15,7 +15,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
@ -15,7 +15,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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.Metadata;
|
||||||
|
|
@ -15,7 +15,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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.application.Platform;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
@ -15,7 +15,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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.Gson;
|
||||||
import com.google.gson.JsonParseException;
|
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.Metadata;
|
||||||
import org.jackhuang.hmcl.countly.CrashReport;
|
import org.jackhuang.hmcl.countly.CrashReport;
|
||||||
import org.jackhuang.hmcl.ui.CrashWindow;
|
import org.jackhuang.hmcl.ui.CrashWindow;
|
||||||
import org.jackhuang.hmcl.upgrade.hmcl.IntegrityChecker;
|
import org.jackhuang.hmcl.upgrade.IntegrityChecker;
|
||||||
import org.jackhuang.hmcl.upgrade.hmcl.UpdateChecker;
|
import org.jackhuang.hmcl.upgrade.UpdateChecker;
|
||||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
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.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.font.override=<font family>` | Override font family. |
|
||||||
| `-Dhmcl.version.override=<version>` | Override the version number. |
|
| `-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.update_source.override=<url>` | Override the update source for HMCL itself. |
|
||||||
| `-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.authlibinjector.location=<path>` | Use specified authlib-injector (instead of downloading one). |
|
| `-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.openjfx.repo=<maven repository url>` | Add custom Maven repository for download OpenJFX. |
|
||||||
| `-Dhmcl.native.encoding=<encoding>` | Override the native encoding. |
|
| `-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.bmclapi.override=<version>` | 覆盖 BMCLAPI 的 API Root, 默认值为 `https://bmclapi2.bangbang93.com`. 例如 `https://download.mcbbs.net`. |
|
||||||
| `-Dhmcl.font.override=<font family>` | 覆盖字族. |
|
| `-Dhmcl.font.override=<font family>` | 覆盖字族. |
|
||||||
| `-Dhmcl.version.override=<version>` | 覆盖版本号. |
|
| `-Dhmcl.version.override=<version>` | 覆盖版本号. |
|
||||||
| ~~`-Dhmcl.update_source.override=<url>`~~ | 覆盖 HMCL 更新源(已弃用,请使用 `hmcl.hmcl_update_source.override`). |
|
| `-Dhmcl.update_source.override=<url>` | 覆盖 HMCL 更新源. |
|
||||||
| `-Dhmcl.hmcl_update_source.override=<url>` | 覆盖 HMCL 更新源. |
|
|
||||||
| `-Dhmcl.resource_update_source.override=<url>` | 覆盖动态远程资源更新源. |
|
|
||||||
| `-Dhmcl.authlibinjector.location=<path>` | 使用指定的 authlib-injector (而非下载一个). |
|
| `-Dhmcl.authlibinjector.location=<path>` | 使用指定的 authlib-injector (而非下载一个). |
|
||||||
| `-Dhmcl.openjfx.repo=<maven repository url>` | 添加用于下载 OpenJFX 的自定义 Maven 仓库 |
|
| `-Dhmcl.openjfx.repo=<maven repository url>` | 添加用于下载 OpenJFX 的自定义 Maven 仓库 |
|
||||||
| `-Dhmcl.native.encoding=<encoding>` | 覆盖原生编码. |
|
| `-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…
x
Reference in New Issue
Block a user