Skip to content

Commit

Permalink
Handle replace_paths differently due to oddities in CK3 mods
Browse files Browse the repository at this point in the history
Improve basic merge for by using topological sort for mod load order
  • Loading branch information
bcssov committed Sep 15, 2024
1 parent 6449c7c commit bf7e931
Showing 1 changed file with 59 additions and 6 deletions.
65 changes: 59 additions & 6 deletions src/IronyModManager.Services/ModMergeService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// Created : 06-19-2020
//
// Last Modified By : Mario
// Last Modified On : 03-20-2024
// Last Modified On : 09-15-2024
// ***********************************************************************
// <copyright file="ModMergeService.cs" company="Mario">
// Mario
Expand Down Expand Up @@ -220,6 +220,9 @@ public virtual async Task<IMod> MergeCollectionByFilesAsync(string collectionNam
return null;
}

// Apply topological sort
collectionMods = SortDependencies(collectionMods);

var mergeCollectionPath = collectionName.GenerateValidFileName();
var modDirPath = GetPatchModDirectory(game, mergeCollectionPath);
await ModWriter.PurgeModDirectoryAsync(new ModWriterParameters { Path = modDirPath }, true);
Expand Down Expand Up @@ -297,8 +300,27 @@ await ModWriter.WriteDescriptorAsync(new ModWriterParameters
{
foreach (var file in collectionMod.Files.Where(p => game.GameFolders.Any(s => p.StartsWith(s, StringComparison.OrdinalIgnoreCase))))
{
// Can we copy this file?
var allowCopy = true;
if (mod.ReplacePath.Any(p => file.StartsWith(p, StringComparison.OrdinalIgnoreCase)))
{
// So this path is mentioned in replace paths, now we need to verify whether this thing can be copied... Believe that handling of replace_path in the game is so any mod before does not copy its output *only* the ones following with replace path will be copied.
var replacePath = mod.ReplacePath.LastOrDefault(p => file.StartsWith(p, StringComparison.OrdinalIgnoreCase));
var lastMod = collectionMods.LastOrDefault(m => m.ReplacePath.Contains(replacePath, StringComparer.OrdinalIgnoreCase));
var modIdx = collectionMods.IndexOf(collectionMod);
var lastModIdx = collectionMods.IndexOf(lastMod);
if (modIdx < lastModIdx)
{
allowCopy = false;
}
}

processed++;
await modMergeExporter.ExportFilesAsync(new ModMergeFileExporterParameters { RootModPath = collectionMod.FullPath, ExportFile = file, ExportPath = mod.FullPath });
if (allowCopy)
{
await modMergeExporter.ExportFilesAsync(new ModMergeFileExporterParameters { RootModPath = collectionMod.FullPath, ExportFile = file, ExportPath = mod.FullPath });
}

var percentage = GetProgressPercentage(totalFiles, processed, 100);
if (lastPercentage.IsNotNearlyEqual(percentage))
{
Expand Down Expand Up @@ -413,7 +435,6 @@ async void ModMergeCompressExporterProcessedFile(object sender, EventArgs e)
}

lastPercentage = percentage;
mutex.Dispose();
}

modMergeCompressExporter.ProcessedFile += ModMergeCompressExporterProcessedFile;
Expand Down Expand Up @@ -464,7 +485,6 @@ async void ModMergeCompressExporterProcessedFile(object sender, EventArgs e)
}

lastPercentage = innerPercentage;
innerProgressLock.Dispose();
}

string path;
Expand Down Expand Up @@ -504,7 +524,6 @@ async void ModMergeCompressExporterProcessedFile(object sender, EventArgs e)
}

lastPercentage = outerPercentage;
outerProgressLock.Dispose();

modMergeCompressExporter.Finalize(queueId,
Path.Combine(modDirRootPath, mergeCollectionPath, path));
Expand All @@ -518,7 +537,6 @@ async void ModMergeCompressExporterProcessedFile(object sender, EventArgs e)
await p.DisposeAsync();
});
await Task.WhenAll(streamTasks);
exportModLock.Dispose();
}
finally
{
Expand Down Expand Up @@ -597,6 +615,41 @@ protected virtual double GetProgressPercentage(double total, double processed, d
return perc;
}

/// <summary>
/// Sorts the dependencies.
/// </summary>
/// <param name="infos">The infos.</param>
/// <returns>System.Collections.Generic.List&lt;IronyModManager.Models.Common.IMod&gt;.</returns>
protected virtual List<IMod> SortDependencies(List<IMod> infos)
{
var sorted = new List<IMod>();
var processed = new HashSet<IMod>();

void process(IMod item)
{
if (processed.Add(item))
{
foreach (var dependency in item.Dependencies)
{
var dependentItem = infos.FirstOrDefault(p => p.Name.Equals(dependency));
if (dependentItem != null)
{
process(dependentItem);
}
}

sorted.Add(item);
}
}

foreach (var item in infos)
{
process(item);
}

return sorted;
}

#endregion Methods
}
}

0 comments on commit bf7e931

Please sign in to comment.