4040import me .itzg .helpers .curseforge .model .ModLoader ;
4141import me .itzg .helpers .errors .GenericException ;
4242import me .itzg .helpers .errors .InvalidParameterException ;
43+ import me .itzg .helpers .errors .RateLimitException ;
4344import me .itzg .helpers .fabric .FabricLauncherInstaller ;
4445import me .itzg .helpers .files .Manifests ;
4546import me .itzg .helpers .files .ResultsFileWriter ;
@@ -70,10 +71,12 @@ public class CurseForgeInstaller {
7071 private final Path outputDir ;
7172 private final Path resultsFile ;
7273
73- @ Getter @ Setter
74+ @ Getter
75+ @ Setter
7476 private String apiBaseUrl = "https://api.curseforge.com" ;
7577
76- @ Getter @ Setter
78+ @ Getter
79+ @ Setter
7780 private String apiKey ;
7881
7982 @ Getter
@@ -157,7 +160,7 @@ public void installFromModpackManifest(String modpackManifestLoc, String slug) t
157160
158161 processModpackManifest (context , modpackManifest ,
159162 () -> new Result (Collections .emptyList (), null )
160- );
163+ );
161164 });
162165 }
163166
@@ -183,14 +186,15 @@ void install(String slug, InstallationEntryPoint entryPoint) throws IOException
183186 if (apiKey == null || apiKey .isEmpty ()) {
184187 if (manifest != null ) {
185188 log .warn ("API key is not set, so will re-use previous modpack installation of {}" ,
186- manifest .getSlug () != null ? manifest .getSlug () : "Project ID " +manifest .getModId ());
189+ manifest .getSlug () != null ? manifest .getSlug () : "Project ID " + manifest .getModId ()
190+ );
187191 log .warn ("Obtain an API key from " + ETERNAL_DEVELOPER_CONSOLE_URL
188- + " and set the environment variable " + API_KEY_VAR +" in order to restore full functionality." );
192+ + " and set the environment variable " + API_KEY_VAR + " in order to restore full functionality." );
189193 return ;
190194 }
191195 else {
192196 throw new InvalidParameterException ("API key is not set. Obtain an API key from " + ETERNAL_DEVELOPER_CONSOLE_URL
193- + " and set the environment variable " + API_KEY_VAR );
197+ + " and set the environment variable " + API_KEY_VAR );
194198 }
195199 }
196200
@@ -209,11 +213,19 @@ void install(String slug, InstallationEntryPoint entryPoint) throws IOException
209213
210214 } catch (FailedRequestException e ) {
211215 if (e .getStatusCode () == 403 ) {
212- throw new InvalidParameterException (String .format ("Access to %s is forbidden or rate-limit has been exceeded."
213- + " Ensure %s is set to a valid API key from %s or allow rate-limit to reset." ,
216+ log .debug ("Failed request details: {}" , e .toString ());
217+
218+ if (e .getBody ().contains ("There might be too much traffic" )) {
219+ throw new RateLimitException (null , String .format ("Access to %s has been rate-limited." , apiBaseUrl ), e );
220+ }
221+ else {
222+ throw new InvalidParameterException (String .format ("Access to %s is forbidden or rate-limit has been exceeded."
223+ + " Ensure %s is set to a valid API key from %s or allow rate-limit to reset." ,
214224 apiBaseUrl , API_KEY_VAR , ETERNAL_DEVELOPER_CONSOLE_URL
215- ), e );
216- } else {
225+ ), e );
226+ }
227+ }
228+ else {
217229 throw e ;
218230 }
219231 }
@@ -229,13 +241,14 @@ static class InstallContext {
229241 }
230242
231243 interface InstallationEntryPoint {
244+
232245 void install (InstallContext context ) throws IOException ;
233246 }
234247
235248 private void installByRetrievingModpackZip (InstallContext context , String fileMatcher , Integer fileId ) throws IOException {
236249 final CurseForgeMod curseForgeMod = context .cfApi .searchMod (context .slug ,
237- context .categoryInfo .getClassIdForSlug (CurseForgeApiClient .CATEGORY_MODPACKS )
238- )
250+ context .categoryInfo .getClassIdForSlug (CurseForgeApiClient .CATEGORY_MODPACKS )
251+ )
239252 .block ();
240253
241254 resolveModpackFileAndProcess (context , curseForgeMod , fileId , fileMatcher );
@@ -273,12 +286,14 @@ else if (Manifests.allFilesPresent(outputDir, context.prevInstallManifest, ignor
273286 }
274287
275288 log .info ("Installing modpack '{}' version {} from provided modpack zip" ,
276- modpackName , modpackVersion );
289+ modpackName , modpackVersion
290+ );
277291
278292 final ModPackResults results = processModpack (context , modpackManifest , overridesApplier );
279293
280294 finalizeResults (context , results ,
281- pseudoModId , pseudoFileId , results .getName ());
295+ pseudoModId , pseudoFileId , results .getName ()
296+ );
282297 }
283298
284299 private static boolean matchesPreviousInstall (InstallContext context , int modId , int fileId ) {
@@ -362,7 +377,8 @@ else if (Manifests.allFilesPresent(outputDir, context.prevInstallManifest, ignor
362377 }
363378
364379 log .info ("Processing modpack '{}' ({}) @ {}:{}" , modFile .getDisplayName (),
365- mod .getSlug (), modFile .getModId (), modFile .getId ());
380+ mod .getSlug (), modFile .getModId (), modFile .getId ()
381+ );
366382
367383 if (modpackZip == null ) {
368384 throw new GenericException ("Download of modpack zip was empty" );
@@ -448,7 +464,8 @@ private void finalizeExistingInstallation(CurseForgeManifest prevManifest) throw
448464 /**
449465 * @throws MissingModsException if any mods need to be manually downloaded
450466 */
451- private void finalizeResults (InstallContext context , ModPackResults results , int modId , int fileId , String displayName ) throws IOException {
467+ private void finalizeResults (InstallContext context , ModPackResults results , int modId , int fileId , String displayName )
468+ throws IOException {
452469 final CurseForgeManifest newManifest = CurseForgeManifest .builder ()
453470 .modpackName (results .getName ())
454471 .modpackVersion (results .getVersion ())
@@ -535,19 +552,19 @@ private ModPackResults processModpack(InstallContext context,
535552
536553 log .debug ("Evaluating projectId={} with exclude={} and forceInclude={}" ,
537554 projectID , exclude , forceInclude
538- );
555+ );
539556
540557 return Mono .just (forceInclude || !exclude )
541558 .flatMap (proceed -> proceed ? Mono .just (true )
542559 : context .cfApi .getModInfo (projectID )
543560 .map (mod -> {
544561 log .info ("Excluding mod file '{}' ({}) due to configuration" ,
545562 mod .getName (), mod .getSlug ()
546- );
563+ );
547564 // and filter away
548565 return false ;
549566 })
550- );
567+ );
551568 })
552569 // ...download and possibly unzip world file
553570 .flatMap (fileRef ->
@@ -567,7 +584,9 @@ private ModPackResults processModpack(InstallContext context,
567584 return buildResults (modpackManifest , modLoader , modFiles , overridesResult );
568585 }
569586
570- private ModPackResults buildResults (MinecraftModpackManifest modpackManifest , ModLoader modLoader , List <PathWithInfo > modFiles , Result overridesResult ) {
587+ private ModPackResults buildResults (MinecraftModpackManifest modpackManifest , ModLoader modLoader ,
588+ List <PathWithInfo > modFiles , Result overridesResult
589+ ) {
571590 return new ModPackResults ()
572591 .setName (modpackManifest .getName ())
573592 .setVersion (modpackManifest .getVersion ())
@@ -722,22 +741,23 @@ else if (category.getSlug().equals("worlds")) {
722741 .setDownloadNeeded (resolveResult .downloadNeeded )
723742 .setModInfo (modInfo )
724743 .setCurseForgeFile (cfFile )
725- : extractWorldZip (modInfo , resolveResult .path , outputPaths .getWorldsDir ())
744+ : extractWorldZip (modInfo , resolveResult .path , outputPaths .getWorldsDir ())
726745 )
727746 : resolvedFileMono
728- .map (resolveResult ->
729- new PathWithInfo (resolveResult .path )
730- .setDownloadNeeded (resolveResult .downloadNeeded )
731- .setModInfo (modInfo )
732- .setCurseForgeFile (cfFile )
733- );
747+ .map (resolveResult ->
748+ new PathWithInfo (resolveResult .path )
749+ .setDownloadNeeded (resolveResult .downloadNeeded )
750+ .setModInfo (modInfo )
751+ .setCurseForgeFile (cfFile )
752+ );
734753 });
735754 })
736755 .checkpoint (String .format ("Downloading file from modpack %d:%d" , projectID , fileID ));
737756 }
738757
739758 @ RequiredArgsConstructor
740759 static class ResolveResult {
760+
741761 final Path path ;
742762 @ Setter
743763 boolean downloadNeeded ;
@@ -815,7 +835,7 @@ private PathWithInfo extractWorldZip(CurseForgeMod modInfo, Path zipPath, Path w
815835 ZipEntry nextEntry = zipInputStream .getNextEntry ();
816836
817837 if (nextEntry == null || !nextEntry .isDirectory ()) {
818- throw new GenericException ("Expected top-level directory in world zip " + zipPath );
838+ throw new GenericException ("Expected top-level directory in world zip " + zipPath );
819839 }
820840
821841 // Will replace top diretory with slug name
0 commit comments