feat: Add new IPC methods, and docs (#2042)

- New methods `HasAction`, `HasFunction`, and `SubscriptionCount` to allow making better decisions about IPC logic.
- Some better documentation for the call gates.
This commit is contained in:
KazWolfe 2024-09-25 09:54:50 -07:00 committed by GitHub
parent 4d802a1bee
commit 73ec12145d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 113 additions and 91 deletions

View file

@ -4,8 +4,24 @@ using Dalamud.Plugin.Ipc.Internal;
namespace Dalamud.Plugin.Ipc;
/// <inheritdoc cref="CallGatePubSubBase"/>
public interface ICallGateProvider<TRet>
/// <summary>
/// The backing interface for the provider ("server") half of an IPC channel. This interface is used to expose methods
/// to other plugins via RPC, as well as to allow other plugins to subscribe to notifications from this plugin.
/// </summary>
public interface ICallGateProvider
{
/// <inheritdoc cref="CallGatePubSubBase.SubscriptionCount"/>
public int SubscriptionCount { get; }
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
public void UnregisterAction();
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
public void UnregisterFunc();
}
/// <inheritdoc cref="ICallGateProvider"/>
public interface ICallGateProvider<TRet> : ICallGateProvider
{
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
public void RegisterAction(Action action);
@ -13,18 +29,12 @@ public interface ICallGateProvider<TRet>
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
public void RegisterFunc(Func<TRet> func);
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
public void UnregisterAction();
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
public void UnregisterFunc();
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
public void SendMessage();
}
/// <inheritdoc cref="CallGatePubSubBase"/>
public interface ICallGateProvider<T1, TRet>
/// <inheritdoc cref="ICallGateProvider"/>
public interface ICallGateProvider<T1, TRet> : ICallGateProvider
{
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
public void RegisterAction(Action<T1> action);
@ -32,18 +42,12 @@ public interface ICallGateProvider<T1, TRet>
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
public void RegisterFunc(Func<T1, TRet> func);
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
public void UnregisterAction();
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
public void UnregisterFunc();
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
public void SendMessage(T1 arg1);
}
/// <inheritdoc cref="CallGatePubSubBase"/>
public interface ICallGateProvider<T1, T2, TRet>
/// <inheritdoc cref="ICallGateProvider"/>
public interface ICallGateProvider<T1, T2, TRet> : ICallGateProvider
{
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
public void RegisterAction(Action<T1, T2> action);
@ -51,18 +55,12 @@ public interface ICallGateProvider<T1, T2, TRet>
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
public void RegisterFunc(Func<T1, T2, TRet> func);
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
public void UnregisterAction();
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
public void UnregisterFunc();
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
public void SendMessage(T1 arg1, T2 arg2);
}
/// <inheritdoc cref="CallGatePubSubBase"/>
public interface ICallGateProvider<T1, T2, T3, TRet>
/// <inheritdoc cref="ICallGateProvider"/>
public interface ICallGateProvider<T1, T2, T3, TRet> : ICallGateProvider
{
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
public void RegisterAction(Action<T1, T2, T3> action);
@ -70,18 +68,12 @@ public interface ICallGateProvider<T1, T2, T3, TRet>
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
public void RegisterFunc(Func<T1, T2, T3, TRet> func);
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
public void UnregisterAction();
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
public void UnregisterFunc();
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
public void SendMessage(T1 arg1, T2 arg2, T3 arg3);
}
/// <inheritdoc cref="CallGatePubSubBase"/>
public interface ICallGateProvider<T1, T2, T3, T4, TRet>
/// <inheritdoc cref="ICallGateProvider"/>
public interface ICallGateProvider<T1, T2, T3, T4, TRet> : ICallGateProvider
{
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
public void RegisterAction(Action<T1, T2, T3, T4> action);
@ -89,18 +81,12 @@ public interface ICallGateProvider<T1, T2, T3, T4, TRet>
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
public void RegisterFunc(Func<T1, T2, T3, T4, TRet> func);
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
public void UnregisterAction();
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
public void UnregisterFunc();
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
}
/// <inheritdoc cref="CallGatePubSubBase"/>
public interface ICallGateProvider<T1, T2, T3, T4, T5, TRet>
/// <inheritdoc cref="ICallGateProvider"/>
public interface ICallGateProvider<T1, T2, T3, T4, T5, TRet> : ICallGateProvider
{
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
public void RegisterAction(Action<T1, T2, T3, T4, T5> action);
@ -108,18 +94,12 @@ public interface ICallGateProvider<T1, T2, T3, T4, T5, TRet>
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
public void RegisterFunc(Func<T1, T2, T3, T4, T5, TRet> func);
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
public void UnregisterAction();
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
public void UnregisterFunc();
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
}
/// <inheritdoc cref="CallGatePubSubBase"/>
public interface ICallGateProvider<T1, T2, T3, T4, T5, T6, TRet>
/// <inheritdoc cref="ICallGateProvider"/>
public interface ICallGateProvider<T1, T2, T3, T4, T5, T6, TRet> : ICallGateProvider
{
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6> action);
@ -127,18 +107,12 @@ public interface ICallGateProvider<T1, T2, T3, T4, T5, T6, TRet>
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
public void RegisterFunc(Func<T1, T2, T3, T4, T5, T6, TRet> func);
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
public void UnregisterAction();
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
public void UnregisterFunc();
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
}
/// <inheritdoc cref="CallGatePubSubBase"/>
public interface ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, TRet>
/// <inheritdoc cref="ICallGateProvider"/>
public interface ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, TRet> : ICallGateProvider
{
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6, T7> action);
@ -146,18 +120,12 @@ public interface ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, TRet>
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
public void RegisterFunc(Func<T1, T2, T3, T4, T5, T6, T7, TRet> func);
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
public void UnregisterAction();
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
public void UnregisterFunc();
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
}
/// <inheritdoc cref="CallGatePubSubBase"/>
public interface ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet>
/// <inheritdoc cref="ICallGateProvider"/>
public interface ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet> : ICallGateProvider
{
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6, T7, T8> action);
@ -165,12 +133,6 @@ public interface ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet>
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
public void RegisterFunc(Func<T1, T2, T3, T4, T5, T6, T7, T8, TRet> func);
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
public void UnregisterAction();
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
public void UnregisterFunc();
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
}

