fix(di): use factory lambdas for internal-ctor services

C3 bootstrap throws "A suitable constructor for type
HellionChat.Ipc.ExtraChat could not be located" because
Microsoft.Extensions.DependencyInjection's ActivatorUtilities only
binds to PUBLIC constructors via reflection. ExtraChat is a public
class with an internal ctor; Commands and StatusBar are internal
classes whose implicit default ctor inherits class accessibility
(internal); every IHostedService adapter is `internal sealed class
X(deps)` with a primary ctor that is also internal.

The fix routes all eight singletons and all seven hosted-service
adapters through factory lambdas. `new T(...)` inside the
PluginHostFactory namespace sees the internal surface, so the
container never has to reflect over internal ctors.
This commit is contained in:
2026-05-17 08:20:02 +02:00
parent 169168cea9
commit 0fe66d2c3c
+41 -15
View File
@@ -86,14 +86,22 @@ internal static class PluginHostFactory
// constraint; ctors that need a Plugin backref go through a factory
// lambda that resolves Plugin from the container.
// -----------------------------------------------------------------
services.AddSingleton<IPlatformUtil, DalamudPlatformUtil>();
services.AddSingleton<IPluginLogProxy, DalamudPluginLogProxy>();
services.AddSingleton<FileDialogManager>();
services.AddSingleton<Commands>();
services.AddSingleton<FontManager>();
services.AddSingleton<StatusBar>();
services.AddSingleton<IpcManager>();
services.AddSingleton<ExtraChat>();
// Factory lambdas across the board: Microsoft.Extensions.DependencyInjection's
// ActivatorUtilities only inspects PUBLIC constructors via reflection,
// and several HellionChat classes are `internal sealed` with implicit-
// internal default ctors (Commands, StatusBar) or explicitly `internal`
// ctors on public classes (ExtraChat). The lambda body compiles inside
// the HellionChat namespace, so `new T()` sees the internal surface.
services.AddSingleton<IPlatformUtil>(_ => new DalamudPlatformUtil());
services.AddSingleton<IPluginLogProxy>(sp => new DalamudPluginLogProxy(
sp.GetRequiredService<IPluginLog>()
));
services.AddSingleton<FileDialogManager>(_ => new FileDialogManager());
services.AddSingleton(_ => new Commands());
services.AddSingleton(_ => new FontManager());
services.AddSingleton(_ => new StatusBar());
services.AddSingleton(_ => new IpcManager());
services.AddSingleton(_ => new ExtraChat());
services.AddSingleton(sp => new ThemeRegistry(
Path.Combine(
@@ -149,13 +157,31 @@ internal static class PluginHostFactory
// making the services themselves implement IHostedService (Lightless'
// pattern) — DI-2a leaves service classes untouched.
// -----------------------------------------------------------------
services.AddHostedService<FontManagerInitHostedService>();
services.AddHostedService<ThemeRegistryInitHostedService>();
services.AddHostedService<IpcManagerInitHostedService>();
services.AddHostedService<TypingIpcInitHostedService>();
services.AddHostedService<ExtraChatInitHostedService>();
services.AddHostedService<MessageManagerInitHostedService>();
services.AddHostedService<AutoTellTabsServiceInitHostedService>();
// Same internal-ctor pitfall as the singletons above - the adapter
// classes are `internal sealed` with primary constructors, so the
// direct AddHostedService<T>() overload's ActivatorUtilities fails.
services.AddHostedService(sp => new FontManagerInitHostedService(
sp.GetRequiredService<FontManager>()
));
services.AddHostedService(sp => new ThemeRegistryInitHostedService(
sp.GetRequiredService<ThemeRegistry>()
));
services.AddHostedService(sp => new IpcManagerInitHostedService(
sp.GetRequiredService<IpcManager>()
));
services.AddHostedService(sp => new TypingIpcInitHostedService(
sp.GetRequiredService<TypingIpc>()
));
services.AddHostedService(sp => new ExtraChatInitHostedService(
sp.GetRequiredService<ExtraChat>()
));
services.AddHostedService(sp => new MessageManagerInitHostedService(
sp.GetRequiredService<IDalamudPluginInterface>(),
sp.GetRequiredService<MessageManager>()
));
services.AddHostedService(sp => new AutoTellTabsServiceInitHostedService(
sp.GetRequiredService<AutoTellTabsService>()
));
}
}