Files
Anvil/Anvil/PluginHostFactory.cs
T
JonKazama-Hellion 186e02c3ee fix(plugin): register UiBuilder.OpenConfigUi / OpenMainUi stubs
Dalamud's plugin validator flagged two issues on v0.1.0:
- "The plugin does not register a config UI callback."
- "The plugin does not register a main UI callback."

v0.1.0 ships the RecipeData layer only - the actual SettingsWindow
and MainWindow arrive with module 08 (UI). To pass validation and to
give users a useful response when they click "Open Settings" or "Open
Main" in the plugin installer, both callbacks now fire toast
notifications via INotificationManager that point at the roadmap.

The handlers wire up in the constructor right after the host build
and unwire in DisposeAsync. INotificationManager joins the Block A
Dalamud services in PluginHostDependencies + PluginHostFactory so the
DI container also exposes it for later modules.
2026-05-27 22:59:01 +02:00

103 lines
4.2 KiB
C#

// Builds the generic-host DI container for Anvil. v0.1.0 is intentionally
// thin - module 01 (RecipeData) plus the bootstrap glue. Module 02+ will
// extend the Block B / Block C sections, not replace them.
//
// Every service registration goes through a factory lambda. The default
// AddSingleton<T>() overload reflects public constructors, and Anvil
// follows the Goatcorp / Dalamud convention of internal-sealed types -
// the activator would throw on the first resolve otherwise (HellionChat
// v1.5.0 C0 smoke documented this). The lambda compiles inside Anvil's
// namespace where internal access works.
using System.Collections.Generic;
using Anvil.Hosting;
using Anvil.Infrastructure.Logging;
using Anvil.RecipeData;
using Anvil.RecipeData.Internal;
using Anvil.SelfTest;
using Dalamud.Plugin;
using Dalamud.Plugin.SelfTest;
using Dalamud.Plugin.Services;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace Anvil;
internal static class PluginHostFactory
{
public static IHost Build(Plugin plugin, PluginHostDependencies dependencies)
{
return new HostBuilder()
.UseContentRoot(dependencies.PluginInterface.ConfigDirectory.FullName)
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.AddDalamudLogging(dependencies.PluginLog);
logging.SetMinimumLevel(LogLevel.Trace);
})
.ConfigureServices(services => ConfigureServices(services, plugin, dependencies))
.Build();
}
private static void ConfigureServices(
IServiceCollection services,
Plugin plugin,
PluginHostDependencies dependencies
)
{
// ---- Block A: Dalamud services ----
services.AddSingleton(dependencies);
services.AddSingleton(dependencies.PluginInterface);
services.AddSingleton(dependencies.PluginLog);
services.AddSingleton(dependencies.DataManager);
services.AddSingleton(dependencies.Framework);
services.AddSingleton(dependencies.SelfTestRegistry);
services.AddSingleton(dependencies.Notification);
// Self-reference. Lets services that genuinely need the plugin
// back-ref (later modules; v0.1.0 has none) reach it without a
// Plugin.Instance static.
services.AddSingleton(plugin);
services.AddSingleton<PluginLifecycle>(sp => new PluginLifecycle(
sp.GetRequiredService<IFramework>(),
sp.GetRequiredService<Plugin>()
));
// ---- Block B: Anvil singletons ----
services.AddSingleton<RecipeDataCatalog>(_ => new RecipeDataCatalog());
services.AddSingleton<LuminaRecipeAdapter>(sp => new LuminaRecipeAdapter(
sp.GetRequiredService<IDataManager>(),
sp.GetRequiredService<ILogger<LuminaRecipeAdapter>>(),
sp.GetRequiredService<RecipeDataCatalog>()
));
// ---- Block C: SelfTest steps (collected as IEnumerable<ISelfTestStep>) ----
services.AddSingleton<ISelfTestStep, RecipeDataAdapterLoadStep>(
sp => new RecipeDataAdapterLoadStep(
sp.GetRequiredService<RecipeDataCatalog>(),
sp.GetRequiredService<ILogger<RecipeDataAdapterLoadStep>>()
)
);
// ---- Block D: Hosted services (init order is registration order) ----
// RecipeData adapter runs first so the catalog is populated before
// the self-test registry sees its steps.
services.AddHostedService<RecipeDataLoadHostedService>(
sp => new RecipeDataLoadHostedService(
sp.GetRequiredService<IFramework>(),
sp.GetRequiredService<LuminaRecipeAdapter>(),
sp.GetRequiredService<ILogger<RecipeDataLoadHostedService>>()
)
);
services.AddHostedService<SelfTestRegistrationHostedService>(
sp => new SelfTestRegistrationHostedService(
sp.GetRequiredService<ISelfTestRegistry>(),
sp.GetRequiredService<IEnumerable<ISelfTestStep>>(),
sp.GetRequiredService<ILogger<SelfTestRegistrationHostedService>>()
)
);
}
}