Compare commits
16 Commits
test-abel
...
2.0.2.72-D
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab305a249c | ||
|
|
9d104a9dd8 | ||
|
|
4eec363cd2 | ||
|
|
d00df84ed6 | ||
|
|
bcd3bd5ca2 | ||
|
|
9048b3bd87 | ||
|
|
c1829a9837 | ||
|
|
a2ed9f8d2b | ||
| 8e08da7471 | |||
|
|
cca23f6e05 | ||
|
|
3205e6e0c3 | ||
|
|
d16e46200d | ||
|
|
5fc13647ae | ||
|
|
39d5d9d7c1 | ||
|
|
c19db58ead | ||
| 30717ba200 |
@@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Authors></Authors>
|
<Authors></Authors>
|
||||||
<Company></Company>
|
<Company></Company>
|
||||||
<Version>2.0.3</Version>
|
<Version>2.0.2.72</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,16 +123,22 @@ 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 character = (Character*)playerPointer;
|
var gameObject = &character->GameObject;
|
||||||
if (character == null)
|
if (gameObject == 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)
|
||||||
@@ -169,11 +175,6 @@ 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();
|
||||||
|
|
||||||
@@ -219,10 +220,6 @@ 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))
|
||||||
@@ -672,7 +669,7 @@ public class PlayerDataFactory
|
|||||||
list.Add(forwardPaths[i].ToLowerInvariant());
|
list.Add(forwardPaths[i].ToLowerInvariant());
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
resolvedPaths[filePath] = [forwardPaths[i].ToLowerInvariant()];
|
resolvedPaths[filePath] = [forwardPathsLower[i]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase, IHighP
|
|||||||
if (msg.Address == Address)
|
if (msg.Address == Address)
|
||||||
{
|
{
|
||||||
_haltProcessing = false;
|
_haltProcessing = false;
|
||||||
|
Refresh();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -176,30 +177,36 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase, IHighP
|
|||||||
Mediator.Publish(new GameObjectHandlerDestroyedMessage(this, _isOwnedObject));
|
Mediator.Publish(new GameObjectHandlerDestroyedMessage(this, _isOwnedObject));
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe void CheckAndUpdateObject()
|
private void CheckAndUpdateObject() => CheckAndUpdateObject(allowPublish: true);
|
||||||
|
|
||||||
|
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;
|
||||||
var drawObjAddr = (IntPtr)gameObject->DrawObject;
|
DrawObjectAddress = (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) return;
|
if (_haltProcessing || !allowPublish) return;
|
||||||
|
|
||||||
bool drawObjDiff = DrawObjectAddress != prevDrawObj;
|
bool drawObjDiff = DrawObjectAddress != prevDrawObj;
|
||||||
bool addrDiff = Address != prevAddr;
|
bool addrDiff = Address != prevAddr;
|
||||||
@@ -356,12 +363,10 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase, IHighP
|
|||||||
|
|
||||||
private void FrameworkUpdate()
|
private void FrameworkUpdate()
|
||||||
{
|
{
|
||||||
if (!_delayedZoningTask?.IsCompleted ?? false) return;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_performanceCollector.LogPerformance(this, $"CheckAndUpdateObject>{(_isOwnedObject ? "Self" : "Other")}+{ObjectKind}/{(string.IsNullOrEmpty(Name) ? "Unk" : Name)}"
|
var zoningDelayActive = !(_delayedZoningTask?.IsCompleted ?? true);
|
||||||
+ $"+{Address.ToString("X")}", CheckAndUpdateObject);
|
_performanceCollector.LogPerformance(this, $"CheckAndUpdateObject>{(_isOwnedObject ? "Self" : "Other")}+{ObjectKind}/{(string.IsNullOrEmpty(Name) ? "Unk" : Name)}", () => CheckAndUpdateObject(allowPublish: !zoningDelayActive));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -462,6 +467,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,7 +25,6 @@ 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;
|
||||||
|
|
||||||
@@ -96,6 +95,9 @@ 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);
|
||||||
@@ -109,6 +111,9 @@ 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;
|
||||||
@@ -2455,6 +2460,8 @@ 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()
|
||||||
{
|
{
|
||||||
@@ -2855,6 +2862,26 @@ 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,
|
||||||
@@ -2879,6 +2906,8 @@ 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,7 +123,6 @@ 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,56 +955,103 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
|||||||
|
|
||||||
private unsafe void CheckCharacterForDrawing(nint address, string characterName)
|
private unsafe void CheckCharacterForDrawing(nint address, string characterName)
|
||||||
{
|
{
|
||||||
var gameObj = (GameObject*)address;
|
if (address == nint.Zero)
|
||||||
var drawObj = gameObj->DrawObject;
|
return;
|
||||||
bool isDrawing = false;
|
|
||||||
bool isDrawingChanged = false;
|
try
|
||||||
if ((nint)drawObj != IntPtr.Zero)
|
|
||||||
{
|
{
|
||||||
isDrawing = gameObj->RenderFlags == (VisibilityFlags)0b100000000000;
|
var gameObj = (GameObject*)address;
|
||||||
if (!isDrawing)
|
|
||||||
|
if (gameObj == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_objectTable.Any(o => o?.Address == address))
|
||||||
{
|
{
|
||||||
isDrawing = ((CharacterBase*)drawObj)->HasModelInSlotLoaded != 0;
|
_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;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isDrawing)
|
if (!isDrawing)
|
||||||
{
|
{
|
||||||
isDrawing = ((CharacterBase*)drawObj)->HasModelFilesInSlotLoaded != 0;
|
try
|
||||||
if (isDrawing && !string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal)
|
|
||||||
&& !string.Equals(_lastGlobalBlockReason, "HasModelFilesInSlotLoaded", StringComparison.Ordinal))
|
|
||||||
{
|
{
|
||||||
_lastGlobalBlockPlayer = characterName;
|
var charBase = (CharacterBase*)drawObj;
|
||||||
_lastGlobalBlockReason = "HasModelFilesInSlotLoaded";
|
if (charBase != null)
|
||||||
isDrawingChanged = true;
|
{
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal)
|
if (!string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal)
|
||||||
&& !string.Equals(_lastGlobalBlockReason, "HasModelInSlotLoaded", StringComparison.Ordinal))
|
&& !string.Equals(_lastGlobalBlockReason, "RenderFlags", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
_lastGlobalBlockPlayer = characterName;
|
_lastGlobalBlockPlayer = characterName;
|
||||||
_lastGlobalBlockReason = "HasModelInSlotLoaded";
|
_lastGlobalBlockReason = "RenderFlags";
|
||||||
isDrawingChanged = true;
|
isDrawingChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (isDrawingChanged)
|
||||||
{
|
{
|
||||||
if (!string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal)
|
_logger.LogTrace("Global draw block: START => {name} ({reason})", characterName, _lastGlobalBlockReason);
|
||||||
&& !string.Equals(_lastGlobalBlockReason, "RenderFlags", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
_lastGlobalBlockPlayer = characterName;
|
|
||||||
_lastGlobalBlockReason = "RenderFlags";
|
|
||||||
isDrawingChanged = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (isDrawingChanged)
|
IsAnythingDrawing |= isDrawing;
|
||||||
|
}
|
||||||
|
catch (AccessViolationException ex)
|
||||||
{
|
{
|
||||||
_logger.LogTrace("Global draw block: START => {name} ({reason})", characterName, _lastGlobalBlockReason);
|
_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"));
|
||||||
}
|
}
|
||||||
|
|
||||||
IsAnythingDrawing |= isDrawing;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FrameworkOnUpdate(IFramework framework)
|
private void FrameworkOnUpdate(IFramework framework)
|
||||||
@@ -1033,38 +1080,74 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
|||||||
}
|
}
|
||||||
|
|
||||||
var playerDescriptors = _actorObjectService.PlayerDescriptors;
|
var playerDescriptors = _actorObjectService.PlayerDescriptors;
|
||||||
for (var i = 0; i < playerDescriptors.Count; i++)
|
var descriptorCount = playerDescriptors.Count;
|
||||||
|
|
||||||
|
for (var i = 0; i < descriptorCount; i++)
|
||||||
{
|
{
|
||||||
var actor = playerDescriptors[i];
|
try
|
||||||
|
|
||||||
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"));
|
if (i >= playerDescriptors.Count)
|
||||||
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;
|
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"));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
catch (AccessViolationException ex)
|
||||||
{
|
{
|
||||||
break;
|
_logger.LogDebug(ex, "Access violation processing actor {index} - object likely destroyed", i);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, "Unexpected error processing actor {index}", i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!IsAnythingDrawing && !string.IsNullOrEmpty(_lastGlobalBlockPlayer))
|
if (!IsAnythingDrawing && !string.IsNullOrEmpty(_lastGlobalBlockPlayer))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -52,12 +52,8 @@ 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;
|
||||||
@@ -171,9 +167,12 @@ 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) => _currentDownloads[msg.DownloadId] = msg.DownloadStatus);
|
Mediator.Subscribe<DownloadStartedMessage>(this, msg =>
|
||||||
|
{
|
||||||
|
_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.ToList());
|
Mediator.Subscribe<RefreshUiMessage>(this, (msg) => _drawFolders = [.. DrawFolders]);
|
||||||
|
|
||||||
_characterAnalyzer = characterAnalyzer;
|
_characterAnalyzer = characterAnalyzer;
|
||||||
_playerPerformanceConfig = playerPerformanceConfig;
|
_playerPerformanceConfig = playerPerformanceConfig;
|
||||||
|
|||||||
@@ -65,12 +65,14 @@ 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 totalFiles = msg.DownloadStatus.Values.Sum(s => s.TotalFiles);
|
var snap = msg.DownloadStatus.ToArray();
|
||||||
var totalBytes = msg.DownloadStatus.Values.Sum(s => s.TotalBytes);
|
var totalFiles = snap.Sum(kv => kv.Value?.TotalFiles ?? 0);
|
||||||
|
var totalBytes = snap.Sum(kv => kv.Value?.TotalBytes ?? 0);
|
||||||
|
|
||||||
_downloadInitialTotals[msg.DownloadId] = (totalFiles, totalBytes);
|
_downloadInitialTotals[msg.DownloadId] = (totalFiles, totalBytes);
|
||||||
_notificationDismissed = false;
|
_notificationDismissed = false;
|
||||||
});
|
});
|
||||||
@@ -79,7 +81,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.Any() && !_notificationDismissed)
|
if (_currentDownloads.IsEmpty && !_notificationDismissed)
|
||||||
{
|
{
|
||||||
Mediator.Publish(new LightlessNotificationDismissMessage("pair_download_progress"));
|
Mediator.Publish(new LightlessNotificationDismissMessage("pair_download_progress"));
|
||||||
_notificationDismissed = true;
|
_notificationDismissed = true;
|
||||||
@@ -474,7 +476,7 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
|||||||
totalBytes += playerTotalBytes;
|
totalBytes += playerTotalBytes;
|
||||||
transferredBytes += playerTransferredBytes;
|
transferredBytes += playerTransferredBytes;
|
||||||
|
|
||||||
// per-player W/Q/P/D
|
// per-player W/Q/P/D/C
|
||||||
var playerDlSlot = 0;
|
var playerDlSlot = 0;
|
||||||
var playerDlQueue = 0;
|
var playerDlQueue = 0;
|
||||||
var playerDlProg = 0;
|
var playerDlProg = 0;
|
||||||
@@ -487,6 +489,7 @@ 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;
|
||||||
@@ -494,10 +497,6 @@ 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++;
|
||||||
@@ -550,11 +549,6 @@ 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);
|
PersistFileToStorage(fileHash, filePath, repl.GamePath, skipDownscale, skipDecimation);
|
||||||
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);
|
PersistFileToStorage(fileHash, filePath, repl.GamePath, skipDownscale, skipDecimation);
|
||||||
}, ct).ConfigureAwait(false);
|
}, ct).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|||||||
Reference in New Issue
Block a user