diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs index a435e24d7..6e9acd0aa 100644 --- a/Dalamud/Dalamud.cs +++ b/Dalamud/Dalamud.cs @@ -17,8 +17,8 @@ using Dalamud.Game.Text.SeStringHandling; using Dalamud.Hooking.Internal; using Dalamud.Interface.Internal; using Dalamud.IoC.Internal; -using Dalamud.Plugin; using Dalamud.Plugin.Internal; +using Dalamud.Plugin.Ipc.Internal; using Serilog; using Serilog.Core; @@ -196,7 +196,9 @@ namespace Dalamud Log.Information("[T2] Data OK!"); +#pragma warning disable CS0618 // Type or member is obsolete Service.Set(); +#pragma warning restore CS0618 // Type or member is obsolete Log.Information("[T2] SeString OK!"); diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index db9755052..34268798d 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -51,7 +51,7 @@ - IDE0003;IDE0044;IDE1006;CA1822;CS1591;CS1701;CS1702 + IDE0002;IDE0003;IDE0044;IDE1006;CA1822;CS1591;CS1701;CS1702 diff --git a/Dalamud/Interface/Internal/Windows/DataWindow.cs b/Dalamud/Interface/Internal/Windows/DataWindow.cs index d438c6450..2ad2d19e3 100644 --- a/Dalamud/Interface/Internal/Windows/DataWindow.cs +++ b/Dalamud/Interface/Internal/Windows/DataWindow.cs @@ -25,8 +25,8 @@ using Dalamud.Game.Text; using Dalamud.Interface.Internal.Notifications; using Dalamud.Interface.Windowing; using Dalamud.Memory; -using Dalamud.Plugin; -using Dalamud.Plugin.Internal; +using Dalamud.Plugin.Ipc; +using Dalamud.Plugin.Ipc.Internal; using Dalamud.Utility; using ImGuiNET; using ImGuiScene; @@ -617,7 +617,7 @@ namespace Dalamud.Interface.Internal.Windows { if (this.ipcPub == null) { - this.ipcPub = Service.Get().GetIpcPubSub("dataDemo1"); + this.ipcPub = new CallGatePubSub("dataDemo1"); this.ipcPub.RegisterAction((msg) => { @@ -633,7 +633,7 @@ namespace Dalamud.Interface.Internal.Windows if (this.ipcSub == null) { - this.ipcSub = Service.Get().GetIpcPubSub("dataDemo1"); + this.ipcSub = new CallGatePubSub("dataDemo1"); this.ipcSub.Subscribe((msg) => { Log.Information("PONG1"); diff --git a/Dalamud/Plugin/DalamudPluginInterface.cs b/Dalamud/Plugin/DalamudPluginInterface.cs index e3abb0219..9fae3c2fc 100644 --- a/Dalamud/Plugin/DalamudPluginInterface.cs +++ b/Dalamud/Plugin/DalamudPluginInterface.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel.Design; using System.Globalization; using System.IO; using System.Linq; @@ -17,6 +16,9 @@ using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Interface; using Dalamud.Interface.Internal; using Dalamud.Plugin.Internal; +using Dalamud.Plugin.Ipc; +using Dalamud.Plugin.Ipc.Exceptions; +using Dalamud.Plugin.Ipc.Internal; namespace Dalamud.Plugin { @@ -145,39 +147,39 @@ namespace Dalamud.Plugin /// An IPC publisher. /// This is thrown when the requested types do not match the previously registered types are different. public ICallGateProvider GetIpcProvider(string name) - => Service.Get().GetIpcPubSub(name); + => new CallGatePubSub(name); /// public ICallGateProvider GetIpcProvider(string name) - => Service.Get().GetIpcPubSub(name); + => new CallGatePubSub(name); /// public ICallGateProvider GetIpcProvider(string name) - => Service.Get().GetIpcPubSub(name); + => new CallGatePubSub(name); /// public ICallGateProvider GetIpcProvider(string name) - => Service.Get().GetIpcPubSub(name); + => new CallGatePubSub(name); /// public ICallGateProvider GetIpcProvider(string name) - => Service.Get().GetIpcPubSub(name); + => new CallGatePubSub(name); /// public ICallGateProvider GetIpcProvider(string name) - => Service.Get().GetIpcPubSub(name); + => new CallGatePubSub(name); /// public ICallGateProvider GetIpcProvider(string name) - => Service.Get().GetIpcPubSub(name); + => new CallGatePubSub(name); /// public ICallGateProvider GetIpcProvider(string name) - => Service.Get().GetIpcPubSub(name); + => new CallGatePubSub(name); /// public ICallGateProvider GetIpcProvider(string name) - => Service.Get().GetIpcPubSub(name); + => new CallGatePubSub(name); /// /// Gets an IPC subscriber. @@ -186,39 +188,39 @@ namespace Dalamud.Plugin /// The name of the IPC registration. /// An IPC publisher. public ICallGateSubscriber GetIpcSubscriber(string name) - => Service.Get().GetIpcPubSub(name); + => new CallGatePubSub(name); /// public ICallGateSubscriber GetIpcSubscriber(string name) - => Service.Get().GetIpcPubSub(name); + => new CallGatePubSub(name); /// public ICallGateSubscriber GetIpcSubscriber(string name) - => Service.Get().GetIpcPubSub(name); + => new CallGatePubSub(name); /// public ICallGateSubscriber GetIpcSubscriber(string name) - => Service.Get().GetIpcPubSub(name); + => new CallGatePubSub(name); /// public ICallGateSubscriber GetIpcSubscriber(string name) - => Service.Get().GetIpcPubSub(name); + => new CallGatePubSub(name); /// public ICallGateSubscriber GetIpcSubscriber(string name) - => Service.Get().GetIpcPubSub(name); + => new CallGatePubSub(name); /// public ICallGateSubscriber GetIpcSubscriber(string name) - => Service.Get().GetIpcPubSub(name); + => new CallGatePubSub(name); /// public ICallGateSubscriber GetIpcSubscriber(string name) - => Service.Get().GetIpcPubSub(name); + => new CallGatePubSub(name); /// public ICallGateSubscriber GetIpcSubscriber(string name) - => Service.Get().GetIpcPubSub(name); + => new CallGatePubSub(name); #endregion diff --git a/Dalamud/Plugin/Internal/CallGate.cs b/Dalamud/Plugin/Internal/CallGate.cs deleted file mode 100644 index 7ec4d9dc7..000000000 --- a/Dalamud/Plugin/Internal/CallGate.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Dalamud.Plugin.Internal -{ - /// - /// This class facilitates inter-plugin communication. - /// - internal class CallGate - { - private Dictionary gates = new(); - - #region GetIpcPubSub - - /// - internal CallGatePubSub GetIpcPubSub(string name) - => (CallGatePubSub)this.GetIpcPubSub(name, typeof(TRet)); - - /// - internal CallGatePubSub GetIpcPubSub(string name) - => (CallGatePubSub)this.GetIpcPubSub(name, typeof(T1), typeof(TRet)); - - /// - internal CallGatePubSub GetIpcPubSub(string name) - => (CallGatePubSub)this.GetIpcPubSub(name, typeof(T1), typeof(T2), typeof(TRet)); - - /// - internal CallGatePubSub GetIpcPubSub(string name) - => (CallGatePubSub)this.GetIpcPubSub(name, typeof(T1), typeof(T2), typeof(T3), typeof(TRet)); - - /// - internal CallGatePubSub GetIpcPubSub(string name) - => (CallGatePubSub)this.GetIpcPubSub(name, typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(TRet)); - - /// - internal CallGatePubSub GetIpcPubSub(string name) - => (CallGatePubSub)this.GetIpcPubSub(name, typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(TRet)); - - /// - internal CallGatePubSub GetIpcPubSub(string name) - => (CallGatePubSub)this.GetIpcPubSub(name, typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(TRet)); - - /// - internal CallGatePubSub GetIpcPubSub(string name) - => (CallGatePubSub)this.GetIpcPubSub(name, typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(TRet)); - - /// - internal CallGatePubSub GetIpcPubSub(string name) - => (CallGatePubSub)this.GetIpcPubSub(name, typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(TRet)); - - #endregion - - /// - /// Gets or sets an IPC pub/sub callgate. - /// - /// Name of the IPC registration. - /// The callgate parameter types. - /// The IPC pub/sub callgate. - 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; - } - } -} diff --git a/Dalamud/Plugin/Ipc/Exceptions/IpcError.cs b/Dalamud/Plugin/Ipc/Exceptions/IpcError.cs new file mode 100644 index 000000000..053331dce --- /dev/null +++ b/Dalamud/Plugin/Ipc/Exceptions/IpcError.cs @@ -0,0 +1,36 @@ +using System; + +namespace Dalamud.Plugin.Ipc.Exceptions +{ + /// + /// This exception is thrown when an IPC errors are encountered. + /// + public abstract class IpcError : Exception + { + /// + /// Initializes a new instance of the class. + /// + public IpcError() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message that describes the error. + public IpcError(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message that describes the error. + /// The exception that is the cause of the current exception. + public IpcError(string message, Exception ex) + : base(message, ex) + { + } + } +} diff --git a/Dalamud/Plugin/Ipc/Exceptions/IpcLengthMismatchError.cs b/Dalamud/Plugin/Ipc/Exceptions/IpcLengthMismatchError.cs new file mode 100644 index 000000000..bb5f64070 --- /dev/null +++ b/Dalamud/Plugin/Ipc/Exceptions/IpcLengthMismatchError.cs @@ -0,0 +1,19 @@ +namespace Dalamud.Plugin.Ipc.Exceptions +{ + /// + /// This exception is thrown when an IPC method is invoked and the number of types does not match what was previously registered. + /// + public class IpcLengthMismatchError : IpcError + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the IPC method. + /// The amount of types requested when checking out the IPC. + /// The amount of types registered by the IPC. + public IpcLengthMismatchError(string name, int requestedLength, int actualLength) + : base($"IPC method {name} has a different number of types than was requested. {requestedLength} != {actualLength}") + { + } + } +} diff --git a/Dalamud/Plugin/Ipc/Exceptions/IpcNotReadyError.cs b/Dalamud/Plugin/Ipc/Exceptions/IpcNotReadyError.cs new file mode 100644 index 000000000..6bfd87ba8 --- /dev/null +++ b/Dalamud/Plugin/Ipc/Exceptions/IpcNotReadyError.cs @@ -0,0 +1,17 @@ +namespace Dalamud.Plugin.Ipc.Exceptions +{ + /// + /// This exception is thrown when an IPC method is invoked, but no actions or funcs have been registered yet. + /// + public class IpcNotReadyError : IpcError + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the IPC method. + public IpcNotReadyError(string name) + : base($"IPC method {name} was not registered yet") + { + } + } +} diff --git a/Dalamud/Plugin/Ipc/Exceptions/IpcTypeMismatchError.cs b/Dalamud/Plugin/Ipc/Exceptions/IpcTypeMismatchError.cs new file mode 100644 index 000000000..2de5adce8 --- /dev/null +++ b/Dalamud/Plugin/Ipc/Exceptions/IpcTypeMismatchError.cs @@ -0,0 +1,22 @@ +using System; + +namespace Dalamud.Plugin.Ipc.Exceptions +{ + /// + /// This exception is thrown when an IPC method is checked out, but the type does not match what was previously registered. + /// + public class IpcTypeMismatchError : IpcError + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the IPC method. + /// The before type. + /// The after type. + /// The exception that is the cause of the current exception. + public IpcTypeMismatchError(string name, Type requestedType, Type actualType, Exception ex) + : base($"IPC method {name} blew up when converting from {requestedType.Name} to {actualType}", ex) + { + } + } +} diff --git a/Dalamud/Plugin/ICallGateProvider.cs b/Dalamud/Plugin/Ipc/ICallGateProvider.cs similarity index 99% rename from Dalamud/Plugin/ICallGateProvider.cs rename to Dalamud/Plugin/Ipc/ICallGateProvider.cs index 991bc41f9..62b95c809 100644 --- a/Dalamud/Plugin/ICallGateProvider.cs +++ b/Dalamud/Plugin/Ipc/ICallGateProvider.cs @@ -1,10 +1,10 @@ using System; -using Dalamud.Plugin.Internal; +using Dalamud.Plugin.Ipc.Internal; #pragma warning disable SA1402 // File may only contain a single type -namespace Dalamud.Plugin +namespace Dalamud.Plugin.Ipc { /// public interface ICallGateProvider diff --git a/Dalamud/Plugin/ICallGateSubscriber.cs b/Dalamud/Plugin/Ipc/ICallGateSubscriber.cs similarity index 98% rename from Dalamud/Plugin/ICallGateSubscriber.cs rename to Dalamud/Plugin/Ipc/ICallGateSubscriber.cs index 779497275..d5ddd1329 100644 --- a/Dalamud/Plugin/ICallGateSubscriber.cs +++ b/Dalamud/Plugin/Ipc/ICallGateSubscriber.cs @@ -1,10 +1,10 @@ using System; -using Dalamud.Plugin.Internal; +using Dalamud.Plugin.Ipc.Internal; #pragma warning disable SA1402 // File may only contain a single type -namespace Dalamud.Plugin +namespace Dalamud.Plugin.Ipc { /// public interface ICallGateSubscriber diff --git a/Dalamud/Plugin/Ipc/Internal/CallGate.cs b/Dalamud/Plugin/Ipc/Internal/CallGate.cs new file mode 100644 index 000000000..dd89b02bc --- /dev/null +++ b/Dalamud/Plugin/Ipc/Internal/CallGate.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; + +namespace Dalamud.Plugin.Ipc.Internal +{ + /// + /// This class facilitates inter-plugin communication. + /// + internal class CallGate + { + private readonly Dictionary gates = new(); + + /// + /// Initializes a new instance of the class. + /// + internal CallGate() + { + } + + /// + /// Gets the provider associated with the specified name. + /// + /// Name of the IPC registration. + /// A CallGate registered under the given name. + public CallGateChannel GetOrCreateChannel(string name) + { + if (!this.gates.TryGetValue(name, out var gate)) + gate = this.gates[name] = new CallGateChannel(name); + return gate; + } + } +} diff --git a/Dalamud/Plugin/Ipc/Internal/CallGateChannel.cs b/Dalamud/Plugin/Ipc/Internal/CallGateChannel.cs new file mode 100644 index 000000000..21563c1a7 --- /dev/null +++ b/Dalamud/Plugin/Ipc/Internal/CallGateChannel.cs @@ -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 +{ + /// + /// This class implements the channels and serialization needed for the typed gates to interact with each other. + /// + internal class CallGateChannel + { + /// + /// Initializes a new instance of the class. + /// + /// The name of this IPC registration. + public CallGateChannel(string name) + { + this.Name = name; + } + + /// + /// Gets the name of the IPC registration. + /// + public string Name { get; init; } + + /// + /// Gets a list of delegate subscriptions for when SendMessage is called. + /// + public List Subscriptions { get; } = new(); + + /// + /// Gets or sets an action for when InvokeAction is called. + /// + public Delegate Action { get; set; } + + /// + /// Gets or sets a func for when InvokeFunc is called. + /// + public Delegate Func { get; set; } + + /// + /// Invoke all actions that have subscribed to this IPC. + /// + /// Message arguments. + 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); + } + } + + /// + /// Invoke an action registered for inter-plugin communication. + /// + /// Action arguments. + /// This is thrown when the IPC publisher has not registered a func for calling yet. + 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); + } + + /// + /// Invoke a function registered for inter-plugin communication. + /// + /// Func arguments. + /// The return value. + /// The return type. + /// This is thrown when the IPC publisher has not registered a func for calling yet. + internal TRet InvokeFunc(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); + } + } + } +} diff --git a/Dalamud/Plugin/Internal/CallGatePubSub.cs b/Dalamud/Plugin/Ipc/Internal/CallGatePubSub.cs similarity index 87% rename from Dalamud/Plugin/Internal/CallGatePubSub.cs rename to Dalamud/Plugin/Ipc/Internal/CallGatePubSub.cs index c9ee1e45e..da1bcda49 100644 --- a/Dalamud/Plugin/Internal/CallGatePubSub.cs +++ b/Dalamud/Plugin/Ipc/Internal/CallGatePubSub.cs @@ -2,11 +2,17 @@ using System; #pragma warning disable SA1402 // File may only contain a single type -namespace Dalamud.Plugin.Internal +namespace Dalamud.Plugin.Ipc.Internal { /// internal class CallGatePubSub : CallGatePubSubBase, ICallGateProvider, ICallGateSubscriber { + /// + public CallGatePubSub(string name) + : base(name) + { + } + /// public void RegisterAction(Action action) => base.RegisterAction(action); @@ -33,12 +39,18 @@ namespace Dalamud.Plugin.Internal /// public TRet InvokeFunc() - => (TRet)base.InvokeFunc(); + => this.InvokeFunc(); } /// internal class CallGatePubSub : CallGatePubSubBase, ICallGateProvider, ICallGateSubscriber { + /// + public CallGatePubSub(string name) + : base(name) + { + } + /// public void RegisterAction(Action action) => base.RegisterAction(action); @@ -65,12 +77,18 @@ namespace Dalamud.Plugin.Internal /// public TRet InvokeFunc(T1 arg1) - => (TRet)base.InvokeFunc(arg1); + => this.InvokeFunc(arg1); } /// internal class CallGatePubSub : CallGatePubSubBase, ICallGateProvider, ICallGateSubscriber { + /// + public CallGatePubSub(string name) + : base(name) + { + } + /// public void RegisterAction(Action action) => base.RegisterAction(action); @@ -97,12 +115,18 @@ namespace Dalamud.Plugin.Internal /// public TRet InvokeFunc(T1 arg1, T2 arg2) - => (TRet)base.InvokeFunc(arg1, arg2); + => this.InvokeFunc(arg1, arg2); } /// internal class CallGatePubSub : CallGatePubSubBase, ICallGateProvider, ICallGateSubscriber { + /// + public CallGatePubSub(string name) + : base(name) + { + } + /// public void RegisterAction(Action action) => base.RegisterAction(action); @@ -129,12 +153,18 @@ namespace Dalamud.Plugin.Internal /// public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3) - => (TRet)base.InvokeFunc(arg1, arg2, arg3); + => this.InvokeFunc(arg1, arg2, arg3); } /// internal class CallGatePubSub : CallGatePubSubBase, ICallGateProvider, ICallGateSubscriber { + /// + public CallGatePubSub(string name) + : base(name) + { + } + /// public void RegisterAction(Action action) => base.RegisterAction(action); @@ -161,12 +191,18 @@ namespace Dalamud.Plugin.Internal /// public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4) - => (TRet)base.InvokeFunc(arg1, arg2, arg3, arg4); + => this.InvokeFunc(arg1, arg2, arg3, arg4); } /// internal class CallGatePubSub : CallGatePubSubBase, ICallGateProvider, ICallGateSubscriber { + /// + public CallGatePubSub(string name) + : base(name) + { + } + /// public void RegisterAction(Action action) => base.RegisterAction(action); @@ -193,12 +229,18 @@ namespace Dalamud.Plugin.Internal /// public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) - => (TRet)base.InvokeFunc(arg1, arg2, arg3, arg4, arg5); + => this.InvokeFunc(arg1, arg2, arg3, arg4, arg5); } /// internal class CallGatePubSub : CallGatePubSubBase, ICallGateProvider, ICallGateSubscriber { + /// + public CallGatePubSub(string name) + : base(name) + { + } + /// public void RegisterAction(Action action) => base.RegisterAction(action); @@ -225,12 +267,18 @@ namespace Dalamud.Plugin.Internal /// 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(arg1, arg2, arg3, arg4, arg5, arg6); } /// internal class CallGatePubSub : CallGatePubSubBase, ICallGateProvider, ICallGateSubscriber { + /// + public CallGatePubSub(string name) + : base(name) + { + } + /// public void RegisterAction(Action action) => base.RegisterAction(action); @@ -257,12 +305,18 @@ namespace Dalamud.Plugin.Internal /// 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(arg1, arg2, arg3, arg4, arg5, arg6, arg7); } /// internal class CallGatePubSub : CallGatePubSubBase, ICallGateProvider, ICallGateSubscriber { + /// + public CallGatePubSub(string name) + : base(name) + { + } + /// public void RegisterAction(Action action) => base.RegisterAction(action); @@ -289,7 +343,7 @@ namespace Dalamud.Plugin.Internal /// 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(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); } } diff --git a/Dalamud/Plugin/Internal/CallGatePubSubBase.cs b/Dalamud/Plugin/Ipc/Internal/CallGatePubSubBase.cs similarity index 63% rename from Dalamud/Plugin/Internal/CallGatePubSubBase.cs rename to Dalamud/Plugin/Ipc/Internal/CallGatePubSubBase.cs index 1744cc875..19b99de05 100644 --- a/Dalamud/Plugin/Internal/CallGatePubSubBase.cs +++ b/Dalamud/Plugin/Ipc/Internal/CallGatePubSubBase.cs @@ -1,9 +1,8 @@ using System; -using System.Collections.Generic; -using Serilog; +using Dalamud.Plugin.Ipc.Exceptions; -namespace Dalamud.Plugin.Internal +namespace Dalamud.Plugin.Ipc.Internal { /// /// This class facilitates inter-plugin communication. @@ -13,88 +12,56 @@ namespace Dalamud.Plugin.Internal /// /// Initializes a new instance of the class. /// - internal CallGatePubSubBase() + /// The name of the IPC registration. + public CallGatePubSubBase(string name) { + this.Channel = Service.Get().GetOrCreateChannel(name); } /// - /// Gets or sets the name of the IPC registration. + /// Gets the underlying channel implementation. /// - public string Name { get; internal set; } - - /// - /// Gets or sets the Action. - /// - protected Delegate? Action { get; set; } - - /// - /// Gets or sets the Func. - /// - protected Delegate? Func { get; set; } - - /// - /// Gets the list of subscribed delegates. - /// - protected List Subs { get; } = new(); + protected CallGateChannel Channel { get; init; } /// /// Removes a registered Action from inter-plugin communication. /// public void UnregisterAction() - => this.Action = null; + => this.Channel.Action = null; /// /// Removes a registered Func from inter-plugin communication. /// public void UnregisterFunc() - => this.Func = null; + => this.Channel.Func = null; /// /// Registers an Action for inter-plugin communication. /// /// Action to register. private protected void RegisterAction(Delegate action) - => this.Action = action; + => this.Channel.Action = action; /// /// Registers a Func for inter-plugin communication. /// /// Func to register. private protected void RegisterFunc(Delegate func) - => this.Func = func; - - /// - /// Invoke all actions that have subscribed to this IPC. - /// - /// Delegate arguments. - 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}"); - } - } - } + => this.Channel.Func = func; /// /// Subscribe an expression to this registration. /// /// Action to subscribe. private protected void Subscribe(Delegate action) - => this.Subs.Add(action); + => this.Channel.Subscriptions.Add(action); /// /// Unsubscribe an expression from this registration. /// /// Action to unsubscribe. private protected void Unsubscribe(Delegate action) - => this.Subs.Remove(action); + => this.Channel.Subscriptions.Remove(action); /// /// Invoke an action registered for inter-plugin communication. @@ -102,15 +69,23 @@ namespace Dalamud.Plugin.Internal /// Action arguments. /// This is thrown when the IPC publisher has not registered an action for calling yet. private protected void InvokeAction(params object?[]? args) - => (this.Action ?? throw new IpcNotReadyError(this.Name)).DynamicInvoke(args); + => this.Channel.InvokeAction(args); /// /// Invoke a function registered for inter-plugin communication. /// /// Parameter args. /// The return value. + /// The return type. /// This is thrown when the IPC publisher has not registered a func for calling yet. - private protected object InvokeFunc(params object?[]? args) - => (this.Func ?? throw new IpcNotReadyError(this.Name)).DynamicInvoke(args); + private protected TRet InvokeFunc(params object?[]? args) + => this.Channel.InvokeFunc(args); + + /// + /// Invoke all actions that have subscribed to this IPC. + /// + /// Delegate arguments. + private protected void SendMessage(params object?[]? args) + => this.Channel.SendMessage(args); } } diff --git a/Dalamud/Plugin/IpcNotReadyError.cs b/Dalamud/Plugin/IpcNotReadyError.cs deleted file mode 100644 index 8be24d577..000000000 --- a/Dalamud/Plugin/IpcNotReadyError.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; - -namespace Dalamud.Plugin -{ - /// - /// This exception is thrown when an IPC method is invoked, but nothing has been registered by that name yet. - /// - public class IpcNotReadyError : Exception - { - /// - /// Initializes a new instance of the class. - /// - /// Name of the IPC method. - public IpcNotReadyError(string name) - { - this.Name = name; - } - - /// - /// Gets the name of the IPC that was invoked. - /// - public string Name { get; } - - /// - public override string Message => $"IPC method {this.Name} was not registered yet"; - } -} diff --git a/Dalamud/Plugin/IpcTypeMismatchError.cs b/Dalamud/Plugin/IpcTypeMismatchError.cs deleted file mode 100644 index 5a73507b7..000000000 --- a/Dalamud/Plugin/IpcTypeMismatchError.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Linq; - -namespace Dalamud.Plugin -{ - /// - /// This exception is thrown when an IPC method is checked out, but the type does not match what was previously registered. - /// - public class IpcTypeMismatchError : Exception - { - private readonly string message; - - /// - /// Initializes a new instance of the class. - /// - /// Name of the IPC method. - /// The types requested when checking out the IPC. - /// The types registered by the IPC. - 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} ]"; - } - - /// - /// Gets the name of the IPC that was invoked. - /// - public string Name { get; } - - /// - /// Gets the types that were requested. - /// - public Type[] RequestedTypes { get; } - - /// - /// Gets the types that were previously registered. - /// - public Type[] ActualTypes { get; } - - /// - public override string Message => this.message; - } -}