potentially fixes an issue with cache creation, do not remove mediator on failure

This commit is contained in:
rootdarkarchon
2023-02-28 22:45:00 +01:00
parent a37281b719
commit ebe9ea47be
10 changed files with 45 additions and 59 deletions

Submodule MareAPI updated: 381f9a4808...85bedb49e3

View File

@@ -25,7 +25,6 @@ public class CharacterDataFactory : MediatorSubscriberBase
private readonly TransientResourceManager _transientResourceManager; private readonly TransientResourceManager _transientResourceManager;
private readonly FileCacheManager _fileCacheManager; private readonly FileCacheManager _fileCacheManager;
private readonly PerformanceCollector _performanceCollector; private readonly PerformanceCollector _performanceCollector;
private readonly ConcurrentQueue<Task<string>> _processingQueue = new();
public CharacterDataFactory(ILogger<CharacterDataFactory> logger, DalamudUtil dalamudUtil, IpcManager ipcManager, public CharacterDataFactory(ILogger<CharacterDataFactory> logger, DalamudUtil dalamudUtil, IpcManager ipcManager,
TransientResourceManager transientResourceManager, FileCacheManager fileReplacementFactory, MareMediator mediator, TransientResourceManager transientResourceManager, FileCacheManager fileReplacementFactory, MareMediator mediator,
@@ -37,13 +36,6 @@ public class CharacterDataFactory : MediatorSubscriberBase
_transientResourceManager = transientResourceManager; _transientResourceManager = transientResourceManager;
_fileCacheManager = fileReplacementFactory; _fileCacheManager = fileReplacementFactory;
_performanceCollector = performanceCollector; _performanceCollector = performanceCollector;
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) =>
{
while (_processingQueue.TryDequeue(out var result))
{
result.RunSynchronously();
}
});
} }
private unsafe bool CheckForNullDrawObject(IntPtr playerPointer) private unsafe bool CheckForNullDrawObject(IntPtr playerPointer)
@@ -51,14 +43,14 @@ public class CharacterDataFactory : MediatorSubscriberBase
return ((Character*)playerPointer)->GameObject.DrawObject == null; return ((Character*)playerPointer)->GameObject.DrawObject == null;
} }
public async Task<CharacterData> BuildCharacterData(CharacterData previousData, GameObjectHandler playerRelatedObject, CancellationToken token) public async Task BuildCharacterData(CharacterData previousData, GameObjectHandler playerRelatedObject, CancellationToken token)
{ {
if (!_ipcManager.Initialized) if (!_ipcManager.Initialized)
{ {
throw new InvalidOperationException("Penumbra is not connected"); throw new InvalidOperationException("Penumbra is not connected");
} }
if (playerRelatedObject == null) return previousData; if (playerRelatedObject == null) return;
bool pointerIsZero = true; bool pointerIsZero = true;
try try
@@ -84,7 +76,7 @@ public class CharacterDataFactory : MediatorSubscriberBase
_logger.LogTrace("Pointer was zero for {objectKind}", playerRelatedObject.ObjectKind); _logger.LogTrace("Pointer was zero for {objectKind}", playerRelatedObject.ObjectKind);
previousData.FileReplacements.Remove(playerRelatedObject.ObjectKind); previousData.FileReplacements.Remove(playerRelatedObject.ObjectKind);
previousData.GlamourerString.Remove(playerRelatedObject.ObjectKind); previousData.GlamourerString.Remove(playerRelatedObject.ObjectKind);
return previousData; return;
} }
var previousFileReplacements = previousData.FileReplacements.ToDictionary(d => d.Key, d => d.Value); var previousFileReplacements = previousData.FileReplacements.ToDictionary(d => d.Key, d => d.Value);
@@ -92,15 +84,14 @@ public class CharacterDataFactory : MediatorSubscriberBase
try try
{ {
_processingQueue.Clear(); await _performanceCollector.LogPerformance(this, "CreateCharacterData>" + playerRelatedObject.ObjectKind, async () =>
return await _performanceCollector.LogPerformance(this, "CreateCharacterData>" + playerRelatedObject.ObjectKind, async () =>
{ {
return await CreateCharacterData(previousData, playerRelatedObject, token).ConfigureAwait(false); await CreateCharacterData(previousData, playerRelatedObject, token).ConfigureAwait(false);
}).ConfigureAwait(true); }).ConfigureAwait(true);
return;
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
_processingQueue.Clear();
_logger.LogDebug("Cancelled creating Character data for {object}", playerRelatedObject); _logger.LogDebug("Cancelled creating Character data for {object}", playerRelatedObject);
throw; throw;
} }
@@ -111,7 +102,7 @@ public class CharacterDataFactory : MediatorSubscriberBase
previousData.FileReplacements = previousFileReplacements; previousData.FileReplacements = previousFileReplacements;
previousData.GlamourerString = previousGlamourerData; previousData.GlamourerString = previousGlamourerData;
return previousData; return;
} }
private async Task<CharacterData> CreateCharacterData(CharacterData previousData, GameObjectHandler playerRelatedObject, CancellationToken token) private async Task<CharacterData> CreateCharacterData(CharacterData previousData, GameObjectHandler playerRelatedObject, CancellationToken token)

