Replaces the inherited Chat-2 IPC guide with a guide that follows the Hellion README structure: top-level intro, a dedicated "Compatibility with Chat 2" section that calls out exactly what the v1.0.0 rename did (and did not) change, a channel-reference table, per-surface sections with separate "Migration from Chat 2" diff blocks, and a closing license/attribution paragraph that points back to NOTICE.md. Content additions on top of the previous version: - Explicit statement that the IPC surface is the Chat-2 surface re- published under the Hellion name. Tuple shapes, lifecycle and call semantics are unchanged; only the channel-string prefix differs - Channel-reference table at the top so integrators can see the full surface at a glance instead of reading two long code samples - Tuple-payload field table for the Typing State IPC with a short meaning per field - Behavior notes for ChatInputStateChanged (fires once on subscribe, then only on real changes) so integrators don't need to read the source to learn the contract - Explicit migration-diff blocks for both surfaces Existing example code is preserved with the same identifiers; only the host-class names are aligned to "HellionChat..." instead of the old "ChatTwoIpc"/"TypingIntegration" placeholders.
12 KiB
Executable File
Hellion Chat IPC Integration Guide
This document describes the inter-plugin-communication (IPC) channels that Hellion Chat exposes to other Dalamud plugins. Two integration surfaces are covered: the Context Menu IPC for adding custom items to Hellion Chat's right-click menus, and the Typing State IPC for reacting to the user's input-box activity.
Compatibility with Chat 2
Hellion Chat is a standalone fork of Chat 2 (EUPL-1.2). The IPC surface is one of the parts the fork inherits directly: the same call shapes, the same tuple payloads, the same call semantics, the same lifecycle. We did not redesign the API, we re-published it under our own plugin name.
Concretely, this means:
- Tuple shapes are identical. A subscriber that worked against Chat 2's
ChatTwo.Invokeworks against Hellion Chat'sHellionChat.Invokewithout any code change beyond the channel string. - Lifecycle is identical. The
Availableping fires when the plugin becomes ready, your subscriber re-registers, and the registration ID is returned by the sameRegistercall as before. - Channel-name prefix changed in v1.0.0. Every
ChatTwo.*channel name is nowHellionChat.*. Existing third-party integrations need a one-line rename per channel string and nothing else.
If your plugin already supports Chat 2 and you want to add Hellion Chat support, the cleanest path is to bind both prefixes and treat whichever one becomes available first as the active host.
Channel Reference
| Surface | Channel | Direction | Payload |
|---|---|---|---|
| Context Menu | HellionChat.Available |
plugin → caller | event, no payload |
| Context Menu | HellionChat.Register |
caller → plugin | returns registration string ID |
| Context Menu | HellionChat.Unregister |
caller → plugin | takes registration ID string |
| Context Menu | HellionChat.Invoke |
plugin → caller | event, see Context Menu section |
| Typing State | HellionChat.GetChatInputState |
caller → plugin | returns the typing-state tuple |
| Typing State | HellionChat.ChatInputStateChanged |
plugin → caller | event, fires once on subscribe and on every state change, payload is the same tuple |
Context Menu IPC
Use this surface to draw your own selectables inside Hellion Chat's
right-click context menus. All registrations are called inside an ImGui
BeginMenu, so anything you draw appears as a regular menu entry.
Lifecycle
- Subscribe to
HellionChat.Available. The host fires this once when it loads or reloads, so your plugin can re-register without polling. - Call
HellionChat.Registerto obtain a registration ID. Save it. You need it to filterInvokecallbacks that target your registration and to callUnregisterlater. - Subscribe to
HellionChat.Invokeand draw your menu items inside the handler when theidmatches your saved registration ID. - On plugin disable or unload, call
HellionChat.Unregisterwith your saved ID and unsubscribe fromInvoke.
Example
public class HellionChatContextMenu {
// Used to register your plugin with the IPC; returns an ID you must save.
private ICallGateSubscriber<string> Register { get; }
// Used to unregister your plugin from the IPC; call this on unload.
private ICallGateSubscriber<string, object?> Unregister { get; }
// Subscribe to receive a notification when Hellion Chat becomes ready
// or reloads, so you can re-register.
private ICallGateSubscriber<object?> Available { get; }
// Subscribe to draw your custom context-menu items.
private ICallGateSubscriber<string, PlayerPayload?, ulong, Payload?, SeString?, SeString?, object?> Invoke { get; }
private string? _id;
public HellionChatContextMenu(DalamudPluginInterface @interface) {
this.Register = @interface.GetIpcSubscriber<string>("HellionChat.Register");
this.Unregister = @interface.GetIpcSubscriber<string, object?>("HellionChat.Unregister");
this.Invoke = @interface.GetIpcSubscriber<string, PlayerPayload?, ulong, Payload?, SeString?, SeString?, object?>("HellionChat.Invoke");
this.Available = @interface.GetIpcSubscriber<object?>("HellionChat.Available");
}
public void Enable() {
// Re-register automatically when Hellion Chat becomes ready or reloads.
this.Available.Subscribe(() => this.DoRegister());
// Register if Hellion Chat is already loaded.
this.DoRegister();
// Listen for context-menu events.
this.Invoke.Subscribe(this.OnInvoke);
}
private void DoRegister() {
this._id = this.Register.InvokeFunc();
}
public void Disable() {
if (this._id != null) {
this.Unregister.InvokeAction(this._id);
this._id = null;
}
this.Invoke.Unsubscribe(this.OnInvoke);
}
private void OnInvoke(string id, PlayerPayload? sender, ulong contentId, Payload? payload, SeString? senderString, SeString? content) {
// Filter: only react to invocations that target our registration.
if (id != this._id) {
return;
}
// Draw your custom menu items here. Available context:
// sender — first PlayerPayload in the sender SeString, or null
// contentId — content ID of the message sender, or 0 when not known
// payload — the payload that was right-clicked, or null when text
// senderString — the full sender SeString
// content — the full message content SeString
if (ImGui.Selectable("Test plugin")) {
PluginLog.Log($"hi!\nsender: {sender}\ncontent id: {contentId:X}\npayload: {payload}\nsender string: {senderString}\ncontent string: {content}");
}
}
}
Migration from Chat 2
If your plugin already integrates with ChatTwo.*, the rename is the only
required change:
-this.Register = @interface.GetIpcSubscriber<string>("ChatTwo.Register");
-this.Unregister = @interface.GetIpcSubscriber<string, object?>("ChatTwo.Unregister");
-this.Invoke = @interface.GetIpcSubscriber<...>("ChatTwo.Invoke");
-this.Available = @interface.GetIpcSubscriber<object?>("ChatTwo.Available");
+this.Register = @interface.GetIpcSubscriber<string>("HellionChat.Register");
+this.Unregister = @interface.GetIpcSubscriber<string, object?>("HellionChat.Unregister");
+this.Invoke = @interface.GetIpcSubscriber<...>("HellionChat.Invoke");
+this.Available = @interface.GetIpcSubscriber<object?>("HellionChat.Available");
Typing State IPC
Use this surface when you need to know whether the player is currently interacting with Hellion Chat's input box. Useful for typing indicators, keyboard-shortcut suppression, or HUD elements that hide while the user is typing.
Tuple Payload
Both HellionChat.GetChatInputState (poll) and
HellionChat.ChatInputStateChanged (event) return the same tuple:
(bool InputVisible, bool InputFocused, bool HasText, bool IsTyping, int TextLength, ChatType ChannelType)
| Field | Type | Meaning |
|---|---|---|
InputVisible |
bool |
True when Hellion Chat is not hidden by user, cutscene or battle settings. |
InputFocused |
bool |
True while the input box currently has keyboard focus. |
HasText |
bool |
True when the input buffer contains more than whitespace. |
IsTyping |
bool |
Convenience flag, equivalent to InputFocused && HasText. |
TextLength |
int |
Length of the raw input buffer in characters. |
ChannelType |
ChatType |
The channel/mode that will be used if the buffer is submitted right now. |
Where ChannelType comes from
ChannelType is the HellionChat.Code.ChatType enum value representing the
target channel for the current submission. It is sourced from the active
tab's UsedChannel (HellionChat/Configuration.cs), which the plugin keeps
in sync by hooking the in-game shell (HellionChat/GameFunctions/Chat.cs)
and by resolving temporary overrides inside the chat UI
(HellionChat/Ui/ChatLogWindow.cs:597). InputChannel values are converted
into the exported ChatType via
HellionChat/Code/InputChannelExt.ToChatType.
Behavior
ChatInputStateChangedfires once immediately after subscribe so you do not need a separateGetChatInputStatepoll for the initial snapshot.- After that it fires only when one or more fields actually change, so it is safe to subscribe without rate-limiting.
GetChatInputStateis available for one-shot polls, e.g. on plugin enable.
Example
public sealed class HellionChatTypingIntegration {
private ICallGateSubscriber<(bool InputVisible, bool InputFocused, bool HasText, bool IsTyping, int TextLength, ChatType ChannelType)> GetChatInputState { get; }
private ICallGateSubscriber<(bool InputVisible, bool InputFocused, bool HasText, bool IsTyping, int TextLength, ChatType ChannelType)> ChatInputStateChanged { get; }
public HellionChatTypingIntegration(DalamudPluginInterface @interface) {
this.GetChatInputState = @interface.GetIpcSubscriber<(bool, bool, bool, bool, int, ChatType)>("HellionChat.GetChatInputState");
this.ChatInputStateChanged = @interface.GetIpcSubscriber<(bool, bool, bool, bool, int, ChatType)>("HellionChat.ChatInputStateChanged");
}
public void Enable() {
this.ChatInputStateChanged.Subscribe(OnChatInputStateChanged);
// Optional: poll once for an initial snapshot. The Subscribe call
// above already fires once, so this is only useful when your code
// path needs the value before the framework hands you the event.
var state = this.GetChatInputState.InvokeFunc();
PluginLog.Information($"Initial typing state: {state}");
}
public void Disable() {
this.ChatInputStateChanged.Unsubscribe(OnChatInputStateChanged);
}
private void OnChatInputStateChanged((bool InputVisible, bool InputFocused, bool HasText, bool IsTyping, int TextLength, ChatType ChannelType) state) {
if (state.IsTyping) {
// Show typing indicator.
} else {
// Hide typing indicator.
}
}
}
Migration from Chat 2
Same shape as the Context Menu surface — only the channel-name prefix needs the rename:
-this.GetChatInputState = @interface.GetIpcSubscriber<...>("ChatTwo.GetChatInputState");
-this.ChatInputStateChanged = @interface.GetIpcSubscriber<...>("ChatTwo.ChatInputStateChanged");
+this.GetChatInputState = @interface.GetIpcSubscriber<...>("HellionChat.GetChatInputState");
+this.ChatInputStateChanged = @interface.GetIpcSubscriber<...>("HellionChat.ChatInputStateChanged");
License & Attribution
This guide and the IPC surface it documents derive directly from the Chat 2
codebase. Hellion Chat is licensed under EUPL-1.2, and credit for
the original IPC design and implementation goes to Infiziert90 (Infi)
and Anna Clemens — see NOTICE.md for full attribution.