Aetheria/Assets/Scripts/Gameplay/AetheriaUnityGameplayBootShell.cs
2026-06-28 01:58:58 +01:00

119 lines
5.3 KiB
C#

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
using System;
using GameCult.Aetheria.State.Verse;
public sealed class AetheriaUnityGameplayBootShell
{
public GameSettings Settings { get; set; }
public ZoneRenderer ZoneRenderer { get; set; }
public AetheriaUnityCockpitHudShell CockpitHudShell { get; set; }
public float TargetSpottedBlinkFrequency { get; set; }
public float TargetSpottedBlinkOffset { get; set; }
public Action<string> Log { get; set; }
public AetheriaUnityGameplayBootResult Boot()
{
var stateBoot = AetheriaRuntimeStateBoot.Inspect(AetheriaUnityRuntimePaths.GameDataDirectory);
Log?.Invoke($"Aetheria runtime target: {stateBoot.TargetLabel} via {stateBoot.TargetKind} ({stateBoot.TargetSource})");
Log?.Invoke($"Aetheria runtime id: {stateBoot.RuntimeId}");
Log?.Invoke($"Aetheria runtime state file: {stateBoot.StateFilePath}");
stateBoot = SyncRemoteReplicaBeforeBoot(stateBoot);
if (!stateBoot.SupportsLocalStateFileRead)
{
throw new InvalidOperationException(
$"Aetheria runtime target cannot read the daemon mirror state required for gameplay boot: {stateBoot.FailureMessage}");
}
if (!stateBoot.StateFileExists)
{
throw new InvalidOperationException(
$"Aetheria runtime state file is missing at {stateBoot.StateFilePath}; gameplay requires an authoritative daemon mirror.");
}
var runtimeCatalog = AetheriaUnityRuntimeClientProvider.ResolveClient(stateBoot.StateFilePath, stateBoot.RuntimeId)
.Aetheria()
.Catalog
.Latest();
if (runtimeCatalog == null)
throw new InvalidOperationException("Aetheria typed runtime catalog is required before gameplay boot.");
Log?.Invoke($"Aetheria runtime catalog: {runtimeCatalog.Items.Count} items, {runtimeCatalog.Corporations.Count} corporations, {runtimeCatalog.NameFiles.Count} name files");
var itemManager = new ItemManager(
runtimeCatalog,
Settings.GameplaySettings,
message => Log?.Invoke(message));
var loadoutItemProjector = new AetheriaUnityLoadoutItemProjector(itemManager, runtimeCatalog);
ZoneRenderer.SetDroppedPickupItemProjector(loadoutItemProjector.CreateLoadoutItem);
ZoneRenderer.BodySettingsCollections = Settings.BodySettingsCollections;
ZoneRenderer.RenderSettings = AetheriaUnityRenderSettingsBridge.Build(
Settings,
TargetSpottedBlinkFrequency,
TargetSpottedBlinkOffset);
CockpitHudShell.SetRenderSettings(ZoneRenderer.RenderSettings);
if (!AetheriaUnityRuntimeClientProvider.PlayerSettings.GraphicsSettings.ShowAsteroidsInMinimap)
ZoneRenderer.ShowAsteroidUI = false;
return new AetheriaUnityGameplayBootResult(
runtimeCatalog,
itemManager,
loadoutItemProjector);
}
private AetheriaRuntimeStateBootReport SyncRemoteReplicaBeforeBoot(AetheriaRuntimeStateBootReport stateBoot)
{
if (!ShouldSyncRemoteReplicaBeforeBoot(stateBoot))
return stateBoot;
Log?.Invoke($"Aetheria remote Verse replica missing; syncing {stateBoot.TargetLabel} from {stateBoot.CultMeshAddress}");
try
{
var target = AetheriaState.At(AetheriaUnityRuntimePaths.GameDataDirectory)
.ClientTarget
.SyncReplica();
AetheriaUnityRuntimePaths.ClearRuntimeStateFilePathCache();
var refreshed = AetheriaRuntimeStateBoot.Inspect(AetheriaUnityRuntimePaths.GameDataDirectory);
Log?.Invoke($"Aetheria remote Verse replica synced: {refreshed.StateFilePath}");
if (!string.IsNullOrWhiteSpace(target.LastReplicaSyncAtUtc))
Log?.Invoke($"Aetheria remote Verse replica sync time: {target.LastReplicaSyncAtUtc}");
return refreshed;
}
catch (Exception ex)
{
AetheriaUnityRuntimePaths.ClearRuntimeStateFilePathCache();
throw new InvalidOperationException(
$"Aetheria remote Verse replica sync failed before gameplay boot: {ex.Message}",
ex);
}
}
private static bool ShouldSyncRemoteReplicaBeforeBoot(AetheriaRuntimeStateBootReport stateBoot)
{
return string.Equals(stateBoot.TargetSource, "client-target", StringComparison.Ordinal)
&& string.Equals(stateBoot.TargetKind, AetheriaRuntimeClientTargetKinds.CultMeshVerse, StringComparison.Ordinal)
&& (!stateBoot.SupportsLocalStateFileRead || !stateBoot.StateFileExists);
}
}
public readonly struct AetheriaUnityGameplayBootResult
{
public AetheriaUnityGameplayBootResult(
AetheriaRuntimeCatalogSnapshot runtimeCatalog,
ItemManager itemManager,
AetheriaUnityLoadoutItemProjector loadoutItemProjector)
{
RuntimeCatalog = runtimeCatalog;
ItemManager = itemManager;
LoadoutItemProjector = loadoutItemProjector;
}
public AetheriaRuntimeCatalogSnapshot RuntimeCatalog { get; }
public ItemManager ItemManager { get; }
public AetheriaUnityLoadoutItemProjector LoadoutItemProjector { get; }
}