From 931a152d00966c87802e32f08683795157698661 Mon Sep 17 00:00:00 2001 From: Jon Kazama Date: Wed, 27 May 2026 20:03:52 +0200 Subject: [PATCH] feat(infra): wire PayloadHandlerInitHostedService (AddonLifecycle + setter) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit G connects PayloadHandler to the runtime — this is the activation point after E1-E6 built the type and F registered it in DI: - StartAsync calls MessageList.AttachPayloadHandler(_payloadHandler) to complete the §6.2 cycle-resolution (ctor-cycle was broken by setter, this is where the setter actually fires) - StartAsync registers AddonLifecycle listener for MoveTooltip on PostUpdate of "ItemDetail" and "ActionDetail" addons - StopAsync unregisters the listener - Both Register/Unregister wrapped in Plugin.Framework.RunOnFrameworkThread as defensive insurance — IAddonLifecycle thread-affinity is not explicitly documented in Dalamud API; wrap keeps the v1.5.6 runtime contract intact (per spec §5-G note) Mirrors existing IpcManagerInitHostedService / TypingIpcInitHostedService pattern in Infrastructure/Hosting/. PluginHostFactory adds the AddHostedService() registration. After G, the chunked-message-render pipeline is end-to-end functional: MessageList renders via ChunkRenderer, _handler is wired so popups fire on click/hover, MoveTooltip repositions native item-tooltips away from the chat window. --- .../Hosting/InitHostedServices.cs | 40 +++++++++++++++++++ HellionChat/PluginHostFactory.cs | 4 ++ 2 files changed, 44 insertions(+) diff --git a/HellionChat/Infrastructure/Hosting/InitHostedServices.cs b/HellionChat/Infrastructure/Hosting/InitHostedServices.cs index 9ffb54f..a957127 100644 --- a/HellionChat/Infrastructure/Hosting/InitHostedServices.cs +++ b/HellionChat/Infrastructure/Hosting/InitHostedServices.cs @@ -1,7 +1,9 @@ +using Dalamud.Game.Addon.Lifecycle; using Dalamud.Plugin; using HellionChat.Integrations; using HellionChat.Ipc; using HellionChat.Themes; +using HellionChat.Ui.Components; using Microsoft.Extensions.Hosting; namespace HellionChat.Infrastructure.Hosting; @@ -101,3 +103,41 @@ internal sealed class FailedTellNotifierInitHostedService(FailedTellNotifier not public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; } + +internal sealed class PayloadHandlerInitHostedService( + PayloadHandler payloadHandler, + MessageList messageList +) : IHostedService +{ + public async Task StartAsync(CancellationToken cancellationToken) + { + // §6.2 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(() => + { + Plugin.AddonLifecycle.UnregisterListener(payloadHandler.MoveTooltip); + }); + } +} diff --git a/HellionChat/PluginHostFactory.cs b/HellionChat/PluginHostFactory.cs index f74f191..c74bc5d 100644 --- a/HellionChat/PluginHostFactory.cs +++ b/HellionChat/PluginHostFactory.cs @@ -312,6 +312,10 @@ internal static class PluginHostFactory sp.GetRequiredService() ) ); + services.AddHostedService(sp => new Infrastructure.Hosting.PayloadHandlerInitHostedService( + sp.GetRequiredService(), + sp.GetRequiredService() + )); } }