diff --git a/MareSynchronos/Models/CachedPlayer.cs b/MareSynchronos/Models/CachedPlayer.cs index 3a239fc..d4e7ccf 100644 --- a/MareSynchronos/Models/CachedPlayer.cs +++ b/MareSynchronos/Models/CachedPlayer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Objects.Types; @@ -42,6 +43,8 @@ public class CachedPlayer } } + private CancellationTokenSource _downloadCancellationTokenSource = new(); + private string _lastGlamourerData = string.Empty; private string _originalGlamourerData = string.Empty; @@ -63,34 +66,37 @@ public class CachedPlayer if (string.IsNullOrEmpty(PlayerName) || e.CharacterNameHash != PlayerNameHash) return; Logger.Debug("Received data for " + this); - List toDownloadReplacements; - using (var db = new FileCacheContext()) + _downloadCancellationTokenSource?.Cancel(); + _downloadCancellationTokenSource = new CancellationTokenSource(); + var downloadToken = _downloadCancellationTokenSource.Token; + + Logger.Debug("Checking for files to download for player " + PlayerName); + Logger.Debug("Hash for data is " + e.CharacterData.Hash); + if (!_cache.ContainsKey(e.CharacterData.Hash)) { - Logger.Debug("Checking for files to download for player " + PlayerName); - Logger.Debug("Hash for data is " + e.CharacterData.Hash); - if (!_cache.ContainsKey(e.CharacterData.Hash)) - { - Logger.Debug("Received total " + e.CharacterData.FileReplacements.Count + " file replacement data"); - _cache[e.CharacterData.Hash] = e.CharacterData; - } - else - { - Logger.Debug("Had valid local cache for " + PlayerName); - } + Logger.Debug("Received total " + e.CharacterData.FileReplacements.Count + " file replacement data"); + _cache[e.CharacterData.Hash] = e.CharacterData; + } + else + { + Logger.Debug("Had valid local cache for " + PlayerName); } - // todo: make this cancellable Task.Run(async () => { + List toDownloadReplacements; + Dictionary moddedPaths; while ((toDownloadReplacements = TryCalculateModdedDictionary(_cache[e.CharacterData.Hash], out moddedPaths)).Count > 0) { Logger.Debug("Downloading missing files for player " + PlayerName); - await _apiController.DownloadFiles(toDownloadReplacements); + await _apiController.DownloadFiles(toDownloadReplacements, downloadToken); } - ApplyCharacterData(e.CharacterData, moddedPaths); - }); + if (_downloadCancellationTokenSource.Token.IsCancellationRequested) + + ApplyCharacterData(e.CharacterData, moddedPaths); + }, downloadToken); } private List TryCalculateModdedDictionary(CharacterCacheDto cache, diff --git a/MareSynchronos/WebAPI/ApiController.cs b/MareSynchronos/WebAPI/ApiController.cs index c86e3a7..faf42a2 100644 --- a/MareSynchronos/WebAPI/ApiController.cs +++ b/MareSynchronos/WebAPI/ApiController.cs @@ -93,31 +93,31 @@ namespace MareSynchronos.WebAPI _ = DisposeHubConnections(); } - public async Task DownloadFile(string hash) + public async Task DownloadFile(string hash, CancellationToken ct) { - var reader = await _fileHub!.StreamAsChannelAsync("DownloadFile", hash); + var reader = await _fileHub!.StreamAsChannelAsync("DownloadFile", hash, ct); int i = 0; string fileName = Path.GetTempFileName(); await using var fs = File.OpenWrite(fileName); - while (await reader.WaitToReadAsync()) + while (await reader.WaitToReadAsync(ct) && !ct.IsCancellationRequested) { - while (reader.TryRead(out var data)) + while (reader.TryRead(out var data) && !ct.IsCancellationRequested) { CurrentDownloads[hash] = (CurrentDownloads[hash].Item1 + data.Length, CurrentDownloads[hash].Item2); - await fs.WriteAsync(data); + await fs.WriteAsync(data, ct); } } return fileName; } - public async Task DownloadFiles(List fileReplacementDto) + public async Task DownloadFiles(List fileReplacementDto, CancellationToken ct) { IsDownloading = true; foreach (var file in fileReplacementDto) { - var fileSize = await _fileHub!.InvokeAsync("GetFileSize", file.Hash); + var fileSize = await _fileHub!.InvokeAsync("GetFileSize", file.Hash, ct); CurrentDownloads[file.Hash] = (0, fileSize); } @@ -128,28 +128,34 @@ namespace MareSynchronos.WebAPI { continue; } + var hash = file.Hash; - var tempFile = await DownloadFile(hash); - var tempFileData = await File.ReadAllBytesAsync(tempFile); + var tempFile = await DownloadFile(hash, ct); + if (ct.IsCancellationRequested) + { + File.Delete(tempFile); + break; + } + + var tempFileData = await File.ReadAllBytesAsync(tempFile, ct); var extractedFile = LZ4Codec.Unwrap(tempFileData); File.Delete(tempFile); - tempFileData = null; var ext = file.GamePaths.First().Split(".").Last(); var filePath = Path.Combine(_pluginConfiguration.CacheFolder, file.Hash + "." + ext); - await File.WriteAllBytesAsync(filePath, extractedFile); + await File.WriteAllBytesAsync(filePath, extractedFile, ct); Logger.Debug("File downloaded to " + filePath); downloadedHashes.Add(hash); } - bool allFilesInDb = false; - while (!allFilesInDb) + var allFilesInDb = false; + while (!allFilesInDb && !ct.IsCancellationRequested) { await using (var db = new FileCacheContext()) { allFilesInDb = downloadedHashes.All(h => db.FileCaches.Any(f => f.Hash == h)); } - await Task.Delay(250); + await Task.Delay(250, ct); } CurrentDownloads.Clear();