View File

@@ -1,11 +1,9 @@
using FFXIVClientStructs.FFXIV.Client.Game.Character; using MareSynchronos.API.Data.Enum;
using MareSynchronos.API.Data.Enum;
using MareSynchronos.Factories; using MareSynchronos.Factories;
using MareSynchronos.Mediator; using MareSynchronos.Mediator;
using MareSynchronos.Models; using MareSynchronos.Models;
using MareSynchronos.Utils; using MareSynchronos.Utils;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Collections;
namespace MareSynchronos.Managers; namespace MareSynchronos.Managers;
@@ -14,7 +12,7 @@ public class CacheCreationService : MediatorSubscriberBase, IDisposable
private readonly CharacterDataFactory _characterDataFactory; private readonly CharacterDataFactory _characterDataFactory;
private Task? _cacheCreationTask; private Task? _cacheCreationTask;
private readonly Dictionary<ObjectKind, GameObjectHandler> _cachesToCreate = new(); private readonly Dictionary<ObjectKind, GameObjectHandler> _cachesToCreate = new();
private readonly CharacterData _lastCreatedData = new(); private readonly CharacterData _playerData = new();
private readonly CancellationTokenSource _cts = new(); private readonly CancellationTokenSource _cts = new();
private readonly List<GameObjectHandler> _playerRelatedObjects = new(); private readonly List<GameObjectHandler> _playerRelatedObjects = new();
private CancellationTokenSource _palettePlusCts = new(); private CancellationTokenSource _palettePlusCts = new();
@@ -43,9 +41,9 @@ public class CacheCreationService : MediatorSubscriberBase, IDisposable
Task.Run(() => Task.Run(() =>
{ {
var actualMsg = (ClearCacheForObjectMessage)msg; var actualMsg = (ClearCacheForObjectMessage)msg;
_lastCreatedData.FileReplacements.Remove(actualMsg.ObjectToCreateFor.ObjectKind); _playerData.FileReplacements.Remove(actualMsg.ObjectToCreateFor.ObjectKind);
_lastCreatedData.GlamourerString.Remove(actualMsg.ObjectToCreateFor.ObjectKind); _playerData.GlamourerString.Remove(actualMsg.ObjectToCreateFor.ObjectKind);
Mediator.Publish(new CharacterDataCreatedMessage(_lastCreatedData)); Mediator.Publish(new CharacterDataCreatedMessage(_playerData.ToAPI()));
}); });
}); });
Mediator.Subscribe<DelayedFrameworkUpdateMessage>(this, (msg) => ProcessCacheCreation()); Mediator.Subscribe<DelayedFrameworkUpdateMessage>(this, (msg) => ProcessCacheCreation());
@@ -57,9 +55,9 @@ public class CacheCreationService : MediatorSubscriberBase, IDisposable
private void PalettePlusChanged(PalettePlusMessage msg) private void PalettePlusChanged(PalettePlusMessage msg)
{ {
if (!string.Equals(msg.Data, _lastCreatedData.PalettePlusPalette, StringComparison.Ordinal)) if (!string.Equals(msg.Data, _playerData.PalettePlusPalette, StringComparison.Ordinal))
{ {
_lastCreatedData.PalettePlusPalette = msg.Data ?? string.Empty; _playerData.PalettePlusPalette = msg.Data ?? string.Empty;
_palettePlusCts?.Cancel(); _palettePlusCts?.Cancel();
_palettePlusCts?.Dispose(); _palettePlusCts?.Dispose();
@@ -69,26 +67,26 @@ public class CacheCreationService : MediatorSubscriberBase, IDisposable
Task.Run(async () => Task.Run(async () =>
{ {
await Task.Delay(TimeSpan.FromSeconds(1), token).ConfigureAwait(false); await Task.Delay(TimeSpan.FromSeconds(1), token).ConfigureAwait(false);
Mediator.Publish(new CharacterDataCreatedMessage(_lastCreatedData)); Mediator.Publish(new CharacterDataCreatedMessage(_playerData.ToAPI()));
}, token); }, token);
} }
} }
private void HeelsOffsetChanged(HeelsOffsetMessage msg) private void HeelsOffsetChanged(HeelsOffsetMessage msg)
{ {
if (msg.Offset != _lastCreatedData.HeelsOffset) if (msg.Offset != _playerData.HeelsOffset)
{ {
_lastCreatedData.HeelsOffset = msg.Offset; _playerData.HeelsOffset = msg.Offset;
Mediator.Publish(new CharacterDataCreatedMessage(_lastCreatedData)); Mediator.Publish(new CharacterDataCreatedMessage(_playerData.ToAPI()));
} }
} }
private void CustomizePlusChanged(CustomizePlusMessage msg) private void CustomizePlusChanged(CustomizePlusMessage msg)
{ {
if (!string.Equals(msg.Data, _lastCreatedData.CustomizePlusScale, StringComparison.Ordinal)) if (!string.Equals(msg.Data, _playerData.CustomizePlusScale, StringComparison.Ordinal))
{ {
_lastCreatedData.CustomizePlusScale = msg.Data ?? string.Empty; _playerData.CustomizePlusScale = msg.Data ?? string.Empty;
Mediator.Publish(new CharacterDataCreatedMessage(_lastCreatedData)); Mediator.Publish(new CharacterDataCreatedMessage(_playerData.ToAPI()));
} }
} }
@@ -104,9 +102,18 @@ public class CacheCreationService : MediatorSubscriberBase, IDisposable
{ {
foreach (var obj in toCreate) foreach (var obj in toCreate)
{ {
var data = await _characterDataFactory.BuildCharacterData(_lastCreatedData, obj.Value, _cts.Token).ConfigureAwait(false); await _characterDataFactory.BuildCharacterData(_playerData, obj.Value, _cts.Token).ConfigureAwait(false);
} }
Mediator.Publish(new CharacterDataCreatedMessage(_lastCreatedData));
int maxWaitingTime = 10000;
while (!_playerData.IsReady && maxWaitingTime > 0)
{
await Task.Delay(100).ConfigureAwait(false);
maxWaitingTime -= 100;
_logger.LogTrace("Waiting for Cache to be ready");
}
Mediator.Publish(new CharacterDataCreatedMessage(_playerData.ToAPI()));
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -115,7 +122,6 @@ public class CacheCreationService : MediatorSubscriberBase, IDisposable
finally finally
{ {
_logger.LogDebug("Cache Creation complete"); _logger.LogDebug("Cache Creation complete");
} }
}, _cts.Token); }, _cts.Token);
} }

