From 8c4afaac1723ea4dd785f0bb920de5218b2165d2 Mon Sep 17 00:00:00 2001 From: Jon Kazama Date: Fri, 15 May 2026 12:47:06 +0200 Subject: [PATCH] feat(ipc): mirror TypingIpc provider slots under ChatTwo namespace (v1.4.9 R4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HellionChat replaces ChatTwo (conflict detection prevents parallel loading) but third-party plugins with a no-fork policy keep subscribing only to the ChatTwo.*-prefixed IPC gates. Mirroring the two TypingIpc provider slots under the ChatTwo namespace lets those plugins keep working without code changes on their side. Mirrored slots: - ChatTwo.GetChatInputState ←→ HellionChat.GetChatInputState - ChatTwo.ChatInputStateChanged ←→ HellionChat.ChatInputStateChanged Implementation: - Two additional ICallGateProvider fields (ChatTwoStateQueryGate + ChatTwoStateChangedGate) with the identical ChatInputState tuple signature. The tuple's underlying types match ChatTwo's surface byte- for-byte (bool/bool/bool/bool/int/ushort — ChatType is `ushort` in both repos), so Dalamud's IPC marshalling matches across plugin boundaries even when the subscribing plugin defines its own copy of the ChatType enum. - ctor registers the new provider gates and binds RegisterFunc(GetState) to ChatTwoStateQueryGate so query calls route to the same backing path. - Update() pushes the state to both ChatTwoStateChangedGate and the existing StateChangedGate in lockstep. - Dispose() unregisters both query gates. Ipc/ExtraChat.cs is intentionally unchanged — it is a subscriber on ExtraChat's own IPC, not a provider, so no compatibility mirror applies. Co-Authored-By: Claude Opus 4.7 (1M context) --- HellionChat/Ipc/TypingIpc.cs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/HellionChat/Ipc/TypingIpc.cs b/HellionChat/Ipc/TypingIpc.cs index 474c8ff..33b6fd6 100644 --- a/HellionChat/Ipc/TypingIpc.cs +++ b/HellionChat/Ipc/TypingIpc.cs @@ -19,6 +19,17 @@ internal sealed class TypingIpc : IDisposable private ICallGateProvider StateQueryGate { get; } private ICallGateProvider StateChangedGate { get; } + // v1.4.9 R4: ChatTwo IPC compatibility mirror. Some third-party plugins + // have a no-fork policy and subscribe only to ChatTwo.*-prefixed IPC + // gates. HellionChat replaces ChatTwo (conflict detection prevents + // parallel loading), so mirroring the ChatTwo provider slots lets those + // plugins keep working without code changes on their side. The tuple + // shape is textually identical to ChatTwo's IPC surface (same member + // order, same underlying types — ChatType is `ushort` in both repos) + // so Dalamud's IPC marshalling matches across plugin boundaries. + private ICallGateProvider ChatTwoStateQueryGate { get; } + private ICallGateProvider ChatTwoStateChangedGate { get; } + private ChatInputState LastState; private bool HasState; @@ -33,7 +44,16 @@ internal sealed class TypingIpc : IDisposable "HellionChat.ChatInputStateChanged" ); + // v1.4.9 R4: ChatTwo-prefixed compatibility slots (see class-level comment). + ChatTwoStateQueryGate = Plugin.Interface.GetIpcProvider( + "ChatTwo.GetChatInputState" + ); + ChatTwoStateChangedGate = Plugin.Interface.GetIpcProvider( + "ChatTwo.ChatInputStateChanged" + ); + StateQueryGate.RegisterFunc(GetState); + ChatTwoStateQueryGate.RegisterFunc(GetState); } private ChatInputState BuildState() @@ -67,10 +87,13 @@ internal sealed class TypingIpc : IDisposable HasState = true; LastState = state; StateChangedGate.SendMessage(state); + // v1.4.9 R4: mirror on ChatTwo-prefixed slot for no-fork-policy plugins. + ChatTwoStateChangedGate.SendMessage(state); } public void Dispose() { StateQueryGate.UnregisterFunc(); + ChatTwoStateQueryGate.UnregisterFunc(); } }