Skip to content

Commit

Permalink
Enhance mod download (#2411)
Browse files Browse the repository at this point in the history
* Support #2376

* Add necessary @nullable annotations

* Display different types of dependencies in different sections.

* Fix checkstyle

* Add I18N for different types of dependencies.

* Enhance UI

* Code cleanup

* Enhance UI

* Manually sort the result from curseforge when searching mods by name.

* Render the search results from remote mod repositories in several pages.

* Fix merge

* Fix

* Add a button which navigates to the modpack download page in the modpack installl page

* Fix I18N

* Render the mod loaders supported by the version in mod info page.

* Fix #2104

* Enhance TwoLineListItem

* Render the mod loader supported by this mod file on the ModListPage

* Fix chinese searching and curseforge searching

* Update I18N

* Fix

* Fix

* Select the specific game version when clicking the 'download' button on ModListPage

* Support HMCL to update mod_data and mod_pack data from https://github.com/huanghongxun/HMCL/raw/javafx/data-json/dynamic-remote-resources.json

* Enhance :HMCL:build.gradle.kts

* Revert parse_mcmod_data.py

* Abstract 'new Image' to FXUtils.newBuiltinImage and FXUtils.newRemoteImage

FXUtils.newBuiltinImage is used to load image which is supposed to be correct definitely and is a file within the jar. Or, it will throw ResourceNotFoundError.

FXUtils.newRemoteImage is used to load image from the internet. It will cache the data of images for the further usage. The cached data will be deleted when HMCL is closed or hidden.

* Add javadoc for FXUtils.newBuiltinImage and FXUtils.newRemoteImage.

* Fix checkstyle

* Fix

* Fix

* Fix

* Add license for RemoteResourceManager

* Remove TODO

* Enhance Chinese searching

* Support to decode metadata for local quilt mod.

* Enhance ModManager

* Fix checkstyle

* Refactor

* Fix

* Fix

* Refactor DownloadPage

* Fix

* Revert "Refactor DownloadPage"

This reverts commit 953558d.

* Refactor DownloadPage

* Refactor

* Fix

* Fix checkstyle

* Set org.jackhuang.hmcl.ui.construct.TwoLineListItem.TagChangeListener as a private static inner class.

* Fix

* Fix

* Fix

* Enhance SimpleMultimap

* Revert TwoLineListItem

* Fix

* Code cleanup

* Code cleanup

* Fix

* Code cleanup

* Add license for IModMetadataReader

* Add prefix 'Minecraft' at the supported minecrft version list in DownloadPage

* Fix #2498

* Update README_cn.md

* Opti ModMananger

* Log a warning message when 'hmcl.update_source.override' is used.

* Fix chinese searching

* Enhance chinese searching.

* Enhance memory usage

* Close the mod version dialog window after clicking the downloading / save as button if the dependency list is empty.

* Cache builtin images.

* Enhance FXUtils (Make tooltip installer faster).

* Fix

* Fix

* Fix #2560

* Fix typo

* Fix remote image cache.

* Fix javadoc

* Fix checkstyle

* Optimize FXUtils::shutdown

* Fix merge

* I have no idea on why the sha1 was matched.

* Revert "Enhance FXUtils (Make tooltip installer faster)."

This reverts commit 0a49eb2.

* Support multi download source in order balance the traffic of hmcl.huangyuhui.net and the download speed in China Mainland.

* Modify dynamic remote resource urls.

* Optimize codes with StringUtils.DynamicCommonSubsequence.

* Prevent unofficial HMCL to access HMCL Resource Update URL.

* Zip the dynamic-remote-resources json by Gradle automatically.

* Remove unnecessary getters.

---------

Co-authored-by: Burning_TNT <[email protected]“>
  • Loading branch information
burningtnt and Burning_TNT authored Dec 31, 2023
1 parent 333fd0e commit 242df8a
Show file tree
Hide file tree
Showing 78 changed files with 1,528 additions and 474 deletions.
72 changes: 71 additions & 1 deletion HMCL/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
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
Expand All @@ -7,12 +10,23 @@ 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"
}

val isOfficial = System.getenv("HMCL_SIGNATURE_KEY") != null
|| (System.getenv("GITHUB_REPOSITORY_OWNER") == "huanghongxun" && System.getenv("GITHUB_BASE_REF").isNullOrEmpty())
|| (System.getenv("GITHUB_REPOSITORY_OWNER") == "huanghongxun" && System.getenv("GITHUB_BASE_REF")
.isNullOrEmpty())