View File

@@ -378,6 +378,7 @@ public class CachedPlayer : MediatorSubscriberBase, IDisposable
if (downloadToken.IsCancellationRequested) if (downloadToken.IsCancellationRequested)
{ {
_logger.LogTrace("Detected cancellation"); _logger.LogTrace("Detected cancellation");
_apiController.CancelDownload(downloadId);
return; return;
} }
@@ -415,12 +416,6 @@ public class CachedPlayer : MediatorSubscriberBase, IDisposable
_logger.LogDebug("[{applicationId}] Application finished", _applicationId); _logger.LogDebug("[{applicationId}] Application finished", _applicationId);
}); });
_downloadCancellationTokenSource = null;
_logger.LogDebug("Download was cancelled");
_apiController.CancelDownload(downloadId);
}, downloadToken); }, downloadToken);
} }

View File

@@ -28,7 +28,7 @@ public class OnlinePlayerManager : MediatorSubscriberBase, IDisposable
Mediator.Subscribe<DelayedFrameworkUpdateMessage>(this, (_) => FrameworkOnUpdate()); Mediator.Subscribe<DelayedFrameworkUpdateMessage>(this, (_) => FrameworkOnUpdate());
Mediator.Subscribe<CharacterDataCreatedMessage>(this, (msg) => Mediator.Subscribe<CharacterDataCreatedMessage>(this, (msg) =>
{ {
var newData = ((CharacterDataCreatedMessage)msg).CharacterData.ToAPI(); var newData = ((CharacterDataCreatedMessage)msg).CharacterData;
if (_lastSentData == null || _lastSentData != null && !string.Equals(newData.DataHash.Value, _lastSentData.DataHash.Value, StringComparison.Ordinal)) if (_lastSentData == null || _lastSentData != null && !string.Equals(newData.DataHash.Value, _lastSentData.DataHash.Value, StringComparison.Ordinal))
{ {
_logger.LogDebug("Pushing data for visible players"); _logger.LogDebug("Pushing data for visible players");

View File

@@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<Authors></Authors> <Authors></Authors>
<Company></Company> <Company></Company>
<Version>0.7.39</Version> <Version>0.7.40</Version>
<Description></Description> <Description></Description>
<Copyright></Copyright> <Copyright></Copyright>
<PackageProjectUrl>https://github.com/Penumbra-Sync/client</PackageProjectUrl> <PackageProjectUrl>https://github.com/Penumbra-Sync/client</PackageProjectUrl>

View File

@@ -22,6 +22,7 @@ public class MareMediator : IDisposable
private readonly ILogger<MareMediator> _logger; private readonly ILogger<MareMediator> _logger;
private readonly PerformanceCollector _performanceCollector; private readonly PerformanceCollector _performanceCollector;
private readonly object _addRemoveLock = new(); private readonly object _addRemoveLock = new();
private readonly Dictionary<object, DateTime> _lastErrorTime = new();
public MareMediator(ILogger<MareMediator> logger, PerformanceCollector performanceCollector) public MareMediator(ILogger<MareMediator> logger, PerformanceCollector performanceCollector)
{ {
@@ -69,11 +70,13 @@ public class MareMediator : IDisposable
} }
catch (Exception ex) catch (Exception ex)
{ {
lock (_addRemoveLock) if (_lastErrorTime.TryGetValue(subscriber, out var lastErrorTime))
{ {
var removed = _subscriberDict[message.GetType()].RemoveWhere(s => s == subscriber); if (lastErrorTime.Add(TimeSpan.FromSeconds(10)) > DateTime.UtcNow) continue;
_logger.LogCritical(ex, "Error executing {type} for subscriber {subscriber}, removed from Mediator: {removeCount}", message.GetType().Name, subscriber.Subscriber.GetType().Name, removed);
} }
_logger.LogCritical(ex, "Error executing {type} for subscriber {subscriber}", message.GetType().Name, subscriber.Subscriber.GetType().Name);
_lastErrorTime[subscriber] = DateTime.UtcNow;
} }
} }
}); });

