From 655c903cb502e55d26c00a43c7aaa5fa791887a0 Mon Sep 17 00:00:00 2001 From: Jon Kazama Date: Fri, 15 May 2026 13:01:12 +0200 Subject: [PATCH] feat(ipc): mirror context-menu IPC gates under ChatTwo namespace (v1.4.9 R4 ext) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends commit 8c4afaa: the TypingIpc mirror covered only two of the six ChatTwo IPC slots. Third-party plugins like Artisan and AllaganTools subscribe to a different ChatTwo IPC surface — the context-menu integration (ChatTwo.Register / Unregister / Available / Invoke) that lets them push item-links into the chat. Smoke test against the deployed v1.4.9 build showed Artisan logging "Chat2 is not available" because those four gates were not yet mirrored. This commit adds the missing four ChatTwo-prefixed provider gates in IpcManager.cs: - ChatTwo.Register (Func) — bound to the existing Register() backing method, so plugins that subscribe via either namespace land in the same Registered list. - ChatTwo.Unregister (Action) — bound to the existing Unregister() backing method, same shared-state rationale. - ChatTwo.Available (Action<>) — SendMessage() fires from the ctor right after AvailableGate.SendMessage(), so any subscriber waiting on the "Chat 2 became available" signal sees both events. - ChatTwo.Invoke (Action) — Invoke() fans the context-menu event out to both InvokeGate and ChatTwoInvokeGate in lockstep. Subscribers compare on the registration ID they got back from Register, so the shared-backing approach keeps that contract intact regardless of which namespace they subscribed under. Dispose() unregisters all four ChatTwo gates plus the four existing HellionChat gates. The conflict-detection that prevents ChatTwo from loading alongside HellionChat guarantees no slot collision at runtime. With this commit the full ChatTwo IPC surface (6 of 6 slots) is mirrored: - ChatTwo.GetChatInputState (TypingIpc, commit 8c4afaa) - ChatTwo.ChatInputStateChanged (TypingIpc, commit 8c4afaa) - ChatTwo.Register (IpcManager, this commit) - ChatTwo.Unregister (IpcManager, this commit) - ChatTwo.Available (IpcManager, this commit) - ChatTwo.Invoke (IpcManager, this commit) Co-Authored-By: Claude Opus 4.7 (1M context) --- HellionChat/IpcManager.cs | 49 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/HellionChat/IpcManager.cs b/HellionChat/IpcManager.cs index b70e49f..cbade56 100755 --- a/HellionChat/IpcManager.cs +++ b/HellionChat/IpcManager.cs @@ -19,6 +19,26 @@ internal sealed class IpcManager : IDisposable object? > InvokeGate { get; } + // v1.4.9 R4: ChatTwo IPC compatibility mirror. Third-party plugins with + // a no-fork policy (e.g. Artisan, AllaganTools) only subscribe to the + // ChatTwo.*-prefixed context-menu integration gates. Mirroring all four + // provider slots under the ChatTwo namespace lets those plugins keep + // working without code changes on their side. Conflict detection + // prevents ChatTwo and HellionChat from loading in parallel, so no slot + // collision risk. + private ICallGateProvider ChatTwoRegisterGate { get; } + private ICallGateProvider ChatTwoUnregisterGate { get; } + private ICallGateProvider ChatTwoAvailableGate { get; } + private ICallGateProvider< + string, + PlayerPayload?, + ulong, + Payload?, + SeString?, + SeString?, + object? + > ChatTwoInvokeGate { get; } + internal List Registered { get; } = []; public IpcManager() @@ -41,7 +61,32 @@ internal sealed class IpcManager : IDisposable object? >("HellionChat.Invoke"); + // v1.4.9 R4: ChatTwo-prefixed mirrors of the four context-menu slots + // above. Share the same Register/Unregister backing methods so a + // plugin that subscribes via either namespace lands in the same + // Registered list. SendMessage on Invoke fans out to both gates. + ChatTwoRegisterGate = Plugin.Interface.GetIpcProvider("ChatTwo.Register"); + ChatTwoRegisterGate.RegisterFunc(Register); + + ChatTwoAvailableGate = Plugin.Interface.GetIpcProvider("ChatTwo.Available"); + + ChatTwoUnregisterGate = Plugin.Interface.GetIpcProvider( + "ChatTwo.Unregister" + ); + ChatTwoUnregisterGate.RegisterAction(Unregister); + + ChatTwoInvokeGate = Plugin.Interface.GetIpcProvider< + string, + PlayerPayload?, + ulong, + Payload?, + SeString?, + SeString?, + object? + >("ChatTwo.Invoke"); + AvailableGate.SendMessage(); + ChatTwoAvailableGate.SendMessage(); } internal void Invoke( @@ -54,6 +99,8 @@ internal sealed class IpcManager : IDisposable ) { InvokeGate.SendMessage(id, sender, contentId, payload, senderString, content); + // v1.4.9 R4: fan out the same event to plugins listening on ChatTwo.Invoke. + ChatTwoInvokeGate.SendMessage(id, sender, contentId, payload, senderString, content); } private string Register() @@ -72,6 +119,8 @@ internal sealed class IpcManager : IDisposable { UnregisterGate.UnregisterAction(); RegisterGate.UnregisterFunc(); + ChatTwoUnregisterGate.UnregisterAction(); + ChatTwoRegisterGate.UnregisterFunc(); Registered.Clear(); } }