feat: Add IPC Context

- Demo of the ipc context idea
- Accessible via ipcPub.GetContext()
This commit is contained in:
Kaz Wolfe 2025-08-25 13:14:13 -07:00
parent 005699e472
commit b18b8b40e5
No known key found for this signature in database
GPG key ID: 258813F53A16EBB4
7 changed files with 143 additions and 63 deletions

View file

@ -5,6 +5,7 @@ using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin.Ipc;
using Dalamud.Plugin.Ipc.Internal;
using Dalamud.Utility;
using Serilog;
namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
@ -48,12 +49,20 @@ internal class PluginIpcWidget : IDataWindowWidget
this.ipcPub.RegisterAction(msg =>
{
Log.Information("Data action was called: {Msg}", msg);
Log.Information(
"Data action was called: {Msg}\n" +
" Context: {Context}",
msg,
this.ipcPub.GetContext());
});
this.ipcPub.RegisterFunc(msg =>
{
Log.Information("Data func was called: {Msg}", msg);
Log.Information(
"Data func was called: {Msg}\n" +
" Context: {Context}",
msg,
this.ipcPub.GetContext());
return Guid.NewGuid().ToString();
});
}
@ -61,14 +70,8 @@ internal class PluginIpcWidget : IDataWindowWidget
if (this.ipcSub == null)
{
this.ipcSub = new CallGatePubSub<string, string>("dataDemo1");
this.ipcSub.Subscribe(_ =>
{
Log.Information("PONG1");
});
this.ipcSub.Subscribe(_ =>
{
Log.Information("PONG2");
});
this.ipcSub.Subscribe(_ => { Log.Information("PONG1"); });
this.ipcSub.Subscribe(_ => { Log.Information("PONG2"); });
this.ipcSub.Subscribe(_ => throw new Exception("PONG3"));
}
@ -78,12 +81,21 @@ internal class PluginIpcWidget : IDataWindowWidget
this.ipcPubGo.RegisterAction(go =>
{
Log.Information("Data action was called: {Name}", go?.Name);
Log.Information(
"Data action was called: {Name}" +
"\n Context: {Context}",
go?.Name,
this.ipcPubGo.GetContext());
});
this.ipcPubGo.RegisterFunc(go =>
{
Log.Information("Data func was called: {Name}", go?.Name);
Log.Information(
"Data func was called: {Name}\n" +
" Context: {Context}",
go?.Name,
this.ipcPubGo.GetContext());
return "test";
});
}

View file

@ -293,39 +293,39 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa
/// <returns>An IPC provider.</returns>
/// <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)
=> new CallGatePubSub<TRet>(name);
=> new CallGatePubSub<TRet>(name, this.plugin);
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
public ICallGateProvider<T1, TRet> GetIpcProvider<T1, TRet>(string name)
=> new CallGatePubSub<T1, TRet>(name);
=> new CallGatePubSub<T1, TRet>(name, this.plugin);
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
public ICallGateProvider<T1, T2, TRet> GetIpcProvider<T1, T2, TRet>(string name)
=> new CallGatePubSub<T1, T2, TRet>(name);
=> new CallGatePubSub<T1, T2, TRet>(name, this.plugin);
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
public ICallGateProvider<T1, T2, T3, TRet> GetIpcProvider<T1, T2, T3, TRet>(string name)
=> new CallGatePubSub<T1, T2, T3, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, TRet>(name, this.plugin);
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
public ICallGateProvider<T1, T2, T3, T4, TRet> GetIpcProvider<T1, T2, T3, T4, TRet>(string name)
=> new CallGatePubSub<T1, T2, T3, T4, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, T4, TRet>(name, this.plugin);
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
public ICallGateProvider<T1, T2, T3, T4, T5, TRet> GetIpcProvider<T1, T2, T3, T4, T5, TRet>(string name)
=> new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name, this.plugin);
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
public ICallGateProvider<T1, T2, T3, T4, T5, T6, TRet> GetIpcProvider<T1, T2, T3, T4, T5, T6, TRet>(string name)
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name, this.plugin);
/// <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)
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet>(name, this.plugin);
/// <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)
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(name, this.plugin);
/// <summary>
/// Gets an IPC subscriber.
@ -334,39 +334,39 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa
/// <param name="name">The name of the IPC registration.</param>
/// <returns>An IPC subscriber.</returns>
public ICallGateSubscriber<TRet> GetIpcSubscriber<TRet>(string name)
=> new CallGatePubSub<TRet>(name);
=> new CallGatePubSub<TRet>(name, this.plugin);
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
public ICallGateSubscriber<T1, TRet> GetIpcSubscriber<T1, TRet>(string name)
=> new CallGatePubSub<T1, TRet>(name);
=> new CallGatePubSub<T1, TRet>(name, this.plugin);
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
public ICallGateSubscriber<T1, T2, TRet> GetIpcSubscriber<T1, T2, TRet>(string name)
=> new CallGatePubSub<T1, T2, TRet>(name);
=> new CallGatePubSub<T1, T2, TRet>(name, this.plugin);
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
public ICallGateSubscriber<T1, T2, T3, TRet> GetIpcSubscriber<T1, T2, T3, TRet>(string name)
=> new CallGatePubSub<T1, T2, T3, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, TRet>(name, this.plugin);
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
public ICallGateSubscriber<T1, T2, T3, T4, TRet> GetIpcSubscriber<T1, T2, T3, T4, TRet>(string name)
=> new CallGatePubSub<T1, T2, T3, T4, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, T4, TRet>(name, this.plugin);
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
public ICallGateSubscriber<T1, T2, T3, T4, T5, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, TRet>(string name)
=> new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name, this.plugin);
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
public ICallGateSubscriber<T1, T2, T3, T4, T5, T6, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, T6, TRet>(string name)
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name, this.plugin);
/// <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)
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet>(name, this.plugin);
/// <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)
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(name, this.plugin);
#endregion

