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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Text.Json;
using XFEEDToolBox.Core.Data;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Native;
using XFEEDToolBox.Core.Navigation;

internal static class LocalDepartureAcceptance
{
    public static async Task RunAsync(int pid,string exactName,string reportPath,CancellationToken externalToken=default,bool stopAfterDeparture=true)
    {
        if(File.Exists(reportPath)||File.Exists(reportPath+".jsonl"))throw new IOException("Use a new report path.");
        var settings=ToolboxSettings.Load(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox","settings.json"));
        await using var journal=new JournalMonitor(settings.JournalDirectory);await journal.StartAsync();var initial=journal.State;var pipe=new NavigationPipeClient(pid);
        var hash=(await pipe.CallAsync(new{op="describe"})).GetProperty("gameSha256").GetString()!;
        using var source=new NativeCurrentSystemSource(pid,hash,()=>journal.State);var catalogue=await source.ReadLocationsAsync();var row=catalogue.Locations.Single(x=>x.Name==exactName);
        if(!initial.Docked||initial.MarketId==row.MarketId||!NavigationAssists.From(initial).AdvancedDocking)throw new InvalidOperationException("Departure acceptance requires a different destination and an enabled advanced docking computer.");
        var target=new SystemNavigationTarget(catalogue.SystemAddress,row.WorldAddress,row.Name,SystemTargetKind.Station,row.MarketId){NativeKey=row.Key,NativeKind=row.NativeKind,NativeIdentity=row.Identity};
        await using var protection=await FlightTestProtectionScope.ArmAsync(pid);
        var driver=new NativeSystemFlightControlDriver(pid,hash,()=>journal.State,new NativeGameInputChannel(pid,hash,Path.GetFullPath("XFEEDToolBox/PluginPackage")));
        using var stop=CancellationTokenSource.CreateLinkedTokenSource(externalToken);stop.CancelAfter(TimeSpan.FromMinutes(7));var start=DateTimeOffset.UtcNow;var engaged=false;var prepared=false;var released=false;double maxHeat=0;string? failure=null;
        Task? preparation=null;SystemFlightFrame? final=null;
        await using var log=new StreamWriter(new FileStream(reportPath+".jsonl",FileMode.CreateNew,FileAccess.Write,FileShare.Read));
        try
        {
            await driver.SelectAsync(target,stop.Token);preparation=driver.PrepareAsync(stop.Token);
            while(!preparation.IsCompleted)
            {
                stop.Token.ThrowIfCancellationRequested();var state=journal.State;
                if(state.CurrentShipId!=initial.CurrentShipId||state.CommanderId!=initial.CommanderId||state.LoadGameAt!=initial.LoadGameAt||state.CargoCount!=initial.CargoCount||NavigationSafety.HasExternalThreat(state))
                    throw new InvalidOperationException("Departure session, ship or reserved cargo changed.");
                try
                {
                    var raw=await pipe.CallAsync(new{op="read"},stop.Token);var local=await pipe.CallAsync(new{op="local.read"},stop.Token);
                    maxHeat=Math.Max(maxHeat,raw.GetProperty("heatPercent").GetDouble());if(maxHeat>=75)throw new InvalidOperationException("Departure heat bound exceeded.");
                    engaged|=local.GetProperty("docking").GetProperty("nativeLaunchActive").GetBoolean();
                    await log.WriteLineAsync(JsonSerializer.Serialize(new{at=DateTimeOffset.UtcNow,state.Docked,raw,local,engaged}));await log.FlushAsync();
                }
                catch(Exception error)when(NavigationRecovery.CanRetry(error,stop.Token))
                {await log.WriteLineAsync(JsonSerializer.Serialize(new{at=DateTimeOffset.UtcNow,recovering=error.Message}));await log.FlushAsync();}
                await Task.Delay(80,stop.Token);
            }
            await preparation;prepared=true;
            while(true)
            {
                final=await driver.ReadSystemFlightAsync(stop.Token);
                if(final.PlayerOverride||final.ExternalThreat||!target.SameIdentity(final.SelectedTarget!))throw new InvalidOperationException("Departure did not retain the requested destination.");
                if(!stopAfterDeparture)break;
                await driver.ApplySystemFlightAsync(new(SystemNavigationPhase.Waiting,"完成离场并停车",null,0),final,target,final.ObservedAt.AddMilliseconds(250),stop.Token);
                if(final.Speed<1)break;await Task.Delay(80,stop.Token);
            }
            if(!engaged||journal.State.Docked||journal.State.Navigation.UndockedAt<start.AddSeconds(-1)||!final.FsdReady)throw new InvalidOperationException("Departure did not prove native launch, real undock and release from mass lock.");
        }
        catch(Exception error){failure=error.ToString();}
        finally
        {
            stop.Cancel();if(preparation is not null)try{await preparation;}catch{}
            try{await driver.ReleaseSystemFlightAsync();released=true;}catch(Exception error){failure=(failure??"")+" Release: "+error.Message;}
        }
        var passed=prepared&&engaged&&released&&failure is null;
        await File.WriteAllTextAsync(reportPath,JsonSerializer.Serialize(new{pid,start,finished=DateTimeOffset.UtcNow,passed,prepared,engaged,released,maxHeat,final,failure,stopAfterDeparture,tradeOrdersSent=0},ToolboxSettings.JsonOptions));
        Console.WriteLine(JsonSerializer.Serialize(new{passed,prepared,engaged,released,maxHeat,failure}));
        if(!passed)throw new InvalidOperationException("Production departure acceptance failed.");
    }
}