feat(launcher): add verified first-run installer

This commit is contained in:
Erik 2026-08-14 20:06:37 +02:00
parent 60f627998c
commit ff6ebb6a6a
28 changed files with 3259 additions and 125 deletions

View file

@ -1,3 +1,4 @@
using AcDream.Launcher.Core.Installation;
using AcDream.Launcher.Core.Launching;
using AcDream.Launcher.Core.Orchestration;
using AcDream.Launcher.Core.Profiles;
@ -22,15 +23,40 @@ public sealed partial class App : Application
{
ApplicationPathSet paths = ApplicationPathSet.Resolve();
LauncherProfileStore profiles = LauncherProfileStore.ForApplicationPaths(paths);
LauncherInstallRecord? install = ResolveDevelopmentInstallRecord();
string executableSuffix = OperatingSystem.IsWindows() ? ".exe" : string.Empty;
var installer = new LauncherInstaller(
paths,
Path.Combine(
AppContext.BaseDirectory,
"acdream-bake" + executableSuffix));
InstallRecordVerification verification;
try
{
// Hashing the package before constructing the orchestrator is
// intentional: no launch action is enabled until the persisted
// size/SHA/tool-version record has been verified.
verification = installer.LoadExistingAsync()
.GetAwaiter()
.GetResult();
}
catch (Exception ex)
{
verification = new InstallRecordVerification(
InstallRecordVerificationState.Invalid,
null,
$"Client content verification failed: {ex.Message}");
}
_orchestrator = new LauncherOrchestrator(
profiles,
paths,
LauncherExecutableSet.FromDirectory(AppContext.BaseDirectory),
install);
verification.Record,
installationStatus: verification.Status);
_viewModel = new LauncherWindowViewModel(
_orchestrator,
new AvaloniaUiDispatcher());
new AvaloniaUiDispatcher(),
installer);
_viewModel.Initialize();
desktop.MainWindow = new MainWindow
@ -43,19 +69,6 @@ public sealed partial class App : Application
base.OnFrameworkInitializationCompleted();
}
private static LauncherInstallRecord? ResolveDevelopmentInstallRecord()
{
// LA9 owns persisted install discovery. LA4 accepts the existing
// developer environment pair at this one composition root so the
// launch/probe UI can be exercised before the first-run body lands.
string? datDirectory = Environment.GetEnvironmentVariable("ACDREAM_DAT_DIR");
string? preparedAssetPath = Environment.GetEnvironmentVariable("ACDREAM_PAK_PATH");
return !string.IsNullOrWhiteSpace(datDirectory)
&& !string.IsNullOrWhiteSpace(preparedAssetPath)
? new LauncherInstallRecord(datDirectory, preparedAssetPath)
: null;
}
private void OnDesktopExit(object? sender, ControlledApplicationLifetimeExitEventArgs e)
{
_viewModel?.Dispose();