Skip to content

Commit

Permalink
fix: make ui reduplication work
Browse files Browse the repository at this point in the history
  • Loading branch information
anna-is-cute committed Aug 21, 2024
1 parent bf00846 commit 9f7c3b2
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 39 deletions.
71 changes: 32 additions & 39 deletions DownloadTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ internal class DownloadTask : IDisposable {
private ConcurrentDeque<Measurement> Entries { get; } = new();
private Util.SentryTransaction? Transaction { get; set; }
private bool SupportsHardLinks { get; set; }

/// <summary>
/// A list of files expected by the group jsons made by this task. These
/// paths should be relative to the files directory.
Expand Down Expand Up @@ -156,11 +157,12 @@ private async Task Run() {

this.PackageName = info.Variant.Package.Name;
this.VariantName = info.Variant.Name;
this.GenerateModDirectoryPath(info);
await this.TestHardLinks();
await this.DownloadFiles(info);
await this.ConstructModPack(info);
await this.AddMod(info);
this.RemoveOldFiles(info);
this.RemoveOldFiles();

// before setting state to finished, set the directory name

Expand Down Expand Up @@ -261,7 +263,7 @@ private async Task<IDownloadTask_GetVersion> GetPackageInfo() {

// sort needed files for dedupe consistency
foreach (var files in version.NeededFiles.Files.Files.Values) {
files.Sort((a, b) => string.Join(':', a).CompareTo(string.Join(':', b)));
files.Sort((a, b) => String.Compare(string.Join(':', a), string.Join(':', b), StringComparison.Ordinal));
}

if (this.DownloadKey != null) {
Expand All @@ -273,6 +275,11 @@ private async Task<IDownloadTask_GetVersion> GetPackageInfo() {
return version;
}

private void GenerateModDirectoryPath(IDownloadTask_GetVersion info) {
var dirName = HeliosphereMeta.ModDirectoryName(info.Variant.Package.Id, info.Variant.Package.Name, info.Version, info.Variant.Id);
this.PenumbraModPath = Path.Join(this.ModDirectory, dirName);
}

private async Task TestHardLinks() {
try {
var a = Path.Join(this.PenumbraModPath, Path.GetRandomFileName());
Expand Down Expand Up @@ -307,8 +314,6 @@ private async Task DownloadFiles(IDownloadTask_GetVersion info) {
)
.ToArray();

var dirName = HeliosphereMeta.ModDirectoryName(info.Variant.Package.Id, info.Variant.Package.Name, info.Version, info.Variant.Id);
this.PenumbraModPath = Path.Join(this.ModDirectory, dirName);
if (directories.Length == 1) {
var oldName = Path.Join(this.ModDirectory, directories[0]!);
if (oldName == this.PenumbraModPath) {
Expand All @@ -319,7 +324,7 @@ private async Task DownloadFiles(IDownloadTask_GetVersion info) {
// the path found is not what we expect it to be, so the version
// has changed. rename the directory to the new version
this._oldModName = directories[0];
Directory.Move(oldName, this.PenumbraModPath);
Directory.Move(oldName, this.PenumbraModPath!);
}
} else if (directories.Length > 1) {
Plugin.Log.Warning($"multiple heliosphere mod directories found for {info.Variant.Package.Name} - not attempting a rename");
Expand All @@ -345,6 +350,8 @@ private IEnumerable<Task> DownloadNormalFiles(IDownloadTask_GetVersion_NeededFil
return neededFiles.Files.Files
.Select(pair => Task.Run(async () => {
var (hash, files) = pair;
// FIXME: here and batched: this does not accound for duplicated ui files.
// this ends up always redownloading ui files that are duped
var outputPaths = GetOutputPaths(files);
using (await SemaphoreGuard.WaitAsync(Plugin.DownloadSemaphore, this.CancellationToken.Token)) {
Expand Down Expand Up @@ -737,11 +744,12 @@ private async Task DuplicateFile(string filesDir, IEnumerable<string> outputPath
}

var dest = Path.Join(filesDir, firstPath);
if (dest.Equals(firstPath, StringComparison.InvariantCultureIgnoreCase)) {
if (dest.Equals(path, StringComparison.InvariantCultureIgnoreCase)) {
return;
}

Plugin.Resilience.Execute(() => File.Move(path, firstPath));
Plugin.Resilience.Execute(() => File.Move(path, dest));
path = dest;
return;
}

Expand Down Expand Up @@ -770,7 +778,7 @@ async Task DuplicateInner(string dest) {
}
}

private void RemoveOldFiles(IDownloadTask_GetVersion info) {
private void RemoveOldFiles() {
using var span = this.Transaction?.StartChild(nameof(this.RemoveOldFiles));

this.State = State.RemovingOldFiles;
Expand Down Expand Up @@ -1049,7 +1057,7 @@ private async Task<DefaultMod> ConstructDefaultMod(IDownloadTask_GetVersion info
: GetReplacedPath(files[0]);

defaultMod.Files[gamePath] = Path.Join("files", replacedPath);
this.ExpectedFiles.Add(replacedPath);
this.ExpectedFiles.Add(replacedPath.ToLowerInvariant());
}
}

Expand Down Expand Up @@ -1217,7 +1225,7 @@ private async Task<List<ModGroup>> ConstructGroups(IDownloadTask_GetVersion info
? GetReplacedPath(file)
: GetReplacedPath(files[0]);
option.Files[gamePath] = Path.Join("files", replacedPath);
this.ExpectedFiles.Add(replacedPath);
this.ExpectedFiles.Add(replacedPath.ToLowerInvariant());
}
}

Expand Down Expand Up @@ -1520,25 +1528,21 @@ private async Task DuplicateUiFiles(DefaultMod defaultMod, List<ModGroup> modGro

// first record unique references
var references = new Dictionary<string, Dictionary<string, (uint, List<Action<string>>)>>();
var resaveDefault = UpdateReferences(defaultMod.Files);
var resaveGroups = new bool[modGroups.Count];
for (var i = 0; i < modGroups.Count; i++) {
var group = modGroups[i];
UpdateReferences(defaultMod.Files);
foreach (var group in modGroups) {
if (group is not StandardModGroup standard) {
continue;
}

var needsResave = false;
foreach (var option in standard.Options) {
needsResave |= UpdateReferences(option.Files);
UpdateReferences(option.Files);
}

resaveGroups[i] = needsResave;
}

// then find any uniquely referenced more than once
foreach (var (gamePath, outputPathCounts) in references) {
foreach (var (outputPath, (refs, updatePathActions)) in outputPathCounts) {
foreach (var (joinedOutputPath, (refs, updatePathActions)) in outputPathCounts) {
var outputPath = joinedOutputPath[6..];
if (refs < 2) {
continue;
}
Expand All @@ -1559,37 +1563,31 @@ private async Task DuplicateUiFiles(DefaultMod defaultMod, List<ModGroup> modGro
var newRelative = Path.ChangeExtension(outputPath, ext);
var dst = Path.Join(filesPath, newRelative);

FileHelper.DeleteIfExists(dst);

Plugin.Resilience.Execute(() => duplicateMethod(src, dst));

// update the path
updatePathActions[i](newRelative);
this.ExpectedFiles.Add(newRelative);
updatePathActions[i](Path.Join("files", newRelative));
this.ExpectedFiles.Add(newRelative.ToLowerInvariant());
}

// remove the original file
Plugin.Resilience.Execute(() => File.Delete(outputPath));
Plugin.Resilience.Execute(() => File.Delete(src));
this.ExpectedFiles.Remove(outputPath);
}
}

// resave any group that needs it
if (resaveDefault) {
await this.SaveDefaultMod(defaultMod);
}
await this.SaveDefaultMod(defaultMod);

for (var i = 0; i < modGroups.Count; i++) {
if (!resaveGroups[i]) {
continue;
}

await this.SaveGroup(i, modGroups[i]);
}

this.StateData += 1;
return;

bool UpdateReferences(Dictionary<string, string> files) {
var needsSaving = false;
void UpdateReferences(Dictionary<string, string> files) {
foreach (var (gamePath, outputPath) in files) {
if (!gamePath.StartsWith(uiPrefix)) {
continue;
Expand All @@ -1600,25 +1598,20 @@ bool UpdateReferences(Dictionary<string, string> files) {

if (!references.TryGetValue(gamePath, out var outputPathCounts)) {
outputPathCounts = [];
references[gamePath] = outputPathCounts;
}

if (!outputPathCounts.TryGetValue(normalised, out var refs)) {
refs = (0, []);
}

refs.Item2.Add((path) => {
refs.Item2.Add(path => {
files[gamePath] = path;
});
refs.Item1 += 1;

outputPathCounts[normalised] = refs;

if (refs.Item1 > 1) {
needsSaving = true;
}
}

return needsSaving;
}
}

Expand Down
8 changes: 8 additions & 0 deletions Util/FileHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ internal static void Delete(string path) {
Wrap(path, File.Delete);
}

internal static void DeleteIfExists(string path) {
try {
Delete(path);
} catch (Exception ex) when (ex is FileNotFoundException or DirectoryNotFoundException) {
// no-op
}
}

private static T Wrap<T>(string path, Func<string, T> action) {
try {
return action(path);
Expand Down

0 comments on commit 9f7c3b2

Please sign in to comment.