XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
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;}
}