make downloads cancellable on new incoming data

This commit is contained in:
Stanley Dimant
2022-06-26 14:56:28 +02:00
parent 0df75fe085
commit 1a1aa5cac5
2 changed files with 43 additions and 31 deletions

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Objects.Types;
@@ -42,6 +43,8 @@ public class CachedPlayer
} }
} }
private CancellationTokenSource _downloadCancellationTokenSource = new();
private string _lastGlamourerData = string.Empty; private string _lastGlamourerData = string.Empty;
private string _originalGlamourerData = string.Empty; private string _originalGlamourerData = string.Empty;
@@ -63,34 +66,37 @@ public class CachedPlayer
if (string.IsNullOrEmpty(PlayerName) || e.CharacterNameHash != PlayerNameHash) return; if (string.IsNullOrEmpty(PlayerName) || e.CharacterNameHash != PlayerNameHash) return;
Logger.Debug("Received data for " + this); Logger.Debug("Received data for " + this);
List<FileReplacementDto> toDownloadReplacements; _downloadCancellationTokenSource?.Cancel();
using (var db = new FileCacheContext()) _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("Received total " + e.CharacterData.FileReplacements.Count + " file replacement data");
Logger.Debug("Hash for data is " + e.CharacterData.Hash); _cache[e.CharacterData.Hash] = e.CharacterData;
if (!_cache.ContainsKey(e.CharacterData.Hash)) }
{ else
Logger.Debug("Received total " + e.CharacterData.FileReplacements.Count + " file replacement data"); {
_cache[e.CharacterData.Hash] = e.CharacterData; Logger.Debug("Had valid local cache for " + PlayerName);
}
else
{
Logger.Debug("Had valid local cache for " + PlayerName);
}
} }
// todo: make this cancellable
Task.Run(async () => Task.Run(async () =>
{ {
List<FileReplacementDto> toDownloadReplacements;
Dictionary<string, string> moddedPaths; Dictionary<string, string> moddedPaths;
while ((toDownloadReplacements = TryCalculateModdedDictionary(_cache[e.CharacterData.Hash], out moddedPaths)).Count > 0) while ((toDownloadReplacements = TryCalculateModdedDictionary(_cache[e.CharacterData.Hash], out moddedPaths)).Count > 0)
{ {
Logger.Debug("Downloading missing files for player " + PlayerName); 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<FileReplacementDto> TryCalculateModdedDictionary(CharacterCacheDto cache, private List<FileReplacementDto> TryCalculateModdedDictionary(CharacterCacheDto cache,

View File

@@ -93,31 +93,31 @@ namespace MareSynchronos.WebAPI
_ = DisposeHubConnections(); _ = DisposeHubConnections();
} }
public async Task<string> DownloadFile(string hash) public async Task<string> DownloadFile(string hash, CancellationToken ct)
{ {
var reader = await _fileHub!.StreamAsChannelAsync<byte[]>("DownloadFile", hash); var reader = await _fileHub!.StreamAsChannelAsync<byte[]>("DownloadFile", hash, ct);
int i = 0; int i = 0;
string fileName = Path.GetTempFileName(); string fileName = Path.GetTempFileName();
await using var fs = File.OpenWrite(fileName); 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); CurrentDownloads[hash] = (CurrentDownloads[hash].Item1 + data.Length, CurrentDownloads[hash].Item2);
await fs.WriteAsync(data); await fs.WriteAsync(data, ct);
} }
} }
return fileName; return fileName;
} }
public async Task DownloadFiles(List<FileReplacementDto> fileReplacementDto) public async Task DownloadFiles(List<FileReplacementDto> fileReplacementDto, CancellationToken ct)
{ {
IsDownloading = true; IsDownloading = true;
foreach (var file in fileReplacementDto) foreach (var file in fileReplacementDto)
{ {
var fileSize = await _fileHub!.InvokeAsync<long>("GetFileSize", file.Hash); var fileSize = await _fileHub!.InvokeAsync<long>("GetFileSize", file.Hash, ct);
CurrentDownloads[file.Hash] = (0, fileSize); CurrentDownloads[file.Hash] = (0, fileSize);
} }
@@ -128,28 +128,34 @@ namespace MareSynchronos.WebAPI
{ {
continue; continue;
} }
var hash = file.Hash; var hash = file.Hash;
var tempFile = await DownloadFile(hash); var tempFile = await DownloadFile(hash, ct);
var tempFileData = await File.ReadAllBytesAsync(tempFile); if (ct.IsCancellationRequested)
{
File.Delete(tempFile);
break;
}
var tempFileData = await File.ReadAllBytesAsync(tempFile, ct);
var extractedFile = LZ4Codec.Unwrap(tempFileData); var extractedFile = LZ4Codec.Unwrap(tempFileData);
File.Delete(tempFile); File.Delete(tempFile);
tempFileData = null;
var ext = file.GamePaths.First().Split(".").Last(); var ext = file.GamePaths.First().Split(".").Last();
var filePath = Path.Combine(_pluginConfiguration.CacheFolder, file.Hash + "." + ext); 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); Logger.Debug("File downloaded to " + filePath);
downloadedHashes.Add(hash); downloadedHashes.Add(hash);
} }
bool allFilesInDb = false; var allFilesInDb = false;
while (!allFilesInDb) while (!allFilesInDb && !ct.IsCancellationRequested)
{ {
await using (var db = new FileCacheContext()) await using (var db = new FileCacheContext())
{ {
allFilesInDb = downloadedHashes.All(h => db.FileCaches.Any(f => f.Hash == h)); allFilesInDb = downloadedHashes.All(h => db.FileCaches.Any(f => f.Hash == h));
} }
await Task.Delay(250); await Task.Delay(250, ct);
} }
CurrentDownloads.Clear(); CurrentDownloads.Clear();