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..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 @@ -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,22 @@ 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(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); + }) + .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..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 @@ -24,18 +24,29 @@ 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; 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; 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 +139,20 @@ 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 || e.getResponseCode() == 404) { + return RemoteMod.BROKEN; + } + throw e; + } catch (FileNotFoundException e) { + return RemoteMod.BROKEN; + } + } + @Override public RemoteMod.File getModFile(String modId, String fileId) throws IOException { throw new UnsupportedOperationException();