197 lines
5.0 KiB
C#
197 lines
5.0 KiB
C#
using Dalamud.Plugin;
|
|
using LightlessSync.Services.Mediator;
|
|
using Microsoft.Extensions.Logging;
|
|
using System.Linq;
|
|
|
|
namespace LightlessSync.Interop.Ipc.Framework;
|
|
|
|
public enum IpcConnectionState
|
|
{
|
|
Unknown = 0,
|
|
MissingPlugin = 1,
|
|
VersionMismatch = 2,
|
|
PluginDisabled = 3,
|
|
NotReady = 4,
|
|
Available = 5,
|
|
Error = 6,
|
|
}
|
|
|
|
public sealed record IpcServiceDescriptor(string InternalName, string DisplayName, Version MinimumVersion)
|
|
{
|
|
public override string ToString()
|
|
=> $"{DisplayName} (>= {MinimumVersion})";
|
|
}
|
|
|
|
public interface IIpcService : IDisposable
|
|
{
|
|
IpcServiceDescriptor Descriptor { get; }
|
|
IpcConnectionState State { get; }
|
|
IDalamudPluginInterface PluginInterface { get; }
|
|
bool APIAvailable { get; }
|
|
void CheckAPI();
|
|
}
|
|
|
|
public interface IIpcInterop : IDisposable
|
|
{
|
|
string Name { get; }
|
|
void OnConnectionStateChanged(IpcConnectionState state);
|
|
}
|
|
|
|
public abstract class IpcInteropBase : IIpcInterop
|
|
{
|
|
protected IpcInteropBase(ILogger logger)
|
|
{
|
|
Logger = logger;
|
|
}
|
|
|
|
protected ILogger Logger { get; }
|
|
|
|
protected IpcConnectionState State { get; private set; } = IpcConnectionState.Unknown;
|
|
|
|
protected bool IsAvailable => State == IpcConnectionState.Available;
|
|
|
|
public abstract string Name { get; }
|
|
|
|
public void OnConnectionStateChanged(IpcConnectionState state)
|
|
{
|
|
if (State == state)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var previous = State;
|
|
State = state;
|
|
HandleStateChange(previous, state);
|
|
}
|
|
|
|
protected abstract void HandleStateChange(IpcConnectionState previous, IpcConnectionState current);
|
|
|
|
public virtual void Dispose()
|
|
{
|
|
}
|
|
}
|
|
|
|
public abstract class IpcServiceBase : DisposableMediatorSubscriberBase, IIpcService
|
|
{
|
|
private readonly List<IIpcInterop> _interops = new();
|
|
|
|
protected IpcServiceBase(
|
|
ILogger logger,
|
|
LightlessMediator mediator,
|
|
IDalamudPluginInterface pluginInterface,
|
|
IpcServiceDescriptor descriptor) : base(logger, mediator)
|
|
{
|
|
PluginInterface = pluginInterface;
|
|
Descriptor = descriptor;
|
|
}
|
|
|
|
protected IDalamudPluginInterface PluginInterface { get; }
|
|
|
|
IDalamudPluginInterface IIpcService.PluginInterface => PluginInterface;
|
|
|
|
protected IpcServiceDescriptor Descriptor { get; }
|
|
|
|
IpcServiceDescriptor IIpcService.Descriptor => Descriptor;
|
|
|
|
public IpcConnectionState State { get; private set; } = IpcConnectionState.Unknown;
|
|
|
|
public bool APIAvailable => State == IpcConnectionState.Available;
|
|
|
|
public virtual void CheckAPI()
|
|
{
|
|
var newState = EvaluateState();
|
|
UpdateState(newState);
|
|
}
|
|
|
|
protected virtual IpcConnectionState EvaluateState()
|
|
{
|
|
try
|
|
{
|
|
var plugin = PluginInterface.InstalledPlugins
|
|
.Where(p => string.Equals(p.InternalName, Descriptor.InternalName, StringComparison.OrdinalIgnoreCase))
|
|
.OrderByDescending(p => p.IsLoaded)
|
|
.FirstOrDefault();
|
|
|
|
if (plugin == null)
|
|
{
|
|
return IpcConnectionState.MissingPlugin;
|
|
}
|
|
|
|
if (plugin.Version < Descriptor.MinimumVersion)
|
|
{
|
|
return IpcConnectionState.VersionMismatch;
|
|
}
|
|
|
|
if (!IsPluginEnabled(plugin))
|
|
{
|
|
return IpcConnectionState.PluginDisabled;
|
|
}
|
|
|
|
if (!IsPluginReady())
|
|
{
|
|
return IpcConnectionState.NotReady;
|
|
}
|
|
|
|
return IpcConnectionState.Available;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogDebug(ex, "Failed to evaluate IPC state for {Service}", Descriptor.DisplayName);
|
|
return IpcConnectionState.Error;
|
|
}
|
|
}
|
|
|
|
protected virtual bool IsPluginEnabled(IExposedPlugin plugin)
|
|
=> plugin.IsLoaded;
|
|
|
|
protected virtual bool IsPluginReady()
|
|
=> true;
|
|
|
|
protected TInterop RegisterInterop<TInterop>(TInterop interop)
|
|
where TInterop : IIpcInterop
|
|
{
|
|
_interops.Add(interop);
|
|
interop.OnConnectionStateChanged(State);
|
|
return interop;
|
|
}
|
|
|
|
private void UpdateState(IpcConnectionState newState)
|
|
{
|
|
if (State == newState)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var previous = State;
|
|
State = newState;
|
|
OnConnectionStateChanged(previous, newState);
|
|
|
|
foreach (var interop in _interops)
|
|
{
|
|
interop.OnConnectionStateChanged(newState);
|
|
}
|
|
}
|
|
|
|
protected virtual void OnConnectionStateChanged(IpcConnectionState previous, IpcConnectionState current)
|
|
{
|
|
Logger.LogTrace("{Service} IPC state transitioned from {Previous} to {Current}", Descriptor.DisplayName, previous, current);
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
base.Dispose(disposing);
|
|
|
|
if (!disposing)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (var i = _interops.Count - 1; i >= 0; --i)
|
|
{
|
|
_interops[i].Dispose();
|
|
}
|
|
|
|
_interops.Clear();
|
|
}
|
|
}
|