using System.Text.Json;
namespace XFEEDToolBox.Core.Navigation;
/// <summary>A fresh original save transition is distinct from a pause or a missing flight sample.</summary>
public static class SavingSceneRecovery
{
public static bool Matches(JsonElement frame,int processId,string gameHash,DateTimeOffset now)
{
try
{
return processId>0&&frame.GetProperty("available").ValueKind==JsonValueKind.True
&&frame.GetProperty("processId").GetInt32()==processId&&frame.GetProperty("gameSha256").GetString()==gameHash
&&frame.GetProperty("state").GetString()=="SaveGameOnEnterLocation"&&frame.GetProperty("nextState").GetString()=="InGame"
&&now.ToUnixTimeMilliseconds()-frame.GetProperty("observedAtUnixMilliseconds").GetInt64() is >=0 and <=500;
}
catch(Exception error)when(error is KeyNotFoundException or InvalidOperationException or FormatException or OverflowException){return false;}
}
public static async Task<bool> ReadAsync(int processId,string gameHash,CancellationToken token)
{
try
{
// A fresh token has never acquired ownership. This returns only the
// service description; it cannot end another owner's session.
var frame=await new NavigationPipeClient(processId).CallAsync(new{op="session.end",owner=Guid.NewGuid().ToString("N")},token);
return Matches(frame,processId,gameHash,DateTimeOffset.UtcNow);
}
catch(Exception error)when(!token.IsCancellationRequested&&error is IOException or InvalidOperationException or OperationCanceledException){return false;}
}
}
public sealed class SavingSceneRecoveryRequiredException(Exception inner):InvalidOperationException("原版抵达位置保存尚未完成,需要核对场景后恢复原航程。",inner)
{
public static bool Present(Exception error)=>error is SavingSceneRecoveryRequiredException||error.InnerException is {} inner&&Present(inner);
}
using System.Text.Json;
namespace XFEEDToolBox.Core.Navigation;
/// <summary>A fresh original save transition is distinct from a pause or a missing flight sample.</summary>
public static class SavingSceneRecovery
{
public static bool Matches(JsonElement frame,int processId,string gameHash,DateTimeOffset now)
{
try
{
return processId>0&&frame.GetProperty("available").ValueKind==JsonValueKind.True
&&frame.GetProperty("processId").GetInt32()==processId&&frame.GetProperty("gameSha256").GetString()==gameHash
&&frame.GetProperty("state").GetString()=="SaveGameOnEnterLocation"&&frame.GetProperty("nextState").GetString()=="InGame"
&&now.ToUnixTimeMilliseconds()-frame.GetProperty("observedAtUnixMilliseconds").GetInt64() is >=0 and <=500;
}
catch(Exception error)when(error is KeyNotFoundException or InvalidOperationException or FormatException or OverflowException){return false;}
}
public static async Task<bool> ReadAsync(int processId,string gameHash,CancellationToken token)
{
try
{
// A fresh token has never acquired ownership. This returns only the
// service description; it cannot end another owner's session.
var frame=await new NavigationPipeClient(processId).CallAsync(new{op="session.end",owner=Guid.NewGuid().ToString("N")},token);
return Matches(frame,processId,gameHash,DateTimeOffset.UtcNow);
}
catch(Exception error)when(!token.IsCancellationRequested&&error is IOException or InvalidOperationException or OperationCanceledException){return false;}
}
}
public sealed class SavingSceneRecoveryRequiredException(Exception inner):InvalidOperationException("原版抵达位置保存尚未完成,需要核对场景后恢复原航程。",inner)
{
public static bool Present(Exception error)=>error is SavingSceneRecoveryRequiredException||error.InnerException is {} inner&&Present(inner);
}