1212import java .nio .file .Files ;
1313import java .nio .file .Path ;
1414import java .nio .file .Paths ;
15- import java .nio .file .StandardCopyOption ;
16- import java .util .ArrayList ;
1715import java .util .Arrays ;
1816import java .util .Collection ;
1917import java .util .Collections ;
3230import lombok .Setter ;
3331import lombok .extern .slf4j .Slf4j ;
3432import me .itzg .helpers .curseforge .ExcludeIncludesContent .ExcludeIncludes ;
33+ import me .itzg .helpers .curseforge .OverridesApplier .Result ;
3534import me .itzg .helpers .curseforge .model .Category ;
3635import me .itzg .helpers .curseforge .model .CurseForgeFile ;
3736import me .itzg .helpers .curseforge .model .CurseForgeMod ;
@@ -64,8 +63,6 @@ public class CurseForgeInstaller {
6463
6564 public static final String ETERNAL_DEVELOPER_CONSOLE_URL = "https://console.curseforge.com/" ;
6665 public static final String CURSEFORGE_ID = "curseforge" ;
67- public static final String LEVEL_DAT_SUFFIX = "/level.dat" ;
68- public static final int LEVEL_DAT_SUFFIX_LEN = LEVEL_DAT_SUFFIX .length ();
6966 public static final String REPO_SUBDIR_MODPACKS = "modpacks" ;
7067 public static final String REPO_SUBDIR_MODS = "mods" ;
7168 public static final String REPO_SUBDIR_WORLDS = "worlds" ;
@@ -108,6 +105,9 @@ public class CurseForgeInstaller {
108105 CurseForgeApiClient .CATEGORY_WORLDS
109106 ));
110107
108+ @ Getter @ Setter
109+ private List <String > overridesExclusions ;
110+
111111 /**
112112 * @throws MissingModsException if any mods need to be manually downloaded
113113 */
@@ -118,8 +118,12 @@ public void installFromModpackZip(Path modpackZip, String slug) throws IOExcepti
118118 final MinecraftModpackManifest modpackManifest = extractModpackManifest (modpackZip );
119119
120120 processModpackManifest (context , modpackManifest ,
121- () -> applyOverrides (modpackZip , modpackManifest .getOverrides ())
122- );
121+ new OverridesFromZipApplier (
122+ outputDir , modpackZip , overridesSkipExisting ,
123+ modpackManifest .getOverrides (),
124+ levelFrom , overridesExclusions
125+ )
126+ );
123127 });
124128 }
125129
@@ -146,7 +150,7 @@ public void installFromModpackManifest(String modpackManifestLoc, String slug) t
146150 }
147151
148152 processModpackManifest (context , modpackManifest ,
149- () -> new OverridesResult (Collections .emptyList (), null )
153+ () -> new Result (Collections .emptyList (), null )
150154 );
151155 });
152156 }
@@ -359,8 +363,13 @@ else if (Manifests.allFilesPresent(outputDir, context.prevInstallManifest)) {
359363 try {
360364
361365 final MinecraftModpackManifest modpackManifest = extractModpackManifest (modpackZip );
362- results = processModpack (context , modpackManifest , () -> applyOverrides (modpackZip ,
363- modpackManifest .getOverrides ()));
366+ results = processModpack (context , modpackManifest ,
367+ new OverridesFromZipApplier (
368+ outputDir , modpackZip , overridesSkipExisting ,
369+ modpackManifest .getOverrides (),
370+ levelFrom , overridesExclusions
371+ )
372+ );
364373 } finally {
365374 Files .delete (modpackZip );
366375 }
@@ -479,9 +488,6 @@ private void trimLevelsContent(CurseForgeManifest manifest) {
479488 });
480489 }
481490
482- /**
483- * @param overridesApplier typically calls {@link #applyOverrides(Path, String)}
484- */
485491 private ModPackResults processModpack (InstallContext context ,
486492 MinecraftModpackManifest modpackManifest , OverridesApplier overridesApplier
487493 ) throws IOException {
@@ -540,14 +546,14 @@ private ModPackResults processModpack(InstallContext context,
540546 .collectList ()
541547 .block ();
542548
543- final OverridesResult overridesResult = overridesApplier .apply ();
549+ final Result overridesResult = overridesApplier .apply ();
544550
545551 prepareModLoader (modLoader .getId (), modpackManifest .getMinecraft ().getVersion ());
546552
547553 return buildResults (modpackManifest , modLoader , modFiles , overridesResult );
548554 }
549555
550- private ModPackResults buildResults (MinecraftModpackManifest modpackManifest , ModLoader modLoader , List <PathWithInfo > modFiles , OverridesResult overridesResult ) {
556+ private ModPackResults buildResults (MinecraftModpackManifest modpackManifest , ModLoader modLoader , List <PathWithInfo > modFiles , Result overridesResult ) {
551557 return new ModPackResults ()
552558 .setName (modpackManifest .getName ())
553559 .setVersion (modpackManifest .getVersion ())
@@ -572,7 +578,7 @@ private ModPackResults buildResults(MinecraftModpackManifest modpackManifest, Mo
572578 .setModLoaderId (modLoader .getId ());
573579 }
574580
575- private String resolveLevelName (List <PathWithInfo > modFiles , OverridesResult overridesResult ) {
581+ private String resolveLevelName (List <PathWithInfo > modFiles , Result overridesResult ) {
576582 if (levelFrom == LevelFrom .OVERRIDES && overridesResult .levelName != null ) {
577583 return overridesResult .levelName ;
578584 }
@@ -638,96 +644,6 @@ private Mono<Set<Integer>> resolveFromSlugOrIds(
638644 .collect (Collectors .toSet ());
639645 }
640646
641- @ AllArgsConstructor
642- static class OverridesResult {
643- List <Path > paths ;
644- String levelName ;
645- }
646-
647- interface OverridesApplier {
648- OverridesResult apply () throws IOException ;
649- }
650-
651- private OverridesResult applyOverrides (Path modpackZip , String overridesDir ) throws IOException {
652- log .debug ("Applying overrides from '{}' in zip file" , overridesDir );
653-
654- final String levelEntryName = findLevelEntryInOverrides (modpackZip , overridesDir );
655- final String levelEntryNamePrefix = levelEntryName != null ? levelEntryName +"/" : null ;
656-
657- final boolean worldOutputDirExists = levelEntryName != null &&
658- Files .exists (outputDir .resolve (levelEntryName ));
659-
660- log .debug ("While applying overrides, found level entry='{}' in modpack overrides and worldOutputDirExists={}" ,
661- levelEntryName , worldOutputDirExists );
662-
663- final String overridesDirPrefix = overridesDir + "/" ;
664- final int overridesPrefixLen = overridesDirPrefix .length ();
665-
666- final List <Path > overrides = new ArrayList <>();
667- try (ZipInputStream zip = new ZipInputStream (Files .newInputStream (modpackZip ))) {
668- ZipEntry entry ;
669- while ((entry = zip .getNextEntry ()) != null ) {
670- if (entry .getName ().startsWith (overridesDirPrefix )) {
671- if (!entry .isDirectory ()) {
672- if (log .isTraceEnabled ()) {
673- log .trace ("Processing override entry={}:{}" , entry .isDirectory () ? "D" :"F" , entry .getName ());
674- }
675- final String subpath = entry .getName ().substring (overridesPrefixLen );
676- final Path outPath = outputDir .resolve (subpath );
677-
678- // Rules
679- // - don't ever overwrite world data
680- // - user has option to not overwrite any existing file from overrides
681- // - otherwise user will want latest modpack's overrides content
682-
683- final boolean isInWorldDirectory = levelEntryNamePrefix != null &&
684- subpath .startsWith (levelEntryNamePrefix );
685-
686- if (worldOutputDirExists && isInWorldDirectory ) {
687- continue ;
688- }
689-
690- if ( !(overridesSkipExisting && Files .exists (outPath )) ) {
691- log .trace ("Applying override {}" , subpath );
692- // zip files don't always list the directories before the files, so just create-as-needed
693- Files .createDirectories (outPath .getParent ());
694- Files .copy (zip , outPath , StandardCopyOption .REPLACE_EXISTING );
695- }
696- else {
697- log .trace ("Skipping override={} since the file already existed" , subpath );
698- }
699-
700- // Track this path for later cleanup
701- // UNLESS it is within a world/level directory
702- if (levelEntryName == null || !isInWorldDirectory ) {
703- overrides .add (outPath );
704- }
705- }
706- }
707- }
708- }
709-
710- return new OverridesResult (overrides ,
711- levelFrom == LevelFrom .OVERRIDES ? levelEntryName : null
712- );
713- }
714-
715- /**
716- * @return if present, the subpath to a world/level directory with the overrides prefix removed otherwise null
717- */
718- private String findLevelEntryInOverrides (Path modpackZip , String overridesDir ) throws IOException {
719- try (ZipInputStream zip = new ZipInputStream (Files .newInputStream (modpackZip ))) {
720- ZipEntry entry ;
721- while ((entry = zip .getNextEntry ()) != null ) {
722- final String name = entry .getName ();
723- if (!entry .isDirectory () && name .startsWith (overridesDir + "/" ) && name .endsWith (LEVEL_DAT_SUFFIX )) {
724- return name .substring (overridesDir .length ()+1 , name .length () - LEVEL_DAT_SUFFIX_LEN );
725- }
726- }
727- }
728- return null ;
729- }
730-
731647 /**
732648 * Downloads the referenced project-file into the appropriate subdirectory from outputPaths
733649 */
0 commit comments