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 LocalNavigationAcceptance
{
    public static async Task RunAsync(int pid,string exactName,string reportPath,bool requireCruise=true,bool useNativeCruise=true,bool allowObscuration=false,CancellationToken externalToken=default,bool manualVisual=false,bool keepHandoffScene=false)
    {
        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 NavigationRecovery.ReadWithRecoveryAsync(source.ReadLocationsAsync,null,externalToken);var row=catalogue.Locations.Single(x=>x.Name==exactName);
        var target=new SystemNavigationTarget(catalogue.SystemAddress,row.WorldAddress,row.Name,SystemTargetKind.Station,row.MarketId){NativeKey=row.Key,NativeKind=row.NativeKind,NativeIdentity=row.Identity};
        if(!NavigationAssists.From(initial).AutoDock||useNativeCruise&&!NavigationAssists.From(initial).Supercruise||initial.Docked)throw new InvalidOperationException("Navigation fixture requires an undocked ship and its chosen fitted assists.");
        var start=DateTimeOffset.UtcNow;FlightTestProtectionScope? protection=null;
        void Session()
        {
            var state=journal.State;
            if(!state.InGame||state.CommanderId!=initial.CommanderId||state.CurrentShipId!=initial.CurrentShipId||state.LoadGameAt!=initial.LoadGameAt
                ||state.Navigation.CurrentSystemAddress!=catalogue.SystemAddress||state.CargoCount!=initial.CargoCount||state.Inventory.Any(x=>x.Count!=initial.Inventory.Where(y=>y.Key==x.Key).Sum(y=>y.Count)))
                throw new InvalidOperationException("Navigation acceptance session, ship, system or reserved cargo changed.");
        }
        async Task EnsureProtection(CancellationToken token)
        {
            if(protection?.Stopped!=true)return;Session();var nav=journal.State.Navigation;
            if(!(nav.SupercruiseEnteredAt>=start.AddSeconds(-1)||nav.SupercruiseExitedAt>=start.AddSeconds(-1)))throw new InvalidOperationException("Unexpected entity change during native-assist navigation.");
            await protection.DisposeAsync();protection=await FlightTestProtectionScope.ArmAsync(pid,!manualVisual,!keepHandoffScene);
        }
        var driver=new NativeSystemFlightControlDriver(pid,hash,()=>journal.State,new NativeGameInputChannel(pid,hash,Path.GetFullPath("XFEEDToolBox/PluginPackage")),EnsureProtection);
        var policy=new SystemFlightNavigator();var arrived=false;var released=false;var cruiseEngaged=false;var dockingGranted=false;var dockingEngaged=false;string? failure=null;
        var inputs=0;double maxHeat=0;double? closest=null;DateTimeOffset? unavailableSince=null;SystemNavigationPhase? lastPhase=null;var cruiseObserved=false;var safeDropRequested=false;
        var fullThrottleApproachSamples=0;double peakNormalSpeedFraction=0;double? firstAtcSpeed=null,firstNativeLandingSpeed=null;
        var obscuredSamples=0;var bypassFullThrottleSamples=0;var nativeObscuredSamples=0;var clearThrottleZeroSamples=0;var nativeCruiseAfterBypass=false;var bypassCleared=false;var previouslyObscured=false;
        var nativeCruiseEnabledSamples=0;var cruiseActivationRequests=0;Task? dockingDiagnostics=null;
        await using var log=new StreamWriter(new FileStream(reportPath+".jsonl",FileMode.CreateNew,FileAccess.Write,FileShare.Read));
        async Task Record(object value){await log.WriteLineAsync(JsonSerializer.Serialize(value));await log.FlushAsync();}
        try
        {
            protection=await FlightTestProtectionScope.ArmAsync(pid,!manualVisual,!keepHandoffScene);using var stop=CancellationTokenSource.CreateLinkedTokenSource(externalToken);stop.CancelAfter(TimeSpan.FromMinutes(10));
            await NavigationRecovery.ReadWithRecoveryAsync(async token=>{await driver.SelectAsync(target,token);return true;},null,stop.Token);
            await driver.PrepareAsync(stop.Token);
            if(!useNativeCruise)
            {
                var cancelUntil=DateTimeOffset.UtcNow.AddSeconds(12);
                while(true)
                {
                    var frame=await driver.ReadSystemFlightAsync(stop.Token);var assist=driver.LastTelemetry!.Value.GetProperty("supercruiseAssist");
                    if(!assist.GetProperty("enabled").GetBoolean()&&!assist.GetProperty("engaged").GetBoolean())break;
                    if(DateTimeOffset.UtcNow>=cancelUntil)throw new TimeoutException("Original cruise assist did not release control.");
                    await driver.ApplySystemFlightAsync(new(SystemNavigationPhase.Waiting,"正在交还游戏超巡辅助并接续工具箱驾驶。",null,null,
                        SystemNavigationAction.CancelOwnedAssists|SystemNavigationAction.ReleaseFlightAxes),frame,target,frame.ObservedAt.AddMilliseconds(250),stop.Token);
                    await Task.Delay(60,stop.Token);
                }
            }
            while(true)
            {
                if(File.Exists(reportPath+".stop"))stop.Cancel();
                Session();stop.Token.ThrowIfCancellationRequested();
                try
                {
                    var frame=await driver.ReadSystemFlightAsync(stop.Token);
                    if(!useNativeCruise)
                    {
                        // Exercise the real software control and safe-drop path
                        // while leaving the user's fitted equipment unchanged.
                        // The original assist must stay disabled throughout.
                        var assist=driver.LastTelemetry!.Value.GetProperty("supercruiseAssist");
                        if(assist.GetProperty("enabled").GetBoolean()||assist.GetProperty("engaged").GetBoolean())throw new InvalidOperationException("Original cruise assist interfered with software-cruise acceptance.");
                        frame=frame with{Assists=frame.Assists with{Supercruise=false}};
                    }
                    maxHeat=Math.Max(maxHeat,frame.HeatPercent??100);closest=Math.Min(closest??frame.TargetDistance,frame.TargetDistance);
                    if(frame.PlayerOverride||frame.ExternalThreat||maxHeat>=75||!allowObscuration&&frame.TargetObscured)
                    {await Record(new{at=DateTimeOffset.UtcNow,safetyStop=true,frame,driver.LastTelemetry});throw new InvalidOperationException("Native navigation acceptance safety bound exceeded.");}
                    cruiseObserved|=frame.Mode==LocalFlightMode.Supercruise;cruiseEngaged|=frame.SupercruiseAssist==NativeAssistState.Engaged;dockingGranted|=frame.Clearance?.Permission==DockingPermission.Granted;dockingEngaged|=frame.DockingAssist==NativeAssistState.Engaged;
                    var decision=policy.Decide(frame,target,DateTimeOffset.UtcNow);
                    var nativeCruise=driver.LastTelemetry!.Value.GetProperty("supercruiseAssist");
                    if(frame.TargetObscured!=nativeCruise.GetProperty("obscured").GetBoolean())
                        throw new InvalidOperationException("Only the original assist may trigger obstruction takeover.");
                    if(useNativeCruise&&frame.Mode==LocalFlightMode.Supercruise&&!frame.TargetObscured&&nativeCruise.GetProperty("enabled").GetBoolean()&&nativeCruise.GetProperty("matchesTarget").GetBoolean())
                    {
                        nativeCruiseEnabledSamples++;
                        if(decision.Throttle is not null||decision.Direction is not null||!decision.Actions.HasFlag(SystemNavigationAction.ReleaseFlightAxes))
                            throw new InvalidOperationException("The enabled native assist must retain throttle and steering ownership.");
                    }
                    if(decision.Actions.HasFlag(SystemNavigationAction.EnableSupercruiseAssist))
                    {
                        cruiseActivationRequests++;
                        if(Vector3d.AngleDegrees(frame.Forward,(frame.TargetPosition-frame.Position).Unit())>frame.AssistAlignmentDegrees||decision.Throttle is not null||!decision.Actions.HasFlag(SystemNavigationAction.ReleaseFlightAxes))
                            throw new InvalidOperationException("Native cruise activation must follow alignment and immediately release flight controls.");
                    }
                    if(frame.TargetObscured)
                    {
                        obscuredSamples++;previouslyObscured=true;
                        if(driver.LastTelemetry!.Value.GetProperty("supercruiseAssist").GetProperty("obscured").GetBoolean())nativeObscuredSamples++;
                        if(decision.Throttle==1&&frame.Throttle>=.999&&decision.Direction is {} side&&Math.Abs(Vector3d.Dot(side.Unit(),(frame.TargetPosition-frame.Position).Unit()))<.02)bypassFullThrottleSamples++;
                    }
                    else if(previouslyObscured)
                    {
                        bypassCleared=true;
                        if(decision.Throttle==0&&frame.Throttle<=.001)clearThrottleZeroSamples++;
                        nativeCruiseAfterBypass|=frame.SupercruiseAssist==NativeAssistState.Engaged;
                    }
                    if(decision.Phase==SystemNavigationPhase.NormalApproach&&frame.Assists.AutoDock&&frame.TargetDistance>SystemFlightNavigator.DockingContactDistance)
                    {
                        if(decision.Throttle!=1)throw new InvalidOperationException("Fitted docking approach must hold full throttle outside ATC range.");
                        fullThrottleApproachSamples++;peakNormalSpeedFraction=Math.Max(peakNormalSpeedFraction,frame.Speed/frame.MaximumNormalSpeed);
                    }
                    if(decision.Actions.HasFlag(SystemNavigationAction.RequestDocking))firstAtcSpeed??=frame.Speed;
                    if(frame.DockingAssist==NativeAssistState.Engaged)firstNativeLandingSpeed??=frame.Speed;
                    if(decision.Phase==SystemNavigationPhase.Stopped)
                    {
                        if(decision.Actions.HasFlag(SystemNavigationAction.ExitForFailedHandoff))
                        {
                            await Record(new{at=DateTimeOffset.UtcNow,protectiveExit=!(manualVisual||keepHandoffScene),manualVisualHandoffStop=manualVisual,keepHandoffScene,frame,decision});
                            await driver.ApplySystemFlightAsync(decision,frame,target,frame.ObservedAt.AddMilliseconds(250),stop.Token);
                        }
                        throw new InvalidOperationException((manualVisual||keepHandoffScene)&&decision.Actions.HasFlag(SystemNavigationAction.ExitForFailedHandoff)
                            ?"交接未完成,已停止自动输入,游戏保持运行。":decision.Message);
                    }
                    if(useNativeCruise&&frame.Mode==LocalFlightMode.Supercruise&&decision.Actions.HasFlag(SystemNavigationAction.CancelOwnedAssists)&&!(allowObscuration&&frame.TargetObscured))throw new InvalidOperationException("Native assist did not engage; software fallback is a separate acceptance case.");
                    if(lastPhase!=decision.Phase){Console.WriteLine(JsonSerializer.Serialize(new{at=DateTimeOffset.UtcNow,phase=decision.Phase.ToString(),decision.Message,frame.TargetDistance,frame.Speed}));lastPhase=decision.Phase;}
                    await driver.ApplySystemFlightAsync(decision,frame,target,frame.ObservedAt.AddMilliseconds(250),stop.Token);inputs++;unavailableSince=null;
                    if(decision.Phase==SystemNavigationPhase.NativeLanding&&dockingDiagnostics is null)
                    {
                        // Read provenance independently; disk/image inspection
                        // must never delay the two-second flight handoff guard.
                        var diagnosticToken=stop.Token;
                        dockingDiagnostics=Task.Run(async()=>
                        {
                            object provenance;
                            try{provenance=await NativeDockingInputDiagnostics.ReadAsync(pid,diagnosticToken);}
                            catch(Exception error){provenance=new{failure=error.ToString(),processAccess="QUERY_INFORMATION | VM_READ",mutationsSent=0};}
                            await File.WriteAllTextAsync(reportPath+".input-provenance.json",JsonSerializer.Serialize(provenance,ToolboxSettings.JsonOptions));
                        });
                    }
                    safeDropRequested|=decision.Actions.HasFlag(SystemNavigationAction.SafeDisengage);
                    await Record(new{at=DateTimeOffset.UtcNow,inputs,cruiseEngaged,dockingGranted,dockingEngaged,frame,decision,driver.LastTelemetry});
                    if(decision.Complete){arrived=true;break;}
                }
                catch(Exception error)when(NavigationRecovery.CanRetry(error,stop.Token))
                {
                    unavailableSince??=DateTimeOffset.UtcNow;await driver.SuspendSystemFlightAsync();await Record(new{at=DateTimeOffset.UtcNow,recovering=error.Message});
                    if(DateTimeOffset.UtcNow-unavailableSince>TimeSpan.FromSeconds(30))throw;
                }
                await Task.Delay(60,stop.Token);
            }
            if(requireCruise&&(useNativeCruise?!cruiseEngaged:!cruiseObserved||!safeDropRequested)||!dockingGranted||!dockingEngaged)throw new InvalidOperationException("Arrival did not prove all required cruise, safe-drop and ATC stages.");
            if(obscuredSamples>0&&(!allowObscuration||bypassFullThrottleSamples==0||!bypassCleared||clearThrottleZeroSamples==0||useNativeCruise&&!nativeCruiseAfterBypass))throw new InvalidOperationException("The actual obscured leg did not prove bypass, zero throttle and its selected cruise-control mode.");
        }
        catch(Exception error){failure=error.ToString();}
        finally
        {
            try{await driver.ReleaseSystemFlightAsync();released=true;}catch(Exception error){failure=(failure??"")+" Release: "+error.Message;}
            if(protection is not null)await protection.DisposeAsync();
            if(dockingDiagnostics is not null)try{await dockingDiagnostics.WaitAsync(TimeSpan.FromSeconds(3));}catch{}
        }
        var passed=arrived&&released&&failure is null;
        await File.WriteAllTextAsync(reportPath,JsonSerializer.Serialize(new{pid,start,finished=DateTimeOffset.UtcNow,passed,arrived,released,manualVisual,keepHandoffScene,automaticExitEnabled=!manualVisual,handoffExitEnabled=!(manualVisual||keepHandoffScene),requireCruise,useNativeCruise,cruiseObserved,safeDropRequested,cruiseEngaged,dockingGranted,dockingEngaged,inputs,maxHeat,closest,
            fullThrottleApproachSamples,peakNormalSpeedFraction,firstAtcSpeed,firstNativeLandingSpeed,
            allowObscuration,obscuredSamples,nativeObscuredSamples,bypassFullThrottleSamples,bypassCleared,clearThrottleZeroSamples,nativeCruiseAfterBypass,nativeCruiseEnabledSamples,cruiseActivationRequests,
            finalMarketId=journal.State.MarketId,finalDocked=journal.State.Docked,failure,tradeOrdersSent=0,completeNavigationAccepted=false},ToolboxSettings.JsonOptions));
        Console.WriteLine(JsonSerializer.Serialize(new{passed,arrived,released,cruiseEngaged,dockingGranted,dockingEngaged,inputs,maxHeat,closest,failure}));
        if(!passed)throw new InvalidOperationException("Native-assist local navigation acceptance failed.");
    }
}