|
20 | 20 | import me.itzg.helpers.curseforge.model.CurseForgeFile; |
21 | 21 | import me.itzg.helpers.curseforge.model.CurseForgeMod; |
22 | 22 | import me.itzg.helpers.curseforge.model.FileDependency; |
| 23 | +import me.itzg.helpers.curseforge.model.FileRelationType; |
23 | 24 | import me.itzg.helpers.curseforge.model.ModLoaderType; |
24 | 25 | import me.itzg.helpers.errors.GenericException; |
25 | 26 | import me.itzg.helpers.errors.InvalidParameterException; |
|
34 | 35 | import reactor.core.publisher.Flux; |
35 | 36 | import reactor.core.publisher.Mono; |
36 | 37 | import reactor.core.scheduler.Schedulers; |
| 38 | +import reactor.util.function.Tuple2; |
| 39 | +import reactor.util.function.Tuples; |
37 | 40 |
|
38 | 41 | @Command(name = "curseforge-files", description = "Download and manage individual mod/plugin files from CurseForge") |
39 | 42 | @Slf4j |
@@ -168,31 +171,70 @@ private Mono<List<FileEntry>> processModFileRefs(CategoryInfo categoryInfo, |
168 | 171 |
|
169 | 172 | return Flux.fromIterable(modFiles) |
170 | 173 | .flatMap(modFile -> { |
171 | | - for (final FileDependency dependency : modFile.getDependencies()) { |
172 | | - if (!requestedModIds.contains(dependency.getModId())) { |
173 | | - log.warn("The mod file {} depends on mod project ID {}, but that is not listed", |
174 | | - modFile.getDisplayName(), dependency.getModId() |
175 | | - ); |
176 | | - } |
177 | | - } |
| 174 | + |
| 175 | + final Mono<FileEntry> retrieval; |
178 | 176 | final ModFileIds modFileIds = idsFrom(modFile); |
179 | 177 | final FileEntry entry = previousFiles.get(modFileIds); |
180 | 178 | if (entry != null |
181 | 179 | && Files.exists(outputDir.resolve(entry.getFilePath())) |
182 | 180 | ) { |
183 | 181 | log.debug("Mod file {} already exists at {}", modFile.getFileName(), entry.getFilePath()); |
184 | | - return Mono.just(entry); |
| 182 | + retrieval = Mono.just(entry); |
185 | 183 | } |
186 | 184 | else { |
187 | | - return retrieveModFile(apiClient, categoryInfo, modFile) |
| 185 | + retrieval = retrieveModFile(apiClient, categoryInfo, modFile) |
188 | 186 | .map(path -> new FileEntry(modFileIds, outputDir.relativize(path).toString())); |
189 | 187 | } |
| 188 | + |
| 189 | + return reportMissingDependencies(apiClient, modFile, requestedModIds) |
| 190 | + .then(retrieval); |
190 | 191 | }); |
191 | 192 | } |
192 | 193 | ) |
193 | 194 | .collectList(); |
194 | 195 | } |
195 | 196 |
|
| 197 | + /** |
| 198 | + * |
| 199 | + * @return flux of missing, required dependencies |
| 200 | + */ |
| 201 | + private static Flux<Tuple2<CurseForgeMod, FileDependency>> reportMissingDependencies(CurseForgeApiClient apiClient, |
| 202 | + CurseForgeFile modFile, Set<Integer> requestedModIds |
| 203 | + ) { |
| 204 | + return Flux.fromIterable(modFile.getDependencies()) |
| 205 | + .mapNotNull(dependency -> { |
| 206 | + if (!requestedModIds.contains(dependency.getModId())) { |
| 207 | + switch (dependency.getRelationType()) { |
| 208 | + case RequiredDependency: |
| 209 | + case OptionalDependency: |
| 210 | + return dependency; |
| 211 | + default: |
| 212 | + return null; |
| 213 | + } |
| 214 | + } |
| 215 | + else { |
| 216 | + return null; |
| 217 | + } |
| 218 | + }) |
| 219 | + .flatMap(dependency -> |
| 220 | + apiClient.getModInfo(dependency.getModId()) |
| 221 | + .map(curseForgeMod -> Tuples.of(curseForgeMod, dependency)) |
| 222 | + ) |
| 223 | + .doOnNext(missingDep -> { |
| 224 | + if (missingDep.getT2().getRelationType() == FileRelationType.RequiredDependency) { |
| 225 | + log.warn("The mod file '{}' depends on mod project '{}' at {}, but it is not listed", |
| 226 | + modFile.getDisplayName(), missingDep.getT1().getName(), missingDep.getT1().getLinks().getWebsiteUrl() |
| 227 | + ); |
| 228 | + } |
| 229 | + else if (missingDep.getT2().getRelationType() == FileRelationType.OptionalDependency) { |
| 230 | + log.debug("The mod file '{}' optionally depends on mod project '{}', but it is not listed", |
| 231 | + modFile.getDisplayName(), missingDep.getT1().getName() |
| 232 | + ); |
| 233 | + } |
| 234 | + }) |
| 235 | + .filter(missingDep -> missingDep.getT2().getRelationType() == FileRelationType.RequiredDependency); |
| 236 | + } |
| 237 | + |
196 | 238 | @NotNull |
197 | 239 | private static Map<ModFileIds, FileEntry> buildPreviousFilesFromManifest(CurseForgeFilesManifest oldManifest) { |
198 | 240 | return oldManifest != null ? |
|
0 commit comments