Compare commits
34 Commits
2.0.2.72-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>
|
||||
<Authors></Authors>
|
||||
<Company></Company>
|
||||
<Version>2.0.2.72</Version>
|
||||
<Version>2.0.3</Version>
|
||||
<Description></Description>
|
||||
<Copyright></Copyright>
|
||||
<PackageProjectUrl>https://github.com/Light-Public-Syncshells/LightlessClient</PackageProjectUrl>
|
||||
|
||||
@@ -123,22 +123,16 @@ public class PlayerDataFactory
|
||||
{
|
||||
if (playerPointer == IntPtr.Zero)
|
||||
return true;
|
||||
try
|
||||
{
|
||||
var character = (Character*)playerPointer;
|
||||
if (character == null)
|
||||
return true;
|
||||
|
||||
var gameObject = &character->GameObject;
|
||||
if (gameObject == null)
|
||||
return true;
|
||||
|
||||
return gameObject->DrawObject == null;
|
||||
}
|
||||
catch (AccessViolationException)
|
||||
{
|
||||
var character = (Character*)playerPointer;
|
||||
if (character == null)
|
||||
return true;
|
||||
}
|
||||
|
||||
var gameObject = &character->GameObject;
|
||||
if (gameObject == null)
|
||||
return true;
|
||||
|
||||
return gameObject->DrawObject == null;
|
||||
}
|
||||
|
||||
private static bool IsCacheFresh(CacheEntry entry)
|
||||
@@ -175,6 +169,11 @@ public class PlayerDataFactory
|
||||
using var cts = new CancellationTokenSource(_hardBuildTimeout);
|
||||
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);
|
||||
PruneCharacterCacheIfNeeded();
|
||||
|
||||
@@ -220,6 +219,10 @@ public class PlayerDataFactory
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// 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();
|
||||
|
||||
if (await CheckForNullDrawObject(playerRelatedObject.Address).ConfigureAwait(false))
|
||||
@@ -669,7 +672,7 @@ public class PlayerDataFactory
|
||||
list.Add(forwardPaths[i].ToLowerInvariant());
|
||||
else
|
||||
{
|
||||
resolvedPaths[filePath] = [forwardPathsLower[i]];
|
||||
resolvedPaths[filePath] = [forwardPaths[i].ToLowerInvariant()];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,6 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase, IHighP
|
||||
if (msg.Address == Address)
|
||||
{
|
||||
_haltProcessing = false;
|
||||
Refresh();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -177,36 +176,30 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase, IHighP
|
||||
Mediator.Publish(new GameObjectHandlerDestroyedMessage(this, _isOwnedObject));
|
||||
}
|
||||
|
||||
private void CheckAndUpdateObject() => CheckAndUpdateObject(allowPublish: true);
|
||||
|
||||
private unsafe void CheckAndUpdateObject(bool allowPublish = true)
|
||||
private unsafe void CheckAndUpdateObject()
|
||||
{
|
||||
var prevAddr = Address;
|
||||
var prevDrawObj = DrawObjectAddress;
|
||||
|
||||
Address = _getAddress();
|
||||
|
||||
if (Address != IntPtr.Zero)
|
||||
{
|
||||
var gameObject = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)Address;
|
||||
DrawObjectAddress = (IntPtr)gameObject->DrawObject;
|
||||
var drawObjAddr = (IntPtr)gameObject->DrawObject;
|
||||
DrawObjectAddress = drawObjAddr;
|
||||
EntityId = gameObject->EntityId;
|
||||
|
||||
var chara = (Character*)Address;
|
||||
var newName = chara->GameObject.NameString;
|
||||
|
||||
if (!string.IsNullOrEmpty(newName) && !string.Equals(newName, Name, StringComparison.Ordinal))
|
||||
Name = newName;
|
||||
CurrentDrawCondition = DrawCondition.None;
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawObjectAddress = IntPtr.Zero;
|
||||
EntityId = uint.MaxValue;
|
||||
CurrentDrawCondition = DrawCondition.DrawObjectZero;
|
||||
}
|
||||
|
||||
CurrentDrawCondition = IsBeingDrawnUnsafe();
|
||||
|
||||
if (_haltProcessing || !allowPublish) return;
|
||||
if (_haltProcessing) return;
|
||||
|
||||
bool drawObjDiff = DrawObjectAddress != prevDrawObj;
|
||||
bool addrDiff = Address != prevAddr;
|
||||
@@ -363,10 +356,12 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase, IHighP
|
||||
|
||||
private void FrameworkUpdate()
|
||||
{
|
||||
if (!_delayedZoningTask?.IsCompleted ?? false) return;
|
||||
|
||||
try
|
||||
{
|
||||
var zoningDelayActive = !(_delayedZoningTask?.IsCompleted ?? true);
|
||||
_performanceCollector.LogPerformance(this, $"CheckAndUpdateObject>{(_isOwnedObject ? "Self" : "Other")}+{ObjectKind}/{(string.IsNullOrEmpty(Name) ? "Unk" : Name)}", () => CheckAndUpdateObject(allowPublish: !zoningDelayActive));
|
||||
_performanceCollector.LogPerformance(this, $"CheckAndUpdateObject>{(_isOwnedObject ? "Self" : "Other")}+{ObjectKind}/{(string.IsNullOrEmpty(Name) ? "Unk" : Name)}"
|
||||
+ $"+{Address.ToString("X")}", CheckAndUpdateObject);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -467,6 +462,6 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase, IHighP
|
||||
Logger.LogDebug("[{this}] Delay after zoning complete", this);
|
||||
_zoningCts.Dispose();
|
||||
}
|
||||
}, _zoningCts.Token);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ using Microsoft.Extensions.Logging;
|
||||
using DalamudObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
|
||||
using ObjectKind = LightlessSync.API.Data.Enum.ObjectKind;
|
||||
using FileReplacementDataComparer = LightlessSync.PlayerData.Data.FileReplacementDataComparer;
|
||||
using LightlessSync.LightlessConfiguration;
|
||||
|
||||
namespace LightlessSync.PlayerData.Pairs;
|
||||
|
||||
@@ -95,9 +96,6 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
private readonly Dictionary<ObjectKind, HashSet<PlayerChanges>> _pendingOwnedChanges = new();
|
||||
private CancellationTokenSource? _ownedRetryCts;
|
||||
private Task _ownedRetryTask = Task.CompletedTask;
|
||||
|
||||
|
||||
|
||||
private static readonly TimeSpan OwnedRetryInitialDelay = TimeSpan.FromSeconds(1);
|
||||
private static readonly TimeSpan OwnedRetryMaxDelay = TimeSpan.FromSeconds(10);
|
||||
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 AnimationValidationMode _lastAnimMode = (AnimationValidationMode)(-1);
|
||||
private bool _lastAllowOneBasedShift;
|
||||
private bool _lastAllowNeighborTolerance;
|
||||
private readonly ConcurrentDictionary<string, byte> _dumpedRemoteSkeletonForHash = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
private DateTime? _invisibleSinceUtc;
|
||||
@@ -2460,8 +2455,6 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
|
||||
try
|
||||
{
|
||||
RefreshPapBlockCacheIfAnimSettingsChanged();
|
||||
|
||||
var replacementList = charaData.FileReplacements.SelectMany(k => k.Value.Where(v => string.IsNullOrEmpty(v.FileSwapPath))).ToList();
|
||||
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(
|
||||
Dictionary<(string GamePath, string? Hash), string> moddedPaths,
|
||||
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,
|
||||
CancellationToken token)
|
||||
{
|
||||
RefreshPapBlockCacheIfAnimSettingsChanged();
|
||||
|
||||
var mode = _configService.Current.AnimationValidationMode;
|
||||
var allowBasedShift = _configService.Current.AnimationAllowOneBasedShift;
|
||||
var allownNightIndex = _configService.Current.AnimationAllowNeighborIndexTolerance;
|
||||
|
||||
@@ -123,6 +123,7 @@ public sealed class Plugin : IDalamudPlugin
|
||||
services.AddSingleton<HubFactory>();
|
||||
services.AddSingleton<FileUploadManager>();
|
||||
services.AddSingleton<FileTransferOrchestrator>();
|
||||
services.AddSingleton<FileDownloadDeduplicator>();
|
||||
services.AddSingleton<LightlessPlugin>();
|
||||
services.AddSingleton<LightlessProfileManager>();
|
||||
services.AddSingleton<TextureCompressionService>();
|
||||
|
||||
@@ -955,103 +955,56 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
||||
|
||||
private unsafe void CheckCharacterForDrawing(nint address, string characterName)
|
||||
{
|
||||
if (address == nint.Zero)
|
||||
return;
|
||||
|
||||
try
|
||||
var gameObj = (GameObject*)address;
|
||||
var drawObj = gameObj->DrawObject;
|
||||
bool isDrawing = false;
|
||||
bool isDrawingChanged = false;
|
||||
if ((nint)drawObj != IntPtr.Zero)
|
||||
{
|
||||
var gameObj = (GameObject*)address;
|
||||
|
||||
if (gameObj == null)
|
||||
return;
|
||||
|
||||
if (!_objectTable.Any(o => o?.Address == address))
|
||||
isDrawing = gameObj->RenderFlags == (VisibilityFlags)0b100000000000;
|
||||
if (!isDrawing)
|
||||
{
|
||||
_logger.LogDebug("Character {name} at {addr} no longer in object table", characterName, address.ToString("X"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (gameObj->ObjectKind == 0)
|
||||
return;
|
||||
|
||||
var drawObj = gameObj->DrawObject;
|
||||
bool isDrawing = false;
|
||||
bool isDrawingChanged = false;
|
||||
|
||||
if ((nint)drawObj != IntPtr.Zero)
|
||||
{
|
||||
try
|
||||
{
|
||||
isDrawing = gameObj->RenderFlags == (VisibilityFlags)0b100000000000;
|
||||
}
|
||||
catch (AccessViolationException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
isDrawing = ((CharacterBase*)drawObj)->HasModelInSlotLoaded != 0;
|
||||
if (!isDrawing)
|
||||
{
|
||||
try
|
||||
isDrawing = ((CharacterBase*)drawObj)->HasModelFilesInSlotLoaded != 0;
|
||||
if (isDrawing && !string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal)
|
||||
&& !string.Equals(_lastGlobalBlockReason, "HasModelFilesInSlotLoaded", StringComparison.Ordinal))
|
||||
{
|
||||
var charBase = (CharacterBase*)drawObj;
|
||||
if (charBase != null)
|
||||
{
|
||||
isDrawing = charBase->HasModelInSlotLoaded != 0;
|
||||
if (!isDrawing)
|
||||
{
|
||||
isDrawing = charBase->HasModelFilesInSlotLoaded != 0;
|
||||
if (isDrawing && !string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal)
|
||||
&& !string.Equals(_lastGlobalBlockReason, "HasModelFilesInSlotLoaded", StringComparison.Ordinal))
|
||||
{
|
||||
_lastGlobalBlockPlayer = characterName;
|
||||
_lastGlobalBlockReason = "HasModelFilesInSlotLoaded";
|
||||
isDrawingChanged = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal)
|
||||
&& !string.Equals(_lastGlobalBlockReason, "HasModelInSlotLoaded", StringComparison.Ordinal))
|
||||
{
|
||||
_lastGlobalBlockPlayer = characterName;
|
||||
_lastGlobalBlockReason = "HasModelInSlotLoaded";
|
||||
isDrawingChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (AccessViolationException)
|
||||
{
|
||||
return;
|
||||
_lastGlobalBlockPlayer = characterName;
|
||||
_lastGlobalBlockReason = "HasModelFilesInSlotLoaded";
|
||||
isDrawingChanged = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal)
|
||||
&& !string.Equals(_lastGlobalBlockReason, "RenderFlags", StringComparison.Ordinal))
|
||||
&& !string.Equals(_lastGlobalBlockReason, "HasModelInSlotLoaded", StringComparison.Ordinal))
|
||||
{
|
||||
_lastGlobalBlockPlayer = characterName;
|
||||
_lastGlobalBlockReason = "RenderFlags";
|
||||
_lastGlobalBlockReason = "HasModelInSlotLoaded";
|
||||
isDrawingChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isDrawingChanged)
|
||||
else
|
||||
{
|
||||
_logger.LogTrace("Global draw block: START => {name} ({reason})", characterName, _lastGlobalBlockReason);
|
||||
if (!string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal)
|
||||
&& !string.Equals(_lastGlobalBlockReason, "RenderFlags", StringComparison.Ordinal))
|
||||
{
|
||||
_lastGlobalBlockPlayer = characterName;
|
||||
_lastGlobalBlockReason = "RenderFlags";
|
||||
isDrawingChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IsAnythingDrawing |= isDrawing;
|
||||
}
|
||||
catch (AccessViolationException ex)
|
||||
if (isDrawingChanged)
|
||||
{
|
||||
_logger.LogDebug(ex, "Memory access violation checking character {name} at {addr}", characterName, address.ToString("X"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Unexpected error checking character {name} at {addr}", characterName, address.ToString("X"));
|
||||
_logger.LogTrace("Global draw block: START => {name} ({reason})", characterName, _lastGlobalBlockReason);
|
||||
}
|
||||
|
||||
IsAnythingDrawing |= isDrawing;
|
||||
}
|
||||
|
||||
private void FrameworkOnUpdate(IFramework framework)
|
||||
@@ -1080,74 +1033,38 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
||||
}
|
||||
|
||||
var playerDescriptors = _actorObjectService.PlayerDescriptors;
|
||||
var descriptorCount = playerDescriptors.Count;
|
||||
|
||||
for (var i = 0; i < descriptorCount; i++)
|
||||
for (var i = 0; i < playerDescriptors.Count; i++)
|
||||
{
|
||||
try
|
||||
var actor = playerDescriptors[i];
|
||||
|
||||
var playerAddress = actor.Address;
|
||||
if (playerAddress == nint.Zero)
|
||||
continue;
|
||||
|
||||
if (actor.ObjectIndex >= 200)
|
||||
continue;
|
||||
|
||||
if (_blockedCharacterHandler.IsCharacterBlocked(playerAddress, actor.ObjectIndex, out bool firstTime) && firstTime)
|
||||
{
|
||||
if (i >= playerDescriptors.Count)
|
||||
_logger.LogTrace("Skipping character {addr}, blocked/muted", playerAddress.ToString("X"));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsAnythingDrawing)
|
||||
{
|
||||
var gameObj = (GameObject*)playerAddress;
|
||||
var currentName = gameObj != null ? gameObj->NameString ?? string.Empty : string.Empty;
|
||||
var charaName = string.IsNullOrEmpty(currentName) ? actor.Name : currentName;
|
||||
CheckCharacterForDrawing(playerAddress, charaName);
|
||||
if (IsAnythingDrawing)
|
||||
break;
|
||||
|
||||
var actor = playerDescriptors[i];
|
||||
|
||||
var playerAddress = actor.Address;
|
||||
if (playerAddress == nint.Zero)
|
||||
continue;
|
||||
|
||||
if (actor.ObjectIndex >= 200)
|
||||
continue;
|
||||
|
||||
if (_blockedCharacterHandler.IsCharacterBlocked(playerAddress, actor.ObjectIndex, out bool firstTime) && firstTime)
|
||||
{
|
||||
_logger.LogTrace("Skipping character {addr}, blocked/muted", playerAddress.ToString("X"));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsAnythingDrawing)
|
||||
{
|
||||
try
|
||||
{
|
||||
var gameObj = (GameObject*)playerAddress;
|
||||
|
||||
if (gameObj == null || gameObj->ObjectKind == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string currentName;
|
||||
try
|
||||
{
|
||||
currentName = gameObj->NameString ?? string.Empty;
|
||||
}
|
||||
catch (AccessViolationException)
|
||||
{
|
||||
currentName = string.Empty;
|
||||
}
|
||||
|
||||
var charaName = string.IsNullOrEmpty(currentName) ? actor.Name : currentName;
|
||||
|
||||
CheckCharacterForDrawing(playerAddress, charaName);
|
||||
|
||||
if (IsAnythingDrawing)
|
||||
break;
|
||||
}
|
||||
catch (AccessViolationException ex)
|
||||
{
|
||||
_logger.LogDebug(ex, "Access violation on GameObject pointer for actor {index} at {addr}", i, playerAddress.ToString("X"));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (AccessViolationException ex)
|
||||
else
|
||||
{
|
||||
_logger.LogDebug(ex, "Access violation processing actor {index} - object likely destroyed", i);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Unexpected error processing actor {index}", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (!IsAnythingDrawing && !string.IsNullOrEmpty(_lastGlobalBlockPlayer))
|
||||
{
|
||||
|
||||
@@ -52,8 +52,12 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
private readonly LightlessConfigService _configService;
|
||||
private readonly LightlessMediator _lightlessMediator;
|
||||
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 PairUiService _pairUiService;
|
||||
private readonly PlayerPerformanceConfigService _playerPerformanceConfig;
|
||||
private readonly ServerConfigurationManager _serverManager;
|
||||
private readonly TagHandler _tagHandler;
|
||||
private readonly UiSharedService _uiSharedService;
|
||||
@@ -167,12 +171,9 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
Mediator.Subscribe<SwitchToIntroUiMessage>(this, (_) => IsOpen = false);
|
||||
Mediator.Subscribe<CutsceneStartMessage>(this, (_) => UiSharedService_GposeStart());
|
||||
Mediator.Subscribe<CutsceneEndMessage>(this, (_) => UiSharedService_GposeEnd());
|
||||
Mediator.Subscribe<DownloadStartedMessage>(this, msg =>
|
||||
{
|
||||
_currentDownloads[msg.DownloadId] = new Dictionary<string, FileDownloadStatus>(msg.DownloadStatus, StringComparer.Ordinal);
|
||||
});
|
||||
Mediator.Subscribe<DownloadStartedMessage>(this, (msg) => _currentDownloads[msg.DownloadId] = msg.DownloadStatus);
|
||||
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;
|
||||
_playerPerformanceConfig = playerPerformanceConfig;
|
||||
|
||||
@@ -65,14 +65,12 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
|
||||
IsOpen = true;
|
||||
|
||||
Mediator.Subscribe<DownloadStartedMessage>(this, msg =>
|
||||
Mediator.Subscribe<DownloadStartedMessage>(this, (msg) =>
|
||||
{
|
||||
_currentDownloads[msg.DownloadId] = msg.DownloadStatus;
|
||||
|
||||
var snap = msg.DownloadStatus.ToArray();
|
||||
var totalFiles = snap.Sum(kv => kv.Value?.TotalFiles ?? 0);
|
||||
var totalBytes = snap.Sum(kv => kv.Value?.TotalBytes ?? 0);
|
||||
|
||||
// Capture initial totals when download starts
|
||||
var totalFiles = msg.DownloadStatus.Values.Sum(s => s.TotalFiles);
|
||||
var totalBytes = msg.DownloadStatus.Values.Sum(s => s.TotalBytes);
|
||||
_downloadInitialTotals[msg.DownloadId] = (totalFiles, totalBytes);
|
||||
_notificationDismissed = false;
|
||||
});
|
||||
@@ -81,7 +79,7 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
_currentDownloads.TryRemove(msg.DownloadId, out _);
|
||||
|
||||
// Dismiss notification if all downloads are complete
|
||||
if (_currentDownloads.IsEmpty && !_notificationDismissed)
|
||||
if (!_currentDownloads.Any() && !_notificationDismissed)
|
||||
{
|
||||
Mediator.Publish(new LightlessNotificationDismissMessage("pair_download_progress"));
|
||||
_notificationDismissed = true;
|
||||
@@ -476,7 +474,7 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
totalBytes += playerTotalBytes;
|
||||
transferredBytes += playerTransferredBytes;
|
||||
|
||||
// per-player W/Q/P/D/C
|
||||
// per-player W/Q/P/D
|
||||
var playerDlSlot = 0;
|
||||
var playerDlQueue = 0;
|
||||
var playerDlProg = 0;
|
||||
@@ -489,7 +487,6 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
switch (fileStatus.DownloadStatus)
|
||||
{
|
||||
case DownloadStatus.Initializing:
|
||||
case DownloadStatus.WaitingForQueue:
|
||||
playerDlQueue++;
|
||||
totalDlQueue++;
|
||||
break;
|
||||
@@ -497,6 +494,10 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
playerDlSlot++;
|
||||
totalDlSlot++;
|
||||
break;
|
||||
case DownloadStatus.WaitingForQueue:
|
||||
playerDlQueue++;
|
||||
totalDlQueue++;
|
||||
break;
|
||||
case DownloadStatus.Downloading:
|
||||
playerDlProg++;
|
||||
totalDlProg++;
|
||||
@@ -549,6 +550,11 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
if (totalFiles == 0 || totalBytes == 0)
|
||||
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 windowPos = ImGui.GetWindowPos();
|
||||
|
||||
|
||||
@@ -504,7 +504,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
||||
&& decompressed.LongLength != expectedRawSize)
|
||||
{
|
||||
await _fileCompactor.WriteAllBytesAsync(filePath, Array.Empty<byte>(), ct).ConfigureAwait(false);
|
||||
PersistFileToStorage(fileHash, filePath, repl.GamePath, skipDownscale, skipDecimation);
|
||||
PersistFileToStorage(fileHash, filePath, repl.GamePath, skipDownscale);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -526,7 +526,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
||||
|
||||
// write to file without compacting during download
|
||||
await File.WriteAllBytesAsync(filePath, decompressed, ct).ConfigureAwait(false);
|
||||
PersistFileToStorage(fileHash, filePath, repl.GamePath, skipDownscale, skipDecimation);
|
||||
PersistFileToStorage(fileHash, filePath, repl.GamePath, skipDownscale);
|
||||
}, ct).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
|
||||
Reference in New Issue
Block a user