Fix #4977 Modrinth 依赖可以指向已经被删除的项目 (#5012)

This commit is contained in:
Burning_TNT
2026-02-06 22:56:44 +08:00
committed by GitHub
parent 570e1e2029
commit 5d57052ea2
4 changed files with 61 additions and 9 deletions

View File

@@ -27,9 +27,18 @@ import javafx.beans.property.SimpleBooleanProperty;
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;
@@ -46,7 +55,11 @@ import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.construct.*;
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 +67,14 @@ import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
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;
@@ -518,8 +538,10 @@ public class DownloadPage extends Control implements DecoratorPage {
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<RemoteMod.DependencyType, List<Node>> dependencies = new EnumMap<>(RemoteMod.DependencyType.class);
List<Task<?>> queue = new ArrayList<>(version.getDependencies().size());
for (RemoteMod.Dependency dependency : version.getDependencies()) {
if (dependency.getType() == RemoteMod.DependencyType.INCOMPATIBLE || dependency.getType() == RemoteMod.DependencyType.BROKEN) {
continue;
@@ -532,11 +554,22 @@ public class DownloadPage extends Control implements DecoratorPage {
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) {

View File

@@ -159,7 +159,7 @@ public final class RemoteMod {
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;

View File

@@ -92,6 +92,10 @@ public interface RemoteModRepository {
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<RemoteMod.Version> getRemoteVersionsById(String id) throws IOException;

View File

@@ -35,6 +35,7 @@ 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;
@@ -154,6 +155,20 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
}
}
@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();