Compare commits

...

20 commits

Author SHA1 Message Date
goat
8334836b6a
Merge pull request #2386 from KazWolfe/ipc-context
Some checks failed
Build Dalamud / Build on Windows (push) Waiting to run
Build Dalamud / Check API Compatibility (push) Blocked by required conditions
Build Dalamud / Deploy dalamud-distrib staging (push) Blocked by required conditions
Rollup changes to next version / check (api14) (push) Failing after 8s
Tag Build / Tag Build (push) Successful in 3s
feat: Add IPC Context
2025-12-16 00:30:06 +01:00
Haselnussbomber
2d096d9b33
Properly initialize GameInventoryItems (#2504)
Some checks failed
Rollup changes to next version / check (api14) (push) Failing after 20s
Tag Build / Tag Build (push) Successful in 5s
Build Dalamud / Build on Windows (push) Has been cancelled
Build Dalamud / Check API Compatibility (push) Has been cancelled
Build Dalamud / Deploy dalamud-distrib staging (push) Has been cancelled
2025-12-13 14:05:03 +10:00
goaaats
e100ec2abd build: 13.0.0.16
Some checks failed
Rollup changes to next version / check (api14) (push) Failing after 3s
Tag Build / Tag Build (push) Failing after 2s
Build Dalamud / Build on Windows (push) Has been cancelled
Build Dalamud / Check API Compatibility (push) Has been cancelled
Build Dalamud / Deploy dalamud-distrib staging (push) Has been cancelled
2025-12-12 00:57:04 +01:00
goat
71b0a757e9
Merge pull request #2501 from Haselnussbomber/clear-ImDrawListSplitter
Clear ImDrawListSplitter when disposing SeStringDrawState
2025-12-11 23:15:21 +01:00
Haselnussbomber
0b55dc3e10
Clear ImDrawListSplitter when disposing SeStringDrawState 2025-12-11 22:59:50 +01:00
goaaats
a39763f161 Mark preset dirty when disabling clickthrough for a window
Some checks failed
Build Dalamud / Build on Windows (push) Waiting to run
Build Dalamud / Check API Compatibility (push) Blocked by required conditions
Build Dalamud / Deploy dalamud-distrib staging (push) Blocked by required conditions
Rollup changes to next version / check (api14) (push) Failing after 3s
Tag Build / Tag Build (push) Successful in 2s
2025-12-10 18:33:37 +01:00
goaaats
201c9cfcf2 Use game window to calculate offsets in fallback mouse position code 2025-12-10 18:13:52 +01:00
goat
e07bda7e58
Merge pull request #2500 from nebel/window-error-pop-dalamud-style
Some checks failed
Build Dalamud / Build on Windows (push) Waiting to run
Build Dalamud / Check API Compatibility (push) Blocked by required conditions
Build Dalamud / Deploy dalamud-distrib staging (push) Blocked by required conditions
Rollup changes to next version / check (api14) (push) Failing after 4s
Tag Build / Tag Build (push) Successful in 2s
Always pop DalamudStandard style if pushed earlier in Draw
2025-12-10 15:26:12 +01:00
nebel
b88a6bb616
Always pop DalamudStandard style if pushed earlier in Draw 2025-12-10 23:12:44 +09:00
goaaats
e53ccdbcc0 build: 13.0.0.15
Some checks failed
Rollup changes to next version / check (api14) (push) Failing after 3s
Tag Build / Tag Build (push) Failing after 3s
Build Dalamud / Build on Windows (push) Has been cancelled
Build Dalamud / Check API Compatibility (push) Has been cancelled
Build Dalamud / Deploy dalamud-distrib staging (push) Has been cancelled
2025-12-09 00:18:28 +01:00
goaaats
97df73acea Ensure that we don't catch mouse up events without corresponding mouse down events
Fixes an issue wherein the cursor could get locked by the game if WantCaptureMouse becomes true in between down and up events
2025-12-08 21:00:08 +01:00
goaaats
2806e59dba Also remove borders for dev bar, to prevent themes from causing weirdness 2025-12-08 20:09:31 +01:00
goaaats
24caa1cb18 PresetWindow.IsDefault can be JsonIgnore 2025-12-08 20:05:14 +01:00
goaaats
5d08170333 Keep rendering title bar buttons if one is not available clickthrough 2025-12-08 20:03:43 +01:00
goaaats
d0110f7251 Hardcode HasModifiedGameDataFiles to false for now until XL is fixed 2025-12-08 20:03:22 +01:00
goaaats
8ed1af30df build: 13.0.0.14
Some checks failed
Build Dalamud / Build on Windows (push) Waiting to run
Build Dalamud / Check API Compatibility (push) Blocked by required conditions
Build Dalamud / Deploy dalamud-distrib staging (push) Blocked by required conditions
Rollup changes to next version / check (api14) (push) Failing after 2s
Tag Build / Tag Build (push) Failing after 2s
2025-12-07 22:55:16 +01:00
goaaats
c45c6aafe1 Don't consider failed index integrity checks as having "modified game data files" 2025-12-07 21:57:54 +01:00
goaaats
2029a0f8a6 Also add fallback for SeStringDrawState.ScreenOffset for now, make sure that it is populated 2025-12-07 21:31:25 +01:00
Kaz Wolfe
8cced4c1d7
fix: use channel threadlocal instead of a ThreadStatic 2025-08-25 13:31:05 -07:00
Kaz Wolfe
b18b8b40e5
feat: Add IPC Context
- Demo of the ipc context idea
- Accessible via ipcPub.GetContext()
2025-08-25 13:14:13 -07:00
18 changed files with 248 additions and 82 deletions

View file

@ -6,7 +6,7 @@
<PropertyGroup Label="Feature"> <PropertyGroup Label="Feature">
<Description>XIV Launcher addon framework</Description> <Description>XIV Launcher addon framework</Description>
<DalamudVersion>13.0.0.13</DalamudVersion> <DalamudVersion>13.0.0.16</DalamudVersion>
<AssemblyVersion>$(DalamudVersion)</AssemblyVersion> <AssemblyVersion>$(DalamudVersion)</AssemblyVersion>
<Version>$(DalamudVersion)</Version> <Version>$(DalamudVersion)</Version>
<FileVersion>$(DalamudVersion)</FileVersion> <FileVersion>$(DalamudVersion)</FileVersion>

View file

@ -82,8 +82,13 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager
var tsInfo = var tsInfo =
JsonConvert.DeserializeObject<LauncherTroubleshootingInfo>( JsonConvert.DeserializeObject<LauncherTroubleshootingInfo>(
dalamud.StartInfo.TroubleshootingPackData); dalamud.StartInfo.TroubleshootingPackData);
this.HasModifiedGameDataFiles =
tsInfo?.IndexIntegrity is LauncherTroubleshootingInfo.IndexIntegrityResult.Failed or LauncherTroubleshootingInfo.IndexIntegrityResult.Exception; // Don't fail for IndexIntegrityResult.Exception, since the check during launch has a very small timeout
// this.HasModifiedGameDataFiles =
// tsInfo?.IndexIntegrity is LauncherTroubleshootingInfo.IndexIntegrityResult.Failed;
// TODO: Put above back when check in XL is fixed
this.HasModifiedGameDataFiles = false;
if (this.HasModifiedGameDataFiles) if (this.HasModifiedGameDataFiles)
Log.Verbose("Game data integrity check failed!\n{TsData}", dalamud.StartInfo.TroubleshootingPackData); Log.Verbose("Game data integrity check failed!\n{TsData}", dalamud.StartInfo.TroubleshootingPackData);

View file

@ -305,7 +305,8 @@ internal class GameInventory : IInternalDisposableService
private GameInventoryItem[] CreateItemsArray(int length) private GameInventoryItem[] CreateItemsArray(int length)
{ {
var items = new GameInventoryItem[length]; var items = new GameInventoryItem[length];
items.Initialize(); foreach (ref var item in items.AsSpan())
item = new();
return items; return items;
} }

View file

@ -7,6 +7,7 @@ using System.Runtime.InteropServices;
using System.Text; using System.Text;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Console;
using Dalamud.Memory; using Dalamud.Memory;
using Dalamud.Utility; using Dalamud.Utility;
@ -37,6 +38,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
private readonly WndProcDelegate wndProcDelegate; private readonly WndProcDelegate wndProcDelegate;
private readonly nint platformNamePtr; private readonly nint platformNamePtr;
private readonly IConsoleVariable<bool> cvLogMouseEvents;
private ViewportHandler viewportHandler; private ViewportHandler viewportHandler;
private int mouseButtonsDown; private int mouseButtonsDown;
@ -87,6 +90,11 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
this.cursors[(int)ImGuiMouseCursor.ResizeNwse] = LoadCursorW(default, IDC.IDC_SIZENWSE); this.cursors[(int)ImGuiMouseCursor.ResizeNwse] = LoadCursorW(default, IDC.IDC_SIZENWSE);
this.cursors[(int)ImGuiMouseCursor.Hand] = LoadCursorW(default, IDC.IDC_HAND); this.cursors[(int)ImGuiMouseCursor.Hand] = LoadCursorW(default, IDC.IDC_HAND);
this.cursors[(int)ImGuiMouseCursor.NotAllowed] = LoadCursorW(default, IDC.IDC_NO); this.cursors[(int)ImGuiMouseCursor.NotAllowed] = LoadCursorW(default, IDC.IDC_NO);
this.cvLogMouseEvents = Service<ConsoleManager>.Get().AddVariable(
"imgui.log_mouse_events",
"Log mouse events to console for debugging",
false);
} }
/// <summary> /// <summary>
@ -267,11 +275,23 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
case WM.WM_XBUTTONDOWN: case WM.WM_XBUTTONDOWN:
case WM.WM_XBUTTONDBLCLK: case WM.WM_XBUTTONDBLCLK:
{ {
if (this.cvLogMouseEvents.Value)
{
Log.Verbose(
"Handle MouseDown {Btn} WantCaptureMouse: {Want} mouseButtonsDown: {Down}",
GetButton(msg, wParam),
io.WantCaptureMouse,
this.mouseButtonsDown);
}
var button = GetButton(msg, wParam); var button = GetButton(msg, wParam);
if (io.WantCaptureMouse) if (io.WantCaptureMouse)
{ {
if (this.mouseButtonsDown == 0 && GetCapture() == nint.Zero) if (this.mouseButtonsDown == 0 && GetCapture() == nint.Zero)
{
SetCapture(hWndCurrent); SetCapture(hWndCurrent);
}
this.mouseButtonsDown |= 1 << button; this.mouseButtonsDown |= 1 << button;
io.AddMouseButtonEvent(button, true); io.AddMouseButtonEvent(button, true);
return default(LRESULT); return default(LRESULT);
@ -288,12 +308,28 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
case WM.WM_MBUTTONUP: case WM.WM_MBUTTONUP:
case WM.WM_XBUTTONUP: case WM.WM_XBUTTONUP:
{ {
if (this.cvLogMouseEvents.Value)
{
Log.Verbose(
"Handle MouseUp {Btn} WantCaptureMouse: {Want} mouseButtonsDown: {Down}",
GetButton(msg, wParam),
io.WantCaptureMouse,
this.mouseButtonsDown);
}
var button = GetButton(msg, wParam); var button = GetButton(msg, wParam);
if (io.WantCaptureMouse)
// Need to check if we captured the button event away from the game here, otherwise the game might get
// a down event but no up event, causing the cursor to get stuck.
// Can happen if WantCaptureMouse becomes true in between down and up
if (io.WantCaptureMouse && (this.mouseButtonsDown & (1 << button)) != 0)
{ {
this.mouseButtonsDown &= ~(1 << button); this.mouseButtonsDown &= ~(1 << button);
if (this.mouseButtonsDown == 0 && GetCapture() == hWndCurrent) if (this.mouseButtonsDown == 0 && GetCapture() == hWndCurrent)
{
ReleaseCapture(); ReleaseCapture();
}
io.AddMouseButtonEvent(button, false); io.AddMouseButtonEvent(button, false);
return default(LRESULT); return default(LRESULT);
} }
@ -458,7 +494,12 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
// (This is the position you can get with ::GetCursorPos() or WM_MOUSEMOVE + ::ClientToScreen(). In theory adding viewport->Pos to a client position would also be the same.) // (This is the position you can get with ::GetCursorPos() or WM_MOUSEMOVE + ::ClientToScreen(). In theory adding viewport->Pos to a client position would also be the same.)
var mousePos = mouseScreenPos; var mousePos = mouseScreenPos;
if ((io.ConfigFlags & ImGuiConfigFlags.ViewportsEnable) == 0) if ((io.ConfigFlags & ImGuiConfigFlags.ViewportsEnable) == 0)
ClientToScreen(focusedWindow, &mousePos); {
// Use game window, otherwise, positions are calculated based on the focused window which might not be the game.
// Leads to offsets.
ClientToScreen(this.hWnd, &mousePos);
}
io.AddMousePosEvent(mousePos.x, mousePos.y); io.AddMousePosEvent(mousePos.x, mousePos.y);
} }

View file

@ -170,7 +170,7 @@ internal class SeStringRenderer : IServiceType
// This also does argument validation for drawParams. Do it here. // This also does argument validation for drawParams. Do it here.
// `using var` makes a struct read-only, but we do want to modify it. // `using var` makes a struct read-only, but we do want to modify it.
var stateStorage = new SeStringDrawState( using var stateStorage = new SeStringDrawState(
sss, sss,
drawParams, drawParams,
ThreadSafety.IsMainThread ? this.colorStackSetMainThread : new(this.colorStackSetMainThread.ColorTypes), ThreadSafety.IsMainThread ? this.colorStackSetMainThread : new(this.colorStackSetMainThread.ColorTypes),

View file

@ -25,7 +25,9 @@ public record struct SeStringDrawParams
public SeStringReplacementEntity.GetEntityDelegate? GetEntity { get; set; } public SeStringReplacementEntity.GetEntityDelegate? GetEntity { get; set; }
/// <summary>Gets or sets the screen offset of the left top corner.</summary> /// <summary>Gets or sets the screen offset of the left top corner.</summary>
/// <value>Screen offset to draw at, or <c>null</c> to use <see cref="ImGui.GetCursorScreenPos()"/>.</value> /// <value>Screen offset to draw at, or <c>null</c> to use <see cref="ImGui.GetCursorScreenPos()"/>, if no <see cref="TargetDrawList"/>
/// is specified. Otherwise, you must specify it (for example, by passing <see cref="ImGui.GetCursorScreenPos()"/> when passing the window
/// draw list.</value>
public Vector2? ScreenOffset { get; set; } public Vector2? ScreenOffset { get; set; }
/// <summary>Gets or sets the font to use.</summary> /// <summary>Gets or sets the font to use.</summary>

View file

@ -10,6 +10,7 @@ using Dalamud.Interface.Utility;
using Dalamud.Utility; using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Component.GUI; using FFXIVClientStructs.FFXIV.Component.GUI;
using Lumina.Text.Payloads; using Lumina.Text.Payloads;
using Lumina.Text.ReadOnly; using Lumina.Text.ReadOnly;
@ -17,7 +18,7 @@ namespace Dalamud.Interface.ImGuiSeStringRenderer;
/// <summary>Calculated values from <see cref="SeStringDrawParams"/> using ImGui styles.</summary> /// <summary>Calculated values from <see cref="SeStringDrawParams"/> using ImGui styles.</summary>
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public unsafe ref struct SeStringDrawState public unsafe ref struct SeStringDrawState : IDisposable
{ {
private static readonly int ChannelCount = Enum.GetValues<SeStringDrawChannel>().Length; private static readonly int ChannelCount = Enum.GetValues<SeStringDrawChannel>().Length;
@ -63,11 +64,12 @@ public unsafe ref struct SeStringDrawState
else else
{ {
this.drawList = ssdp.TargetDrawList.Value; this.drawList = ssdp.TargetDrawList.Value;
this.ScreenOffset = Vector2.Zero; this.ScreenOffset = ssdp.ScreenOffset ?? Vector2.Zero;
// API14: Remove, always throw // API14: Remove, always throw
if (ThreadSafety.IsMainThread) if (ThreadSafety.IsMainThread)
{ {
this.ScreenOffset = ssdp.ScreenOffset ?? ImGui.GetCursorScreenPos();
this.FontSize = ssdp.FontSize ?? ImGui.GetFontSize(); this.FontSize = ssdp.FontSize ?? ImGui.GetFontSize();
} }
else else
@ -193,6 +195,9 @@ public unsafe ref struct SeStringDrawState
/// <summary>Gets the text fragments.</summary> /// <summary>Gets the text fragments.</summary>
internal List<TextFragment> Fragments { get; } internal List<TextFragment> Fragments { get; }
/// <inheritdoc/>
public void Dispose() => this.splitter.ClearFreeMemory();
/// <summary>Sets the current channel in the ImGui draw list splitter.</summary> /// <summary>Sets the current channel in the ImGui draw list splitter.</summary>
/// <param name="channelIndex">Channel to switch to.</param> /// <param name="channelIndex">Channel to switch to.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]

View file

@ -669,6 +669,8 @@ internal class DalamudInterface : IInternalDisposableService
{ {
using var barColor = ImRaii.PushColor(ImGuiCol.WindowBg, new Vector4(0.060f, 0.060f, 0.060f, 0.773f)); using var barColor = ImRaii.PushColor(ImGuiCol.WindowBg, new Vector4(0.060f, 0.060f, 0.060f, 0.773f));
barColor.Push(ImGuiCol.MenuBarBg, Vector4.Zero); barColor.Push(ImGuiCol.MenuBarBg, Vector4.Zero);
barColor.Push(ImGuiCol.Border, Vector4.Zero);
barColor.Push(ImGuiCol.BorderShadow, Vector4.Zero);
if (ImGui.BeginMainMenuBar()) if (ImGui.BeginMainMenuBar())
{ {
var pluginManager = Service<PluginManager>.Get(); var pluginManager = Service<PluginManager>.Get();

View file

@ -48,12 +48,20 @@ internal class PluginIpcWidget : IDataWindowWidget
this.ipcPub.RegisterAction(msg => 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 => 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(); return Guid.NewGuid().ToString();
}); });
} }
@ -61,14 +69,8 @@ internal class PluginIpcWidget : IDataWindowWidget
if (this.ipcSub == null) if (this.ipcSub == null)
{ {
this.ipcSub = new CallGatePubSub<string, string>("dataDemo1"); this.ipcSub = new CallGatePubSub<string, string>("dataDemo1");
this.ipcSub.Subscribe(_ => this.ipcSub.Subscribe(_ => { Log.Information("PONG1"); });
{ this.ipcSub.Subscribe(_ => { Log.Information("PONG2"); });
Log.Information("PONG1");
});
this.ipcSub.Subscribe(_ =>
{
Log.Information("PONG2");
});
this.ipcSub.Subscribe(_ => throw new Exception("PONG3")); this.ipcSub.Subscribe(_ => throw new Exception("PONG3"));
} }
@ -78,12 +80,21 @@ internal class PluginIpcWidget : IDataWindowWidget
this.ipcPubGo.RegisterAction(go => 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 => 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"; return "test";
}); });
} }

View file

@ -177,6 +177,24 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget
ImGuiHelpers.SeStringWrapped(this.logkind.Value.Data.Span, this.style); ImGuiHelpers.SeStringWrapped(this.logkind.Value.Data.Span, this.style);
} }
if (ImGui.CollapsingHeader("Draw into drawlist"))
{
ImGuiHelpers.ScaledDummy(100);
ImGui.SetCursorScreenPos(ImGui.GetItemRectMin() + ImGui.GetStyle().FramePadding);
var clipMin = ImGui.GetItemRectMin() + ImGui.GetStyle().FramePadding;
var clipMax = ImGui.GetItemRectMax() - ImGui.GetStyle().FramePadding;
clipMin.Y = MathF.Max(clipMin.Y, ImGui.GetWindowPos().Y);
clipMax.Y = MathF.Min(clipMax.Y, ImGui.GetWindowPos().Y + ImGui.GetWindowHeight());
var dl = ImGui.GetWindowDrawList();
dl.PushClipRect(clipMin, clipMax);
ImGuiHelpers.CompileSeStringWrapped(
"<icon(1)>Test test<icon(1)>",
new SeStringDrawParams
{ Color = 0xFFFFFFFF, WrapWidth = float.MaxValue, TargetDrawList = dl });
dl.PopClipRect();
}
if (ImGui.CollapsingHeader("Addon Table"u8)) if (ImGui.CollapsingHeader("Addon Table"u8))
{ {
if (ImGui.BeginTable("Addon Sheet"u8, 3)) if (ImGui.BeginTable("Addon Sheet"u8, 3))

View file

@ -53,6 +53,7 @@ internal class PresetModel
/// <summary> /// <summary>
/// Gets a value indicating whether this preset is in the default state. /// Gets a value indicating whether this preset is in the default state.
/// </summary> /// </summary>
[JsonIgnore]
public bool IsDefault => public bool IsDefault =>
!this.IsPinned && !this.IsPinned &&
!this.IsClickThrough && !this.IsClickThrough &&

View file

@ -84,7 +84,7 @@ public abstract class Window
Click = _ => Click = _ =>
{ {
this.internalIsClickthrough = false; this.internalIsClickthrough = false;
this.presetDirty = false; this.presetDirty = true;
ImGui.OpenPopup(AdditionsPopupName); ImGui.OpenPopup(AdditionsPopupName);
}, },
Priority = int.MinValue, Priority = int.MinValue,
@ -672,16 +672,13 @@ public abstract class Window
Task.FromResult<IDalamudTextureWrap>(tex)); Task.FromResult<IDalamudTextureWrap>(tex));
} }
if (!this.hasError)
{
this.PostDraw();
}
else
{
if (isErrorStylePushed) if (isErrorStylePushed)
{ {
Style.StyleModelV1.DalamudStandard.Pop(); Style.StyleModelV1.DalamudStandard.Pop();
} }
else
{
this.PostDraw();
} }
this.PostHandlePreset(persistence); this.PostHandlePreset(persistence);
@ -864,7 +861,7 @@ public abstract class Window
foreach (var button in this.allButtons) foreach (var button in this.allButtons)
{ {
if (this.internalIsClickthrough && !button.AvailableClickthrough) if (this.internalIsClickthrough && !button.AvailableClickthrough)
return; continue;
Vector2 position = new(titleBarRect.Max.X - padR - buttonSize, titleBarRect.Min.Y + style.FramePadding.Y); Vector2 position = new(titleBarRect.Max.X - padR - buttonSize, titleBarRect.Min.Y + style.FramePadding.Y);
padR += buttonSize + style.ItemInnerSpacing.X; padR += buttonSize + style.ItemInnerSpacing.X;
@ -908,7 +905,7 @@ public abstract class Window
private void DrawErrorMessage() private void DrawErrorMessage()
{ {
// TODO: Once window systems are services, offer to reload the plugin // TODO: Once window systems are services, offer to reload the plugin
ImGui.TextColoredWrapped(ImGuiColors.DalamudRed,Loc.Localize("WindowSystemErrorOccurred", "An error occurred while rendering this window. Please contact the developer for details.")); ImGui.TextColoredWrapped(ImGuiColors.DalamudRed, Loc.Localize("WindowSystemErrorOccurred", "An error occurred while rendering this window. Please contact the developer for details."));
ImGuiHelpers.ScaledDummy(5); ImGuiHelpers.ScaledDummy(5);

View file

@ -248,75 +248,75 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa
/// <inheritdoc/> /// <inheritdoc/>
public ICallGateProvider<TRet> GetIpcProvider<TRet>(string name) public ICallGateProvider<TRet> GetIpcProvider<TRet>(string name)
=> new CallGatePubSub<TRet>(name); => new CallGatePubSub<TRet>(name, this.plugin);
/// <inheritdoc/> /// <inheritdoc/>
public ICallGateProvider<T1, TRet> GetIpcProvider<T1, TRet>(string name) public ICallGateProvider<T1, TRet> GetIpcProvider<T1, TRet>(string name)
=> new CallGatePubSub<T1, TRet>(name); => new CallGatePubSub<T1, TRet>(name, this.plugin);
/// <inheritdoc/> /// <inheritdoc/>
public ICallGateProvider<T1, T2, TRet> GetIpcProvider<T1, T2, TRet>(string name) 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/> /// <inheritdoc/>
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)
=> new CallGatePubSub<T1, T2, T3, TRet>(name); => new CallGatePubSub<T1, T2, T3, TRet>(name, this.plugin);
/// <inheritdoc/> /// <inheritdoc/>
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)
=> new CallGatePubSub<T1, T2, T3, T4, TRet>(name); => new CallGatePubSub<T1, T2, T3, T4, TRet>(name, this.plugin);
/// <inheritdoc/> /// <inheritdoc/>
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)
=> new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name); => new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name, this.plugin);
/// <inheritdoc/> /// <inheritdoc/>
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)
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name); => new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name, this.plugin);
/// <inheritdoc/> /// <inheritdoc/>
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)
=> 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/> /// <inheritdoc/>
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)
=> 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);
/// <inheritdoc/> /// <inheritdoc/>
public ICallGateSubscriber<TRet> GetIpcSubscriber<TRet>(string name) public ICallGateSubscriber<TRet> GetIpcSubscriber<TRet>(string name)
=> new CallGatePubSub<TRet>(name); => new CallGatePubSub<TRet>(name, this.plugin);
/// <inheritdoc/> /// <inheritdoc/>
public ICallGateSubscriber<T1, TRet> GetIpcSubscriber<T1, TRet>(string name) public ICallGateSubscriber<T1, TRet> GetIpcSubscriber<T1, TRet>(string name)
=> new CallGatePubSub<T1, TRet>(name); => new CallGatePubSub<T1, TRet>(name, this.plugin);
/// <inheritdoc/> /// <inheritdoc/>
public ICallGateSubscriber<T1, T2, TRet> GetIpcSubscriber<T1, T2, TRet>(string name) 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/> /// <inheritdoc/>
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)
=> new CallGatePubSub<T1, T2, T3, TRet>(name); => new CallGatePubSub<T1, T2, T3, TRet>(name, this.plugin);
/// <inheritdoc/> /// <inheritdoc/>
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)
=> new CallGatePubSub<T1, T2, T3, T4, TRet>(name); => new CallGatePubSub<T1, T2, T3, T4, TRet>(name, this.plugin);
/// <inheritdoc/> /// <inheritdoc/>
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)
=> new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name); => new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name, this.plugin);
/// <inheritdoc/> /// <inheritdoc/>
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)
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name); => new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name, this.plugin);
/// <inheritdoc/> /// <inheritdoc/>
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)
=> 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/> /// <inheritdoc/>
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)
=> 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 #endregion

