make downloads cancellable on new incoming data
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user