View file

@ -4,8 +4,21 @@ using Dalamud.Plugin.Ipc.Internal;
namespace Dalamud.Plugin.Ipc;
/// <summary>
/// An interface for all IPC subscribers.
/// </summary>
public interface ICallGateSubscriber
{
/// <inheritdoc cref="CallGatePubSubBase.HasAction"/>
public bool HasAction { get; }
/// <inheritdoc cref="CallGatePubSubBase.HasFunction"/>
public bool HasFunction { get; }
}
/// <inheritdoc cref="CallGatePubSubBase"/>
public interface ICallGateSubscriber<TRet>
public interface ICallGateSubscriber<TRet> : ICallGateSubscriber
{
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
public void Subscribe(Action action);
@ -21,7 +34,7 @@ public interface ICallGateSubscriber<TRet>
}
/// <inheritdoc cref="CallGatePubSubBase"/>
public interface ICallGateSubscriber<T1, TRet>
public interface ICallGateSubscriber<T1, TRet> : ICallGateSubscriber
{
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
public void Subscribe(Action<T1> action);
@ -37,7 +50,7 @@ public interface ICallGateSubscriber<T1, TRet>
}
/// <inheritdoc cref="CallGatePubSubBase"/>
public interface ICallGateSubscriber<T1, T2, TRet>
public interface ICallGateSubscriber<T1, T2, TRet> : ICallGateSubscriber
{
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
public void Subscribe(Action<T1, T2> action);
@ -53,7 +66,7 @@ public interface ICallGateSubscriber<T1, T2, TRet>
}
/// <inheritdoc cref="CallGatePubSubBase"/>
public interface ICallGateSubscriber<T1, T2, T3, TRet>
public interface ICallGateSubscriber<T1, T2, T3, TRet> : ICallGateSubscriber
{
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
public void Subscribe(Action<T1, T2, T3> action);
@ -69,7 +82,7 @@ public interface ICallGateSubscriber<T1, T2, T3, TRet>
}
/// <inheritdoc cref="CallGatePubSubBase"/>
public interface ICallGateSubscriber<T1, T2, T3, T4, TRet>
public interface ICallGateSubscriber<T1, T2, T3, T4, TRet> : ICallGateSubscriber
{
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
public void Subscribe(Action<T1, T2, T3, T4> action);
@ -84,7 +97,7 @@ public interface ICallGateSubscriber<T1, T2, T3, T4, TRet>
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
}
/// <inheritdoc cref="CallGatePubSubBase"/>
/// <inheritdoc cref="CallGatePubSubBase"/> : ICallGateSubscriber
public interface ICallGateSubscriber<T1, T2, T3, T4, T5, TRet>
{
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
@ -100,7 +113,7 @@ public interface ICallGateSubscriber<T1, T2, T3, T4, T5, TRet>
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
}
/// <inheritdoc cref="CallGatePubSubBase"/>
/// <inheritdoc cref="CallGatePubSubBase"/> : ICallGateSubscriber
public interface ICallGateSubscriber<T1, T2, T3, T4, T5, T6, TRet>
{
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
@ -116,7 +129,7 @@ public interface ICallGateSubscriber<T1, T2, T3, T4, T5, T6, TRet>
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
}
/// <inheritdoc cref="CallGatePubSubBase"/>
/// <inheritdoc cref="CallGatePubSubBase"/> : ICallGateSubscriber
public interface ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet>
{
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
@ -132,7 +145,7 @@ public interface ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet>
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
}
/// <inheritdoc cref="CallGatePubSubBase"/>
/// <inheritdoc cref="CallGatePubSubBase"/> : ICallGateSubscriber
public interface ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, T8, TRet>
{
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>

View file

@ -16,71 +16,118 @@ internal abstract class CallGatePubSubBase
this.Channel = Service<CallGate>.Get().GetOrCreateChannel(name);
}
/// <summary>
/// Gets a value indicating whether this IPC call gate has an associated Action. Only exposed to
/// <see cref="ICallGateSubscriber"/>s.
/// </summary>
public bool HasAction => this.Channel.Action != null;
/// <summary>
/// Gets a value indicating whether this IPC call gate has an associated Function. Only exposed to
/// <see cref="ICallGateSubscriber"/>s.
/// </summary>
public bool HasFunction => this.Channel.Func != null;
/// <summary>
/// Gets the count of subscribers listening for messages through this call gate. Only exposed to
/// <see cref="ICallGateProvider"/>s, and can be used to determine if messages should be sent through the gate.
/// </summary>
public int SubscriptionCount => this.Channel.Subscriptions.Count;
/// <summary>
/// Gets the underlying channel implementation.
/// </summary>
protected CallGateChannel Channel { get; init; }
/// <summary>
/// Removes a registered Action from inter-plugin communication.
/// Removes the associated Action from this call gate, effectively disabling RPC calls.
/// </summary>
/// <seealso cref="RegisterAction"/>
public void UnregisterAction()
=> this.Channel.Action = null;
/// <summary>
/// Removes a registered Func from inter-plugin communication.
/// Removes the associated Function from this call gate.
/// </summary>
/// <seealso cref="RegisterFunc"/>
public void UnregisterFunc()
=> this.Channel.Func = null;
/// <summary>
/// Registers an Action for inter-plugin communication.
/// Registers a <see cref="Delegate"/> for use by other plugins via RPC. This Delegate must satisfy the constraints
/// of an <see cref="Action"/> type as defined by the interface, meaning they may not return a value and must have
/// the proper number of parameters.
/// </summary>
/// <param name="action">Action to register.</param>
/// <seealso cref="UnregisterAction"/>
/// <seealso cref="InvokeAction"/>
private protected void RegisterAction(Delegate action)
=> this.Channel.Action = action;
/// <summary>
/// Registers a Func for inter-plugin communication.
/// Registers a <see cref="Delegate"/> for use by other plugins via RPC. This Delegate must satisfy the constraints
/// of a <see cref="Func{TResult}"/> type as defined by the interface, meaning its return type and parameters must
/// match accordingly.
/// </summary>
/// <param name="func">Func to register.</param>
/// <seealso cref="UnregisterFunc"/>
/// <seealso cref="InvokeFunc{TRet}"/>
private protected void RegisterFunc(Delegate func)
=> this.Channel.Func = func;
/// <summary>
/// Subscribe an expression to this registration.
/// Registers a <see cref="Delegate"/> (of type <see cref="Action{T}"/>) that will be called when the providing
/// plugin calls <see cref="ICallGateProvider{T1}.SendMessage"/>. This method can be used to receive notifications
/// of events or data updates from a specific plugin.
/// </summary>
/// <param name="action">Action to subscribe.</param>
/// <seealso cref="Unsubscribe"/>
private protected void Subscribe(Delegate action)
=> this.Channel.Subscribe(action);
/// <summary>
/// Unsubscribe an expression from this registration.
/// Removes a subscription created through <see cref="Subscribe"/>. Note that the <see cref="Delegate"/> to be
/// unsubscribed must be the same instance as the one passed in.
/// </summary>
/// <param name="action">Action to unsubscribe.</param>
/// <seealso cref="Subscribe"/>
private protected void Unsubscribe(Delegate action)
=> this.Channel.Unsubscribe(action);
/// <summary>
/// Invoke an action registered for inter-plugin communication.
/// Executes the Action registered for this IPC call gate via <see cref="RegisterAction"/>. This method is intended
/// to be called by plugins wishing to access another plugin via RPC. The parameters passed to this method will be
/// passed to the owning plugin, with appropriate serialization for complex data types. Primitive data types will
/// be passed as-is. The target Action will be called on the <em>same thread</em> as the caller.
/// </summary>
/// <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>
/// <seealso cref="RegisterAction"/>
/// <seealso cref="UnregisterAction"/>
private protected void InvokeAction(params object?[]? args)
=> this.Channel.InvokeAction(args);
/// <summary>
/// Invoke a function registered for inter-plugin communication.
/// Executes the Function registered for this IPC call gate via <see cref="RegisterFunc"/>. This method is intended
/// to be called by plugins wishing to access another plugin via RPC. The parameters passed to this method will be
/// passed to the owning plugin, with appropriate serialization for complex data types. Primitive data types will
/// be passed as-is. The target Action will be called on the <em>same thread</em> as the caller.
/// </summary>
/// <param name="args">Parameter args.</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>
/// <seealso cref="RegisterFunc"/>
/// <seealso cref="UnregisterFunc"/>
private protected TRet InvokeFunc<TRet>(params object?[]? args)
=> this.Channel.InvokeFunc<TRet>(args);
/// <summary>
/// Invoke all actions that have subscribed to this IPC.
/// Send the given arguments to all subscribers (through <see cref="Subscribe"/>) of this IPC call gate. This method
/// is intended to be used by the provider plugin to notify all subscribers of an event or data update. The
/// parameters passed to this method will be passed to all subscribers, with appropriate serialization for complex
/// data types. Primitive data types will be passed as-is. The subscription actions will be called sequentially in
/// order of registration on the <em>same thread</em> as the caller.
/// </summary>
/// <param name="args">Delegate arguments.</param>
private protected void SendMessage(params object?[]? args)