Codes like POP-1c or B4b-2 name a task in a planning document, not anything in the code. A reader has no way to resolve them and they age into noise the moment the document is closed. Where a code was used as a reference, the sentence now names the function it meant.
209 lines
8.0 KiB
C#
209 lines
8.0 KiB
C#
using Dalamud.Game.Addon.Lifecycle;
|
|
using Dalamud.Plugin;
|
|
using HellionChat.Integrations;
|
|
using HellionChat.Ipc;
|
|
using HellionChat.Themes;
|
|
using HellionChat.Ui;
|
|
using HellionChat.Ui.Components;
|
|
using HellionChat.Ui.Windows;
|
|
using Microsoft.Extensions.Hosting;
|
|
|
|
namespace HellionChat.Infrastructure.Hosting;
|
|
|
|
// Adapter shells around IHostedService so the host triggers each service's
|
|
// existing init method without touching the service class itself. Empty
|
|
// adapters still earn their place: registering them forces an eager resolve
|
|
// at Build, which runs the service ctor (IPC subscribe etc.) right then
|
|
// instead of lazily on first GetRequiredService.
|
|
|
|
internal sealed class ThemeRegistryInitHostedService(
|
|
ThemeRegistry registry,
|
|
FontManager fontManager
|
|
) : IHostedService
|
|
{
|
|
public async Task StartAsync(CancellationToken cancellationToken)
|
|
{
|
|
// Materialise the lazy AllCustom enumerable so the slug lookup hits a
|
|
// warm cache; otherwise the first Switch falls through to the built-in
|
|
// default when Config.Theme points at a custom slug.
|
|
foreach (var _ in registry.AllCustom()) { }
|
|
registry.SwitchSilent(Plugin.Config.Theme);
|
|
|
|
// Point font sizes at the active theme's typography, wire future
|
|
// theme switches to the atlas rebuild, and apply the boot theme's override.
|
|
fontManager.SetTypographySource(() => registry.Active.Typography);
|
|
registry.SetActiveChangedCallback(() => fontManager.RebuildDelegateFontsIfChanged());
|
|
await Plugin.Framework.RunOnFrameworkThread(() =>
|
|
fontManager.RebuildDelegateFontsIfChanged()
|
|
);
|
|
}
|
|
|
|
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
}
|
|
|
|
// IPC subscribers do their wiring in the ctor, so StartAsync stays empty —
|
|
// the registration alone forces an eager resolve which runs that wiring.
|
|
|
|
internal sealed class IpcManagerInitHostedService(IpcManager ipc) : IHostedService
|
|
{
|
|
private readonly IpcManager _ipc = ipc;
|
|
|
|
public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
|
|
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
}
|
|
|
|
internal sealed class TypingIpcInitHostedService(TypingIpc typingIpc) : IHostedService
|
|
{
|
|
private readonly TypingIpc _typingIpc = typingIpc;
|
|
|
|
public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
|
|
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
}
|
|
|
|
internal sealed class ExtraChatInitHostedService(ExtraChat extraChat) : IHostedService
|
|
{
|
|
private readonly ExtraChat _extraChat = extraChat;
|
|
|
|
public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
|
|
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
}
|
|
|
|
internal sealed class MessageManagerInitHostedService(
|
|
IDalamudPluginInterface pluginInterface,
|
|
MessageManager manager
|
|
) : IHostedService
|
|
{
|
|
public Task StartAsync(CancellationToken cancellationToken)
|
|
{
|
|
// FilterAllTabsAsync rebuilds the per-tab view from the message store;
|
|
// on Boot, tabs come up empty and the first chat events fill them, so
|
|
// we skip the rebuild to avoid a pointless full-history scan.
|
|
if (pluginInterface.Reason is not PluginLoadReason.Boot)
|
|
manager.FilterAllTabsAsync();
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
}
|
|
|
|
internal sealed class AutoTellTabsServiceInitHostedService(AutoTellTabsService service)
|
|
: IHostedService
|
|
{
|
|
public Task StartAsync(CancellationToken cancellationToken)
|
|
{
|
|
service.Initialize();
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
}
|
|
|
|
internal sealed class TellRouterServiceInitHostedService(Services.TellRouterService service)
|
|
: IHostedService
|
|
{
|
|
public Task StartAsync(CancellationToken cancellationToken)
|
|
{
|
|
service.Initialize();
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
}
|
|
|
|
// Eager-resolve trigger: resolving FailedTellNotifier in this adapter's ctor
|
|
// enables its game hook during host startup. StartAsync itself is a no-op.
|
|
internal sealed class FailedTellNotifierInitHostedService(FailedTellNotifier notifier)
|
|
: IHostedService
|
|
{
|
|
// No-op adapter: the ctor dependency above is the actual eager-resolve
|
|
// trigger. Field kept to match the IpcManager/TypingIpc/ExtraChat no-op
|
|
// adapters and to avoid the CS9113 unread-parameter warning.
|
|
private readonly FailedTellNotifier _notifier = notifier;
|
|
|
|
public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
|
|
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
}
|
|
|
|
internal sealed class PayloadHandlerInitHostedService(
|
|
PayloadHandler payloadHandler,
|
|
MessageList messageList
|
|
) : IHostedService
|
|
{
|
|
public async Task StartAsync(CancellationToken cancellationToken)
|
|
{
|
|
// Cycle resolution: both singletons exist by the time HostedServices
|
|
// run, so this is the first safe point to wire the setter.
|
|
messageList.AttachPayloadHandler(payloadHandler);
|
|
|
|
// IAddonLifecycle thread-affinity is not explicitly documented; wrap is
|
|
// defensive insurance — mirrors the window-registration RunOnFrameworkThread
|
|
// pattern established in PluginLifecycle.cs.
|
|
await Plugin.Framework.RunOnFrameworkThread(() =>
|
|
{
|
|
Plugin.AddonLifecycle.RegisterListener(
|
|
AddonEvent.PostUpdate,
|
|
"ItemDetail",
|
|
payloadHandler.MoveTooltip
|
|
);
|
|
Plugin.AddonLifecycle.RegisterListener(
|
|
AddonEvent.PostUpdate,
|
|
"ActionDetail",
|
|
payloadHandler.MoveTooltip
|
|
);
|
|
});
|
|
}
|
|
|
|
public async Task StopAsync(CancellationToken cancellationToken)
|
|
{
|
|
await Plugin.Framework.RunOnFrameworkThread(() =>
|
|
{
|
|
// Single call using the params-overload removes the delegate from all addons it was registered for (ItemDetail + ActionDetail both cleaned in one shot).
|
|
Plugin.AddonLifecycle.UnregisterListener(payloadHandler.MoveTooltip);
|
|
});
|
|
}
|
|
}
|
|
|
|
// Wires MainWindow into CommandHelpWindow post-container-build. CommandHelpWindow
|
|
// cannot take MainWindow as a ctor-param because that would close the cycle
|
|
// InputBar -> CommandHelpWindow -> MainWindow -> InputBar (MS.DI does not catch
|
|
// it through FactoryCallSite registrations and the resolve recurses silently).
|
|
// Both singletons exist by host.StartAsync time, so this is the first safe point
|
|
// to wire the setter — same setter-injection pattern as MessageList.AttachPayloadHandler.
|
|
internal sealed class CommandHelpWindowInitHostedService(
|
|
CommandHelpWindow commandHelpWindow,
|
|
MainWindow mainWindow
|
|
) : IHostedService
|
|
{
|
|
public Task StartAsync(CancellationToken cancellationToken)
|
|
{
|
|
commandHelpWindow.AttachMainWindow(mainWindow);
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
}
|
|
|
|
// Attaches the singleton PayloadHandler to every pre-allocated pop-out
|
|
// window's MessageList post-container-build. Pool/window cannot take the
|
|
// PayloadHandler via ctor (that would close the silent FactoryCallSite cycle —
|
|
// same setter-injection reason as MessageList.AttachPayloadHandler / CommandHelpWindow.
|
|
// AttachMainWindow). Both singletons exist by host.StartAsync time.
|
|
internal sealed class ChannelPopoutInitHostedService(
|
|
ChannelPopoutPool pool,
|
|
PayloadHandler payloadHandler
|
|
) : IHostedService
|
|
{
|
|
public Task StartAsync(CancellationToken cancellationToken)
|
|
{
|
|
foreach (var window in pool.Instances)
|
|
window.AttachPayloadHandler(payloadHandler);
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
}
|