2026-06-17 10:39:28 +01:00
using System ;
2026-06-13 11:44:01 +01:00
using System.IO ;
2026-06-17 12:14:16 +01:00
using System.Linq ;
2026-06-13 11:44:01 +01:00
2026-06-21 09:54:15 +01:00
namespace GameCult.Aetheria.State.Verse
2026-06-13 11:44:01 +01:00
{
public readonly struct AetheriaRuntimeStateBootReport
{
2026-06-17 10:39:28 +01:00
public AetheriaRuntimeStateBootReport (
string clientTargetPath ,
string targetKind ,
string targetSource ,
string title ,
string verseId ,
2026-06-25 18:09:33 +01:00
string runtimeId ,
2026-06-17 10:39:28 +01:00
string cultMeshAddress ,
string stateFilePath ,
2026-06-17 13:51:36 +01:00
string replicaStateFilePath ,
2026-06-17 10:39:28 +01:00
bool stateFileExists ,
bool supportsLocalStateFileRead ,
2026-06-17 12:14:16 +01:00
string failureMessage ,
string [ ] discoveryEndpoints ,
AetheriaRuntimeDiscoveredVerse [ ] discoveredVerses ,
string lastDiscoveryAtUtc ,
2026-06-17 14:29:41 +01:00
string lastDiscoveryError ,
string lastReplicaSyncAtUtc ,
string lastReplicaSyncError )
2026-06-13 11:44:01 +01:00
{
2026-06-17 10:39:28 +01:00
ClientTargetPath = clientTargetPath ;
TargetKind = targetKind ;
TargetSource = targetSource ;
Title = title ;
VerseId = verseId ;
2026-06-25 18:09:33 +01:00
RuntimeId = runtimeId ;
2026-06-17 10:39:28 +01:00
CultMeshAddress = cultMeshAddress ;
2026-06-13 11:44:01 +01:00
StateFilePath = stateFilePath ;
2026-06-17 13:51:36 +01:00
ReplicaStateFilePath = replicaStateFilePath ;
2026-06-13 11:44:01 +01:00
StateFileExists = stateFileExists ;
2026-06-17 10:39:28 +01:00
SupportsLocalStateFileRead = supportsLocalStateFileRead ;
FailureMessage = failureMessage ;
2026-06-17 12:14:16 +01:00
DiscoveryEndpoints = discoveryEndpoints ? ? Array . Empty < string > ( ) ;
DiscoveredVerses = discoveredVerses ? ? Array . Empty < AetheriaRuntimeDiscoveredVerse > ( ) ;
LastDiscoveryAtUtc = lastDiscoveryAtUtc ? ? "" ;
LastDiscoveryError = lastDiscoveryError ? ? "" ;
2026-06-17 14:29:41 +01:00
LastReplicaSyncAtUtc = lastReplicaSyncAtUtc ? ? "" ;
LastReplicaSyncError = lastReplicaSyncError ? ? "" ;
2026-06-13 11:44:01 +01:00
}
2026-06-17 10:39:28 +01:00
public string ClientTargetPath { get ; }
public string TargetKind { get ; }
public string TargetSource { get ; }
public string Title { get ; }
public string VerseId { get ; }
2026-06-25 18:09:33 +01:00
public string RuntimeId { get ; }
2026-06-17 10:39:28 +01:00
public string CultMeshAddress { get ; }
2026-06-13 11:44:01 +01:00
public string StateFilePath { get ; }
2026-06-17 13:51:36 +01:00
public string ReplicaStateFilePath { get ; }
2026-06-13 11:44:01 +01:00
public bool StateFileExists { get ; }
2026-06-17 10:39:28 +01:00
public bool SupportsLocalStateFileRead { get ; }
public string FailureMessage { get ; }
2026-06-17 12:14:16 +01:00
public string [ ] DiscoveryEndpoints { get ; }
public AetheriaRuntimeDiscoveredVerse [ ] DiscoveredVerses { get ; }
public string LastDiscoveryAtUtc { get ; }
public string LastDiscoveryError { get ; }
2026-06-17 14:29:41 +01:00
public string LastReplicaSyncAtUtc { get ; }
public string LastReplicaSyncError { get ; }
2026-06-17 10:39:28 +01:00
public string TargetLabel = >
string . IsNullOrWhiteSpace ( Title )
? ( string . IsNullOrWhiteSpace ( VerseId ) ? "Unknown Verse" : VerseId )
: ( string . IsNullOrWhiteSpace ( VerseId ) | | string . Equals ( Title , VerseId , StringComparison . Ordinal )
? Title
: $"{Title} ({VerseId})" ) ;
2026-06-13 11:44:01 +01:00
}
public static class AetheriaRuntimeStateBoot
{
2026-06-17 10:39:28 +01:00
public static AetheriaRuntimeStateBootReport Inspect (
DirectoryInfo gameDataDirectory ,
string explicitStateFilePathOverride = "" )
2026-06-13 11:44:01 +01:00
{
2026-06-17 10:39:28 +01:00
if ( gameDataDirectory = = null ) throw new ArgumentNullException ( nameof ( gameDataDirectory ) ) ;
var defaultStateFilePath = AetheriaRuntimeStateBoundary . GetStateFilePath ( gameDataDirectory ) ;
var clientTargetPath = AetheriaRuntimeStateBoundary . GetClientTargetPath ( gameDataDirectory ) ;
var target = AetheriaRuntimeClientTargetStore . ReadOrInitialize ( clientTargetPath , defaultStateFilePath ) ;
var configuredOverride = string . IsNullOrWhiteSpace ( explicitStateFilePathOverride )
? AetheriaRuntimeStateBoundary . ResolveStatePathOverride ( )
: explicitStateFilePathOverride ;
var targetSource = string . IsNullOrWhiteSpace ( configuredOverride )
? "client-target"
: "state-path-override" ;
var targetKind = target . TargetKind ? ? "" ;
var targetTitle = target . Title ? ? "" ;
var targetVerseId = target . VerseId ? ? "" ;
2026-06-25 18:09:33 +01:00
var targetRuntimeId = string . IsNullOrWhiteSpace ( AetheriaRuntimeStateBoundary . ResolveRuntimeIdOverride ( ) )
? target . RuntimeId ? ? ""
: AetheriaRuntimeStateBoundary . ResolveRuntimeIdOverride ( ) ;
2026-06-17 10:39:28 +01:00
var targetCultMeshAddress = target . CultMeshAddress ? ? "" ;
var supportsLocalStateFileRead = true ;
var failureMessage = "" ;
string stateFilePath ;
2026-06-17 13:51:36 +01:00
string replicaStateFilePath ;
2026-06-17 10:39:28 +01:00
if ( ! string . IsNullOrWhiteSpace ( configuredOverride ) )
{
stateFilePath = Path . GetFullPath ( configuredOverride ) ;
2026-06-17 13:51:36 +01:00
replicaStateFilePath = target . ReplicaStateFilePath ? ? "" ;
2026-06-17 10:39:28 +01:00
}
else if ( string . Equals ( targetKind , AetheriaRuntimeClientTargetKinds . CultMeshVerse , StringComparison . Ordinal ) )
{
2026-06-17 13:51:36 +01:00
replicaStateFilePath = string . IsNullOrWhiteSpace ( target . ReplicaStateFilePath )
? AetheriaRuntimeStateBoundary . GetReplicaStateFilePath ( gameDataDirectory , targetVerseId )
: Path . GetFullPath ( target . ReplicaStateFilePath ) ;
stateFilePath = replicaStateFilePath ;
supportsLocalStateFileRead = File . Exists ( replicaStateFilePath ) ;
failureMessage = supportsLocalStateFileRead
? ""
: $"Selected client Verse target '{BuildTargetLabel(targetTitle, targetVerseId)}' follows remote CultMesh endpoint '{targetCultMeshAddress}'. Sync the local replica at '{replicaStateFilePath}' before booting Unity from that Verse." ;
2026-06-17 10:39:28 +01:00
}
else
{
var configuredStateFilePath = string . IsNullOrWhiteSpace ( target . StateFilePath )
? defaultStateFilePath
: target . StateFilePath ;
stateFilePath = Path . GetFullPath ( configuredStateFilePath ) ;
2026-06-17 13:51:36 +01:00
replicaStateFilePath = string . IsNullOrWhiteSpace ( target . ReplicaStateFilePath )
? AetheriaRuntimeStateBoundary . GetReplicaStateFilePath ( gameDataDirectory , targetVerseId )
: Path . GetFullPath ( target . ReplicaStateFilePath ) ;
2026-06-17 10:39:28 +01:00
}
2026-06-13 11:44:01 +01:00
return new AetheriaRuntimeStateBootReport (
2026-06-17 10:39:28 +01:00
clientTargetPath ,
targetKind ,
targetSource ,
targetTitle ,
targetVerseId ,
2026-07-05 20:57:22 +01:00
string . IsNullOrWhiteSpace ( targetRuntimeId ) ? AetheriaRuntimeStateBoundary . DefaultClientRuntimeId : targetRuntimeId ,
2026-06-17 10:39:28 +01:00
targetCultMeshAddress ,
2026-06-13 11:44:01 +01:00
stateFilePath ,
2026-06-17 13:51:36 +01:00
replicaStateFilePath ,
2026-06-17 10:39:28 +01:00
supportsLocalStateFileRead & & File . Exists ( stateFilePath ) ,
supportsLocalStateFileRead ,
2026-06-17 12:14:16 +01:00
failureMessage ,
target . DiscoveryEndpoints ? . Where ( value = > ! string . IsNullOrWhiteSpace ( value ) ) . ToArray ( ) ? ? Array . Empty < string > ( ) ,
target . DiscoveredVerses ? ? Array . Empty < AetheriaRuntimeDiscoveredVerse > ( ) ,
target . LastDiscoveryAtUtc ? ? "" ,
2026-06-17 14:29:41 +01:00
target . LastDiscoveryError ? ? "" ,
target . LastReplicaSyncAtUtc ? ? "" ,
target . LastReplicaSyncError ? ? "" ) ;
2026-06-17 10:39:28 +01:00
}
private static string BuildTargetLabel ( string title , string verseId )
{
if ( string . IsNullOrWhiteSpace ( title ) )
return string . IsNullOrWhiteSpace ( verseId ) ? "Unknown Verse" : verseId ;
return string . IsNullOrWhiteSpace ( verseId ) | | string . Equals ( title , verseId , StringComparison . Ordinal )
? title
: $"{title} ({verseId})" ;
2026-06-13 11:44:01 +01:00
}
}
}