val buildNumber = System.getenv("BUILD_NUMBER")?.toInt().let { number ->
val offset = System.getenv("BUILD_NUMBER_OFFSET")?.toInt() ?: 0
Expand Down Expand Up @@ -80,6 +94,62 @@ 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 ->
Files.newBufferedReader(
rootProject.rootDir.toPath().resolve("data-json/dynamic-remote-resources-raw.json"),
Charsets.UTF_8
).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",
Files.readAllBytes(rootProject.rootDir.toPath().resolve(localPath))
).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.toPath().resolve("data-json/dynamic-remote-resources.json").also { zippedPath ->
gsonInstance.toJson(data).also { expectedData ->
if (Files.exists(zippedPath)) {
Files.readString(zippedPath, Charsets.UTF_8).also { rawData ->
if (!rawData.equals(expectedData)) {
if (System.getenv("GITHUB_SHA") == null) {
Files.writeString(zippedPath, expectedData, Charsets.UTF_8)
} else {
throw IllegalStateException("Mismatched zipped dynamic-remote-resources json file!")
}
}
}
} else {
Files.writeString(zippedPath, expectedData, Charsets.UTF_8)
}
}
}
}
}
}
})
}

val java11 = sourceSets.create("java11") {
java {
srcDir("src/main/java11")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
import javafx.scene.transform.Scale;
import javafx.scene.transform.Translate;

import org.jackhuang.hmcl.ui.FXUtils;
import org.jetbrains.annotations.Nullable;

public class SkinCanvas extends Group {

public static final Image ALEX = new Image("/assets/img/skin/alex.png");
public static final Image STEVE = new Image("/assets/img/skin/steve.png");
public static final Image ALEX = FXUtils.newBuiltinImage("/assets/img/skin/alex.png");
public static final Image STEVE = FXUtils.newBuiltinImage("/assets/img/skin/steve.png");

public static final SkinCube ALEX_LARM = new SkinCube(3, 12, 4, 14F / 64F, 16F / 64F, 32F / 64F, 48F / 64F, 0F, true);
public static final SkinCube ALEX_RARM = new SkinCube(3, 12, 4, 14F / 64F, 16F / 64F, 40F / 64F, 16F / 64F, 0F, true);
Expand Down
90 changes: 52 additions & 38 deletions HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@
import org.jackhuang.hmcl.task.AsyncTaskExecutor;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.upgrade.UpdateChecker;
import org.jackhuang.hmcl.upgrade.UpdateHandler;
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.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;
Expand Down Expand Up @@ -71,42 +73,7 @@ public void start(Stage primaryStage) {

CookieHandler.setDefault(COOKIE_MANAGER);

Skin.registerDefaultSkinLoader((type) -> {
switch (type) {
case ALEX:
return Skin.class.getResourceAsStream("/assets/img/skin/alex.png");
case ARI:
return Skin.class.getResourceAsStream("/assets/img/skin/ari.png");
case EFE:
return Skin.class.getResourceAsStream("/assets/img/skin/efe.png");
case KAI:
return Skin.class.getResourceAsStream("/assets/img/skin/kai.png");
case MAKENA:
return Skin.class.getResourceAsStream("/assets/img/skin/makena.png");
case NOOR:
return Skin.class.getResourceAsStream("/assets/img/skin/noor.png");
case STEVE:
return Skin.class.getResourceAsStream("/assets/img/skin/steve.png");
case SUNNY:
return Skin.class.getResourceAsStream("/assets/img/skin/sunny.png");
case ZURI:
return Skin.class.getResourceAsStream("/assets/img/skin/zuri.png");
default:
return null;
}
});

RemoteMod.registerEmptyRemoteMod(new RemoteMod("", "", i18n("mods.broken_dependency.title"), i18n("mods.broken_dependency.desc"), new ArrayList<>(), "", "/assets/img/icon.png", new RemoteMod.IMod() {
@Override
public List<RemoteMod> loadDependencies(RemoteModRepository modRepository) throws IOException {
throw new IOException();
}

@Override
public Stream<RemoteMod.Version> loadVersions(RemoteModRepository modRepository) throws IOException {
throw new IOException();
}
}));
register();

LOG.info("JavaFX Version: " + System.getProperty("javafx.runtime.version"));
try {
Expand Down Expand Up @@ -156,13 +123,56 @@ public Stream<RemoteMod.Version> loadVersions(RemoteModRepository modRepository)

UpdateChecker.init();

RemoteResourceManager.init();

RemoteResourceManager.register();

primaryStage.show();
});
} catch (Throwable e) {
CRASH_REPORTER.uncaughtException(Thread.currentThread(), e);
}
}

private static void register() {
Skin.registerDefaultSkinLoader((type) -> {
switch (type) {
case ALEX:
return Skin.class.getResourceAsStream("/assets/img/skin/alex.png");
case ARI:
return Skin.class.getResourceAsStream("/assets/img/skin/ari.png");
case EFE:
return Skin.class.getResourceAsStream("/assets/img/skin/efe.png");
case KAI:
return Skin.class.getResourceAsStream("/assets/img/skin/kai.png");
case MAKENA:
return Skin.class.getResourceAsStream("/assets/img/skin/makena.png");
case NOOR:
return Skin.class.getResourceAsStream("/assets/img/skin/noor.png");
case STEVE:
return Skin.class.getResourceAsStream("/assets/img/skin/steve.png");
case SUNNY:
return Skin.class.getResourceAsStream("/assets/img/skin/sunny.png");
case ZURI:
return Skin.class.getResourceAsStream("/assets/img/skin/zuri.png");
default:
return null;
}
});

RemoteMod.registerEmptyRemoteMod(new RemoteMod("", "", i18n("mods.broken_dependency.title"), i18n("mods.broken_dependency.desc"), new ArrayList<>(), "", "/assets/img/[email protected]", new RemoteMod.IMod() {
@Override
public List<RemoteMod> loadDependencies(RemoteModRepository modRepository) throws IOException {
throw new IOException();
}

@Override
public Stream<RemoteMod.Version> loadVersions(RemoteModRepository modRepository) throws IOException {
throw new IOException();
}
}));
}

private static ButtonType showAlert(AlertType alertType, String contentText, ButtonType... buttons) {
return new Alert(alertType, contentText, buttons).showAndWait().orElse(null);
}
Expand Down Expand Up @@ -283,6 +293,10 @@ public static void main(String[] args) {
if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX)
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);
Expand Down
4 changes: 3 additions & 1 deletion HMCL/src/main/java/org/jackhuang/hmcl/Metadata.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ private Metadata() {}
public static final String TITLE = NAME + " " + VERSION;
public static final String FULL_TITLE = FULL_NAME + " v" + VERSION;

