Merge pull request #516 from daemitus/ipc

This commit is contained in:
goaaats 2021-08-29 23:13:18 +02:00 committed by GitHub
commit abe256ec9a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 378 additions and 255 deletions

View file

@ -17,8 +17,8 @@ using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Hooking.Internal; using Dalamud.Hooking.Internal;
using Dalamud.Interface.Internal; using Dalamud.Interface.Internal;
using Dalamud.IoC.Internal; using Dalamud.IoC.Internal;
using Dalamud.Plugin;
using Dalamud.Plugin.Internal; using Dalamud.Plugin.Internal;
using Dalamud.Plugin.Ipc.Internal;
using Serilog; using Serilog;
using Serilog.Core; using Serilog.Core;
@ -196,7 +196,9 @@ namespace Dalamud
Log.Information("[T2] Data OK!"); Log.Information("[T2] Data OK!");
#pragma warning disable CS0618 // Type or member is obsolete
Service<SeStringManager>.Set(); Service<SeStringManager>.Set();
#pragma warning restore CS0618 // Type or member is obsolete
Log.Information("[T2] SeString OK!"); Log.Information("[T2] SeString OK!");

View file

@ -51,7 +51,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup Label="Warnings"> <PropertyGroup Label="Warnings">
<NoWarn>IDE0003;IDE0044;IDE1006;CA1822;CS1591;CS1701;CS1702</NoWarn> <NoWarn>IDE0002;IDE0003;IDE0044;IDE1006;CA1822;CS1591;CS1701;CS1702</NoWarn>
<!-- IDE1006 - Naming violation --> <!-- IDE1006 - Naming violation -->
<!-- CA1822 - Can be marked as static --> <!-- CA1822 - Can be marked as static -->
<!-- CS1591 - Missing XML comment for publicly visible type or member --> <!-- CS1591 - Missing XML comment for publicly visible type or member -->

View file

