2626import me .itzg .helpers .http .Fetch ;
2727import me .itzg .helpers .http .SharedFetchArgs ;
2828import me .itzg .helpers .json .ObjectMappers ;
29- import me .itzg .helpers .modrinth .model .Constants ;
3029import me .itzg .helpers .modrinth .model .DependencyType ;
3130import me .itzg .helpers .modrinth .model .Project ;
3231import me .itzg .helpers .modrinth .model .ProjectType ;
4443public class ModrinthCommand implements Callable <Integer > {
4544
4645 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"
5053 )
5154 List <String > projects ;
5255
@@ -75,7 +78,7 @@ public enum DownloadDependencies {
7578 /**
7679 * Implies {@link #REQUIRED}
7780 */
78- OPTIONAL
81+ OPTIONAL ,
7982 }
8083
8184 @ Option (names = "--allowed-version-type" , defaultValue = "release" , description = "Valid values: ${COMPLETION-CANDIDATES}" )
@@ -128,9 +131,13 @@ private List<Path> processProjects(List<String> projects) {
128131 .defaultIfEmpty (Collections .emptyList ())
129132 .block ()
130133 .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+ )
134141 .collect (Collectors .toList ());
135142 }
136143 }
@@ -142,9 +149,9 @@ private ModrinthManifest loadManifest() throws IOException {
142149 final ObjectMapper objectMapper = ObjectMappers .defaultMapper ();
143150
144151 final LegacyModrinthManifest legacyManifest = objectMapper .readValue (
145- legacyManifestPath .toFile (),
146- LegacyModrinthManifest .class
147- );
152+ legacyManifestPath .toFile (),
153+ LegacyModrinthManifest .class
154+ );
148155
149156 Files .delete (legacyManifestPath );
150157
@@ -157,7 +164,13 @@ private ModrinthManifest loadManifest() throws IOException {
157164 return Manifests .load (outputDirectory , ModrinthManifest .ID , ModrinthManifest .class );
158165 }
159166
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+ ) {
161174 log .debug ("Expanding dependencies of version={}" , version );
162175 return version .getDependencies ().stream ()
163176 .filter (this ::filterDependency )
@@ -170,7 +183,7 @@ private Stream<Version> expandDependencies(ModrinthApiClient modrinthApiClient,
170183 if (dep .getVersionId () == null ) {
171184 log .debug ("Fetching versions of dep={} and picking" , dep );
172185 depVersion = pickVersion (
173- getVersionsForProject (modrinthApiClient , dep .getProjectId ())
186+ getVersionsForProject (modrinthApiClient , dep .getProjectId (), loader , gameVersion )
174187 );
175188 }
176189 else {
@@ -192,8 +205,8 @@ private Stream<Version> expandDependencies(ModrinthApiClient modrinthApiClient,
192205 if (depVersion != null ) {
193206 log .debug ("Resolved version={} for dep={}" , depVersion .getVersionNumber (), dep );
194207 return Stream .concat (
195- Stream .of (depVersion ),
196- expandDependencies (modrinthApiClient , project , depVersion )
208+ Stream .of (depVersion ),
209+ expandDependencies (modrinthApiClient , loader , gameVersion , project , depVersion )
197210 )
198211 .peek (expandedVer -> log .debug ("Expanded dependency={} into version={}" , dep , expandedVer ));
199212 }
@@ -229,16 +242,14 @@ private Version pickVersion(List<Version> versions, VersionType versionType) {
229242 return null ;
230243 }
231244
232- private Path download (boolean isDatapack , VersionFile versionFile ) {
245+ private Path download (Loader loader , VersionFile versionFile ) {
233246 final Path outPath ;
234247 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
242253 if (worldDirectory .isAbsolute ()) {
243254 outPath = Files .createDirectories (worldDirectory
244255 .resolve (DATAPACKS_SUBDIR )
@@ -253,9 +264,15 @@ private Path download(boolean isDatapack, VersionFile versionFile) {
253264 .resolve (versionFile .getFilename ());
254265 }
255266 }
267+ else {
268+ outPath = Files .createDirectories (outputDirectory
269+ .resolve (outputType )
270+ )
271+ .resolve (versionFile .getFilename ());
272+ }
256273
257274 } catch (IOException e ) {
258- throw new RuntimeException ("Creating mods directory" , e );
275+ throw new RuntimeException ("Creating output directory" , e );
259276 }
260277
261278 try {
@@ -267,11 +284,11 @@ private Path download(boolean isDatapack, VersionFile versionFile) {
267284 .handleStatus (Fetch .loggingDownloadStatusHandler (log ))
268285 .execute ();
269286 } catch (IOException e ) {
270- throw new RuntimeException ("Downloading mod file" , e );
287+ throw new RuntimeException ("Downloading file" , e );
271288 }
272289 }
273290
274- private List <Version > getVersionsForProject (ModrinthApiClient modrinthApiClient , String project ) {
291+ private List <Version > getVersionsForProject (ModrinthApiClient modrinthApiClient , String project , Loader loader , String gameVersion ) {
275292 final List <Version > versions = modrinthApiClient .getVersionsForProject (
276293 project , loader , gameVersion
277294 )
@@ -294,10 +311,19 @@ private Stream<Path> processProject(ModrinthApiClient modrinthApiClient, Project
294311 log .debug ("Starting with project='{}' slug={}" , project .getTitle (), project .getSlug ());
295312
296313 if (projectsProcessed .add (project .getId ())) {
314+ final Loader effectiveLoader = projectRef .getLoader () != null
315+ ? projectRef .getLoader ()
316+ : this .loader ;
317+
297318 final Version version ;
298319 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
301327 )
302328 .block ();
303329 } catch (NoApplicableVersionsException | NoFilesAvailableException e ) {
@@ -306,36 +332,46 @@ private Stream<Path> processProject(ModrinthApiClient modrinthApiClient, Project
306332
307333 if (version != null ) {
308334 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+ );
310341 }
311342
312- final boolean isDatapack = isDatapack (version );
313-
314343 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
317351 )
352+ )
318353 .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 {
323362 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+ );
327370 }
328371 }
329372 return Stream .empty ();
330373 }
331374
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-
339375 /**
340376 * If downloadedFile ends in .zip, then expand it, return its files and given file.
341377 *
0 commit comments