Compare commits
34 Commits
2.0.2.71-D
...
test-abel
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43d2b31eda | ||
| 17dd8a307b | |||
| 24d0c38f59 | |||
|
|
5920622b9a | ||
|
|
d357e37c65 | ||
| 4da2548e03 | |||
|
|
2d526bcfbf | ||
|
|
4664071eb3 | ||
|
|
11099c05ff | ||
|
|
57a076ae77 | ||
|
|
6af61451dc | ||
|
|
02d091eefa | ||
|
|
e41a7149c5 | ||
| b6b9c81a57 | |||
| a824d94ffe | |||
|
|
e16ddb0a1d | ||
| 4b13dfe8d4 | |||
|
|
ba26edc33c | ||
|
|
14c4c1d669 | ||
|
|
e8c157d8ac | ||
|
|
2af0b5774b | ||
|
|
bb365442cf | ||
|
|
277d368f83 | ||
|
|
3487891185 | ||
|
|
96f8d33cde | ||
|
|
a033d4d4d8 | ||
|
|
7d2a914c84 | ||
|
|
d6fe09ba8e | ||
| 979443d9bb | |||
| 92cb861710 | |||
| aeed8503c2 | |||
| 44bb53023e | |||
| 05b91ed243 | |||
| cfc5c1e0f3 |
@@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Authors></Authors>
|
<Authors></Authors>
|
||||||
<Company></Company>
|
<Company></Company>
|
||||||
<Version>2.0.2.71</Version>
|
<Version>2.0.3</Version>
|
||||||
<Description></Description>
|
<Description></Description>
|
||||||
<Copyright></Copyright>
|
<Copyright></Copyright>
|
||||||
<PackageProjectUrl>https://github.com/Light-Public-Syncshells/LightlessClient</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/Light-Public-Syncshells/LightlessClient</PackageProjectUrl>
|
||||||
|
|||||||
@@ -123,22 +123,16 @@ public class PlayerDataFactory
|
|||||||
{
|
{
|
||||||
if (playerPointer == IntPtr.Zero)
|
if (playerPointer == IntPtr.Zero)
|
||||||
return true;
|
return true;
|
||||||
try
|
|
||||||
{
|
|
||||||
var character = (Character*)playerPointer;
|
|
||||||
if (character == null)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
var gameObject = &character->GameObject;
|
var character = (Character*)playerPointer;
|
||||||
if (gameObject == null)
|
if (character == null)
|
||||||
return true;
|
|
||||||
|
|
||||||
return gameObject->DrawObject == null;
|
|
||||||
}
|
|
||||||
catch (AccessViolationException)
|
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
var gameObject = &character->GameObject;
|
||||||
|
if (gameObject == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return gameObject->DrawObject == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsCacheFresh(CacheEntry entry)
|
private static bool IsCacheFresh(CacheEntry entry)
|
||||||
@@ -175,6 +169,11 @@ public class PlayerDataFactory
|
|||||||
using var cts = new CancellationTokenSource(_hardBuildTimeout);
|
using var cts = new CancellationTokenSource(_hardBuildTimeout);
|
||||||
var fragment = await CreateCharacterDataInternal(obj, cts.Token).ConfigureAwait(false);
|
var fragment = await CreateCharacterDataInternal(obj, cts.Token).ConfigureAwait(false);
|
||||||
|
|
||||||
|
fragment.FileReplacements =
|
||||||
|
new HashSet<FileReplacement>(resolvedPaths.Select(c => new FileReplacement([.. c.Value], c.Key)), FileReplacementComparer.Instance)
|
||||||
|
.Where(p => p.HasFileReplacement).ToHashSet();
|
||||||
|
var allowedExtensions = CacheMonitor.AllowedFileExtensions;
|
||||||
|
fragment.FileReplacements.RemoveWhere(c => c.GamePaths.Any(g => !allowedExtensions.Any(e => g.EndsWith(e, StringComparison.OrdinalIgnoreCase))));
|
||||||
_characterBuildCache[key] = new CacheEntry(fragment, DateTime.UtcNow);
|
_characterBuildCache[key] = new CacheEntry(fragment, DateTime.UtcNow);
|
||||||
PruneCharacterCacheIfNeeded();
|
PruneCharacterCacheIfNeeded();
|
||||||
|
|
||||||
@@ -220,6 +219,10 @@ public class PlayerDataFactory
|
|||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
// get all remaining paths and resolve them
|
// get all remaining paths and resolve them
|
||||||
|
var transientPaths = ManageSemiTransientData(objectKind);
|
||||||
|
var resolvedTransientPaths = transientPaths.Count == 0
|
||||||
|
? new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase).AsReadOnly()
|
||||||
|
: await GetFileReplacementsFromPaths(playerRelatedObject, transientPaths, new HashSet<string>(StringComparer.Ordinal)).ConfigureAwait(false);
|
||||||
ct.ThrowIfCancellationRequested();
|
ct.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
if (await CheckForNullDrawObject(playerRelatedObject.Address).ConfigureAwait(false))
|
if (await CheckForNullDrawObject(playerRelatedObject.Address).ConfigureAwait(false))
|
||||||
@@ -669,7 +672,7 @@ public class PlayerDataFactory
|
|||||||
list.Add(forwardPaths[i].ToLowerInvariant());
|
list.Add(forwardPaths[i].ToLowerInvariant());
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
resolvedPaths[filePath] = [forwardPathsLower[i]];
|
resolvedPaths[filePath] = [forwardPaths[i].ToLowerInvariant()];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase, IHighP
|
|||||||
if (msg.Address == Address)
|
if (msg.Address == Address)
|
||||||
{
|
{
|
||||||
_haltProcessing = false;
|
_haltProcessing = false;
|
||||||
Refresh();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -177,36 +176,30 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase, IHighP
|
|||||||
Mediator.Publish(new GameObjectHandlerDestroyedMessage(this, _isOwnedObject));
|
Mediator.Publish(new GameObjectHandlerDestroyedMessage(this, _isOwnedObject));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckAndUpdateObject() => CheckAndUpdateObject(allowPublish: true);
|
private unsafe void CheckAndUpdateObject()
|
||||||
|
|
||||||
private unsafe void CheckAndUpdateObject(bool allowPublish = true)
|
|
||||||
{
|
{
|
||||||
var prevAddr = Address;
|
var prevAddr = Address;
|
||||||
var prevDrawObj = DrawObjectAddress;
|
var prevDrawObj = DrawObjectAddress;
|
||||||
|
|
||||||
Address = _getAddress();
|
Address = _getAddress();
|
||||||
|
|
||||||
if (Address != IntPtr.Zero)
|
if (Address != IntPtr.Zero)
|
||||||
{
|
{
|
||||||
var gameObject = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)Address;
|
var gameObject = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)Address;
|
||||||
DrawObjectAddress = (IntPtr)gameObject->DrawObject;
|
var drawObjAddr = (IntPtr)gameObject->DrawObject;
|
||||||
|
DrawObjectAddress = drawObjAddr;
|
||||||
EntityId = gameObject->EntityId;
|
EntityId = gameObject->EntityId;
|
||||||
|
CurrentDrawCondition = DrawCondition.None;
|
||||||
var chara = (Character*)Address;
|
|
||||||
var newName = chara->GameObject.NameString;
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(newName) && !string.Equals(newName, Name, StringComparison.Ordinal))
|
|
||||||
Name = newName;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DrawObjectAddress = IntPtr.Zero;
|
DrawObjectAddress = IntPtr.Zero;
|
||||||
EntityId = uint.MaxValue;
|
EntityId = uint.MaxValue;
|
||||||
|
CurrentDrawCondition = DrawCondition.DrawObjectZero;
|
||||||
}
|
}
|
||||||
|
|
||||||
CurrentDrawCondition = IsBeingDrawnUnsafe();
|
CurrentDrawCondition = IsBeingDrawnUnsafe();
|
||||||
|
|
||||||
if (_haltProcessing || !allowPublish) return;
|
if (_haltProcessing) return;
|
||||||
|
|
||||||
bool drawObjDiff = DrawObjectAddress != prevDrawObj;
|
bool drawObjDiff = DrawObjectAddress != prevDrawObj;
|
||||||
bool addrDiff = Address != prevAddr;
|
bool addrDiff = Address != prevAddr;
|
||||||
@@ -363,10 +356,12 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase, IHighP
|
|||||||
|
|
||||||
private void FrameworkUpdate()
|
private void FrameworkUpdate()
|
||||||
{
|
{
|
||||||
|
if (!_delayedZoningTask?.IsCompleted ?? false) return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var zoningDelayActive = !(_delayedZoningTask?.IsCompleted ?? true);
|
_performanceCollector.LogPerformance(this, $"CheckAndUpdateObject>{(_isOwnedObject ? "Self" : "Other")}+{ObjectKind}/{(string.IsNullOrEmpty(Name) ? "Unk" : Name)}"
|
||||||
_performanceCollector.LogPerformance(this, $"CheckAndUpdateObject>{(_isOwnedObject ? "Self" : "Other")}+{ObjectKind}/{(string.IsNullOrEmpty(Name) ? "Unk" : Name)}", () => CheckAndUpdateObject(allowPublish: !zoningDelayActive));
|
+ $"+{Address.ToString("X")}", CheckAndUpdateObject);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -467,6 +462,6 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase, IHighP
|
|||||||
Logger.LogDebug("[{this}] Delay after zoning complete", this);
|
Logger.LogDebug("[{this}] Delay after zoning complete", this);
|
||||||
_zoningCts.Dispose();
|
_zoningCts.Dispose();
|
||||||
}
|
}
|
||||||
}, _zoningCts.Token);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,6 +25,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
using DalamudObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
|
using DalamudObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
|
||||||
using ObjectKind = LightlessSync.API.Data.Enum.ObjectKind;
|
using ObjectKind = LightlessSync.API.Data.Enum.ObjectKind;
|
||||||
using FileReplacementDataComparer = LightlessSync.PlayerData.Data.FileReplacementDataComparer;
|
using FileReplacementDataComparer = LightlessSync.PlayerData.Data.FileReplacementDataComparer;
|
||||||
|
using LightlessSync.LightlessConfiguration;
|
||||||
|
|
||||||
namespace LightlessSync.PlayerData.Pairs;
|
namespace LightlessSync.PlayerData.Pairs;
|
||||||
|
|
||||||
@@ -95,9 +96,6 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
private readonly Dictionary<ObjectKind, HashSet<PlayerChanges>> _pendingOwnedChanges = new();
|
private readonly Dictionary<ObjectKind, HashSet<PlayerChanges>> _pendingOwnedChanges = new();
|
||||||
private CancellationTokenSource? _ownedRetryCts;
|
private CancellationTokenSource? _ownedRetryCts;
|
||||||
private Task _ownedRetryTask = Task.CompletedTask;
|
private Task _ownedRetryTask = Task.CompletedTask;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static readonly TimeSpan OwnedRetryInitialDelay = TimeSpan.FromSeconds(1);
|
private static readonly TimeSpan OwnedRetryInitialDelay = TimeSpan.FromSeconds(1);
|
||||||
private static readonly TimeSpan OwnedRetryMaxDelay = TimeSpan.FromSeconds(10);
|
private static readonly TimeSpan OwnedRetryMaxDelay = TimeSpan.FromSeconds(10);
|
||||||
private static readonly TimeSpan OwnedRetryStaleDataGrace = TimeSpan.FromMinutes(5);
|
private static readonly TimeSpan OwnedRetryStaleDataGrace = TimeSpan.FromMinutes(5);
|
||||||
@@ -111,9 +109,6 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
};
|
};
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, byte> _blockedPapHashes = new(StringComparer.OrdinalIgnoreCase);
|
private readonly ConcurrentDictionary<string, byte> _blockedPapHashes = new(StringComparer.OrdinalIgnoreCase);
|
||||||
private AnimationValidationMode _lastAnimMode = (AnimationValidationMode)(-1);
|
|
||||||
private bool _lastAllowOneBasedShift;
|
|
||||||
private bool _lastAllowNeighborTolerance;
|
|
||||||
private readonly ConcurrentDictionary<string, byte> _dumpedRemoteSkeletonForHash = new(StringComparer.OrdinalIgnoreCase);
|
private readonly ConcurrentDictionary<string, byte> _dumpedRemoteSkeletonForHash = new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
private DateTime? _invisibleSinceUtc;
|
private DateTime? _invisibleSinceUtc;
|
||||||
@@ -2460,8 +2455,6 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
RefreshPapBlockCacheIfAnimSettingsChanged();
|
|
||||||
|
|
||||||
var replacementList = charaData.FileReplacements.SelectMany(k => k.Value.Where(v => string.IsNullOrEmpty(v.FileSwapPath))).ToList();
|
var replacementList = charaData.FileReplacements.SelectMany(k => k.Value.Where(v => string.IsNullOrEmpty(v.FileSwapPath))).ToList();
|
||||||
Parallel.ForEach(replacementList, new ParallelOptions()
|
Parallel.ForEach(replacementList, new ParallelOptions()
|
||||||
{
|
{
|
||||||
@@ -2862,26 +2855,6 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RefreshPapBlockCacheIfAnimSettingsChanged()
|
|
||||||
{
|
|
||||||
var cfg = _configService.Current;
|
|
||||||
|
|
||||||
if (cfg.AnimationValidationMode != _lastAnimMode
|
|
||||||
|| cfg.AnimationAllowOneBasedShift != _lastAllowOneBasedShift
|
|
||||||
|| cfg.AnimationAllowNeighborIndexTolerance != _lastAllowNeighborTolerance)
|
|
||||||
{
|
|
||||||
_lastAnimMode = cfg.AnimationValidationMode;
|
|
||||||
_lastAllowOneBasedShift = cfg.AnimationAllowOneBasedShift;
|
|
||||||
_lastAllowNeighborTolerance = cfg.AnimationAllowNeighborIndexTolerance;
|
|
||||||
|
|
||||||
_blockedPapHashes.Clear();
|
|
||||||
_dumpedRemoteSkeletonForHash.Clear();
|
|
||||||
|
|
||||||
Logger.LogDebug("{handler}: Cleared blocked PAP cache due to animation setting change (mode={mode}, shift={shift}, neigh={neigh})",
|
|
||||||
GetLogIdentifier(), _lastAnimMode, _lastAllowOneBasedShift, _lastAllowNeighborTolerance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SplitPapMappings(
|
private static void SplitPapMappings(
|
||||||
Dictionary<(string GamePath, string? Hash), string> moddedPaths,
|
Dictionary<(string GamePath, string? Hash), string> moddedPaths,
|
||||||
out Dictionary<(string GamePath, string? Hash), string> withoutPap,
|
out Dictionary<(string GamePath, string? Hash), string> withoutPap,
|
||||||
@@ -2906,8 +2879,6 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
Dictionary<(string GamePath, string? Hash), string> papOnly,
|
Dictionary<(string GamePath, string? Hash), string> papOnly,
|
||||||
CancellationToken token)
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
RefreshPapBlockCacheIfAnimSettingsChanged();
|
|
||||||
|
|
||||||
var mode = _configService.Current.AnimationValidationMode;
|
var mode = _configService.Current.AnimationValidationMode;
|
||||||
var allowBasedShift = _configService.Current.AnimationAllowOneBasedShift;
|
var allowBasedShift = _configService.Current.AnimationAllowOneBasedShift;
|
||||||
var allownNightIndex = _configService.Current.AnimationAllowNeighborIndexTolerance;
|
var allownNightIndex = _configService.Current.AnimationAllowNeighborIndexTolerance;
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
services.AddSingleton<HubFactory>();
|
services.AddSingleton<HubFactory>();
|
||||||
services.AddSingleton<FileUploadManager>();
|
services.AddSingleton<FileUploadManager>();
|
||||||
services.AddSingleton<FileTransferOrchestrator>();
|
services.AddSingleton<FileTransferOrchestrator>();
|
||||||
|
services.AddSingleton<FileDownloadDeduplicator>();
|
||||||
services.AddSingleton<LightlessPlugin>();
|
services.AddSingleton<LightlessPlugin>();
|
||||||
services.AddSingleton<LightlessProfileManager>();
|
services.AddSingleton<LightlessProfileManager>();
|
||||||
services.AddSingleton<TextureCompressionService>();
|
services.AddSingleton<TextureCompressionService>();
|
||||||
|
|||||||
@@ -955,13 +955,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
|||||||
|
|
||||||
private unsafe void CheckCharacterForDrawing(nint address, string characterName)
|
private unsafe void CheckCharacterForDrawing(nint address, string characterName)
|
||||||
{
|
{
|
||||||
if (address == nint.Zero)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var gameObj = (GameObject*)address;
|
var gameObj = (GameObject*)address;
|
||||||
|
|
||||||
if (gameObj == null || gameObj->ObjectKind == 0)
|
|
||||||
return;
|
|
||||||
var drawObj = gameObj->DrawObject;
|
var drawObj = gameObj->DrawObject;
|
||||||
bool isDrawing = false;
|
bool isDrawing = false;
|
||||||
bool isDrawingChanged = false;
|
bool isDrawingChanged = false;
|
||||||
@@ -1058,28 +1052,16 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
|||||||
|
|
||||||
if (!IsAnythingDrawing)
|
if (!IsAnythingDrawing)
|
||||||
{
|
{
|
||||||
try
|
var gameObj = (GameObject*)playerAddress;
|
||||||
{
|
var currentName = gameObj != null ? gameObj->NameString ?? string.Empty : string.Empty;
|
||||||
var gameObj = (GameObject*)playerAddress;
|
var charaName = string.IsNullOrEmpty(currentName) ? actor.Name : currentName;
|
||||||
|
CheckCharacterForDrawing(playerAddress, charaName);
|
||||||
if (gameObj == null || gameObj->ObjectKind == 0)
|
if (IsAnythingDrawing)
|
||||||
{
|
break;
|
||||||
continue;
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
var currentName = gameObj->NameString ?? string.Empty;
|
break;
|
||||||
var charaName = string.IsNullOrEmpty(currentName) ? actor.Name : currentName;
|
|
||||||
|
|
||||||
CheckCharacterForDrawing(playerAddress, charaName);
|
|
||||||
|
|
||||||
if (IsAnythingDrawing)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
catch (AccessViolationException ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex, "Memory access violation reading character at {addr}", playerAddress.ToString("X"));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -52,8 +52,12 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
private readonly LightlessConfigService _configService;
|
private readonly LightlessConfigService _configService;
|
||||||
private readonly LightlessMediator _lightlessMediator;
|
private readonly LightlessMediator _lightlessMediator;
|
||||||
private readonly PairLedger _pairLedger;
|
private readonly PairLedger _pairLedger;
|
||||||
|
private readonly ConcurrentDictionary<GameObjectHandler, IReadOnlyDictionary<string, FileDownloadStatus>> _currentDownloads = new();
|
||||||
|
private readonly DrawEntityFactory _drawEntityFactory;
|
||||||
|
private readonly FileUploadManager _fileTransferManager;
|
||||||
private readonly PlayerPerformanceConfigService _playerPerformanceConfig;
|
private readonly PlayerPerformanceConfigService _playerPerformanceConfig;
|
||||||
private readonly PairUiService _pairUiService;
|
private readonly PairUiService _pairUiService;
|
||||||
|
private readonly PlayerPerformanceConfigService _playerPerformanceConfig;
|
||||||
private readonly ServerConfigurationManager _serverManager;
|
private readonly ServerConfigurationManager _serverManager;
|
||||||
private readonly TagHandler _tagHandler;
|
private readonly TagHandler _tagHandler;
|
||||||
private readonly UiSharedService _uiSharedService;
|
private readonly UiSharedService _uiSharedService;
|
||||||
@@ -167,12 +171,9 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
Mediator.Subscribe<SwitchToIntroUiMessage>(this, (_) => IsOpen = false);
|
Mediator.Subscribe<SwitchToIntroUiMessage>(this, (_) => IsOpen = false);
|
||||||
Mediator.Subscribe<CutsceneStartMessage>(this, (_) => UiSharedService_GposeStart());
|
Mediator.Subscribe<CutsceneStartMessage>(this, (_) => UiSharedService_GposeStart());
|
||||||
Mediator.Subscribe<CutsceneEndMessage>(this, (_) => UiSharedService_GposeEnd());
|
Mediator.Subscribe<CutsceneEndMessage>(this, (_) => UiSharedService_GposeEnd());
|
||||||
Mediator.Subscribe<DownloadStartedMessage>(this, msg =>
|
Mediator.Subscribe<DownloadStartedMessage>(this, (msg) => _currentDownloads[msg.DownloadId] = msg.DownloadStatus);
|
||||||
{
|
|
||||||
_currentDownloads[msg.DownloadId] = new Dictionary<string, FileDownloadStatus>(msg.DownloadStatus, StringComparer.Ordinal);
|
|
||||||
});
|
|
||||||
Mediator.Subscribe<DownloadFinishedMessage>(this, (msg) => _currentDownloads.TryRemove(msg.DownloadId, out _));
|
Mediator.Subscribe<DownloadFinishedMessage>(this, (msg) => _currentDownloads.TryRemove(msg.DownloadId, out _));
|
||||||
Mediator.Subscribe<RefreshUiMessage>(this, (msg) => _drawFolders = [.. DrawFolders]);
|
Mediator.Subscribe<RefreshUiMessage>(this, (msg) => _drawFolders = DrawFolders.ToList());
|
||||||
|
|
||||||
_characterAnalyzer = characterAnalyzer;
|
_characterAnalyzer = characterAnalyzer;
|
||||||
_playerPerformanceConfig = playerPerformanceConfig;
|
_playerPerformanceConfig = playerPerformanceConfig;
|
||||||
|
|||||||
@@ -65,14 +65,12 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
IsOpen = true;
|
IsOpen = true;
|
||||||
|
|
||||||
Mediator.Subscribe<DownloadStartedMessage>(this, msg =>
|
Mediator.Subscribe<DownloadStartedMessage>(this, (msg) =>
|
||||||
{
|
{
|
||||||
_currentDownloads[msg.DownloadId] = msg.DownloadStatus;
|
_currentDownloads[msg.DownloadId] = msg.DownloadStatus;
|
||||||
|
// Capture initial totals when download starts
|
||||||
var snap = msg.DownloadStatus.ToArray();
|
var totalFiles = msg.DownloadStatus.Values.Sum(s => s.TotalFiles);
|
||||||
var totalFiles = snap.Sum(kv => kv.Value?.TotalFiles ?? 0);
|
var totalBytes = msg.DownloadStatus.Values.Sum(s => s.TotalBytes);
|
||||||
var totalBytes = snap.Sum(kv => kv.Value?.TotalBytes ?? 0);
|
|
||||||
|
|
||||||
_downloadInitialTotals[msg.DownloadId] = (totalFiles, totalBytes);
|
_downloadInitialTotals[msg.DownloadId] = (totalFiles, totalBytes);
|
||||||
_notificationDismissed = false;
|
_notificationDismissed = false;
|
||||||
});
|
});
|
||||||
@@ -81,7 +79,7 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
|||||||
_currentDownloads.TryRemove(msg.DownloadId, out _);
|
_currentDownloads.TryRemove(msg.DownloadId, out _);
|
||||||
|
|
||||||
// Dismiss notification if all downloads are complete
|
// Dismiss notification if all downloads are complete
|
||||||
if (_currentDownloads.IsEmpty && !_notificationDismissed)
|
if (!_currentDownloads.Any() && !_notificationDismissed)
|
||||||
{
|
{
|
||||||
Mediator.Publish(new LightlessNotificationDismissMessage("pair_download_progress"));
|
Mediator.Publish(new LightlessNotificationDismissMessage("pair_download_progress"));
|
||||||
_notificationDismissed = true;
|
_notificationDismissed = true;
|
||||||
@@ -476,7 +474,7 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
|||||||
totalBytes += playerTotalBytes;
|
totalBytes += playerTotalBytes;
|
||||||
transferredBytes += playerTransferredBytes;
|
transferredBytes += playerTransferredBytes;
|
||||||
|
|
||||||
// per-player W/Q/P/D/C
|
// per-player W/Q/P/D
|
||||||
var playerDlSlot = 0;
|
var playerDlSlot = 0;
|
||||||
var playerDlQueue = 0;
|
var playerDlQueue = 0;
|
||||||
var playerDlProg = 0;
|
var playerDlProg = 0;
|
||||||
@@ -489,7 +487,6 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
|||||||
switch (fileStatus.DownloadStatus)
|
switch (fileStatus.DownloadStatus)
|
||||||
{
|
{
|
||||||
case DownloadStatus.Initializing:
|
case DownloadStatus.Initializing:
|
||||||
case DownloadStatus.WaitingForQueue:
|
|
||||||
playerDlQueue++;
|
playerDlQueue++;
|
||||||
totalDlQueue++;
|
totalDlQueue++;
|
||||||
break;
|
break;
|
||||||
@@ -497,6 +494,10 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
|||||||
playerDlSlot++;
|
playerDlSlot++;
|
||||||
totalDlSlot++;
|
totalDlSlot++;
|
||||||
break;
|
break;
|
||||||
|
case DownloadStatus.WaitingForQueue:
|
||||||
|
playerDlQueue++;
|
||||||
|
totalDlQueue++;
|
||||||
|
break;
|
||||||
case DownloadStatus.Downloading:
|
case DownloadStatus.Downloading:
|
||||||
playerDlProg++;
|
playerDlProg++;
|
||||||
totalDlProg++;
|
totalDlProg++;
|
||||||
@@ -549,6 +550,11 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
|||||||
if (totalFiles == 0 || totalBytes == 0)
|
if (totalFiles == 0 || totalBytes == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// max speed for per-player bar scale (clamped)
|
||||||
|
double maxSpeed = perPlayer.Count > 0 ? perPlayer.Max(p => p.SpeedBytesPerSecond) : 0;
|
||||||
|
if (maxSpeed <= 0)
|
||||||
|
maxSpeed = 1;
|
||||||
|
|
||||||
var drawList = ImGui.GetBackgroundDrawList();
|
var drawList = ImGui.GetBackgroundDrawList();
|
||||||
var windowPos = ImGui.GetWindowPos();
|
var windowPos = ImGui.GetWindowPos();
|
||||||
|
|
||||||
|
|||||||
@@ -504,7 +504,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
&& decompressed.LongLength != expectedRawSize)
|
&& decompressed.LongLength != expectedRawSize)
|
||||||
{
|
{
|
||||||
await _fileCompactor.WriteAllBytesAsync(filePath, Array.Empty<byte>(), ct).ConfigureAwait(false);
|
await _fileCompactor.WriteAllBytesAsync(filePath, Array.Empty<byte>(), ct).ConfigureAwait(false);
|
||||||
PersistFileToStorage(fileHash, filePath, repl.GamePath, skipDownscale, skipDecimation);
|
PersistFileToStorage(fileHash, filePath, repl.GamePath, skipDownscale);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -526,7 +526,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
// write to file without compacting during download
|
// write to file without compacting during download
|
||||||
await File.WriteAllBytesAsync(filePath, decompressed, ct).ConfigureAwait(false);
|
await File.WriteAllBytesAsync(filePath, decompressed, ct).ConfigureAwait(false);
|
||||||
PersistFileToStorage(fileHash, filePath, repl.GamePath, skipDownscale, skipDecimation);
|
PersistFileToStorage(fileHash, filePath, repl.GamePath, skipDownscale);
|
||||||
}, ct).ConfigureAwait(false);
|
}, ct).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|||||||
Reference in New Issue
Block a user