@ -25,8 +25,8 @@ using Dalamud.Game.Text;
using Dalamud.Interface.Internal.Notifications; using Dalamud.Interface.Internal.Notifications;
using Dalamud.Interface.Windowing; using Dalamud.Interface.Windowing;
using Dalamud.Memory; using Dalamud.Memory;
using Dalamud.Plugin; using Dalamud.Plugin.Ipc;
using Dalamud.Plugin.Internal; using Dalamud.Plugin.Ipc.Internal;
using Dalamud.Utility; using Dalamud.Utility;
using ImGuiNET; using ImGuiNET;
using ImGuiScene; using ImGuiScene;
@ -617,7 +617,7 @@ namespace Dalamud.Interface.Internal.Windows
{ {
if (this.ipcPub == null) if (this.ipcPub == null)
{ {
this.ipcPub = Service<CallGate>.Get().GetIpcPubSub<string, string>("dataDemo1"); this.ipcPub = new CallGatePubSub<string, string>("dataDemo1");
this.ipcPub.RegisterAction((msg) => this.ipcPub.RegisterAction((msg) =>
{ {
@ -633,7 +633,7 @@ namespace Dalamud.Interface.Internal.Windows
if (this.ipcSub == null) if (this.ipcSub == null)
{ {
this.ipcSub = Service<CallGate>.Get().GetIpcPubSub<string, string>("dataDemo1"); this.ipcSub = new CallGatePubSub<string, string>("dataDemo1");
this.ipcSub.Subscribe((msg) => this.ipcSub.Subscribe((msg) =>
{ {
Log.Information("PONG1"); Log.Information("PONG1");

View file

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -17,6 +16,9 @@ using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.Internal; using Dalamud.Interface.Internal;
using Dalamud.Plugin.Internal; using Dalamud.Plugin.Internal;
using Dalamud.Plugin.Ipc;
using Dalamud.Plugin.Ipc.Exceptions;
using Dalamud.Plugin.Ipc.Internal;
namespace Dalamud.Plugin namespace Dalamud.Plugin
{ {
@ -145,39 +147,39 @@ namespace Dalamud.Plugin
/// <returns>An IPC publisher.</returns> /// <returns>An IPC publisher.</returns>
/// <exception cref="IpcTypeMismatchError">This is thrown when the requested types do not match the previously registered types are different.</exception> /// <exception cref="IpcTypeMismatchError">This is thrown when the requested types do not match the previously registered types are different.</exception>
public ICallGateProvider<TRet> GetIpcProvider<TRet>(string name) public ICallGateProvider<TRet> GetIpcProvider<TRet>(string name)
=> Service<CallGate>.Get().GetIpcPubSub<TRet>(name); => new CallGatePubSub<TRet>(name);
/// <inheritdoc cref="ICallGateProvider{TRet}"/> /// <inheritdoc cref="ICallGateProvider{TRet}"/>
public ICallGateProvider<T1, TRet> GetIpcProvider<T1, TRet>(string name) public ICallGateProvider<T1, TRet> GetIpcProvider<T1, TRet>(string name)
=> Service<CallGate>.Get().GetIpcPubSub<T1, TRet>(name); => new CallGatePubSub<T1, TRet>(name);
/// <inheritdoc cref="ICallGateProvider{TRet}"/> /// <inheritdoc cref="ICallGateProvider{TRet}"/>
public ICallGateProvider<T1, T2, TRet> GetIpcProvider<T1, T2, TRet>(string name) public ICallGateProvider<T1, T2, TRet> GetIpcProvider<T1, T2, TRet>(string name)
=> Service<CallGate>.Get().GetIpcPubSub<T1, T2, TRet>(name); => new CallGatePubSub<T1, T2, TRet>(name);
/// <inheritdoc cref="ICallGateProvider{TRet}"/> /// <inheritdoc cref="ICallGateProvider{TRet}"/>
public ICallGateProvider<T1, T2, T3, TRet> GetIpcProvider<T1, T2, T3, TRet>(string name) public ICallGateProvider<T1, T2, T3, TRet> GetIpcProvider<T1, T2, T3, TRet>(string name)
=> Service<CallGate>.Get().GetIpcPubSub<T1, T2, T3, TRet>(name); => new CallGatePubSub<T1, T2, T3, TRet>(name);
/// <inheritdoc cref="ICallGateProvider{TRet}"/> /// <inheritdoc cref="ICallGateProvider{TRet}"/>
public ICallGateProvider<T1, T2, T3, T4, TRet> GetIpcProvider<T1, T2, T3, T4, TRet>(string name) public ICallGateProvider<T1, T2, T3, T4, TRet> GetIpcProvider<T1, T2, T3, T4, TRet>(string name)
=> Service<CallGate>.Get().GetIpcPubSub<T1, T2, T3, T4, TRet>(name); => new CallGatePubSub<T1, T2, T3, T4, TRet>(name);
/// <inheritdoc cref="ICallGateProvider{TRet}"/> /// <inheritdoc cref="ICallGateProvider{TRet}"/>
public ICallGateProvider<T1, T2, T3, T4, T5, TRet> GetIpcProvider<T1, T2, T3, T4, T5, TRet>(string name) public ICallGateProvider<T1, T2, T3, T4, T5, TRet> GetIpcProvider<T1, T2, T3, T4, T5, TRet>(string name)
=> Service<CallGate>.Get().GetIpcPubSub<T1, T2, T3, T4, T5, TRet>(name); => new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name);
/// <inheritdoc cref="ICallGateProvider{TRet}"/> /// <inheritdoc cref="ICallGateProvider{TRet}"/>
public ICallGateProvider<T1, T2, T3, T4, T5, T6, TRet> GetIpcProvider<T1, T2, T3, T4, T5, T6, TRet>(string name) public ICallGateProvider<T1, T2, T3, T4, T5, T6, TRet> GetIpcProvider<T1, T2, T3, T4, T5, T6, TRet>(string name)
=> Service<CallGate>.Get().GetIpcPubSub<T1, T2, T3, T4, T5, T6, TRet>(name); => new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name);
/// <inheritdoc cref="ICallGateProvider{TRet}"/> /// <inheritdoc cref="ICallGateProvider{TRet}"/>
public ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, TRet> GetIpcProvider<T1, T2, T3, T4, T5, T6, T7, TRet>(string name) public ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, TRet> GetIpcProvider<T1, T2, T3, T4, T5, T6, T7, TRet>(string name)
=> Service<CallGate>.Get().GetIpcPubSub<T1, T2, T3, T4, T5, T6, T7, TRet>(name); => new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet>(name);
/// <inheritdoc cref="ICallGateProvider{TRet}"/> /// <inheritdoc cref="ICallGateProvider{TRet}"/>
public ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet> GetIpcProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(string name) public ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet> GetIpcProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(string name)
=> Service<CallGate>.Get().GetIpcPubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(name); => new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(name);
/// <summary> /// <summary>
/// Gets an IPC subscriber. /// Gets an IPC subscriber.
@ -186,39 +188,39 @@ namespace Dalamud.Plugin
/// <param name="name">The name of the IPC registration.</param> /// <param name="name">The name of the IPC registration.</param>
/// <returns>An IPC publisher.</returns> /// <returns>An IPC publisher.</returns>
public ICallGateSubscriber<TRet> GetIpcSubscriber<TRet>(string name) public ICallGateSubscriber<TRet> GetIpcSubscriber<TRet>(string name)
=> Service<CallGate>.Get().GetIpcPubSub<TRet>(name); => new CallGatePubSub<TRet>(name);
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/> /// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
public ICallGateSubscriber<T1, TRet> GetIpcSubscriber<T1, TRet>(string name) public ICallGateSubscriber<T1, TRet> GetIpcSubscriber<T1, TRet>(string name)
=> Service<CallGate>.Get().GetIpcPubSub<T1, TRet>(name); => new CallGatePubSub<T1, TRet>(name);
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/> /// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
public ICallGateSubscriber<T1, T2, TRet> GetIpcSubscriber<T1, T2, TRet>(string name) public ICallGateSubscriber<T1, T2, TRet> GetIpcSubscriber<T1, T2, TRet>(string name)
=> Service<CallGate>.Get().GetIpcPubSub<T1, T2, TRet>(name); => new CallGatePubSub<T1, T2, TRet>(name);
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/> /// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
public ICallGateSubscriber<T1, T2, T3, TRet> GetIpcSubscriber<T1, T2, T3, TRet>(string name) public ICallGateSubscriber<T1, T2, T3, TRet> GetIpcSubscriber<T1, T2, T3, TRet>(string name)
=> Service<CallGate>.Get().GetIpcPubSub<T1, T2, T3, TRet>(name); => new CallGatePubSub<T1, T2, T3, TRet>(name);
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/> /// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
public ICallGateSubscriber<T1, T2, T3, T4, TRet> GetIpcSubscriber<T1, T2, T3, T4, TRet>(string name) public ICallGateSubscriber<T1, T2, T3, T4, TRet> GetIpcSubscriber<T1, T2, T3, T4, TRet>(string name)
=> Service<CallGate>.Get().GetIpcPubSub<T1, T2, T3, T4, TRet>(name); => new CallGatePubSub<T1, T2, T3, T4, TRet>(name);
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/> /// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
public ICallGateSubscriber<T1, T2, T3, T4, T5, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, TRet>(string name) public ICallGateSubscriber<T1, T2, T3, T4, T5, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, TRet>(string name)
=> Service<CallGate>.Get().GetIpcPubSub<T1, T2, T3, T4, T5, TRet>(name); => new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name);
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/> /// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
public ICallGateSubscriber<T1, T2, T3, T4, T5, T6, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, T6, TRet>(string name) public ICallGateSubscriber<T1, T2, T3, T4, T5, T6, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, T6, TRet>(string name)
=> Service<CallGate>.Get().GetIpcPubSub<T1, T2, T3, T4, T5, T6, TRet>(name); => new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name);
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/> /// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
public ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet>(string name) public ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet>(string name)
=> Service<CallGate>.Get().GetIpcPubSub<T1, T2, T3, T4, T5, T6, T7, TRet>(name); => new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet>(name);
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/> /// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
public ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, T8, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(string name) public ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, T8, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(string name)
=> Service<CallGate>.Get().GetIpcPubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(name); => new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(name);
#endregion #endregion

View file

@ -1,92 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Dalamud.Plugin.Internal
{
/// <summary>
/// This class facilitates inter-plugin communication.
/// </summary>
internal class CallGate
{
private Dictionary<string, CallGatePubSubBase> gates = new();
#region GetIpcPubSub
/// <inheritdoc cref="GetIpcPubSub"/>
internal CallGatePubSub<TRet> GetIpcPubSub<TRet>(string name)
=> (CallGatePubSub<TRet>)this.GetIpcPubSub(name, typeof(TRet));
/// <inheritdoc cref="GetIpcPubSub"/>
internal CallGatePubSub<T1, TRet> GetIpcPubSub<T1, TRet>(string name)
=> (CallGatePubSub<T1, TRet>)this.GetIpcPubSub(name, typeof(T1), typeof(TRet));
/// <inheritdoc cref="GetIpcPubSub"/>
internal CallGatePubSub<T1, T2, TRet> GetIpcPubSub<T1, T2, TRet>(string name)
=> (CallGatePubSub<T1, T2, TRet>)this.GetIpcPubSub(name, typeof(T1), typeof(T2), typeof(TRet));
/// <inheritdoc cref="GetIpcPubSub"/>
internal CallGatePubSub<T1, T2, T3, TRet> GetIpcPubSub<T1, T2, T3, TRet>(string name)
=> (CallGatePubSub<T1, T2, T3, TRet>)this.GetIpcPubSub(name, typeof(T1), typeof(T2), typeof(T3), typeof(TRet));
/// <inheritdoc cref="GetIpcPubSub"/>
internal CallGatePubSub<T1, T2, T3, T4, TRet> GetIpcPubSub<T1, T2, T3, T4, TRet>(string name)
=> (CallGatePubSub<T1, T2, T3, T4, TRet>)this.GetIpcPubSub(name, typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(TRet));
/// <inheritdoc cref="GetIpcPubSub"/>
internal CallGatePubSub<T1, T2, T3, T4, T5, TRet> GetIpcPubSub<T1, T2, T3, T4, T5, TRet>(string name)
=> (CallGatePubSub<T1, T2, T3, T4, T5, TRet>)this.GetIpcPubSub(name, typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(TRet));
/// <inheritdoc cref="GetIpcPubSub"/>
internal CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet> GetIpcPubSub<T1, T2, T3, T4, T5, T6, TRet>(string name)
=> (CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>)this.GetIpcPubSub(name, typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(TRet));
/// <inheritdoc cref="GetIpcPubSub"/>
internal CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet> GetIpcPubSub<T1, T2, T3, T4, T5, T6, T7, TRet>(string name)
=> (CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet>)this.GetIpcPubSub(name, typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(TRet));
/// <inheritdoc cref="GetIpcPubSub"/>
internal CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet> GetIpcPubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(string name)
=> (CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>)this.GetIpcPubSub(name, typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(TRet));
#endregion
/// <summary>
/// Gets or sets an IPC pub/sub callgate.
/// </summary>
/// <param name="name">Name of the IPC registration.</param>
/// <param name="types">The callgate parameter types.</param>
/// <returns>The IPC pub/sub callgate.</returns>
private CallGatePubSubBase GetIpcPubSub(string name, params Type[] types)
{
if (!this.gates.TryGetValue(name, out var callGate))
{
var generic = types.Length switch
{
1 => typeof(CallGatePubSub<>),
2 => typeof(CallGatePubSub<,>),
3 => typeof(CallGatePubSub<,,>),
4 => typeof(CallGatePubSub<,,,>),
5 => typeof(CallGatePubSub<,,,,>),
6 => typeof(CallGatePubSub<,,,,,>),
7 => typeof(CallGatePubSub<,,,,,,>),
8 => typeof(CallGatePubSub<,,,,,,,>),
9 => typeof(CallGatePubSub<,,,,,,,,>),
_ => throw new Exception("Misconfigured number of type args"),
};
var type = generic.MakeGenericType(types);
callGate = (CallGatePubSubBase)Activator.CreateInstance(type);
callGate.Name = name;
this.gates[name] = callGate;
}
var requested = callGate.GetType().GenericTypeArguments;
if (!Enumerable.SequenceEqual(requested, types))
throw new IpcTypeMismatchError(name, requested, types);
return callGate;
}
}
}

View file

@ -0,0 +1,36 @@
using System;
namespace Dalamud.Plugin.Ipc.Exceptions
{
/// <summary>
/// This exception is thrown when an IPC errors are encountered.
/// </summary>
public abstract class IpcError : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="IpcError"/> class.
/// </summary>
public IpcError()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="IpcError"/> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public IpcError(string message)
: base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="IpcError"/> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
/// <param name="ex">The exception that is the cause of the current exception.</param>
public IpcError(string message, Exception ex)
: base(message, ex)
{
}
}
}

View file

@ -0,0 +1,19 @@
namespace Dalamud.Plugin.Ipc.Exceptions
{
/// <summary>
/// This exception is thrown when an IPC method is invoked and the number of types does not match what was previously registered.
/// </summary>
public class IpcLengthMismatchError : IpcError
{
/// <summary>
/// Initializes a new instance of the <see cref="IpcLengthMismatchError"/> class.
/// </summary>
/// <param name="name">Name of the IPC method.</param>
/// <param name="requestedLength">The amount of types requested when checking out the IPC.</param>
/// <param name="actualLength">The amount of types registered by the IPC.</param>
public IpcLengthMismatchError(string name, int requestedLength, int actualLength)
: base($"IPC method {name} has a different number of types than was requested. {requestedLength} != {actualLength}")
{
}
}
}

View file

@ -0,0 +1,17 @@
namespace Dalamud.Plugin.Ipc.Exceptions
{
/// <summary>
/// This exception is thrown when an IPC method is invoked, but no actions or funcs have been registered yet.
/// </summary>
public class IpcNotReadyError : IpcError
{
/// <summary>
/// Initializes a new instance of the <see cref="IpcNotReadyError"/> class.
/// </summary>
/// <param name="name">Name of the IPC method.</param>
public IpcNotReadyError(string name)
: base($"IPC method {name} was not registered yet")
{
}
}
}

View file

@ -0,0 +1,22 @@
using System;
namespace Dalamud.Plugin.Ipc.Exceptions
{
/// <summary>
/// This exception is thrown when an IPC method is checked out, but the type does not match what was previously registered.
/// </summary>
public class IpcTypeMismatchError : IpcError
{
/// <summary>
/// Initializes a new instance of the <see cref="IpcTypeMismatchError"/> class.
/// </summary>
/// <param name="name">Name of the IPC method.</param>
/// <param name="requestedType">The before type.</param>
/// <param name="actualType">The after type.</param>
/// <param name="ex">The exception that is the cause of the current exception.</param>
public IpcTypeMismatchError(string name, Type requestedType, Type actualType, Exception ex)
: base($"IPC method {name} blew up when converting from {requestedType.Name} to {actualType}", ex)
{
}
}
}

View file

@ -1,10 +1,10 @@
using System; using System;
using Dalamud.Plugin.Internal; using Dalamud.Plugin.Ipc.Internal;
#pragma warning disable SA1402 // File may only contain a single type #pragma warning disable SA1402 // File may only contain a single type
namespace Dalamud.Plugin namespace Dalamud.Plugin.Ipc
{ {
/// <inheritdoc cref="CallGatePubSubBase"/> /// <inheritdoc cref="CallGatePubSubBase"/>
public interface ICallGateProvider<TRet> public interface ICallGateProvider<TRet>

View file

@ -1,10 +1,10 @@
using System; using System;
using Dalamud.Plugin.Internal; using Dalamud.Plugin.Ipc.Internal;
#pragma warning disable SA1402 // File may only contain a single type #pragma warning disable SA1402 // File may only contain a single type
namespace Dalamud.Plugin namespace Dalamud.Plugin.Ipc
{ {
/// <inheritdoc cref="CallGatePubSubBase"/> /// <inheritdoc cref="CallGatePubSubBase"/>
public interface ICallGateSubscriber<TRet> public interface ICallGateSubscriber<TRet>

View file

@ -0,0 +1,31 @@
using System.Collections.Generic;
namespace Dalamud.Plugin.Ipc.Internal
{
/// <summary>
/// This class facilitates inter-plugin communication.
/// </summary>
internal class CallGate
{
private readonly Dictionary<string, CallGateChannel> gates = new();
/// <summary>
/// Initializes a new instance of the <see cref="CallGate"/> class.
/// </summary>
internal CallGate()
{
}
/// <summary>
/// Gets the provider associated with the specified name.
/// </summary>
/// <param name="name">Name of the IPC registration.</param>
/// <returns>A CallGate registered under the given name.</returns>
public CallGateChannel GetOrCreateChannel(string name)
{
if (!this.gates.TryGetValue(name, out var gate))
gate = this.gates[name] = new CallGateChannel(name);
return gate;
}
}
}

View file

@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Dalamud.Plugin.Ipc.Exceptions;
using Newtonsoft.Json;
namespace Dalamud.Plugin.Ipc.Internal
{
/// <summary>
/// This class implements the channels and serialization needed for the typed gates to interact with each other.
/// </summary>
internal class CallGateChannel
{
/// <summary>
/// Initializes a new instance of the <see cref="CallGateChannel"/> class.
/// </summary>
/// <param name="name">The name of this IPC registration.</param>
public CallGateChannel(string name)
{
this.Name = name;
}
/// <summary>
/// Gets the name of the IPC registration.
/// </summary>
public string Name { get; init; }
/// <summary>
/// Gets a list of delegate subscriptions for when SendMessage is called.
/// </summary>
public List<Delegate> Subscriptions { get; } = new();
/// <summary>
/// Gets or sets an action for when InvokeAction is called.
/// </summary>
public Delegate Action { get; set; }
/// <summary>
/// Gets or sets a func for when InvokeFunc is called.
/// </summary>
public Delegate Func { get; set; }
/// <summary>
/// Invoke all actions that have subscribed to this IPC.
/// </summary>
/// <param name="args">Message arguments.</param>
internal void SendMessage(object?[]? args)
{
if (this.Subscriptions.Count == 0)
return;
foreach (var subscription in this.Subscriptions)
{
var methodInfo = subscription.GetMethodInfo();
this.CheckAndConvertArgs(args, methodInfo);
subscription.DynamicInvoke(args);
}
}
/// <summary>
/// Invoke an action registered for inter-plugin communication.
/// </summary>
/// <param name="args">Action arguments.</param>
/// <exception cref="IpcNotReadyError">This is thrown when the IPC publisher has not registered a func for calling yet.</exception>
internal void InvokeAction(object?[]? args)
{
if (this.Action == null)
throw new IpcNotReadyError(this.Name);
var methodInfo = this.Action.GetMethodInfo();
this.CheckAndConvertArgs(args, methodInfo);
this.Action.DynamicInvoke(args);
}
/// <summary>
/// Invoke a function registered for inter-plugin communication.
/// </summary>
/// <param name="args">Func arguments.</param>
/// <returns>The return value.</returns>
/// <typeparam name="TRet">The return type.</typeparam>
/// <exception cref="IpcNotReadyError">This is thrown when the IPC publisher has not registered a func for calling yet.</exception>
internal TRet InvokeFunc<TRet>(object?[]? args)
{
if (this.Func == null)
throw new IpcNotReadyError(this.Name);
var methodInfo = this.Func.GetMethodInfo();
this.CheckAndConvertArgs(args, methodInfo);
var result = this.Func.DynamicInvoke(args);
if (typeof(TRet) != methodInfo.ReturnType)
result = this.ConvertObject(result, typeof(TRet));
return (TRet)result;
}
private void CheckAndConvertArgs(object?[]? args, MethodInfo methodInfo)
{
var paramTypes = methodInfo.GetParameters()
.Select(pi => pi.ParameterType).ToArray();
if (args.Length != paramTypes.Length)
throw new IpcLengthMismatchError(this.Name, args.Length, paramTypes.Length);
for (var i = 0; i < args.Length; i++)
{
var arg = args[i];
var paramType = paramTypes[i];
if (arg.GetType() != paramType)
args[i] = this.ConvertObject(arg, paramType);
}
}
private object ConvertObject(object? obj, Type type)
{
try
{
var json = JsonConvert.SerializeObject(obj);
return JsonConvert.DeserializeObject(json, type);
}
catch (Exception ex)
{
throw new IpcTypeMismatchError(this.Name, obj.GetType(), type, ex);
}
}
}
}

View file

@ -2,11 +2,17 @@ using System;
#pragma warning disable SA1402 // File may only contain a single type #pragma warning disable SA1402 // File may only contain a single type
namespace Dalamud.Plugin.Internal namespace Dalamud.Plugin.Ipc.Internal
{ {
/// <inheritdoc cref="CallGatePubSubBase"/> /// <inheritdoc cref="CallGatePubSubBase"/>
internal class CallGatePubSub<TRet> : CallGatePubSubBase, ICallGateProvider<TRet>, ICallGateSubscriber<TRet> internal class CallGatePubSub<TRet> : CallGatePubSubBase, ICallGateProvider<TRet>, ICallGateSubscriber<TRet>
{ {
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
{
}
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/> /// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
public void RegisterAction(Action action) public void RegisterAction(Action action)
=> base.RegisterAction(action); => base.RegisterAction(action);
@ -33,12 +39,18 @@ namespace Dalamud.Plugin.Internal
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/> /// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
public TRet InvokeFunc() public TRet InvokeFunc()
=> (TRet)base.InvokeFunc(); => this.InvokeFunc<TRet>();
} }
/// <inheritdoc cref="CallGatePubSubBase"/> /// <inheritdoc cref="CallGatePubSubBase"/>
internal class CallGatePubSub<T1, TRet> : CallGatePubSubBase, ICallGateProvider<T1, TRet>, ICallGateSubscriber<T1, TRet> internal class CallGatePubSub<T1, TRet> : CallGatePubSubBase, ICallGateProvider<T1, TRet>, ICallGateSubscriber<T1, TRet>
{ {
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
{
}
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/> /// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
public void RegisterAction(Action<T1> action) public void RegisterAction(Action<T1> action)
=> base.RegisterAction(action); => base.RegisterAction(action);
@ -65,12 +77,18 @@ namespace Dalamud.Plugin.Internal
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/> /// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
public TRet InvokeFunc(T1 arg1) public TRet InvokeFunc(T1 arg1)
=> (TRet)base.InvokeFunc(arg1); => this.InvokeFunc<TRet>(arg1);
} }
/// <inheritdoc cref="CallGatePubSubBase"/> /// <inheritdoc cref="CallGatePubSubBase"/>
internal class CallGatePubSub<T1, T2, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, TRet>, ICallGateSubscriber<T1, T2, TRet> internal class CallGatePubSub<T1, T2, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, TRet>, ICallGateSubscriber<T1, T2, TRet>
{ {
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
{
}
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/> /// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
public void RegisterAction(Action<T1, T2> action) public void RegisterAction(Action<T1, T2> action)
=> base.RegisterAction(action); => base.RegisterAction(action);
@ -97,12 +115,18 @@ namespace Dalamud.Plugin.Internal
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/> /// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
public TRet InvokeFunc(T1 arg1, T2 arg2) public TRet InvokeFunc(T1 arg1, T2 arg2)
=> (TRet)base.InvokeFunc(arg1, arg2); => this.InvokeFunc<TRet>(arg1, arg2);
} }
/// <inheritdoc cref="CallGatePubSubBase"/> /// <inheritdoc cref="CallGatePubSubBase"/>
internal class CallGatePubSub<T1, T2, T3, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, TRet>, ICallGateSubscriber<T1, T2, T3, TRet> internal class CallGatePubSub<T1, T2, T3, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, TRet>, ICallGateSubscriber<T1, T2, T3, TRet>
{ {
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
{
}
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/> /// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
public void RegisterAction(Action<T1, T2, T3> action) public void RegisterAction(Action<T1, T2, T3> action)
=> base.RegisterAction(action); => base.RegisterAction(action);
@ -129,12 +153,18 @@ namespace Dalamud.Plugin.Internal
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/> /// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3) public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3)
=> (TRet)base.InvokeFunc(arg1, arg2, arg3); => this.InvokeFunc<TRet>(arg1, arg2, arg3);
} }
/// <inheritdoc cref="CallGatePubSubBase"/> /// <inheritdoc cref="CallGatePubSubBase"/>
internal class CallGatePubSub<T1, T2, T3, T4, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, TRet>, ICallGateSubscriber<T1, T2, T3, T4, TRet> internal class CallGatePubSub<T1, T2, T3, T4, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, TRet>, ICallGateSubscriber<T1, T2, T3, T4, TRet>
{ {
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
{
}
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/> /// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
public void RegisterAction(Action<T1, T2, T3, T4> action) public void RegisterAction(Action<T1, T2, T3, T4> action)
=> base.RegisterAction(action); => base.RegisterAction(action);
@ -161,12 +191,18 @@ namespace Dalamud.Plugin.Internal
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/> /// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4) public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
=> (TRet)base.InvokeFunc(arg1, arg2, arg3, arg4); => this.InvokeFunc<TRet>(arg1, arg2, arg3, arg4);
} }
/// <inheritdoc cref="CallGatePubSubBase"/> /// <inheritdoc cref="CallGatePubSubBase"/>
internal class CallGatePubSub<T1, T2, T3, T4, T5, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, TRet> internal class CallGatePubSub<T1, T2, T3, T4, T5, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, TRet>
{ {
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
{
}
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/> /// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
public void RegisterAction(Action<T1, T2, T3, T4, T5> action) public void RegisterAction(Action<T1, T2, T3, T4, T5> action)
=> base.RegisterAction(action); => base.RegisterAction(action);
@ -193,12 +229,18 @@ namespace Dalamud.Plugin.Internal
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/> /// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
=> (TRet)base.InvokeFunc(arg1, arg2, arg3, arg4, arg5); => this.InvokeFunc<TRet>(arg1, arg2, arg3, arg4, arg5);
} }
/// <inheritdoc cref="CallGatePubSubBase"/> /// <inheritdoc cref="CallGatePubSubBase"/>
internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, T6, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, T6, TRet> internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, T6, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, T6, TRet>
{ {
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
{
}
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/> /// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6> action) public void RegisterAction(Action<T1, T2, T3, T4, T5, T6> action)
=> base.RegisterAction(action); => base.RegisterAction(action);
@ -225,12 +267,18 @@ namespace Dalamud.Plugin.Internal
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/> /// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6)
=> (TRet)base.InvokeFunc(arg1, arg2, arg3, arg4, arg5, arg6); => this.InvokeFunc<TRet>(arg1, arg2, arg3, arg4, arg5, arg6);
} }
/// <inheritdoc cref="CallGatePubSubBase"/> /// <inheritdoc cref="CallGatePubSubBase"/>
internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet> internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet>
{ {
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
{
}
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/> /// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6, T7> action) public void RegisterAction(Action<T1, T2, T3, T4, T5, T6, T7> action)
=> base.RegisterAction(action); => base.RegisterAction(action);
@ -257,12 +305,18 @@ namespace Dalamud.Plugin.Internal
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/> /// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7)
=> (TRet)base.InvokeFunc(arg1, arg2, arg3, arg4, arg5, arg6, arg7); => this.InvokeFunc<TRet>(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
} }
/// <inheritdoc cref="CallGatePubSubBase"/> /// <inheritdoc cref="CallGatePubSubBase"/>
internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, T8, TRet> internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, T8, TRet>
{ {
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
{
}
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/> /// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6, T7, T8> action) public void RegisterAction(Action<T1, T2, T3, T4, T5, T6, T7, T8> action)
=> base.RegisterAction(action); => base.RegisterAction(action);
@ -289,7 +343,7 @@ namespace Dalamud.Plugin.Internal
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/> /// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8)
=> (TRet)base.InvokeFunc(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); => this.InvokeFunc<TRet>(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
} }
} }

View file

@ -1,9 +1,8 @@
using System; using System;
using System.Collections.Generic;
using Serilog; using Dalamud.Plugin.Ipc.Exceptions;
namespace Dalamud.Plugin.Internal namespace Dalamud.Plugin.Ipc.Internal
{ {
/// <summary> /// <summary>
/// This class facilitates inter-plugin communication. /// This class facilitates inter-plugin communication.
@ -13,88 +12,56 @@ namespace Dalamud.Plugin.Internal
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="CallGatePubSubBase"/> class. /// Initializes a new instance of the <see cref="CallGatePubSubBase"/> class.
/// </summary> /// </summary>
internal CallGatePubSubBase() /// <param name="name">The name of the IPC registration.</param>
public CallGatePubSubBase(string name)
{ {
this.Channel = Service<CallGate>.Get().GetOrCreateChannel(name);
} }
/// <summary> /// <summary>
/// Gets or sets the name of the IPC registration. /// Gets the underlying channel implementation.
/// </summary> /// </summary>
public string Name { get; internal set; } protected CallGateChannel Channel { get; init; }
/// <summary>
/// Gets or sets the Action.
/// </summary>
protected Delegate? Action { get; set; }
/// <summary>
/// Gets or sets the Func.
/// </summary>
protected Delegate? Func { get; set; }
/// <summary>
/// Gets the list of subscribed delegates.
/// </summary>
protected List<Delegate> Subs { get; } = new();
/// <summary> /// <summary>
/// Removes a registered Action from inter-plugin communication. /// Removes a registered Action from inter-plugin communication.
/// </summary> /// </summary>
public void UnregisterAction() public void UnregisterAction()
=> this.Action = null; => this.Channel.Action = null;
/// <summary> /// <summary>
/// Removes a registered Func from inter-plugin communication. /// Removes a registered Func from inter-plugin communication.
/// </summary> /// </summary>
public void UnregisterFunc() public void UnregisterFunc()
=> this.Func = null; => this.Channel.Func = null;
/// <summary> /// <summary>
/// Registers an Action for inter-plugin communication. /// Registers an Action for inter-plugin communication.
/// </summary> /// </summary>
/// <param name="action">Action to register.</param> /// <param name="action">Action to register.</param>
private protected void RegisterAction(Delegate action) private protected void RegisterAction(Delegate action)
=> this.Action = action; => this.Channel.Action = action;
/// <summary> /// <summary>
/// Registers a Func for inter-plugin communication. /// Registers a Func for inter-plugin communication.
/// </summary> /// </summary>
/// <param name="func">Func to register.</param> /// <param name="func">Func to register.</param>
private protected void RegisterFunc(Delegate func) private protected void RegisterFunc(Delegate func)
=> this.Func = func; => this.Channel.Func = func;
/// <summary>
/// Invoke all actions that have subscribed to this IPC.
/// </summary>
/// <param name="args">Delegate arguments.</param>
private protected void SendMessage(params object?[]? args)
{
foreach (var sub in this.Subs)
{
try
{
sub.DynamicInvoke(args);
}
catch (Exception ex)
{
Log.Error(ex, $"Error invoking a subscription of {this.Name}");
}
}
}
/// <summary> /// <summary>
/// Subscribe an expression to this registration. /// Subscribe an expression to this registration.
/// </summary> /// </summary>
/// <param name="action">Action to subscribe.</param> /// <param name="action">Action to subscribe.</param>
private protected void Subscribe(Delegate action) private protected void Subscribe(Delegate action)
=> this.Subs.Add(action); => this.Channel.Subscriptions.Add(action);
/// <summary> /// <summary>
/// Unsubscribe an expression from this registration. /// Unsubscribe an expression from this registration.
/// </summary> /// </summary>
/// <param name="action">Action to unsubscribe.</param> /// <param name="action">Action to unsubscribe.</param>
private protected void Unsubscribe(Delegate action) private protected void Unsubscribe(Delegate action)
=> this.Subs.Remove(action); => this.Channel.Subscriptions.Remove(action);
/// <summary> /// <summary>
/// Invoke an action registered for inter-plugin communication. /// Invoke an action registered for inter-plugin communication.
@ -102,15 +69,23 @@ namespace Dalamud.Plugin.Internal
/// <param name="args">Action arguments.</param> /// <param name="args">Action arguments.</param>
/// <exception cref="IpcNotReadyError">This is thrown when the IPC publisher has not registered an action for calling yet.</exception> /// <exception cref="IpcNotReadyError">This is thrown when the IPC publisher has not registered an action for calling yet.</exception>
private protected void InvokeAction(params object?[]? args) private protected void InvokeAction(params object?[]? args)
=> (this.Action ?? throw new IpcNotReadyError(this.Name)).DynamicInvoke(args); => this.Channel.InvokeAction(args);
/// <summary> /// <summary>
/// Invoke a function registered for inter-plugin communication. /// Invoke a function registered for inter-plugin communication.
/// </summary> /// </summary>
/// <param name="args">Parameter args.</param> /// <param name="args">Parameter args.</param>
/// <returns>The return value.</returns> /// <returns>The return value.</returns>
/// <typeparam name="TRet">The return type.</typeparam>
/// <exception cref="IpcNotReadyError">This is thrown when the IPC publisher has not registered a func for calling yet.</exception> /// <exception cref="IpcNotReadyError">This is thrown when the IPC publisher has not registered a func for calling yet.</exception>
private protected object InvokeFunc(params object?[]? args) private protected TRet InvokeFunc<TRet>(params object?[]? args)
=> (this.Func ?? throw new IpcNotReadyError(this.Name)).DynamicInvoke(args); => this.Channel.InvokeFunc<TRet>(args);
/// <summary>
/// Invoke all actions that have subscribed to this IPC.
/// </summary>
/// <param name="args">Delegate arguments.</param>
private protected void SendMessage(params object?[]? args)
=> this.Channel.SendMessage(args);
} }
} }

View file

@ -1,27 +0,0 @@
using System;
namespace Dalamud.Plugin
{
/// <summary>
/// This exception is thrown when an IPC method is invoked, but nothing has been registered by that name yet.
/// </summary>
public class IpcNotReadyError : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="IpcNotReadyError"/> class.
/// </summary>
/// <param name="name">Name of the IPC method.</param>
public IpcNotReadyError(string name)
{
this.Name = name;
}
/// <summary>
/// Gets the name of the IPC that was invoked.
/// </summary>
public string Name { get; }
/// <inheritdoc/>
public override string Message => $"IPC method {this.Name} was not registered yet";
}
}

View file

@ -1,48 +0,0 @@
using System;
using System.Linq;
namespace Dalamud.Plugin
{
/// <summary>
/// This exception is thrown when an IPC method is checked out, but the type does not match what was previously registered.
/// </summary>
public class IpcTypeMismatchError : Exception
{
private readonly string message;
/// <summary>
/// Initializes a new instance of the <see cref="IpcTypeMismatchError"/> class.
/// </summary>
/// <param name="name">Name of the IPC method.</param>
/// <param name="requestedTypes">The types requested when checking out the IPC.</param>
/// <param name="actualTypes">The types registered by the IPC.</param>
public IpcTypeMismatchError(string name, Type[] requestedTypes, Type[] actualTypes)
{
this.Name = name;
this.RequestedTypes = requestedTypes;
this.ActualTypes = actualTypes;
var t1 = string.Join(", ", this.RequestedTypes.Select(t => t.Name));
var t2 = string.Join(", ", this.ActualTypes.Select(t => t.Name));
this.message = $"IPC method {this.Name} has a different type than was requested. [ {t1} ] != [ {t2} ]";
}
/// <summary>
/// Gets the name of the IPC that was invoked.
/// </summary>
public string Name { get; }
/// <summary>
/// Gets the types that were requested.
/// </summary>
public Type[] RequestedTypes { get; }
/// <summary>
/// Gets the types that were previously registered.
/// </summary>
public Type[] ActualTypes { get; }
/// <inheritdoc/>
public override string Message => this.message;
}
}