using System; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Plugin; using Dalamud.Plugin.Ipc; using LightlessSync.PlayerData.Handlers; using LightlessSync.Services; using LightlessSync.Services.Mediator; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace LightlessSync.Interop.Ipc; public class IpcProvider : IHostedService, IMediatorSubscriber { private readonly ILogger _logger; private readonly IDalamudPluginInterface _pi; private readonly CharaDataManager _charaDataManager; private readonly List _ipcRegisters = []; private readonly List _activeGameObjectHandlers = []; public LightlessMediator Mediator { get; init; } public IpcProvider(ILogger logger, IDalamudPluginInterface pi, CharaDataManager charaDataManager, LightlessMediator lightlessMediator) { _logger = logger; _pi = pi; _charaDataManager = charaDataManager; Mediator = lightlessMediator; Mediator.Subscribe(this, (msg) => { if (msg.OwnedObject) return; _activeGameObjectHandlers.Add(msg.GameObjectHandler); }); Mediator.Subscribe(this, (msg) => { if (msg.OwnedObject) return; _activeGameObjectHandlers.Remove(msg.GameObjectHandler); }); } public Task StartAsync(CancellationToken cancellationToken) { _logger.LogInformation("Starting IpcProviderService"); _ipcRegisters.Add(RegisterFunc("LightlessSync.LoadMcdf", LoadMcdf)); _ipcRegisters.Add(RegisterFunc>("LightlessSync.LoadMcdfAsync", LoadMcdfAsync)); _ipcRegisters.Add(RegisterFunc("LightlessSync.GetHandledAddresses", GetHandledAddresses)); _logger.LogInformation("Started IpcProviderService"); return Task.CompletedTask; } public Task StopAsync(CancellationToken cancellationToken) { _logger.LogDebug("Stopping IpcProvider Service"); foreach (var register in _ipcRegisters) { register.Dispose(); } _ipcRegisters.Clear(); Mediator.UnsubscribeAll(this); return Task.CompletedTask; } private async Task LoadMcdfAsync(string path, IGameObject target) { await ApplyFileAsync(path, target).ConfigureAwait(false); return true; } private bool LoadMcdf(string path, IGameObject target) { _ = Task.Run(async () => await ApplyFileAsync(path, target).ConfigureAwait(false)).ConfigureAwait(false); return true; } private async Task ApplyFileAsync(string path, IGameObject target) { _charaDataManager.LoadMcdf(path); await (_charaDataManager.LoadedMcdfHeader ?? Task.CompletedTask).ConfigureAwait(false); _charaDataManager.McdfApplyToTarget(target.Name.TextValue); } private List GetHandledAddresses() { return _activeGameObjectHandlers.Where(g => g.Address != nint.Zero).Select(g => g.Address).Distinct().ToList(); } private IpcRegister RegisterFunc(string label, Func> handler) { var provider = _pi.GetIpcProvider>(label); provider.RegisterFunc(handler); return new IpcRegister(provider.UnregisterFunc); } private IpcRegister RegisterFunc(string label, Func handler) { var provider = _pi.GetIpcProvider(label); provider.RegisterFunc(handler); return new IpcRegister(provider.UnregisterFunc); } private sealed class IpcRegister : IDisposable { private readonly Action _unregister; private bool _disposed; public IpcRegister(Action unregister) { _unregister = unregister; } public void Dispose() { if (_disposed) { return; } _unregister(); _disposed = true; } } }