using MareSynchronos.API.Data.Enum; using MareSynchronos.Factories; using MareSynchronos.Mediator; using MareSynchronos.Models; using MareSynchronos.Utils; using Microsoft.Extensions.Logging; namespace MareSynchronos.Managers; public class CacheCreationService : MediatorSubscriberBase, IDisposable { private readonly CharacterDataFactory _characterDataFactory; private Task? _cacheCreationTask; private readonly Dictionary _cachesToCreate = new(); private readonly CharacterData _playerData = new(); private readonly CancellationTokenSource _cts = new(); private readonly List _playerRelatedObjects = new(); private CancellationTokenSource _palettePlusCts = new(); private SemaphoreSlim _cacheCreateLock = new(1); public CacheCreationService(ILogger logger, MareMediator mediator, GameObjectHandlerFactory gameObjectHandlerFactory, CharacterDataFactory characterDataFactory, DalamudUtil dalamudUtil) : base(logger, mediator) { _characterDataFactory = characterDataFactory; Mediator.Subscribe(this, (msg) => { var actualMsg = (CreateCacheForObjectMessage)msg; _cacheCreateLock.Wait(); _cachesToCreate[actualMsg.ObjectToCreateFor.ObjectKind] = actualMsg.ObjectToCreateFor; _cacheCreateLock.Release(); }); _playerRelatedObjects.AddRange(new List() { gameObjectHandlerFactory.Create(ObjectKind.Player, () => dalamudUtil.PlayerPointer, isWatched: true), gameObjectHandlerFactory.Create(ObjectKind.MinionOrMount, () => dalamudUtil.GetMinionOrMount(), isWatched: true), gameObjectHandlerFactory.Create(ObjectKind.Pet, () => dalamudUtil.GetPet(), isWatched: true), gameObjectHandlerFactory.Create(ObjectKind.Companion, () => dalamudUtil.GetCompanion(), isWatched: true), }); Mediator.Subscribe(this, (msg) => { Task.Run(() => { var actualMsg = (ClearCacheForObjectMessage)msg; _playerData.FileReplacements.Remove(actualMsg.ObjectToCreateFor.ObjectKind); _playerData.GlamourerString.Remove(actualMsg.ObjectToCreateFor.ObjectKind); Mediator.Publish(new CharacterDataCreatedMessage(_playerData.ToAPI())); }); }); Mediator.Subscribe(this, (msg) => ProcessCacheCreation()); Mediator.Subscribe(this, async (_) => await AddPlayerCacheToCreate().ConfigureAwait(false)); Mediator.Subscribe(this, async (_) => await AddPlayerCacheToCreate().ConfigureAwait(false)); Mediator.Subscribe(this, (_) => PalettePlusChanged()); Mediator.Subscribe(this, async (msg) => await AddPlayerCacheToCreate().ConfigureAwait(false)); } private async Task AddPlayerCacheToCreate() { await _cacheCreateLock.WaitAsync().ConfigureAwait(false); _cachesToCreate[ObjectKind.Player] = _playerRelatedObjects.First(p => p.ObjectKind == ObjectKind.Player); _cacheCreateLock.Release(); } private void PalettePlusChanged() { _palettePlusCts?.Cancel(); _palettePlusCts?.Dispose(); _palettePlusCts = new(); var token = _palettePlusCts.Token; Task.Run(async () => { await Task.Delay(TimeSpan.FromSeconds(1), token).ConfigureAwait(false); await AddPlayerCacheToCreate().ConfigureAwait(false); }, token); } private void ProcessCacheCreation() { if (_cachesToCreate.Any() && (_cacheCreationTask?.IsCompleted ?? true)) { _cacheCreateLock.Wait(); var toCreate = _cachesToCreate.ToList(); _cachesToCreate.Clear(); _cacheCreateLock.Release(); _cacheCreationTask = Task.Run(async () => { try { foreach (var obj in toCreate) { await _characterDataFactory.BuildCharacterData(_playerData, obj.Value, _cts.Token).ConfigureAwait(false); } 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) { _logger.LogCritical(ex, "Error during Cache Creation Processing"); } finally { _logger.LogDebug("Cache Creation complete"); } }, _cts.Token); } else if (_cachesToCreate.Any()) { _logger.LogDebug("Cache Creation stored until previous creation finished"); } } public override void Dispose() { base.Dispose(); _playerRelatedObjects.ForEach(p => p.Dispose()); _cts.Dispose(); } }