diff --git a/MareSynchronos/FileCache/FileCache.cs b/MareSynchronos/FileCache/FileCache.cs index f1f2ec4..38aceb9 100644 --- a/MareSynchronos/FileCache/FileCache.cs +++ b/MareSynchronos/FileCache/FileCache.cs @@ -8,10 +8,10 @@ namespace MareSynchronos.FileCache; public class FileCache { - public string ResolvedFilepath { get; private set; } + public string ResolvedFilepath { get; private set; } = string.Empty; public string Hash { get; set; } public string PrefixedFilePath { get; init; } - public string LastModifiedDateTicks { get; init; } + public string LastModifiedDateTicks { get; set; } public FileCache(string hash, string path, string lastModifiedDateTicks) { diff --git a/MareSynchronos/FileCache/FileDbManager.cs b/MareSynchronos/FileCache/FileDbManager.cs index 3cb5b6e..4e566e0 100644 --- a/MareSynchronos/FileCache/FileDbManager.cs +++ b/MareSynchronos/FileCache/FileDbManager.cs @@ -50,7 +50,7 @@ public class FileCacheManager : IDisposable var hash = splittedEntry[0]; var path = splittedEntry[1]; var time = splittedEntry[2]; - FileCaches[hash] = new FileCache(hash, path, time); + FileCaches[path] = new FileCache(hash, path, time); } } } @@ -58,7 +58,7 @@ public class FileCacheManager : IDisposable public void WriteOutFullCsv() { StringBuilder sb = new StringBuilder(); - foreach (var entry in FileCaches) + foreach (var entry in FileCaches.OrderBy(f => f.Value.PrefixedFilePath)) { sb.AppendLine(entry.Value.CsvEntry); } @@ -68,7 +68,15 @@ public class FileCacheManager : IDisposable } lock (_fileWriteLock) { - File.WriteAllText(CsvPath, sb.ToString()); + try + { + File.WriteAllText(CsvPath, sb.ToString()); + File.Delete(CsvBakPath); + } + catch + { + File.WriteAllText(CsvBakPath, sb.ToString()); + } } } @@ -76,9 +84,9 @@ public class FileCacheManager : IDisposable public FileCache? GetFileCacheByHash(string hash) { - if (FileCaches.ContainsKey(hash)) + if (FileCaches.Any(f => f.Value.Hash == hash)) { - return GetValidatedFileCache(FileCaches[hash]); + return GetValidatedFileCache(FileCaches.FirstOrDefault(f => f.Value.Hash == hash).Value); } return null; @@ -124,7 +132,7 @@ public class FileCacheManager : IDisposable var fullName = fi.FullName.ToLowerInvariant(); if (!fullName.Contains(_configuration.CacheFolder.ToLowerInvariant())) return null; string prefixedPath = fullName.Replace(_configuration.CacheFolder.ToLowerInvariant(), CachePrefix + "\\").Replace("\\\\", "\\"); - return CreateFileCacheEntity(fi, prefixedPath); + return CreateFileCacheEntity(fi, prefixedPath, fi.Name.ToUpper()); } public FileCache? CreateFileEntry(string path) @@ -138,16 +146,20 @@ public class FileCacheManager : IDisposable return CreateFileCacheEntity(fi, prefixedPath); } - private FileCache? CreateFileCacheEntity(FileInfo fileInfo, string prefixedPath) + private FileCache? CreateFileCacheEntity(FileInfo fileInfo, string prefixedPath, string? hash = null) { - var hash = Crypto.GetFileHash(fileInfo.FullName); + if (hash == null) + { + hash = Crypto.GetFileHash(fileInfo.FullName); + } var entity = new FileCache(hash, prefixedPath, fileInfo.LastWriteTimeUtc.Ticks.ToString(CultureInfo.InvariantCulture)); - FileCaches[hash] = entity; + entity = ReplacePathPrefixes(entity); + FileCaches[prefixedPath] = entity; lock (_fileWriteLock) { File.AppendAllLines(CsvPath, new[] { entity.CsvEntry }); } - var result = GetFileCacheByPath(prefixedPath); + var result = GetFileCacheByPath(fileInfo.FullName); Logger.Debug("Creating file cache for " + fileInfo.FullName + " success: " + (result != null)); return result; } @@ -164,7 +176,7 @@ public class FileCacheManager : IDisposable var file = new FileInfo(fileCache.ResolvedFilepath); if (!file.Exists) { - FileCaches.Remove(fileCache.Hash, out _); + FileCaches.Remove(fileCache.PrefixedFilePath, out _); return null; } @@ -183,10 +195,11 @@ public class FileCacheManager : IDisposable public void UpdateHash(FileCache fileCache) { - var prevHash = fileCache.Hash; + Logger.Debug("Updating hash for " + fileCache.ResolvedFilepath); fileCache.Hash = Crypto.GetFileHash(fileCache.ResolvedFilepath); - FileCaches.Remove(prevHash, out _); - FileCaches[fileCache.Hash] = fileCache; + fileCache.LastModifiedDateTicks = new FileInfo(fileCache.ResolvedFilepath).LastWriteTimeUtc.Ticks.ToString(CultureInfo.InvariantCulture); + FileCaches.Remove(fileCache.PrefixedFilePath, out _); + FileCaches[fileCache.PrefixedFilePath] = fileCache; } private FileCache ReplacePathPrefixes(FileCache fileCache) diff --git a/MareSynchronos/FileCache/PeriodicFileScanner.cs b/MareSynchronos/FileCache/PeriodicFileScanner.cs index c83557a..eeaa4e4 100644 --- a/MareSynchronos/FileCache/PeriodicFileScanner.cs +++ b/MareSynchronos/FileCache/PeriodicFileScanner.cs @@ -201,27 +201,27 @@ public class PeriodicFileScanner : IDisposable ConcurrentBag entitiesToUpdate = new(); try { - foreach (var value in _fileDbManager.GetAllFileCaches()) + foreach (var cache in _fileDbManager.GetAllFileCaches()) { var idx = Task.WaitAny(dbTasks, ct); dbTasks[idx] = Task.Run(() => { try { - var result = _fileDbManager.ValidateFileCacheEntity(value); - scannedFiles[result.Item2.ResolvedFilepath] = true; - if (result.Item1 == FileState.RequireUpdate) + var validatedCacheResult = _fileDbManager.ValidateFileCacheEntity(cache); + scannedFiles[validatedCacheResult.Item2.ResolvedFilepath] = true; + if (validatedCacheResult.Item1 == FileState.RequireUpdate) { - entitiesToUpdate.Add(result.Item2); + entitiesToUpdate.Add(validatedCacheResult.Item2); } - else if (result.Item1 == FileState.RequireDeletion) + else if (validatedCacheResult.Item1 == FileState.RequireDeletion) { - entitiesToRemove.Add(result.Item2); + entitiesToRemove.Add(validatedCacheResult.Item2); } } catch (Exception ex) { - Logger.Warn("Failed validating " + value.ResolvedFilepath); + Logger.Warn("Failed validating " + cache.ResolvedFilepath); Logger.Warn(ex.Message); Logger.Warn(ex.StackTrace); } @@ -278,6 +278,7 @@ public class PeriodicFileScanner : IDisposable { Logger.Warn("Failed adding " + c.Key); Logger.Warn(ex.Message); + Logger.Warn(ex.StackTrace); } Interlocked.Increment(ref currentFileProgress); diff --git a/MareSynchronos/Managers/CachedPlayer.cs b/MareSynchronos/Managers/CachedPlayer.cs index 58d28f4..b85d936 100644 --- a/MareSynchronos/Managers/CachedPlayer.cs +++ b/MareSynchronos/Managers/CachedPlayer.cs @@ -157,8 +157,11 @@ public class CachedPlayer while ((toDownloadReplacements = TryCalculateModdedDictionary(out moddedPaths)).Count > 0 && attempts++ <= 10) { Logger.Debug("Downloading missing files for player " + PlayerName + ", kind: " + objectKind); - await _apiController.DownloadFiles(downloadId, toDownloadReplacements, downloadToken); - _apiController.CancelDownload(downloadId); + if (toDownloadReplacements.Any()) + { + await _apiController.DownloadFiles(downloadId, toDownloadReplacements, downloadToken); + _apiController.CancelDownload(downloadId); + } if (downloadToken.IsCancellationRequested) { Logger.Verbose("Detected cancellation"); diff --git a/MareSynchronos/WebAPI/ApIController.Functions.Files.cs b/MareSynchronos/WebAPI/ApIController.Functions.Files.cs index 82a480e..a5b99f0 100644 --- a/MareSynchronos/WebAPI/ApIController.Functions.Files.cs +++ b/MareSynchronos/WebAPI/ApIController.Functions.Files.cs @@ -74,6 +74,23 @@ namespace MareSynchronos.WebAPI public int GetDownloadId() => _downloadId++; public async Task DownloadFiles(int currentDownloadId, List fileReplacementDto, CancellationToken ct) + { + DownloadStarted?.Invoke(); + try + { + await DownloadFilesInternal(currentDownloadId, fileReplacementDto, ct); + } + catch + { + CancelDownload(currentDownloadId); + } + finally + { + DownloadFinished?.Invoke(); + } + } + + private async Task DownloadFilesInternal(int currentDownloadId, List fileReplacementDto, CancellationToken ct) { DownloadStarted?.Invoke(); Logger.Debug("Downloading files (Download ID " + currentDownloadId + ")");