using System.Diagnostics;
using System.Text.Json;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Navigation;
namespace XFEEDToolBox.Core.Native;
public sealed class NativeCurrentSystemSource(int processId,string gameSha256,Func<PlayerState> player,
Func<CurrentSystemSnapshot,IReadOnlyList<SystemOrbit>>? orbitProvider=null):ICurrentSystemLocationSource,IDisposable
{
private readonly NavigationPipeClient pipe=new(processId);
private readonly SemaphoreSlim gate=new(1,1);
private ReadOnlyPlayerMemory? memory;
public async Task<CurrentSystemSnapshot> ReadLocationsAsync(CancellationToken token=default)
{
await gate.WaitAsync(token);
try
{
if(gameSha256!=NativeSystemLocationReader.GameSha256)throw new NotSupportedException("当前游戏构建尚未支持星系内地点读取。");
// A docked main ship can stop producing the flight-physics frame
// while the station services remain open. Its independent cockpit
// identity still brackets the read-only catalogue; this does not
// authorize flight controls without the normal live flight frame.
var initial=player();var docked=initial.Docked;var operation=docked?"health.read":"read";
var before=await pipe.CallAsync(new{op=operation},token);
var at=DateTimeOffset.FromUnixTimeMilliseconds(before.GetProperty("observedAtUnixMilliseconds").GetInt64());
var entity=ulong.Parse(before.GetProperty("entityId").GetString()!,System.Globalization.CultureInfo.InvariantCulture);
var now=DateTimeOffset.UtcNow;
if(!initial.InGame||!docked&&!before.GetProperty("inGame").GetBoolean()||!before.GetProperty("localPlayerShipVerified").GetBoolean()
||entity==0||before.GetProperty("sequence").GetInt64()<=0||at>now.AddMilliseconds(100)||now-at>TimeSpan.FromMilliseconds(500))
throw new FlightTelemetryUnavailableException("等待驾驶舱当前主船的最新飞行数据。");
var snapshot=await Task.Run(()=>
{
using var process=Process.GetProcessById(processId);
memory??=new(processId,process.StartTime.ToUniversalTime().Ticks,gameSha256);
memory.Validate();
return NativeSystemLocationReader.Read(memory,entity,before.GetProperty("sequence").GetInt64(),at,initial.SystemName);
},token);
var after=await pipe.CallAsync(new{op=operation},token);var latest=player();
var afterAt=DateTimeOffset.FromUnixTimeMilliseconds(after.GetProperty("observedAtUnixMilliseconds").GetInt64());now=DateTimeOffset.UtcNow;
if(!latest.InGame||!docked&&!after.GetProperty("inGame").GetBoolean()||!after.GetProperty("localPlayerShipVerified").GetBoolean()
||after.GetProperty("sequence").GetInt64()<before.GetProperty("sequence").GetInt64()||afterAt<at
||afterAt>now.AddMilliseconds(100)||now-afterAt>TimeSpan.FromMilliseconds(500))
throw new FlightTelemetryUnavailableException("正在恢复当前主船的地点观测。");
if(after.GetProperty("entityId").GetString()!=entity.ToString(System.Globalization.CultureInfo.InvariantCulture)||!docked&&after.GetProperty("referenceWorldAddress").GetString()!=before.GetProperty("referenceWorldAddress").GetString()
||initial.Docked!=latest.Docked||initial.Navigation.CurrentSystemAddress!=snapshot.SystemAddress
||initial.CommanderId!=latest.CommanderId||initial.CurrentShipId!=latest.CurrentShipId||initial.LoadGameAt!=latest.LoadGameAt||latest.Navigation.CurrentSystemAddress!=snapshot.SystemAddress)
throw new FlightTelemetryUnavailableException("当前飞船或星系正在切换,正在重新读取。",recovery:FlightRecovery.ReselectTarget);
if(orbitProvider is null)return snapshot;
var native=snapshot.Orbits.Where(orbit=>orbit.Quality==SystemOrbitQuality.NativeVerified).ToArray();
var verified=native.Select(orbit=>orbit.ChildIdentity).ToHashSet(StringComparer.Ordinal);
return snapshot with{Orbits=native.Concat(orbitProvider(snapshot).Where(orbit=>!verified.Contains(orbit.ChildIdentity))).ToArray()};
}
catch(InvalidDataException error)when(error.Message is "当前星系地点正在变化。" or "当前星系或飞船身份正在变化。" or "天体变换正在更新。" or "等待当前星系天体资源。" or "等待当前飞船服务。")
{
memory?.Dispose();memory=null;
throw new FlightTelemetryUnavailableException("当前星系地点与坐标正在更新,正在重新读取。",error,FlightRecovery.ReselectTarget);
}
catch(System.ComponentModel.Win32Exception error)when(error.NativeErrorCode is 299 or 487)
{
memory?.Dispose();memory=null;
throw new FlightTelemetryUnavailableException("当前星系对象正在切换,正在重新读取。",error,FlightRecovery.ReselectTarget);
}
catch{memory?.Dispose();memory=null;throw;}
finally{gate.Release();}
}
public void Dispose(){memory?.Dispose();memory=null;}
}
using System.Diagnostics;
using System.Text.Json;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Navigation;
namespace XFEEDToolBox.Core.Native;
public sealed class NativeCurrentSystemSource(int processId,string gameSha256,Func<PlayerState> player,
Func<CurrentSystemSnapshot,IReadOnlyList<SystemOrbit>>? orbitProvider=null):ICurrentSystemLocationSource,IDisposable
{
private readonly NavigationPipeClient pipe=new(processId);
private readonly SemaphoreSlim gate=new(1,1);
private ReadOnlyPlayerMemory? memory;
public async Task<CurrentSystemSnapshot> ReadLocationsAsync(CancellationToken token=default)
{
await gate.WaitAsync(token);
try
{
if(gameSha256!=NativeSystemLocationReader.GameSha256)throw new NotSupportedException("当前游戏构建尚未支持星系内地点读取。");
// A docked main ship can stop producing the flight-physics frame
// while the station services remain open. Its independent cockpit
// identity still brackets the read-only catalogue; this does not
// authorize flight controls without the normal live flight frame.
var initial=player();var docked=initial.Docked;var operation=docked?"health.read":"read";
var before=await pipe.CallAsync(new{op=operation},token);
var at=DateTimeOffset.FromUnixTimeMilliseconds(before.GetProperty("observedAtUnixMilliseconds").GetInt64());
var entity=ulong.Parse(before.GetProperty("entityId").GetString()!,System.Globalization.CultureInfo.InvariantCulture);
var now=DateTimeOffset.UtcNow;
if(!initial.InGame||!docked&&!before.GetProperty("inGame").GetBoolean()||!before.GetProperty("localPlayerShipVerified").GetBoolean()
||entity==0||before.GetProperty("sequence").GetInt64()<=0||at>now.AddMilliseconds(100)||now-at>TimeSpan.FromMilliseconds(500))
throw new FlightTelemetryUnavailableException("等待驾驶舱当前主船的最新飞行数据。");
var snapshot=await Task.Run(()=>
{
using var process=Process.GetProcessById(processId);
memory??=new(processId,process.StartTime.ToUniversalTime().Ticks,gameSha256);
memory.Validate();
return NativeSystemLocationReader.Read(memory,entity,before.GetProperty("sequence").GetInt64(),at,initial.SystemName);
},token);
var after=await pipe.CallAsync(new{op=operation},token);var latest=player();
var afterAt=DateTimeOffset.FromUnixTimeMilliseconds(after.GetProperty("observedAtUnixMilliseconds").GetInt64());now=DateTimeOffset.UtcNow;
if(!latest.InGame||!docked&&!after.GetProperty("inGame").GetBoolean()||!after.GetProperty("localPlayerShipVerified").GetBoolean()
||after.GetProperty("sequence").GetInt64()<before.GetProperty("sequence").GetInt64()||afterAt<at
||afterAt>now.AddMilliseconds(100)||now-afterAt>TimeSpan.FromMilliseconds(500))
throw new FlightTelemetryUnavailableException("正在恢复当前主船的地点观测。");
if(after.GetProperty("entityId").GetString()!=entity.ToString(System.Globalization.CultureInfo.InvariantCulture)||!docked&&after.GetProperty("referenceWorldAddress").GetString()!=before.GetProperty("referenceWorldAddress").GetString()
||initial.Docked!=latest.Docked||initial.Navigation.CurrentSystemAddress!=snapshot.SystemAddress
||initial.CommanderId!=latest.CommanderId||initial.CurrentShipId!=latest.CurrentShipId||initial.LoadGameAt!=latest.LoadGameAt||latest.Navigation.CurrentSystemAddress!=snapshot.SystemAddress)
throw new FlightTelemetryUnavailableException("当前飞船或星系正在切换,正在重新读取。",recovery:FlightRecovery.ReselectTarget);
if(orbitProvider is null)return snapshot;
var native=snapshot.Orbits.Where(orbit=>orbit.Quality==SystemOrbitQuality.NativeVerified).ToArray();
var verified=native.Select(orbit=>orbit.ChildIdentity).ToHashSet(StringComparer.Ordinal);
return snapshot with{Orbits=native.Concat(orbitProvider(snapshot).Where(orbit=>!verified.Contains(orbit.ChildIdentity))).ToArray()};
}
catch(InvalidDataException error)when(error.Message is "当前星系地点正在变化。" or "当前星系或飞船身份正在变化。" or "天体变换正在更新。" or "等待当前星系天体资源。" or "等待当前飞船服务。")
{
memory?.Dispose();memory=null;
throw new FlightTelemetryUnavailableException("当前星系地点与坐标正在更新,正在重新读取。",error,FlightRecovery.ReselectTarget);
}
catch(System.ComponentModel.Win32Exception error)when(error.NativeErrorCode is 299 or 487)
{
memory?.Dispose();memory=null;
throw new FlightTelemetryUnavailableException("当前星系对象正在切换,正在重新读取。",error,FlightRecovery.ReselectTarget);
}
catch{memory?.Dispose();memory=null;throw;}
finally{gate.Release();}
}
public void Dispose(){memory?.Dispose();memory=null;}
}