View file

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

View file

@ -2,7 +2,9 @@ using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading;
using Dalamud.Plugin.Internal.Types;
using Dalamud.Plugin.Ipc.Exceptions; using Dalamud.Plugin.Ipc.Exceptions;
using Dalamud.Plugin.Ipc.Internal.Converters; using Dalamud.Plugin.Ipc.Internal.Converters;
@ -16,6 +18,8 @@ namespace Dalamud.Plugin.Ipc.Internal;
/// </summary> /// </summary>
internal class CallGateChannel internal class CallGateChannel
{ {
private readonly ThreadLocal<IpcContext> ipcExecutionContext = new();
/// <summary> /// <summary>
/// The actual storage. /// The actual storage.
/// </summary> /// </summary>
@ -145,6 +149,21 @@ internal class CallGateChannel
return (TRet)result; return (TRet)result;
} }
internal void SetInvocationContext(IpcContext ipcContext)
{
this.ipcExecutionContext.Value = ipcContext;
}
internal IpcContext? GetInvocationContext()
{
return this.ipcExecutionContext.IsValueCreated ? this.ipcExecutionContext.Value : null;
}
internal void ClearInvocationContext()
{
this.ipcExecutionContext.Value = null;
}
private void CheckAndConvertArgs(object?[]? args, MethodInfo methodInfo) private void CheckAndConvertArgs(object?[]? args, MethodInfo methodInfo)
{ {
var paramTypes = methodInfo.GetParameters() var paramTypes = methodInfo.GetParameters()

View file

@ -1,3 +1,5 @@
using Dalamud.Plugin.Internal.Types;
#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.Ipc.Internal; namespace Dalamud.Plugin.Ipc.Internal;
@ -5,9 +7,9 @@ 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)"/> /// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name) public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name) : base(name, owningPlugin)
{ {
} }
@ -43,9 +45,9 @@ internal class CallGatePubSub<TRet> : CallGatePubSubBase, ICallGateProvider<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)"/> /// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name) public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name) : base(name, owningPlugin)
{ {
} }
@ -81,9 +83,9 @@ internal class CallGatePubSub<T1, TRet> : CallGatePubSubBase, ICallGateProvider<
/// <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)"/> /// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name) public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name) : base(name, owningPlugin)
{ {
} }
@ -119,9 +121,9 @@ internal class CallGatePubSub<T1, T2, TRet> : CallGatePubSubBase, ICallGateProvi
/// <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)"/> /// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name) public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name) : base(name, owningPlugin)
{ {
} }
@ -157,9 +159,9 @@ internal class CallGatePubSub<T1, T2, T3, TRet> : CallGatePubSubBase, ICallGateP
/// <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)"/> /// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name) public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name) : base(name, owningPlugin)
{ {
} }
@ -195,9 +197,9 @@ internal class CallGatePubSub<T1, T2, T3, T4, TRet> : CallGatePubSubBase, ICallG
/// <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)"/> /// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name) public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name) : base(name, owningPlugin)
{ {
} }
@ -233,9 +235,9 @@ internal class CallGatePubSub<T1, T2, T3, T4, T5, TRet> : CallGatePubSubBase, IC
/// <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)"/> /// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name) public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name) : base(name, owningPlugin)
{ {
} }
@ -271,9 +273,9 @@ internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet> : CallGatePubSubBase
/// <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)"/> /// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name) public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name) : base(name, owningPlugin)
{ {
} }
@ -309,9 +311,9 @@ internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet> : CallGatePubSub
/// <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)"/> /// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name) public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name) : 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.Plugin.Ipc.Exceptions;
using Dalamud.Utility;
using Serilog;
namespace Dalamud.Plugin.Ipc.Internal; namespace Dalamud.Plugin.Ipc.Internal;
@ -11,9 +18,11 @@ internal abstract class CallGatePubSubBase
/// Initializes a new instance of the <see cref="CallGatePubSubBase"/> class. /// Initializes a new instance of the <see cref="CallGatePubSubBase"/> class.
/// </summary> /// </summary>
/// <param name="name">The name of the IPC registration.</param> /// <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.Channel = Service<CallGate>.Get().GetOrCreateChannel(name);
this.OwningPlugin = owningPlugin;
} }
/// <summary> /// <summary>
@ -39,6 +48,11 @@ internal abstract class CallGatePubSubBase
/// </summary> /// </summary>
protected CallGateChannel Channel { get; init; } protected CallGateChannel Channel { get; init; }
/// <summary>
/// Gets the plugin that owns this pubsub instance.
/// </summary>
protected LocalPlugin? OwningPlugin { get; init; }
/// <summary> /// <summary>
/// Removes the associated Action from this call gate, effectively disabling RPC calls. /// Removes the associated Action from this call gate, effectively disabling RPC calls.
/// </summary> /// </summary>
@ -53,6 +67,16 @@ internal abstract class CallGatePubSubBase
public void UnregisterFunc() public void UnregisterFunc()
=> this.Channel.Func = null; => 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 this.Channel.GetInvocationContext();
}
/// <summary> /// <summary>
/// Registers a <see cref="Delegate"/> for use by other plugins via RPC. This Delegate must satisfy the constraints /// 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 /// of an <see cref="Action"/> type as defined by the interface, meaning they may not return a value and must have
@ -105,7 +129,12 @@ internal abstract class CallGatePubSubBase
/// <seealso cref="RegisterAction"/> /// <seealso cref="RegisterAction"/>
/// <seealso cref="UnregisterAction"/> /// <seealso cref="UnregisterAction"/>
private protected void InvokeAction(params object?[]? args) private protected void InvokeAction(params object?[]? args)
=> this.Channel.InvokeAction(args); {
using (this.BuildContext())
{
this.Channel.InvokeAction(args);
}
}
/// <summary> /// <summary>
/// Executes the Function registered for this IPC call gate via <see cref="RegisterFunc"/>. This method is intended /// Executes the Function registered for this IPC call gate via <see cref="RegisterFunc"/>. This method is intended
@ -120,7 +149,12 @@ internal abstract class CallGatePubSubBase
/// <seealso cref="RegisterFunc"/> /// <seealso cref="RegisterFunc"/>
/// <seealso cref="UnregisterFunc"/> /// <seealso cref="UnregisterFunc"/>
private protected TRet InvokeFunc<TRet>(params object?[]? args) private protected TRet InvokeFunc<TRet>(params object?[]? args)
=> this.Channel.InvokeFunc<TRet>(args); {
using (this.BuildContext())
{
return this.Channel.InvokeFunc<TRet>(args);
}
}
/// <summary> /// <summary>
/// Send the given arguments to all subscribers (through <see cref="Subscribe"/>) of this IPC call gate. This method /// Send the given arguments to all subscribers (through <see cref="Subscribe"/>) of this IPC call gate. This method
@ -132,4 +166,14 @@ internal abstract class CallGatePubSubBase
/// <param name="args">Delegate arguments.</param> /// <param name="args">Delegate arguments.</param>
private protected void SendMessage(params object?[]? args) private protected void SendMessage(params object?[]? args)
=> this.Channel.SendMessage(args); => this.Channel.SendMessage(args);
private IDisposable BuildContext()
{
this.Channel.SetInvocationContext(new IpcContext
{
SourcePlugin = this.OwningPlugin != null ? new ExposedPlugin(this.OwningPlugin) : null,
});
return Disposable.Create(() => { this.Channel.ClearInvocationContext(); });
}
} }

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"}>";
}