public static final String UPDATE_URL = System.getProperty("hmcl.update_source.override", "https://hmcl.huangyuhui.net/api/update_link");
// 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 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/";
Expand Down
20 changes: 10 additions & 10 deletions HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
import java.util.stream.Stream;

import static org.jackhuang.hmcl.setting.ConfigHolder.config;
import static org.jackhuang.hmcl.ui.FXUtils.newImage;
import static org.jackhuang.hmcl.ui.FXUtils.newBuiltinImage;
import static org.jackhuang.hmcl.util.Logging.LOG;
import static org.jackhuang.hmcl.util.Pair.pair;

Expand Down Expand Up @@ -263,7 +263,7 @@ public File getVersionIconFile(String id) {

public Image getVersionIconImage(String id) {
if (id == null || !isLoaded())
return newImage("/assets/img/grass.png");
return newBuiltinImage("/assets/img/grass.png");

VersionSetting vs = getLocalVersionSettingOrCreate(id);
VersionIconType iconType = Optional.ofNullable(vs).map(VersionSetting::getVersionIcon).orElse(VersionIconType.DEFAULT);
Expand All @@ -276,21 +276,21 @@ public Image getVersionIconImage(String id) {
else if (LibraryAnalyzer.isModded(this, version)) {
LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version);
if (libraryAnalyzer.has(LibraryAnalyzer.LibraryType.FABRIC))
return newImage("/assets/img/fabric.png");
return newBuiltinImage("/assets/img/fabric.png");
else if (libraryAnalyzer.has(LibraryAnalyzer.LibraryType.FORGE))
return newImage("/assets/img/forge.png");
return newBuiltinImage("/assets/img/forge.png");
else if (libraryAnalyzer.has(LibraryAnalyzer.LibraryType.QUILT))
return newImage("/assets/img/quilt.png");
return newBuiltinImage("/assets/img/quilt.png");
else if (libraryAnalyzer.has(LibraryAnalyzer.LibraryType.OPTIFINE))
return newImage("/assets/img/command.png");
return newBuiltinImage("/assets/img/command.png");
else if (libraryAnalyzer.has(LibraryAnalyzer.LibraryType.LITELOADER))
return newImage("/assets/img/chicken.png");
return newBuiltinImage("/assets/img/chicken.png");
else
return newImage("/assets/img/furnace.png");
return newBuiltinImage("/assets/img/furnace.png");
} else
return newImage("/assets/img/grass.png");
return newBuiltinImage("/assets/img/grass.png");
} else {
return newImage(iconType.getResourceUrl());
return newBuiltinImage(iconType.getResourceUrl());
}
}

Expand Down
Loading

0 comments on commit 242df8a

Please sign in to comment.