From 1e731c1e6f97f46473e96af84038fc63f28195b6 Mon Sep 17 00:00:00 2001 From: Geoff Bourne Date: Sat, 13 Jul 2024 10:19:54 -0500 Subject: [PATCH] cf: check downloads repo before attempting download of mod in modpack --- .../curseforge/CurseForgeApiClient.java | 19 +++++++------ .../curseforge/CurseForgeInstaller.java | 28 ++++++++++++------- .../helpers/curseforge/FileHashVerifier.java | 11 +++++++- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/src/main/java/me/itzg/helpers/curseforge/CurseForgeApiClient.java b/src/main/java/me/itzg/helpers/curseforge/CurseForgeApiClient.java index 3d8a0b96..622fc5d3 100644 --- a/src/main/java/me/itzg/helpers/curseforge/CurseForgeApiClient.java +++ b/src/main/java/me/itzg/helpers/curseforge/CurseForgeApiClient.java @@ -218,18 +218,19 @@ public Mono getModFileInfo( } return e; }) - .map(GetModFileResponse::getData); + .map(GetModFileResponse::getData) + .checkpoint(); } public Mono download(CurseForgeFile cfFile, Path outputFile, FileDownloadStatusHandler handler) { - return preparedFetch.fetch( - cfFile.getDownloadUrl() != null ? - normalizeDownloadUrl(cfFile.getDownloadUrl()) - : downloadFallbackUriBuilder.resolve( - "/v1/mods/{modId}/files/{fileId}/download", - cfFile.getModId(), cfFile.getId() - ) - ) + final URI uriToDownload = cfFile.getDownloadUrl() != null ? + normalizeDownloadUrl(cfFile.getDownloadUrl()) + : downloadFallbackUriBuilder.resolve( + "/v1/mods/{modId}/files/{fileId}/download", + cfFile.getModId(), cfFile.getId() + ); + log.trace("Downloading cfFile={} from normalizedUrl={}", cfFile, uriToDownload); + return preparedFetch.fetch(uriToDownload) .toFile(outputFile) .skipExisting(true) .handleStatus(handler) diff --git a/src/main/java/me/itzg/helpers/curseforge/CurseForgeInstaller.java b/src/main/java/me/itzg/helpers/curseforge/CurseForgeInstaller.java index a173e7aa..6e48df88 100644 --- a/src/main/java/me/itzg/helpers/curseforge/CurseForgeInstaller.java +++ b/src/main/java/me/itzg/helpers/curseforge/CurseForgeInstaller.java @@ -56,6 +56,7 @@ import me.itzg.helpers.json.ObjectMappers; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipFile; +import org.jetbrains.annotations.Nullable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; @@ -435,17 +436,19 @@ private Path locateWorldZipInRepo(String fileName) { return locateFileIn(fileName, downloadsRepo, downloadsRepo.resolve(REPO_SUBDIR_WORLDS)); } - private static Path locateFileIn(String fileName, Path... dirs) { + private static Path locateFileIn(String fileName, @Nullable Path... dirs) { for (Path dir : dirs) { - final Path resolved = dir.resolve(fileName); - if (Files.exists(resolved)) { - return resolved; - } + if (dir != null) { + final Path resolved = dir.resolve(fileName); + if (Files.exists(resolved)) { + return resolved; + } - // When downloading, the browser may replace spaces with +'s - final Path altResolved = dir.resolve(fileName.replace(' ', '+')); - if (Files.exists(altResolved)) { - return altResolved; + // When downloading, the browser may replace spaces with +'s + final Path altResolved = dir.resolve(fileName.replace(' ', '+')); + if (Files.exists(altResolved)) { + return altResolved; + } } } return null; @@ -581,6 +584,7 @@ private ModPackResults processModpack(InstallContext context, excludeIncludeIds.getForceIncludeIds(), context.categoryInfo ) + .checkpoint() ) .collectList() .block(); @@ -741,6 +745,7 @@ else if (category.getSlug().equals("worlds")) { final Mono resolvedFileMono = Mono.defer(() -> downloadOrResolveFile(context, modInfo, isWorld, outputDir, cfFile) + .checkpoint() ) // retry the deferred part above if one of the expected failure cases .retryWhen( @@ -795,7 +800,10 @@ private Mono downloadOrResolveFile(InstallContext context, CurseF // Will try to locate an existing file by alternate names that browser might create, // but only for non-world files of the modpack - final Path locatedFile = !isWorld ? locateFileIn(cfFile.getFileName(), outputDir) : null; + final Path locatedFile = !isWorld ? locateFileIn(cfFile.getFileName(), + outputDir, + downloadsRepo + ) : null; if (locatedFile != null) { log.info("Mod file {} already exists", locatedFile); diff --git a/src/main/java/me/itzg/helpers/curseforge/FileHashVerifier.java b/src/main/java/me/itzg/helpers/curseforge/FileHashVerifier.java index 1e9552d3..7c382a6e 100644 --- a/src/main/java/me/itzg/helpers/curseforge/FileHashVerifier.java +++ b/src/main/java/me/itzg/helpers/curseforge/FileHashVerifier.java @@ -5,6 +5,7 @@ import java.util.EnumMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import me.itzg.helpers.curseforge.model.FileHash; import me.itzg.helpers.curseforge.model.HashAlgo; @@ -24,6 +25,10 @@ public class FileHashVerifier { } public static Mono verify(Path file, List hashes) { + if (hashes.isEmpty()) { + return Mono.just(file); + } + for (final FileHash hash : hashes) { final ChecksumAlgo checksumAlgo = algos.get(hash.getAlgo()); if (checksumAlgo != null) { @@ -42,6 +47,10 @@ public static Mono verify(Path file, List hashes) { } } - return Mono.error(new IllegalArgumentException("Unable to find compatible checksum algorithm")); + return Mono.error(new IllegalArgumentException("Unable to find compatible checksum algorithm from " + + hashes.stream() + .map(FileHash::getAlgo) + .collect(Collectors.toList()) + )); } }