refactor(plugin): centralise slash-command registration for lazy-window readiness (v1.4.9 R1 stage 1)

Pull the four user-triggered slash-commands (/hellion, /hellionView,
/hellionDebugger, /hellionSeString) plus the two Plugin-Manager
UiBuilder hooks (OpenConfigUi, OpenMainUi) out of their window
constructors and into a central Plugin.SetupCommands method so they
work before their target window has been opened the first time. A
matching TearDownCommands runs as the first CaptureFailure inside the
framework-thread teardown lambda. /hellion and /hellionSeString stay
under the same #if DEBUG guard SeStringDebugger had before. The four
window classes keep their public Dispose method signatures so the
existing Plugin.DisposeAsync method-group binding still resolves —
the bodies are now empty pointers to TearDownCommands. The pre-v1.4.9
`OpenMainUi` body that flipped SettingsWindow.IsOpen and the three
private Toggle(string, string) method-group wrappers are gone since
the central handlers call SettingsWindow.Toggle() / DbViewer.Toggle()
etc. directly.

The properties stay eager in stage 1 — the lazy-init switch lands in
stage 2 with the matching `_lazyWindowLock` guard around AddWindow
and RemoveAllWindows. Doing it in two commits keeps the slash-command
correctness verifiable on its own.

Smoke (release build): /hellion, /hellionView, /hellionDebugger,
/clearhellion plus Plugin-Manager Settings and Open buttons all
toggle their target window. /hellionSeString remains DEBUG-only as
before.
This commit is contained in:
2026-05-15 00:28:18 +02:00
parent 6051e49307
commit 8ed10a536b
5 changed files with 86 additions and 48 deletions
+82 -4
View File
@@ -289,6 +289,12 @@ public sealed class Plugin : IAsyncDalamudPlugin
cancellationToken.ThrowIfCancellationRequested();
// Populate the command dictionary + UiBuilder hooks BEFORE
// Commands.Initialise() walks the dictionary and registers each
// entry with Dalamud's CommandManager (Commands.cs:15-28). Adding
// wrappers after Initialise() would leak them — they'd live in
// the dictionary but never reach Dalamud.
SetupCommands();
Commands.Initialise();
// Daily retention sweep — fire-and-forget, skips when disabled
@@ -424,7 +430,6 @@ public sealed class Plugin : IAsyncDalamudPlugin
Framework.Update += FrameworkUpdate;
Interface.UiBuilder.Draw += Draw;
Interface.LanguageChanged += LanguageChanged;
Interface.UiBuilder.OpenMainUi += OpenMainUi;
}
catch
{
@@ -455,7 +460,6 @@ public sealed class Plugin : IAsyncDalamudPlugin
Exception? failure = null;
// Unsubscribe hooks first — mirrors the hooks-last subscribe order in LoadAsync.
failure = CaptureFailure(failure, () => Interface.UiBuilder.OpenMainUi -= OpenMainUi);
failure = CaptureFailure(failure, () => Interface.LanguageChanged -= LanguageChanged);
failure = CaptureFailure(failure, () => Interface.UiBuilder.Draw -= Draw);
failure = CaptureFailure(failure, () => Framework.Update -= FrameworkUpdate);
@@ -505,6 +509,11 @@ public sealed class Plugin : IAsyncDalamudPlugin
await Framework
.RunOnFrameworkThread(() =>
{
// TearDown slash-commands + UiBuilder hooks before windows
// tear down. Slash-commands holding handlers that reach
// the windows would otherwise see a half-torn Plugin.
failure = CaptureFailure(failure, TearDownCommands);
failure = CaptureFailure(
failure,
() => GameFunctions.GameFunctions.SetChatInteractable(true)
@@ -683,11 +692,80 @@ public sealed class Plugin : IAsyncDalamudPlugin
}
}
private void OpenMainUi()
// Central slash-command + UiBuilder.OpenConfigUi/OpenMainUi subscribe so
// the four lazy windows (Settings, DbViewer, SeStringDebugger, Debugger)
// have working entry points before they're constructed.
private void SetupCommands()
{
SettingsWindow.IsOpen = !SettingsWindow.IsOpen;
// ChatLogWindow.cs:128 already registers /hellion (ToggleChat). The
// description-arg here keeps the Dalamud help list populated.
Commands.Register("/hellion", "Perform various actions with Hellion Chat.").Execute +=
OnHellionSettingsCommand;
Commands
.Register(
"/hellionView",
"Get access to your message history, with simple filter options.",
true
)
.Execute += OnHellionViewCommand;
Commands.Register("/hellionDebugger", showInHelp: false).Execute +=
OnHellionDebuggerCommand;
#if DEBUG
// SeStringDebugger.cs lives under #if DEBUG too; keep this out of release builds.
Commands.Register("/hellionSeString", showInHelp: false).Execute +=
OnHellionSeStringCommand;
#endif
// Plugin-Manager "Settings" button. Was in Settings.cs:67 pre-v1.4.9.
Interface.UiBuilder.OpenConfigUi += OnOpenConfigUi;
// Plugin-Manager "Open" button. Was in Plugin.cs LoadAsync pre-v1.4.9
// (separate OpenMainUi handler that flipped SettingsWindow.IsOpen).
Interface.UiBuilder.OpenMainUi += OnOpenMainUi;
}
private void TearDownCommands()
{
Interface.UiBuilder.OpenMainUi -= OnOpenMainUi;
Interface.UiBuilder.OpenConfigUi -= OnOpenConfigUi;
Commands.Register("/hellion", "Perform various actions with Hellion Chat.").Execute -=
OnHellionSettingsCommand;
Commands
.Register(
"/hellionView",
"Get access to your message history, with simple filter options.",
true
)
.Execute -= OnHellionViewCommand;
Commands.Register("/hellionDebugger", showInHelp: false).Execute -=
OnHellionDebuggerCommand;
#if DEBUG
Commands.Register("/hellionSeString", showInHelp: false).Execute -=
OnHellionSeStringCommand;
#endif
}
private void OnHellionSettingsCommand(string command, string arguments)
{
// /hellion with args is intentionally a no-op (matches pre-v1.4.9
// Settings.cs:76-80 behaviour).
if (string.IsNullOrWhiteSpace(arguments))
SettingsWindow.Toggle();
}
private void OnOpenConfigUi() => SettingsWindow.Toggle();
private void OnOpenMainUi() => SettingsWindow.Toggle();
private void OnHellionViewCommand(string _, string __) => DbViewer.Toggle();
private void OnHellionDebuggerCommand(string _, string __) => DebuggerWindow.Toggle();
#if DEBUG
private void OnHellionSeStringCommand(string _, string __) => SeStringDebugger.Toggle();
#endif
private void RunRetentionSweepIfDue()
{
if (!Config.RetentionEnabled)
+1 -17
View File
@@ -93,29 +93,13 @@ public class DbViewer : Window
RespectCloseHotkey = false;
DisableWindowSounds = true;
Plugin
.Commands.Register(
"/hellionView",
"Get access to your message history, with simple filter options.",
true
)
.Execute += Toggle;
}
public void Dispose()
{
Plugin
.Commands.Register(
"/hellionView",
"Get access to your message history, with simple filter options.",
true
)
.Execute -= Toggle;
// Slash-command tear-down moved to Plugin.TearDownCommands.
}
private void Toggle(string _, string __) => Toggle();
public override void Draw()
{
var totalPages = (int)Math.Ceiling((double)Count / RowPerPage);
+1 -5
View File
@@ -28,17 +28,13 @@ public class DebuggerWindow : Window, IDisposable
RespectCloseHotkey = false;
DisableWindowSounds = true;
Plugin.Commands.Register("/hellionDebugger", showInHelp: false).Execute += Toggle;
}
public void Dispose()
{
Plugin.Commands.Register("/hellionDebugger", showInHelp: false).Execute -= Toggle;
// Slash-command tear-down moved to Plugin.TearDownCommands.
}
private void Toggle(string _, string __) => Toggle();
public override unsafe void Draw()
{
var agent = (nint)AgentItemDetail.Instance();
+1 -9
View File
@@ -29,21 +29,13 @@ public class SeStringDebugger : Window
RespectCloseHotkey = false;
DisableWindowSounds = true;
#if DEBUG
Plugin.Commands.Register("/hellionSeString", showInHelp: false).Execute += Toggle;
#endif
}
public void Dispose()
{
#if DEBUG
Plugin.Commands.Register("/hellionSeString", showInHelp: false).Execute -= Toggle;
#endif
// Slash-command tear-down moved to Plugin.TearDownCommands.
}
private void Toggle(string _, string __) => Toggle();
public override void Draw()
{
if (Plugin.MessageManager.LastMessage.Sender == null)
+1 -13
View File
@@ -60,23 +60,11 @@ public sealed class SettingsWindow : Dalamud.Interface.Windowing.Window
DisableWindowSounds = true;
Initialise();
Plugin
.Commands.Register("/hellion", "Perform various actions with Hellion Chat.")
.Execute += Command;
Plugin.Interface.UiBuilder.OpenConfigUi += Toggle;
}
public void Dispose()
{
Plugin.Interface.UiBuilder.OpenConfigUi -= Toggle;
Plugin.Commands.Register("/hellion").Execute -= Command;
}
private void Command(string command, string args)
{
if (string.IsNullOrWhiteSpace(args))
Toggle();
// Slash-command + OpenConfigUi tear-down moved to Plugin.TearDownCommands.
}
private void Initialise()