View file

@ -19,6 +19,9 @@ public interface ICallGateProvider
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
public void UnregisterFunc();
/// <inheritdoc cref="CallGatePubSubBase.GetContext"/>
public IpcContext? GetContext();
}
/// <inheritdoc cref="ICallGateProvider"/>

View file

@ -3,6 +3,7 @@ using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using Dalamud.Plugin.Internal.Types;
using Dalamud.Plugin.Ipc.Exceptions;
using Dalamud.Plugin.Ipc.Internal.Converters;

View file

@ -1,3 +1,5 @@
using Dalamud.Plugin.Internal.Types;
#pragma warning disable SA1402 // File may only contain a single type
namespace Dalamud.Plugin.Ipc.Internal;
@ -5,9 +7,9 @@ namespace Dalamud.Plugin.Ipc.Internal;
/// <inheritdoc cref="CallGatePubSubBase"/>
internal class CallGatePubSub<TRet> : CallGatePubSubBase, ICallGateProvider<TRet>, ICallGateSubscriber<TRet>
{
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
/// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name, owningPlugin)
{
}
@ -43,9 +45,9 @@ internal class CallGatePubSub<TRet> : CallGatePubSubBase, ICallGateProvider<TRet
/// <inheritdoc cref="CallGatePubSubBase"/>
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(string, LocalPlugin?)"/>
public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name, owningPlugin)
{
}
@ -81,9 +83,9 @@ internal class CallGatePubSub<T1, TRet> : CallGatePubSubBase, ICallGateProvider<
/// <inheritdoc cref="CallGatePubSubBase"/>
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(string, LocalPlugin?)"/>
public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name, owningPlugin)
{
}
@ -119,9 +121,9 @@ internal class CallGatePubSub<T1, T2, TRet> : CallGatePubSubBase, ICallGateProvi
/// <inheritdoc cref="CallGatePubSubBase"/>
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(string, LocalPlugin?)"/>
public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name, owningPlugin)
{
}
@ -157,9 +159,9 @@ internal class CallGatePubSub<T1, T2, T3, TRet> : CallGatePubSubBase, ICallGateP
/// <inheritdoc cref="CallGatePubSubBase"/>
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(string, LocalPlugin?)"/>
public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name, owningPlugin)
{
}
@ -195,9 +197,9 @@ internal class CallGatePubSub<T1, T2, T3, T4, TRet> : CallGatePubSubBase, ICallG
/// <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>
{
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
/// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name, owningPlugin)
{
}
@ -233,9 +235,9 @@ internal class CallGatePubSub<T1, T2, T3, T4, T5, TRet> : CallGatePubSubBase, IC
/// <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>
{
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
/// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name, owningPlugin)
{
}
@ -271,9 +273,9 @@ internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet> : 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>
{
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
/// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name, owningPlugin)
{
}
@ -309,9 +311,9 @@ internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet> : CallGatePubSub
/// <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>
{
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
/// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name, owningPlugin)
{
}

View file

@ -1,4 +1,11 @@
using System.Reactive.Disposables;
using System.Threading;
using Dalamud.Plugin.Internal.Types;
using Dalamud.Plugin.Ipc.Exceptions;
using Dalamud.Utility;
using Serilog;
namespace Dalamud.Plugin.Ipc.Internal;
@ -7,13 +14,18 @@ namespace Dalamud.Plugin.Ipc.Internal;
/// </summary>
internal abstract class CallGatePubSubBase
{
[ThreadStatic]
private static IpcContext? ipcExecutionContext;
/// <summary>
/// Initializes a new instance of the <see cref="CallGatePubSubBase"/> class.
/// </summary>
/// <param name="name">The name of the IPC registration.</param>
protected CallGatePubSubBase(string name)
/// <param name="owningPlugin">The plugin that owns this IPC pubsub.</param>
protected CallGatePubSubBase(string name, LocalPlugin? owningPlugin)
{
this.Channel = Service<CallGate>.Get().GetOrCreateChannel(name);
this.OwningPlugin = owningPlugin;
}
/// <summary>
@ -21,7 +33,7 @@ internal abstract class CallGatePubSubBase
/// <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.
@ -33,12 +45,17 @@ internal abstract class CallGatePubSubBase
/// <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>
/// Gets the plugin that owns this pubsub instance.
/// </summary>
protected LocalPlugin? OwningPlugin { get; init; }
/// <summary>
/// Removes the associated Action from this call gate, effectively disabling RPC calls.
/// </summary>
@ -53,6 +70,16 @@ internal abstract class CallGatePubSubBase
public void UnregisterFunc()
=> this.Channel.Func = null;
/// <summary>
/// Gets the current context for this IPC call. This will only be present when called from within an IPC action
/// or function handler, and will be null otherwise.
/// </summary>
/// <returns>Returns a potential IPC context.</returns>
public IpcContext? GetContext()
{
return ipcExecutionContext;
}
/// <summary>
/// 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
@ -105,7 +132,12 @@ internal abstract class CallGatePubSubBase
/// <seealso cref="RegisterAction"/>
/// <seealso cref="UnregisterAction"/>
private protected void InvokeAction(params object?[]? args)
=> this.Channel.InvokeAction(args);
{
using (this.BuildContext())
{
this.Channel.InvokeAction(args);
}
}
/// <summary>
/// Executes the Function registered for this IPC call gate via <see cref="RegisterFunc"/>. This method is intended
@ -120,7 +152,12 @@ internal abstract class CallGatePubSubBase
/// <seealso cref="RegisterFunc"/>
/// <seealso cref="UnregisterFunc"/>
private protected TRet InvokeFunc<TRet>(params object?[]? args)
=> this.Channel.InvokeFunc<TRet>(args);
{
using (this.BuildContext())
{
return this.Channel.InvokeFunc<TRet>(args);
}
}
/// <summary>
/// Send the given arguments to all subscribers (through <see cref="Subscribe"/>) of this IPC call gate. This method
@ -132,4 +169,14 @@ internal abstract class CallGatePubSubBase
/// <param name="args">Delegate arguments.</param>
private protected void SendMessage(params object?[]? args)
=> this.Channel.SendMessage(args);
private IDisposable BuildContext()
{
ipcExecutionContext = new IpcContext
{
SourcePlugin = this.OwningPlugin != null ? new ExposedPlugin(this.OwningPlugin) : null,
};
return Disposable.Create(() => { ipcExecutionContext = null; });
}
}

View file

@ -0,0 +1,15 @@
namespace Dalamud.Plugin.Ipc;
/// <summary>
/// The context associated for an IPC call. Reads from ThreadLocal.
/// </summary>
public class IpcContext
{
/// <summary>
/// Gets the plugin that initiated this IPC call.
/// </summary>
public IExposedPlugin? SourcePlugin { get; init; }
/// <inheritdoc/>
public override string ToString() => $"<IpcContext SourcePlugin={this.SourcePlugin?.Name ?? "null"}>";
}