From cdc3e18dcbea6758966915c14168958e90e7b071 Mon Sep 17 00:00:00 2001 From: burningtnt Date: Fri, 19 Dec 2025 22:46:51 +0800 Subject: [PATCH 1/5] [WIP] Fix #4977 --- .../hmcl/ui/versions/DownloadPage.java | 53 +++++++++++++++---- .../org/jackhuang/hmcl/mod/RemoteMod.java | 2 +- .../hmcl/mod/RemoteModRepository.java | 4 ++ .../modrinth/ModrinthRemoteModRepository.java | 26 ++++++++- 4 files changed, 73 insertions(+), 12 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java index db518f3896..cd8260faec 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java @@ -27,9 +27,18 @@ import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Node; -import javafx.scene.control.*; +import javafx.scene.control.Control; +import javafx.scene.control.Label; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.Skin; +import javafx.scene.control.SkinBase; import javafx.scene.image.ImageView; -import javafx.scene.layout.*; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; import javafx.stage.FileChooser; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.game.HMCLGameRepository; @@ -44,9 +53,18 @@ import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; -import org.jackhuang.hmcl.ui.construct.*; +import org.jackhuang.hmcl.ui.construct.ComponentList; +import org.jackhuang.hmcl.ui.construct.DialogCloseEvent; +import org.jackhuang.hmcl.ui.construct.JFXHyperlink; +import org.jackhuang.hmcl.ui.construct.RipplerContainer; +import org.jackhuang.hmcl.ui.construct.SpinnerPane; +import org.jackhuang.hmcl.ui.construct.TwoLineListItem; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; -import org.jackhuang.hmcl.util.*; +import org.jackhuang.hmcl.util.Lang; +import org.jackhuang.hmcl.util.Pair; +import org.jackhuang.hmcl.util.SimpleMultimap; +import org.jackhuang.hmcl.util.StringUtils; +import org.jackhuang.hmcl.util.TaskCancellationAction; import org.jackhuang.hmcl.util.i18n.I18n; import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.javafx.BindingMapping; @@ -54,7 +72,14 @@ import org.jetbrains.annotations.Nullable; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -524,8 +549,10 @@ public ModVersion(RemoteMod.Version version, DownloadPage selfPage) { private void loadDependencies(RemoteMod.Version version, DownloadPage selfPage, SpinnerPane spinnerPane, ComponentList dependenciesList) { spinnerPane.setLoading(true); - Task.supplyAsync(() -> { + Task.composeAsync(() -> { + // TODO: Massive tasks may cause OOM. EnumMap> dependencies = new EnumMap<>(RemoteMod.DependencyType.class); + List> queue = new ArrayList<>(version.getDependencies().size()); for (RemoteMod.Dependency dependency : version.getDependencies()) { if (dependency.getType() == RemoteMod.DependencyType.INCOMPATIBLE || dependency.getType() == RemoteMod.DependencyType.BROKEN) { continue; @@ -538,11 +565,19 @@ private void loadDependencies(RemoteMod.Version version, DownloadPage selfPage, list.add(title); dependencies.put(dependency.getType(), list); } - DependencyModItem dependencyModItem = new DependencyModItem(selfPage.page, dependency.load(), selfPage.version, selfPage.callback); - dependencies.get(dependency.getType()).add(dependencyModItem); + + queue.add(Task.supplyAsync(dependency::load) + .setSignificance(Task.TaskSignificance.MINOR) + .thenAcceptAsync(Schedulers.javafx(), dep -> { + DependencyModItem dependencyModItem = new DependencyModItem(selfPage.page, dep, selfPage.version, selfPage.callback); + dependencies.get(dependency.getType()).add(dependencyModItem); + }) + .setSignificance(Task.TaskSignificance.MINOR)); } - return dependencies.values().stream().flatMap(Collection::stream).collect(Collectors.toList()); + return Task.allOf(queue).thenSupplyAsync(() -> + dependencies.values().stream().flatMap(Collection::stream).collect(Collectors.toList()) + ); }).whenComplete(Schedulers.javafx(), (result, exception) -> { spinnerPane.setLoading(false); if (exception == null) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/RemoteMod.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/RemoteMod.java index a936f887be..ce7c56e235 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/RemoteMod.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/RemoteMod.java @@ -159,7 +159,7 @@ public RemoteMod load() throws IOException { if (this.type == DependencyType.BROKEN) { this.remoteMod = RemoteMod.BROKEN; } else { - this.remoteMod = this.remoteModRepository.getModById(this.id); + this.remoteMod = this.remoteModRepository.resolveDependency(this.id); } } return this.remoteMod; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/RemoteModRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/RemoteModRepository.java index 5f74ba7d49..27c16e0179 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/RemoteModRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/RemoteModRepository.java @@ -91,6 +91,10 @@ SearchResult search(DownloadProvider downloadProvider, String gameVersion, @Null RemoteMod getModById(String id) throws IOException; + default RemoteMod resolveDependency(String id) throws IOException { + return getModById(id); + } + RemoteMod.File getModFile(String modId, String fileId) throws IOException; Stream getRemoteVersionsById(String id) throws IOException; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthRemoteModRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthRemoteModRepository.java index 4b60bf3cdd..9a4e8cac12 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthRemoteModRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthRemoteModRepository.java @@ -24,7 +24,11 @@ import org.jackhuang.hmcl.mod.ModLoaderType; import org.jackhuang.hmcl.mod.RemoteMod; import org.jackhuang.hmcl.mod.RemoteModRepository; -import org.jackhuang.hmcl.util.*; +import org.jackhuang.hmcl.util.DigestUtils; +import org.jackhuang.hmcl.util.Immutable; +import org.jackhuang.hmcl.util.Lang; +import org.jackhuang.hmcl.util.Pair; +import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.HttpRequest; import org.jackhuang.hmcl.util.io.NetworkUtils; @@ -35,7 +39,13 @@ import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.time.Instant; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -128,6 +138,18 @@ public RemoteMod getModById(String id) throws IOException { return project.toMod(); } + @Override + public RemoteMod resolveDependency(String id) throws IOException { + try { + return getModById(id); + } catch (ResponseCodeException e) { + if (e.getResponseCode() == 502) { + return RemoteMod.BROKEN; + } + throw e; + } + } + @Override public RemoteMod.File getModFile(String modId, String fileId) throws IOException { throw new UnsupportedOperationException(); From 4492be26cedd8cfc7d420063d24cf88a6b0514e6 Mon Sep 17 00:00:00 2001 From: burningtnt Date: Sat, 20 Dec 2025 20:32:25 +0800 Subject: [PATCH 2/5] Fix #4977 --- .../hmcl/mod/modrinth/ModrinthRemoteModRepository.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthRemoteModRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthRemoteModRepository.java index 9a4e8cac12..76a0c15215 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthRemoteModRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthRemoteModRepository.java @@ -35,6 +35,7 @@ import org.jackhuang.hmcl.util.io.ResponseCodeException; import org.jetbrains.annotations.Nullable; +import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.NoSuchFileException; import java.nio.file.Path; @@ -147,6 +148,8 @@ public RemoteMod resolveDependency(String id) throws IOException { return RemoteMod.BROKEN; } throw e; + } catch (FileNotFoundException e) { + return RemoteMod.BROKEN; } } From f03d8110c025b2dd316c18689d08fd29cfbe0e02 Mon Sep 17 00:00:00 2001 From: burningtnt Date: Sat, 20 Dec 2025 20:52:11 +0800 Subject: [PATCH 3/5] Dispatch IO to virtual thread. --- .../main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java index cd8260faec..5ab8c47485 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java @@ -566,7 +566,7 @@ private void loadDependencies(RemoteMod.Version version, DownloadPage selfPage, dependencies.put(dependency.getType(), list); } - queue.add(Task.supplyAsync(dependency::load) + queue.add(Task.supplyAsync(Schedulers.io(), dependency::load) .setSignificance(Task.TaskSignificance.MINOR) .thenAcceptAsync(Schedulers.javafx(), dep -> { DependencyModItem dependencyModItem = new DependencyModItem(selfPage.page, dep, selfPage.version, selfPage.callback); From 941a435485fd12661398875d18db677f219f7a7d Mon Sep 17 00:00:00 2001 From: burningtnt Date: Sat, 20 Dec 2025 20:57:40 +0800 Subject: [PATCH 4/5] Fix. --- .../hmcl/mod/modrinth/ModrinthRemoteModRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthRemoteModRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthRemoteModRepository.java index 76a0c15215..5204d7bdcf 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthRemoteModRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthRemoteModRepository.java @@ -144,7 +144,7 @@ public RemoteMod resolveDependency(String id) throws IOException { try { return getModById(id); } catch (ResponseCodeException e) { - if (e.getResponseCode() == 502) { + if (e.getResponseCode() == 502 || e.getResponseCode() == 404) { return RemoteMod.BROKEN; } throw e; From c50fc896d228b0ed0a7b1a5a4e6a572b70923e90 Mon Sep 17 00:00:00 2001 From: burningtnt Date: Sat, 20 Dec 2025 22:02:21 +0800 Subject: [PATCH 5/5] Fix. --- .../main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java index 5ab8c47485..5f661064c8 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java @@ -569,6 +569,9 @@ private void loadDependencies(RemoteMod.Version version, DownloadPage selfPage, queue.add(Task.supplyAsync(Schedulers.io(), dependency::load) .setSignificance(Task.TaskSignificance.MINOR) .thenAcceptAsync(Schedulers.javafx(), dep -> { + if (dep == RemoteMod.BROKEN) { + return; + } DependencyModItem dependencyModItem = new DependencyModItem(selfPage.page, dep, selfPage.version, selfPage.callback); dependencies.get(dependency.getType()).add(dependencyModItem); })