From 6b44f549b41cd3e7ed9c59d3ce19e969686050d4 Mon Sep 17 00:00:00 2001 From: Jon Kazama Date: Tue, 12 May 2026 20:10:40 +0200 Subject: [PATCH] feat(util): add IPlatformUtil indirection over Dalamud.Utility.Util (F12.1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces a thin interface around Util.IsWine and Util.OpenLink so services can be constructed in an isolated xUnit AppDomain without forcing Dalamud.dll onto the assembly search path. Production wiring (DalamudPlatformUtil) caches IsWine at ctor time — it's a runtime probe that never changes for the lifetime of a plugin instance, mirroring the Lightless DalamudUtilService pattern. Plugin.PlatformUtil is wired in the Phase-1 ctor so any service that LoadAsync allocates can resolve the platform indirection without plumbing the instance through additional constructor params. Follow-up commits route MessageStore and the OpenLink call-sites through this interface. --- HellionChat/Plugin.cs | 9 +++++++++ HellionChat/Util/DalamudPlatformUtil.cs | 16 ++++++++++++++++ HellionChat/Util/IPlatformUtil.cs | 11 +++++++++++ 3 files changed, 36 insertions(+) create mode 100644 HellionChat/Util/DalamudPlatformUtil.cs create mode 100644 HellionChat/Util/IPlatformUtil.cs diff --git a/HellionChat/Plugin.cs b/HellionChat/Plugin.cs index 3d49324..717e3fc 100755 --- a/HellionChat/Plugin.cs +++ b/HellionChat/Plugin.cs @@ -113,6 +113,10 @@ public sealed class Plugin : IAsyncDalamudPlugin internal Ui.StatusBar StatusBar { get; private set; } = null!; internal Integrations.HonorificService HonorificService { get; private set; } = null!; + // Platform indirection over Dalamud.Utility.Util. Wired in Phase-1 ctor so + // any service allocated in LoadAsync can read Plugin.PlatformUtil. + internal static IPlatformUtil PlatformUtil { get; private set; } = null!; + // Idempotency guard — Dalamud may fire DisposeAsync twice in a reload race. private int _disposeStarted; @@ -154,6 +158,11 @@ public sealed class Plugin : IAsyncDalamudPlugin Config = Interface.GetPluginConfig() as Configuration ?? new Configuration(); + // Wire platform indirection before LoadAsync allocates anything that + // needs Util.* — services then read Plugin.PlatformUtil instead of + // hitting the Dalamud static surface directly. + PlatformUtil = new DalamudPlatformUtil(); + // Schema gate: v1.4.x requires config v16. Users on older schemas // must install v1.4.2 first to run the migration chain. if (Config.Version < 16) diff --git a/HellionChat/Util/DalamudPlatformUtil.cs b/HellionChat/Util/DalamudPlatformUtil.cs new file mode 100644 index 0000000..fbed84b --- /dev/null +++ b/HellionChat/Util/DalamudPlatformUtil.cs @@ -0,0 +1,16 @@ +namespace HellionChat.Util; + +internal sealed class DalamudPlatformUtil : IPlatformUtil +{ + public DalamudPlatformUtil() + { + // Util.IsWine probes the host process and never changes for the + // lifetime of a plugin instance, so we cache it once at ctor. + // Mirrors LightlessSync/Services/DalamudUtilService:154. + IsWine = Dalamud.Utility.Util.IsWine(); + } + + public bool IsWine { get; } + + public void OpenLink(string url) => Dalamud.Utility.Util.OpenLink(url); +} diff --git a/HellionChat/Util/IPlatformUtil.cs b/HellionChat/Util/IPlatformUtil.cs new file mode 100644 index 0000000..6fadc03 --- /dev/null +++ b/HellionChat/Util/IPlatformUtil.cs @@ -0,0 +1,11 @@ +namespace HellionChat.Util; + +// Indirection over Dalamud.Utility.Util's static surface so services can be +// constructed in an isolated xUnit AppDomain without loading Dalamud.dll. +// Production wiring lives in DalamudPlatformUtil; tests substitute a fake. +internal interface IPlatformUtil +{ + bool IsWine { get; } + + void OpenLink(string url); +}