26
26
import me .itzg .helpers .http .Fetch ;
27
27
import me .itzg .helpers .http .SharedFetchArgs ;
28
28
import me .itzg .helpers .json .ObjectMappers ;
29
- import me .itzg .helpers .modrinth .model .Constants ;
30
29
import me .itzg .helpers .modrinth .model .DependencyType ;
31
30
import me .itzg .helpers .modrinth .model .Project ;
32
31
import me .itzg .helpers .modrinth .model .ProjectType ;
44
43
public class ModrinthCommand implements Callable <Integer > {
45
44
46
45
public static final String DATAPACKS_SUBDIR = "datapacks" ;
47
- @ Option (names = "--projects" , description = "Project ID or Slug" ,
48
- split = SPLIT_COMMA_NL , splitSynopsisLabel = SPLIT_SYNOPSIS_COMMA_NL ,
49
- paramLabel = "id|slug"
46
+
47
+ @ Option (
48
+ names = "--projects" ,
49
+ description = "Project ID or Slug. Prefix with loader: e.g. fabric:project-id" ,
50
+ split = SPLIT_COMMA_NL ,
51
+ splitSynopsisLabel = SPLIT_SYNOPSIS_COMMA_NL ,
52
+ paramLabel = "[loader:]id|slug"
50
53
)
51
54
List <String > projects ;
52
55
@@ -75,7 +78,7 @@ public enum DownloadDependencies {
75
78
/**
76
79
* Implies {@link #REQUIRED}
77
80
*/
78
- OPTIONAL
81
+ OPTIONAL ,
79
82
}
80
83
81
84
@ Option (names = "--allowed-version-type" , defaultValue = "release" , description = "Valid values: ${COMPLETION-CANDIDATES}" )
@@ -128,9 +131,13 @@ private List<Path> processProjects(List<String> projects) {
128
131
.defaultIfEmpty (Collections .emptyList ())
129
132
.block ()
130
133
.stream ()
131
- .flatMap (resolvedProject -> processProject (
132
- modrinthApiClient , resolvedProject .getProjectRef (), resolvedProject .getProject ()
133
- ))
134
+ .flatMap (resolvedProject ->
135
+ processProject (
136
+ modrinthApiClient ,
137
+ resolvedProject .getProjectRef (),
138
+ resolvedProject .getProject ()
139
+ )
140
+ )
134
141
.collect (Collectors .toList ());
135
142
}
136
143
}
@@ -142,9 +149,9 @@ private ModrinthManifest loadManifest() throws IOException {
142
149
final ObjectMapper objectMapper = ObjectMappers .defaultMapper ();
143
150
144
151
final LegacyModrinthManifest legacyManifest = objectMapper .readValue (
145
- legacyManifestPath .toFile (),
146
- LegacyModrinthManifest .class
147
- );
152
+ legacyManifestPath .toFile (),
153
+ LegacyModrinthManifest .class
154
+ );
148
155
149
156
Files .delete (legacyManifestPath );
150
157
@@ -157,7 +164,13 @@ private ModrinthManifest loadManifest() throws IOException {
157
164
return Manifests .load (outputDirectory , ModrinthManifest .ID , ModrinthManifest .class );
158
165
}
159
166
160
- private Stream <Version > expandDependencies (ModrinthApiClient modrinthApiClient , Project project , Version version ) {
167
+ private Stream <Version > expandDependencies (
168
+ ModrinthApiClient modrinthApiClient ,
169
+ Loader loader ,
170
+ String gameVersion ,
171
+ Project project ,
172
+ Version version
173
+ ) {
161
174
log .debug ("Expanding dependencies of version={}" , version );
162
175
return version .getDependencies ().stream ()
163
176
.filter (this ::filterDependency )
@@ -170,7 +183,7 @@ private Stream<Version> expandDependencies(ModrinthApiClient modrinthApiClient,
170
183
if (dep .getVersionId () == null ) {
171
184
log .debug ("Fetching versions of dep={} and picking" , dep );
172
185
depVersion = pickVersion (
173
- getVersionsForProject (modrinthApiClient , dep .getProjectId ())
186
+ getVersionsForProject (modrinthApiClient , dep .getProjectId (), loader , gameVersion )
174
187
);
175
188
}
176
189
else {
@@ -192,8 +205,8 @@ private Stream<Version> expandDependencies(ModrinthApiClient modrinthApiClient,
192
205
if (depVersion != null ) {
193
206
log .debug ("Resolved version={} for dep={}" , depVersion .getVersionNumber (), dep );
194
207
return Stream .concat (
195
- Stream .of (depVersion ),
196
- expandDependencies (modrinthApiClient , project , depVersion )
208
+ Stream .of (depVersion ),
209
+ expandDependencies (modrinthApiClient , loader , gameVersion , project , depVersion )
197
210
)
198
211
.peek (expandedVer -> log .debug ("Expanded dependency={} into version={}" , dep , expandedVer ));
199
212
}
@@ -229,16 +242,14 @@ private Version pickVersion(List<Version> versions, VersionType versionType) {
229
242
return null ;
230
243
}
231
244
232
- private Path download (boolean isDatapack , VersionFile versionFile ) {
245
+ private Path download (Loader loader , VersionFile versionFile ) {
233
246
final Path outPath ;
234
247
try {
235
- if (!isDatapack ) {
236
- outPath = Files .createDirectories (outputDirectory
237
- .resolve (loader .getType ())
238
- )
239
- .resolve (versionFile .getFilename ());
240
- }
241
- else {
248
+ final Loader effectiveLoader = loader != null ? loader : this .loader ;
249
+ final String outputType = effectiveLoader .getType ();
250
+
251
+ if (outputType == null ) {
252
+ // Datapack case
242
253
if (worldDirectory .isAbsolute ()) {
243
254
outPath = Files .createDirectories (worldDirectory
244
255
.resolve (DATAPACKS_SUBDIR )
@@ -253,9 +264,15 @@ private Path download(boolean isDatapack, VersionFile versionFile) {
253
264
.resolve (versionFile .getFilename ());
254
265
}
255
266
}
267
+ else {
268
+ outPath = Files .createDirectories (outputDirectory
269
+ .resolve (outputType )
270
+ )
271
+ .resolve (versionFile .getFilename ());
272
+ }
256
273
257
274
} catch (IOException e ) {
258
- throw new RuntimeException ("Creating mods directory" , e );
275
+ throw new RuntimeException ("Creating output directory" , e );
259
276
}
260
277
261
278
try {
@@ -267,11 +284,11 @@ private Path download(boolean isDatapack, VersionFile versionFile) {
267
284
.handleStatus (Fetch .loggingDownloadStatusHandler (log ))
268
285
.execute ();
269
286
} catch (IOException e ) {
270
- throw new RuntimeException ("Downloading mod file" , e );
287
+ throw new RuntimeException ("Downloading file" , e );
271
288
}
272
289
}
273
290
274
- private List <Version > getVersionsForProject (ModrinthApiClient modrinthApiClient , String project ) {
291
+ private List <Version > getVersionsForProject (ModrinthApiClient modrinthApiClient , String project , Loader loader , String gameVersion ) {
275
292
final List <Version > versions = modrinthApiClient .getVersionsForProject (
276
293
project , loader , gameVersion
277
294
)
@@ -294,10 +311,19 @@ private Stream<Path> processProject(ModrinthApiClient modrinthApiClient, Project
294
311
log .debug ("Starting with project='{}' slug={}" , project .getTitle (), project .getSlug ());
295
312
296
313
if (projectsProcessed .add (project .getId ())) {
314
+ final Loader effectiveLoader = projectRef .getLoader () != null
315
+ ? projectRef .getLoader ()
316
+ : this .loader ;
317
+
297
318
final Version version ;
298
319
try {
299
- version = modrinthApiClient .resolveProjectVersion (
300
- project , projectRef , loader , gameVersion , defaultVersionType
320
+ version = modrinthApiClient
321
+ .resolveProjectVersion (
322
+ project ,
323
+ projectRef ,
324
+ effectiveLoader ,
325
+ gameVersion ,
326
+ defaultVersionType
301
327
)
302
328
.block ();
303
329
} catch (NoApplicableVersionsException | NoFilesAvailableException e ) {
@@ -306,36 +332,46 @@ private Stream<Path> processProject(ModrinthApiClient modrinthApiClient, Project
306
332
307
333
if (version != null ) {
308
334
if (version .getFiles ().isEmpty ()) {
309
- throw new GenericException (String .format ("Project %s has no files declared" , project .getSlug ()));
335
+ throw new GenericException (
336
+ String .format (
337
+ "Project %s has no files declared" ,
338
+ project .getSlug ()
339
+ )
340
+ );
310
341
}
311
342
312
- final boolean isDatapack = isDatapack (version );
313
-
314
343
return Stream .concat (
315
- Stream .of (version ),
316
- expandDependencies (modrinthApiClient , project , version )
344
+ Stream .of (version ),
345
+ expandDependencies (
346
+ modrinthApiClient ,
347
+ effectiveLoader ,
348
+ gameVersion ,
349
+ project ,
350
+ version
317
351
)
352
+ )
318
353
.map (ModrinthApiClient ::pickVersionFile )
319
- .map (versionFile -> download (isDatapack , versionFile ))
320
- .flatMap (downloadedFile -> !isDatapack ? expandIfZip (downloadedFile ) : Stream .empty ());
321
- }
322
- else {
354
+ .map (versionFile -> download (effectiveLoader , versionFile ))
355
+ .flatMap (downloadedFile -> {
356
+ // Only expand ZIPs for non-datapack loaders
357
+ return effectiveLoader == Loader .datapack
358
+ ? Stream .of (downloadedFile )
359
+ : expandIfZip (downloadedFile );
360
+ });
361
+ } else {
323
362
throw new InvalidParameterException (
324
- String .format ("Project %s does not have any matching versions for loader %s, game version %s" ,
325
- projectRef , loader , gameVersion
326
- ));
363
+ String .format (
364
+ "Project %s does not have any matching versions for loader %s, game version %s" ,
365
+ projectRef ,
366
+ effectiveLoader ,
367
+ gameVersion
368
+ )
369
+ );
327
370
}
328
371
}
329
372
return Stream .empty ();
330
373
}
331
374
332
- private boolean isDatapack (Version version ) {
333
- return
334
- version .getLoaders () != null
335
- && version .getLoaders ().size () == 1
336
- && version .getLoaders ().get (0 ).equals (Constants .LOADER_DATAPACK );
337
- }
338
-
339
375
/**
340
376
* If downloadedFile ends in .zip, then expand it, return its files and given file.
341
377
*
0 commit comments