View File

@@ -40,7 +40,7 @@ public record NotificationMessage
(string Title, string Message, NotificationType Type, uint TimeShownOnScreen = 3000) : IMessage; (string Title, string Message, NotificationType Type, uint TimeShownOnScreen = 3000) : IMessage;
public record CreateCacheForObjectMessage(GameObjectHandler ObjectToCreateFor) : IMessage; public record CreateCacheForObjectMessage(GameObjectHandler ObjectToCreateFor) : IMessage;
public record ClearCacheForObjectMessage(GameObjectHandler ObjectToCreateFor) : IMessage; public record ClearCacheForObjectMessage(GameObjectHandler ObjectToCreateFor) : IMessage;
public record CharacterDataCreatedMessage(CharacterData CharacterData) : IMessage; public record CharacterDataCreatedMessage(API.Data.CharacterData CharacterData) : IMessage;
public record PenumbraStartRedrawMessage(IntPtr Address) : IMessage; public record PenumbraStartRedrawMessage(IntPtr Address) : IMessage;
public record PenumbraEndRedrawMessage(IntPtr Address) : IMessage; public record PenumbraEndRedrawMessage(IntPtr Address) : IMessage;
public record HubReconnectingMessage(Exception? Exception) : IMessage; public record HubReconnectingMessage(Exception? Exception) : IMessage;

