8c4afaac17
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) <noreply@anthropic.com>
100 lines
3.2 KiB
C#
100 lines
3.2 KiB
C#
using Dalamud.Plugin.Ipc;
|
|
using HellionChat.Code;
|
|
|
|
namespace HellionChat.Ipc;
|
|
|
|
using ChatInputState = (
|
|
bool InputVisible,
|
|
bool InputFocused,
|
|
bool HasText,
|
|
bool IsTyping,
|
|
int TextLength,
|
|
ChatType ChannelType
|
|
);
|
|
|
|
internal sealed class TypingIpc : IDisposable
|
|
{
|
|
private Plugin Plugin { get; }
|
|
|
|
private ICallGateProvider<ChatInputState> StateQueryGate { get; }
|
|
private ICallGateProvider<ChatInputState, object?> 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<ChatInputState> ChatTwoStateQueryGate { get; }
|
|
private ICallGateProvider<ChatInputState, object?> ChatTwoStateChangedGate { get; }
|
|
|
|
private ChatInputState LastState;
|
|
private bool HasState;
|
|
|
|
internal TypingIpc(Plugin plugin)
|
|
{
|
|
Plugin = plugin;
|
|
|
|
StateQueryGate = Plugin.Interface.GetIpcProvider<ChatInputState>(
|
|
"HellionChat.GetChatInputState"
|
|
);
|
|
StateChangedGate = Plugin.Interface.GetIpcProvider<ChatInputState, object?>(
|
|
"HellionChat.ChatInputStateChanged"
|
|
);
|
|
|
|
// v1.4.9 R4: ChatTwo-prefixed compatibility slots (see class-level comment).
|
|
ChatTwoStateQueryGate = Plugin.Interface.GetIpcProvider<ChatInputState>(
|
|
"ChatTwo.GetChatInputState"
|
|
);
|
|
ChatTwoStateChangedGate = Plugin.Interface.GetIpcProvider<ChatInputState, object?>(
|
|
"ChatTwo.ChatInputStateChanged"
|
|
);
|
|
|
|
StateQueryGate.RegisterFunc(GetState);
|
|
ChatTwoStateQueryGate.RegisterFunc(GetState);
|
|
}
|
|
|
|
private ChatInputState BuildState()
|
|
{
|
|
var log = Plugin.ChatLogWindow;
|
|
|
|
var usedChannel = Plugin.CurrentTab.CurrentChannel;
|
|
var inputChannel = usedChannel.UseTempChannel
|
|
? usedChannel.TempChannel
|
|
: usedChannel.Channel;
|
|
var channelType = inputChannel.ToChatType();
|
|
|
|
return (
|
|
InputVisible: !log.IsHidden,
|
|
log.InputFocused,
|
|
HasText: log.Chat.Length > 0,
|
|
IsTyping: log is { InputFocused: true, Chat.Length: > 0 },
|
|
TextLength: log.Chat.Length,
|
|
ChannelType: channelType
|
|
);
|
|
}
|
|
|
|
private ChatInputState GetState() => BuildState();
|
|
|
|
internal void Update()
|
|
{
|
|
var state = BuildState();
|
|
if (HasState && state.Equals(LastState))
|
|
return;
|
|
|
|
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();
|
|
}
|
|
}
|