rebuild the temp collection if cached files don't persist
This commit is contained in:
@@ -77,6 +77,8 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
private CancellationTokenSource? _downloadCancellationTokenSource = new();
|
private CancellationTokenSource? _downloadCancellationTokenSource = new();
|
||||||
private bool _forceApplyMods = false;
|
private bool _forceApplyMods = false;
|
||||||
private bool _forceFullReapply;
|
private bool _forceFullReapply;
|
||||||
|
private Dictionary<(string GamePath, string? Hash), string>? _lastAppliedModdedPaths;
|
||||||
|
private bool _needsCollectionRebuild;
|
||||||
private bool _isVisible;
|
private bool _isVisible;
|
||||||
private Guid _penumbraCollection;
|
private Guid _penumbraCollection;
|
||||||
private readonly object _collectionGate = new();
|
private readonly object _collectionGate = new();
|
||||||
@@ -349,12 +351,14 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
private void ResetPenumbraCollection(bool releaseFromPenumbra = true, string? reason = null)
|
private void ResetPenumbraCollection(bool releaseFromPenumbra = true, string? reason = null)
|
||||||
{
|
{
|
||||||
Guid toRelease = Guid.Empty;
|
Guid toRelease = Guid.Empty;
|
||||||
|
bool hadCollection = false;
|
||||||
lock (_collectionGate)
|
lock (_collectionGate)
|
||||||
{
|
{
|
||||||
if (_penumbraCollection != Guid.Empty)
|
if (_penumbraCollection != Guid.Empty)
|
||||||
{
|
{
|
||||||
toRelease = _penumbraCollection;
|
toRelease = _penumbraCollection;
|
||||||
_penumbraCollection = Guid.Empty;
|
_penumbraCollection = Guid.Empty;
|
||||||
|
hadCollection = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,6 +366,13 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
if (cached.HasValue && cached.Value != Guid.Empty)
|
if (cached.HasValue && cached.Value != Guid.Empty)
|
||||||
{
|
{
|
||||||
toRelease = cached.Value;
|
toRelease = cached.Value;
|
||||||
|
hadCollection = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hadCollection)
|
||||||
|
{
|
||||||
|
_needsCollectionRebuild = true;
|
||||||
|
_forceFullReapply = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!releaseFromPenumbra || toRelease == Guid.Empty || !_ipcManager.Penumbra.APIAvailable)
|
if (!releaseFromPenumbra || toRelease == Guid.Empty || !_ipcManager.Penumbra.APIAvailable)
|
||||||
@@ -600,6 +611,25 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool HasValidCachedModdedPaths()
|
||||||
|
{
|
||||||
|
if (_lastAppliedModdedPaths is null || _lastAppliedModdedPaths.Count == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var entry in _lastAppliedModdedPaths)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(entry.Value) || !File.Exists(entry.Value))
|
||||||
|
{
|
||||||
|
Logger.LogDebug("Cached file path {path} missing for {handler}, forcing recalculation", entry.Value ?? "empty", GetLogIdentifier());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private bool CanApplyNow()
|
private bool CanApplyNow()
|
||||||
{
|
{
|
||||||
return !_dalamudUtil.IsInCombat
|
return !_dalamudUtil.IsInCombat
|
||||||
@@ -844,6 +874,8 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
{
|
{
|
||||||
PlayerName = null;
|
PlayerName = null;
|
||||||
_cachedData = null;
|
_cachedData = null;
|
||||||
|
_lastAppliedModdedPaths = null;
|
||||||
|
_needsCollectionRebuild = false;
|
||||||
Logger.LogDebug("Disposing {name} complete", name);
|
Logger.LogDebug("Disposing {name} complete", name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1012,73 +1044,103 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
|
|
||||||
var updateModdedPaths = updatedData.Values.Any(v => v.Any(p => p == PlayerChanges.ModFiles));
|
var updateModdedPaths = updatedData.Values.Any(v => v.Any(p => p == PlayerChanges.ModFiles));
|
||||||
var updateManip = updatedData.Values.Any(v => v.Any(p => p == PlayerChanges.ModManip));
|
var updateManip = updatedData.Values.Any(v => v.Any(p => p == PlayerChanges.ModManip));
|
||||||
|
var needsCollectionRebuild = _needsCollectionRebuild;
|
||||||
|
var reuseCachedModdedPaths = !updateModdedPaths && needsCollectionRebuild && _lastAppliedModdedPaths is not null;
|
||||||
|
updateModdedPaths = updateModdedPaths || needsCollectionRebuild;
|
||||||
|
updateManip = updateManip || needsCollectionRebuild;
|
||||||
|
Dictionary<(string GamePath, string? Hash), string>? cachedModdedPaths = null;
|
||||||
|
if (reuseCachedModdedPaths)
|
||||||
|
{
|
||||||
|
if (HasValidCachedModdedPaths())
|
||||||
|
{
|
||||||
|
cachedModdedPaths = _lastAppliedModdedPaths;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.LogDebug("{handler}: Cached files missing, recalculating mappings", GetLogIdentifier());
|
||||||
|
_lastAppliedModdedPaths = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_downloadCancellationTokenSource = _downloadCancellationTokenSource?.CancelRecreate() ?? new CancellationTokenSource();
|
_downloadCancellationTokenSource = _downloadCancellationTokenSource?.CancelRecreate() ?? new CancellationTokenSource();
|
||||||
var downloadToken = _downloadCancellationTokenSource.Token;
|
var downloadToken = _downloadCancellationTokenSource.Token;
|
||||||
_ = DownloadAndApplyCharacterAsync(applicationBase, charaData, updatedData, updateModdedPaths, updateManip, downloadToken).ConfigureAwait(false);
|
_ = DownloadAndApplyCharacterAsync(applicationBase, charaData, updatedData, updateModdedPaths, updateManip, cachedModdedPaths, downloadToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task? _pairDownloadTask;
|
private Task? _pairDownloadTask;
|
||||||
|
|
||||||
private async Task DownloadAndApplyCharacterAsync(Guid applicationBase, CharacterData charaData, Dictionary<ObjectKind, HashSet<PlayerChanges>> updatedData,
|
private async Task DownloadAndApplyCharacterAsync(Guid applicationBase, CharacterData charaData, Dictionary<ObjectKind, HashSet<PlayerChanges>> updatedData,
|
||||||
bool updateModdedPaths, bool updateManip, CancellationToken downloadToken)
|
bool updateModdedPaths, bool updateManip, Dictionary<(string GamePath, string? Hash), string>? cachedModdedPaths, CancellationToken downloadToken)
|
||||||
{
|
{
|
||||||
await using var concurrencyLease = await _pairProcessingLimiter.AcquireAsync(downloadToken).ConfigureAwait(false);
|
await using var concurrencyLease = await _pairProcessingLimiter.AcquireAsync(downloadToken).ConfigureAwait(false);
|
||||||
Dictionary<(string GamePath, string? Hash), string> moddedPaths = [];
|
|
||||||
bool skipDownscaleForPair = ShouldSkipDownscale();
|
bool skipDownscaleForPair = ShouldSkipDownscale();
|
||||||
var user = GetPrimaryUserData();
|
var user = GetPrimaryUserData();
|
||||||
|
Dictionary<(string GamePath, string? Hash), string> moddedPaths;
|
||||||
|
|
||||||
if (updateModdedPaths)
|
if (updateModdedPaths)
|
||||||
{
|
{
|
||||||
int attempts = 0;
|
if (cachedModdedPaths is not null)
|
||||||
List<FileReplacementData> toDownloadReplacements = TryCalculateModdedDictionary(applicationBase, charaData, out moddedPaths, downloadToken);
|
|
||||||
|
|
||||||
while (toDownloadReplacements.Count > 0 && attempts++ <= 10 && !downloadToken.IsCancellationRequested)
|
|
||||||
{
|
{
|
||||||
if (_pairDownloadTask != null && !_pairDownloadTask.IsCompleted)
|
moddedPaths = new Dictionary<(string GamePath, string? Hash), string>(cachedModdedPaths, cachedModdedPaths.Comparer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int attempts = 0;
|
||||||
|
List<FileReplacementData> toDownloadReplacements = TryCalculateModdedDictionary(applicationBase, charaData, out moddedPaths, downloadToken);
|
||||||
|
|
||||||
|
while (toDownloadReplacements.Count > 0 && attempts++ <= 10 && !downloadToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
Logger.LogDebug("[BASE-{appBase}] Finishing prior running download task for player {name}, {kind}", applicationBase, PlayerName, updatedData);
|
if (_pairDownloadTask != null && !_pairDownloadTask.IsCompleted)
|
||||||
|
{
|
||||||
|
Logger.LogDebug("[BASE-{appBase}] Finishing prior running download task for player {name}, {kind}", applicationBase, PlayerName, updatedData);
|
||||||
|
await _pairDownloadTask.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.LogDebug("[BASE-{appBase}] Downloading missing files for player {name}, {kind}", applicationBase, PlayerName, updatedData);
|
||||||
|
|
||||||
|
Mediator.Publish(new EventMessage(new Event(PlayerName, user, nameof(PairHandlerAdapter), EventSeverity.Informational,
|
||||||
|
$"Starting download for {toDownloadReplacements.Count} files")));
|
||||||
|
var toDownloadFiles = await _downloadManager.InitiateDownloadList(_charaHandler!, toDownloadReplacements, downloadToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (!_playerPerformanceService.ComputeAndAutoPauseOnVRAMUsageThresholds(this, charaData, toDownloadFiles))
|
||||||
|
{
|
||||||
|
_downloadManager.ClearDownload();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var handlerForDownload = _charaHandler;
|
||||||
|
_pairDownloadTask = Task.Run(async () => await _downloadManager.DownloadFiles(handlerForDownload, toDownloadReplacements, downloadToken, skipDownscaleForPair).ConfigureAwait(false));
|
||||||
|
|
||||||
await _pairDownloadTask.ConfigureAwait(false);
|
await _pairDownloadTask.ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (downloadToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
Logger.LogTrace("[BASE-{appBase}] Detected cancellation", applicationBase);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
toDownloadReplacements = TryCalculateModdedDictionary(applicationBase, charaData, out moddedPaths, downloadToken);
|
||||||
|
|
||||||
|
if (toDownloadReplacements.TrueForAll(c => _downloadManager.ForbiddenTransfers.Exists(f => string.Equals(f.Hash, c.Hash, StringComparison.Ordinal))))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(2), downloadToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.LogDebug("[BASE-{appBase}] Downloading missing files for player {name}, {kind}", applicationBase, PlayerName, updatedData);
|
if (!await _playerPerformanceService.CheckBothThresholds(this, charaData).ConfigureAwait(false))
|
||||||
|
|
||||||
Mediator.Publish(new EventMessage(new Event(PlayerName, user, nameof(PairHandlerAdapter), EventSeverity.Informational,
|
|
||||||
$"Starting download for {toDownloadReplacements.Count} files")));
|
|
||||||
var toDownloadFiles = await _downloadManager.InitiateDownloadList(_charaHandler!, toDownloadReplacements, downloadToken).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (!_playerPerformanceService.ComputeAndAutoPauseOnVRAMUsageThresholds(this, charaData, toDownloadFiles))
|
|
||||||
{
|
{
|
||||||
_downloadManager.ClearDownload();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var handlerForDownload = _charaHandler;
|
|
||||||
_pairDownloadTask = Task.Run(async () => await _downloadManager.DownloadFiles(handlerForDownload, toDownloadReplacements, downloadToken, skipDownscaleForPair).ConfigureAwait(false));
|
|
||||||
|
|
||||||
await _pairDownloadTask.ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (downloadToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
Logger.LogTrace("[BASE-{appBase}] Detected cancellation", applicationBase);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
toDownloadReplacements = TryCalculateModdedDictionary(applicationBase, charaData, out moddedPaths, downloadToken);
|
|
||||||
|
|
||||||
if (toDownloadReplacements.TrueForAll(c => _downloadManager.ForbiddenTransfers.Exists(f => string.Equals(f.Hash, c.Hash, StringComparison.Ordinal))))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(2), downloadToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!await _playerPerformanceService.CheckBothThresholds(this, charaData).ConfigureAwait(false))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
moddedPaths = cachedModdedPaths is not null
|
||||||
|
? new Dictionary<(string GamePath, string? Hash), string>(cachedModdedPaths, cachedModdedPaths.Comparer)
|
||||||
|
: [];
|
||||||
|
}
|
||||||
|
|
||||||
downloadToken.ThrowIfCancellationRequested();
|
downloadToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
@@ -1162,6 +1224,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
|
|
||||||
await _ipcManager.Penumbra.SetTemporaryModsAsync(Logger, _applicationId, penumbraCollection,
|
await _ipcManager.Penumbra.SetTemporaryModsAsync(Logger, _applicationId, penumbraCollection,
|
||||||
moddedPaths.ToDictionary(k => k.Key.GamePath, k => k.Value, StringComparer.Ordinal)).ConfigureAwait(false);
|
moddedPaths.ToDictionary(k => k.Key.GamePath, k => k.Value, StringComparer.Ordinal)).ConfigureAwait(false);
|
||||||
|
_lastAppliedModdedPaths = new Dictionary<(string GamePath, string? Hash), string>(moddedPaths, moddedPaths.Comparer);
|
||||||
LastAppliedDataBytes = -1;
|
LastAppliedDataBytes = -1;
|
||||||
foreach (var path in moddedPaths.Values.Distinct(StringComparer.OrdinalIgnoreCase).Select(v => new FileInfo(v)).Where(p => p.Exists))
|
foreach (var path in moddedPaths.Values.Distinct(StringComparer.OrdinalIgnoreCase).Select(v => new FileInfo(v)).Where(p => p.Exists))
|
||||||
{
|
{
|
||||||
@@ -1187,6 +1250,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
_cachedData = charaData;
|
_cachedData = charaData;
|
||||||
_pairStateCache.Store(Ident, charaData);
|
_pairStateCache.Store(Ident, charaData);
|
||||||
_forceFullReapply = false;
|
_forceFullReapply = false;
|
||||||
|
_needsCollectionRebuild = false;
|
||||||
if (LastAppliedApproximateVRAMBytes < 0 || LastAppliedApproximateEffectiveVRAMBytes < 0)
|
if (LastAppliedApproximateVRAMBytes < 0 || LastAppliedApproximateEffectiveVRAMBytes < 0)
|
||||||
{
|
{
|
||||||
_playerPerformanceService.ComputeAndAutoPauseOnVRAMUsageThresholds(this, charaData, new List<DownloadFileTransfer>());
|
_playerPerformanceService.ComputeAndAutoPauseOnVRAMUsageThresholds(this, charaData, new List<DownloadFileTransfer>());
|
||||||
|
|||||||
Reference in New Issue
Block a user