View File

@@ -1,31 +1,22 @@
using Newtonsoft.Json; using System.Text;
using System.Text;
using MareSynchronos.API.Data.Enum; using MareSynchronos.API.Data.Enum;
using MareSynchronos.API.Data; using MareSynchronos.API.Data;
namespace MareSynchronos.Models; namespace MareSynchronos.Models;
[JsonObject(MemberSerialization.OptIn)]
public class CharacterData public class CharacterData
{ {
[JsonProperty]
public Dictionary<ObjectKind, HashSet<FileReplacement>> FileReplacements { get; set; } = new(); public Dictionary<ObjectKind, HashSet<FileReplacement>> FileReplacements { get; set; } = new();
[JsonProperty]
public Dictionary<ObjectKind, string> GlamourerString { get; set; } = new(); public Dictionary<ObjectKind, string> GlamourerString { get; set; } = new();
public bool IsReady => FileReplacements.SelectMany(k => k.Value).All(f => f.Computed); public bool IsReady => FileReplacements.SelectMany(k => k.Value).All(f => f.Computed);
[JsonProperty]
public string ManipulationString { get; set; } = string.Empty; public string ManipulationString { get; set; } = string.Empty;
[JsonProperty]
public float HeelsOffset { get; set; } = 0f; public float HeelsOffset { get; set; } = 0f;
[JsonProperty]
public string CustomizePlusScale { get; set; } = string.Empty; public string CustomizePlusScale { get; set; } = string.Empty;
[JsonProperty]
public string PalettePlusPalette { get; set; } = string.Empty; public string PalettePlusPalette { get; set; } = string.Empty;
public API.Data.CharacterData ToAPI() public API.Data.CharacterData ToAPI()

View File

@@ -62,7 +62,7 @@ public class SettingsUi : WindowMediatorSubscriberBase, IDisposable
Mediator.Subscribe<SwitchToIntroUiMessage>(this, (_) => IsOpen = false); Mediator.Subscribe<SwitchToIntroUiMessage>(this, (_) => IsOpen = false);
Mediator.Subscribe<CutsceneStartMessage>(this, (_) => UiShared_GposeStart()); Mediator.Subscribe<CutsceneStartMessage>(this, (_) => UiShared_GposeStart());
Mediator.Subscribe<CutsceneEndMessage>(this, (_) => UiShared_GposeEnd()); Mediator.Subscribe<CutsceneEndMessage>(this, (_) => UiShared_GposeEnd());
Mediator.Subscribe<CharacterDataCreatedMessage>(this, (msg) => LastCreatedCharacterData = ((CharacterDataCreatedMessage)msg).CharacterData.ToAPI()); Mediator.Subscribe<CharacterDataCreatedMessage>(this, (msg) => LastCreatedCharacterData = ((CharacterDataCreatedMessage)msg).CharacterData);
windowSystem.AddWindow(this); windowSystem.AddWindow(this);
} }