Apply code-quality fixes to Plugin.cs IAsyncDalamudPlugin refactor

I-1: rewrite property-shape comment to reflect that all properties (not
just Phase-2 ones) moved to { get; private set; } = null!;.
I-3: drop plan-jargon (Q1=A / Q3=B / Task 5) from source comments;
replace with durable rationale and a version-anchored TODO for the
FontManager.BuildFontsAsync follow-up.
I-4: remove German-word leak ("pflicht") from English comment in
DisposeAsync.
M-5: wrap each cleanup line inside Framework.RunOnFrameworkThread with
CaptureFailure so a single Dispose throw no longer strands subsequent
cleanup. Drops the inline try/swallow on SetChatInteractable. Mirrors
Lightless DisposeFrameworkBoundServicesAsync pattern.
This commit is contained in:
2026-05-08 19:46:11 +02:00
parent a531973c0d
commit daa800c8b1
+29 -23
View File
@@ -49,9 +49,10 @@ public sealed class Plugin : IAsyncDalamudPlugin
public readonly WindowSystem WindowSystem = new(PluginName); public readonly WindowSystem WindowSystem = new(PluginName);
// v1.4.3: Phase-2 services need private setters now that LoadAsync // v1.4.3: properties moved from { get; } to { get; private set; } = null!;
// owns their construction. Phase-1-only services (Commands, Functions, // because LoadAsync now owns construction of the Phase-2 services.
// Ipc, ExtraChat, TypingIpc) keep their setters for symmetry. // Phase-1 services use the same shape for consistency, even though
// they're still allocated in the ctor.
public SettingsWindow SettingsWindow { get; private set; } = null!; public SettingsWindow SettingsWindow { get; private set; } = null!;
public ChatLogWindow ChatLogWindow { get; private set; } = null!; public ChatLogWindow ChatLogWindow { get; private set; } = null!;
public DbViewer DbViewer { get; private set; } = null!; public DbViewer DbViewer { get; private set; } = null!;
@@ -486,14 +487,13 @@ public sealed class Plugin : IAsyncDalamudPlugin
try try
{ {
// Group A: Font + Theme parallel (Q1=A). Both are CPU-bound, // Group A: Font + Theme parallel — both CPU-bound, independent, and
// independent, and dominate the load-time profile. Everything // dominate the load-time profile. Everything else stays sequential to
// else stays sequential to keep ordering simple. // keep ordering simple.
// Q3=B transition: BuildFonts() is sync today; Task 5 converts
// FontManager itself to BuildFontsAsync.
var fontTask = Task.Run(() => var fontTask = Task.Run(() =>
{ {
FontManager = new FontManager(); FontManager = new FontManager();
// TODO(v1.4.x): replace with FontManager.BuildFontsAsync(cancellationToken)
FontManager.BuildFonts(); FontManager.BuildFonts();
}, cancellationToken); }, cancellationToken);
@@ -620,7 +620,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
}); });
// Auto-Tell-Tabs unsubscribes from MessageProcessed before MessageManager // Auto-Tell-Tabs unsubscribes from MessageProcessed before MessageManager
// goes away. Pure-memory cleanup, no framework-thread pflicht. // goes away. Pure-memory cleanup, no framework-thread requirement.
failure = CaptureFailure(failure, () => AutoTellTabsService?.Dispose()); failure = CaptureFailure(failure, () => AutoTellTabsService?.Dispose());
// v1.4.0 F6.2 — MessageManager has its own async dispose path // v1.4.0 F6.2 — MessageManager has its own async dispose path
@@ -636,35 +636,41 @@ public sealed class Plugin : IAsyncDalamudPlugin
// framework thread. WindowSystem mutations and IPC subscriber // framework thread. WindowSystem mutations and IPC subscriber
// disposes touch Dalamud state that's only safe from the framework. // disposes touch Dalamud state that's only safe from the framework.
// Worker-thread DisposeAsync would race the next Draw tick. // Worker-thread DisposeAsync would race the next Draw tick.
failure = await CaptureFailureAsync(failure, async () => // Per-line CaptureFailure so a single throw can't strand the lines
// behind it; mirrors Lightless DisposeFrameworkBoundServicesAsync.
try
{ {
await Framework.RunOnFrameworkThread(() => await Framework.RunOnFrameworkThread(() =>
{ {
// Game-Functions first — other services may still query // Game-Functions first — other services may still query
// chat-interactable state during their Dispose. // chat-interactable state during their Dispose.
try { GameFunctions.GameFunctions.SetChatInteractable(true); } catch { /* swallowed */ } failure = CaptureFailure(failure, () => GameFunctions.GameFunctions.SetChatInteractable(true));
// IPC subscribers — dispose before windows so any final // IPC subscribers — dispose before windows so any final
// event firing from the IPC source can't reach a half-torn // event firing from the IPC source can't reach a half-torn
// ChatLogWindow. // ChatLogWindow.
HonorificService?.Dispose(); failure = CaptureFailure(failure, () => HonorificService?.Dispose());
TypingIpc?.Dispose(); failure = CaptureFailure(failure, () => TypingIpc?.Dispose());
ExtraChat?.Dispose(); failure = CaptureFailure(failure, () => ExtraChat?.Dispose());
Ipc?.Dispose(); failure = CaptureFailure(failure, () => Ipc?.Dispose());
// Windows — RemoveAllWindows first, then per-window Dispose. // Windows — RemoveAllWindows first, then per-window Dispose.
// Order matches the pre-v1.4.3 Dispose body byte-for-byte. // Order matches the pre-v1.4.3 Dispose body byte-for-byte.
// CommandHelpWindow and FirstRunWizard don't implement // CommandHelpWindow and FirstRunWizard don't implement
// IDisposable; their resources are reclaimed via WindowSystem. // IDisposable; their resources are reclaimed via WindowSystem.
WindowSystem?.RemoveAllWindows(); failure = CaptureFailure(failure, () => WindowSystem?.RemoveAllWindows());
ChatLogWindow?.Dispose(); failure = CaptureFailure(failure, () => ChatLogWindow?.Dispose());
DbViewer?.Dispose(); failure = CaptureFailure(failure, () => DbViewer?.Dispose());
InputPreview?.Dispose(); failure = CaptureFailure(failure, () => InputPreview?.Dispose());
SettingsWindow?.Dispose(); failure = CaptureFailure(failure, () => SettingsWindow?.Dispose());
DebuggerWindow?.Dispose(); failure = CaptureFailure(failure, () => DebuggerWindow?.Dispose());
SeStringDebugger?.Dispose(); failure = CaptureFailure(failure, () => SeStringDebugger?.Dispose());
}).ConfigureAwait(false); }).ConfigureAwait(false);
}).ConfigureAwait(false); }
catch (Exception ex)
{
failure ??= ex;
}
// Pure-memory cleanups — no Framework / UI / IPC touch, so they // Pure-memory cleanups — no Framework / UI / IPC touch, so they
// run on whatever thread DisposeAsync resumes on. // run on whatever thread DisposeAsync resumes on.