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.Data;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Native;
using XFEEDToolBox.Core.Navigation;
using XFEEDToolBox.Core.Session;

internal static class OriginalDockingResetAcceptance
{
    // Fixed-image 12C1040 initializes this original task to 15.0 seconds;
    // 2C84660 forwards its update delta to 12BF440, which expires that task.
    private static readonly TimeSpan OriginalAtcPendingWindow=TimeSpan.FromSeconds(15);
    private static bool OriginalGrantReady(SystemFlightFrame frame,SystemNavigationTarget target,ulong entity,DateTimeOffset requestedAt,DateTimeOffset now,JsonElement? refusal)
    {
        if(frame.ShipEntity!=entity||frame.SelectedTarget is not {} selected||!target.SameIdentity(selected)||frame.TargetDistance>7500
            ||frame.ObservedAt>now||now-frame.ObservedAt>TimeSpan.FromMilliseconds(250))throw new InvalidDataException("Original ATC request identity, station or fresh frame changed.");
        if(refusal is not null||frame.Clearance is {Permission:DockingPermission.Denied} denied&&denied.MarketId==target.MarketId)throw new InvalidOperationException("The original station refused or cancelled this ATC request.");
        if(frame.Clearance is {} permission&&permission.MarketId!=target.MarketId)throw new InvalidDataException("An unrelated station clearance cannot complete this ATC request.");
        if(now>=requestedAt+OriginalAtcPendingWindow)throw new TimeoutException("The original fifteen-second ATC request deadline expired; the request will not be replayed.");
        return frame.Clearance?.Allows(target,now)==true;
    }
    internal static async Task RunAsync(int pid,string report,string recoveryPackage,bool requireNonzero=true,long targetMarketId=4210924803,string targetName="Homer Platform",string? sharedStopFile=null,string? protectionWorkRoot=null)
    {
        if(targetMarketId is <=0 or >uint.MaxValue||string.IsNullOrWhiteSpace(targetName))throw new ArgumentException("One exact station name and original market identity are required.");
        report=Path.GetFullPath(report);recoveryPackage=Path.GetFullPath(recoveryPackage);
        if(File.Exists(report)||File.Exists(report+".jsonl"))throw new IOException("Use a fresh original-reset report.");
        Directory.CreateDirectory(Path.GetDirectoryName(report)!);
        FileStream? exclusivity=new(Path.Combine(Path.GetDirectoryName(report)!,"docking-fixture-wait.lock"),FileMode.OpenOrCreate,FileAccess.ReadWrite,FileShare.None);
        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;
        if(!initial.InGame||initial.Docked||initial.CurrentShipId!=5||initial.CargoCount!=0||initial.CargoCapacity!=192||NavigationSafety.HasExternalThreat(initial)||NavigationSafety.UnhealthyHull(initial))
            throw new InvalidOperationException("Healthy empty undocked Corsair/5 required for the bounded normal-space reset test.");
        using var process=Process.GetProcessById(pid);var started=process.StartTime.ToUniversalTime().Ticks;
        using var guardian=await PersistentProtectionProcess.StartOrReuseAsync(pid,protectionWorkRoot??Path.GetDirectoryName(report)!,CancellationToken.None,allowStart:false);
        PersistentProtectionProcess.Current=guardian;FlightTestProtectionScope? protection=null;
        var start=DateTimeOffset.UtcNow;var phase="preflight";string? failure=null;var resetZero=false;var meaningfulNonzero=false;var initiallyZero=false;var originalStart=false;var ai=false;var docked=false;var recovered=false;var controlStarted=false;var atcRequestSent=false;DateTimeOffset? grantAt=null,atcRequestAt=null;JsonElement? receipt=null,dockedEvidence=null,inputProvenance=null,publishedBefore=null,atcRefusal=null;
        var atcEventLock=new object();
        void ObserveAtc(ObservedJournalEvent observed)
        {
            var value=observed.Data;
            if(!value.TryGetProperty("event",out var kind)||kind.GetString() is not("DockingDenied" or "DockingCancelled"))return;
            var marketMatches=value.TryGetProperty("MarketID",out var market)&&market.ValueKind==JsonValueKind.Number&&market.TryGetInt64(out var id)&&id==targetMarketId;
            var nameMatches=value.TryGetProperty("StationName",out var name)&&name.GetString()==targetName;
            lock(atcEventLock)if(atcRequestAt is {} requested&&observed.ObservedAt>=requested&&(marketMatches||nameMatches&&(!value.TryGetProperty("MarketID",out var rawMarket)||rawMarket.ValueKind!=JsonValueKind.Number)))atcRefusal=value.Clone();
        }
        journal.JournalEvent+=ObserveAtc;
        using var timeout=new CancellationTokenSource(TimeSpan.FromMinutes(8));var token=timeout.Token;
        await using var log=new StreamWriter(new FileStream(report+".jsonl",FileMode.CreateNew,FileAccess.Write,FileShare.Read));
        async Task Log(object value){await log.WriteLineAsync(JsonSerializer.Serialize(value,ToolboxSettings.JsonOptions));await log.FlushAsync();}
        async Task Save()
        {
            var temp=report+".tmp";
            await File.WriteAllTextAsync(temp,JsonSerializer.Serialize(new{passed=resetZero&&ai&&docked&&(requireNonzero?meaningfulNonzero:originalStart&&initiallyZero)&&failure is null,pid,started,start,finishedAt=DateTimeOffset.UtcNow,phase,
                validationKind=requireNonzero?"nonzero-reset-and-docking":"original-start-with-confirmed-zero",requireNonzero,initiallyZero,originalDockingStartCalled=originalStart,
                station=targetName,marketId=targetMarketId,meaningfulNonzero,nonzeroReproduction=meaningfulNonzero,originalResetZero=meaningfulNonzero&&resetZero,fourSourcesZeroConfirmed=resetZero,aiEngaged=ai,actualDocked=docked,grantAt,atcRequestSent,atcRequestAt,atcDeadline=atcRequestAt+OriginalAtcPendingWindow,atcRefusal,receipt,dockedEvidence,inputProvenance,publishedBefore,recoveredViaOriginalMenu=recovered,
                persistent075=false,persistent075Acceptance=false,supercruiseUsed=false,gameSupercruiseAssistUsed=false,tradeOrdersSent=0,clientTerminated=false,failure},ToolboxSettings.JsonOptions));File.Move(temp,report,true);
        }
        void CheckSession()
        {
            process.Refresh();var current=journal.State;
            if(File.Exists(sharedStopFile??report+".stop"))throw new OperationCanceledException("Original reset diagnostic explicitly stopped.");
            if(process.HasExited||process.StartTime.ToUniversalTime().Ticks!=started||!current.InGame||current.CommanderId!=initial.CommanderId||current.CurrentShipId!=5||current.CargoCount!=0||current.CargoCapacity!=192||current.LoadGameAt!=initial.LoadGameAt
                ||NavigationSafety.HasExternalThreat(current)||NavigationSafety.UnhealthyHull(current))throw new InvalidOperationException("Reset diagnostic session, ship or safety identity changed.");
            guardian.RequireReady(pid);
        }
        var driver=new NativeSystemFlightControlDriver(pid,NativeSystemLocationReader.GameSha256,()=>journal.State,
            new NativeGameInputChannel(pid,NativeSystemLocationReader.GameSha256,Path.Combine(AppContext.BaseDirectory,"PluginPackage")),t=>{t.ThrowIfCancellationRequested();CheckSession();NativeFlightTestProtection.CheckBeforeInput(pid);return Task.CompletedTask;},useNativeSupercruiseAssist:false);
        try
        {
            protection=await FlightTestProtectionScope.ArmAsync(pid,exitForFailedHandoff:false);
            await driver.EnableOriginalDockingResetTestAsync(startDockingAfterZero:true,token);
            inputProvenance=JsonSerializer.SerializeToElement(await NativeDockingInputDiagnostics.ReadForStationAsync(pid,targetMarketId,token));
            var mainNode=inputProvenance.Value.GetProperty("graph").GetProperty("matches").EnumerateArray().Single(x=>x.GetProperty("registryMatches").GetBoolean()&&x.GetProperty("outputMatches").GetBoolean());
            var node=mainNode.GetProperty("node").GetString()!;var output=mainNode.GetProperty("output").GetString()!;var registry=mainNode.GetProperty("registry").GetString()!;
            if(inputProvenance.Value.GetProperty("entity").GetString() is not {Length:>0})throw new InvalidDataException("Original source owner is not verified.");
            static bool Zero(double value)=>double.IsFinite(value)&&Math.Abs(value)<=.001;
            initiallyZero=Zero(mainNode.GetProperty("accumulator").GetDouble())&&Zero(inputProvenance.Value.GetProperty("dockingThrottle").GetDouble())
                &&Zero(inputProvenance.Value.GetProperty("actualThrottle").GetDouble())
                &&(Zero(inputProvenance.Value.GetProperty("controllerThrottle").GetDouble())||inputProvenance.Value.GetProperty("controllerThrottle").GetDouble()==-100);
            if(!requireNonzero&&!initiallyZero)throw new InvalidDataException("The separate original AI start test requires an already confirmed zero source; it cannot stand in for a nonzero reset reproduction.");
            using var source=new NativeCurrentSystemSource(pid,NativeSystemLocationReader.GameSha256,()=>journal.State);
            var catalogue=await source.ReadLocationsAsync(token);var row=catalogue.Locations.Single(x=>x.MarketId==targetMarketId&&x.Name==targetName);
            var target=new SystemNavigationTarget(catalogue.SystemAddress,row.WorldAddress,row.Name,SystemTargetKind.Station,row.MarketId){NativeKey=row.Key,NativeKind=row.NativeKind,NativeIdentity=row.Identity};
            await driver.SelectAsync(target,token);await driver.PrepareAsync(token);phase="normal-space-approach";Console.WriteLine(phase);
            async Task<SystemFlightFrame> Read()
            {
                var unavailableUntil=DateTimeOffset.UtcNow.AddSeconds(3);
                while(true)
                {
                    token.ThrowIfCancellationRequested();CheckSession();
                    if(grantAt is {} granted&&!ai&&DateTimeOffset.UtcNow-granted>TimeSpan.FromSeconds(2))throw new InvalidOperationException("Original AI takeover was not confirmed inside two seconds.");
                    if(DateTimeOffset.UtcNow>unavailableUntil)throw new FlightTelemetryUnavailableException("Original normal-space telemetry remained unavailable for three seconds.");
                    try
                    {
                        var frame=await driver.ReadSystemFlightAsync(token);
                        if(frame.Mode is not(LocalFlightMode.Normal or LocalFlightMode.Docked)||frame.PlayerOverride||frame.ExternalThreat||frame.SupercruiseAssist==NativeAssistState.Engaged||(frame.HeatPercent??100)>=75)
                            throw new InvalidOperationException("Bounded normal-space reset safety condition failed.");
                        return frame;
                    }
                    catch(Exception error)when(NavigationRecovery.CanRetry(error,token)){await Task.Delay(40,token);}
                }
            }
            while(true)
            {
                var frame=await Read();await Log(new{at=DateTimeOffset.UtcNow,phase,frame,local=driver.LastTelemetry});
                if(frame.TargetDistance<=7000)break;
                var aim=(frame.TargetPosition-frame.Position).Unit();var throttle=Vector3d.AngleDegrees(frame.Forward,aim)<=7?.6:0;
                controlStarted=true;await driver.ApplySystemFlightAsync(new(SystemNavigationPhase.NormalApproach,phase,aim,throttle),frame,target,frame.ObservedAt.AddMilliseconds(250),token);
                await Task.Delay(60,token);
            }
            phase="original-atc";Console.WriteLine(phase);
            var atcFrame=await Read();
            var atcEntity=ulong.Parse(inputProvenance.Value.GetProperty("entity").GetString()!);
            if(atcFrame.ShipEntity!=atcEntity)throw new InvalidDataException("Original main ship changed before the ATC request.");
            lock(atcEventLock)atcRequestAt=atcFrame.ObservedAt;
            if(atcFrame.Clearance?.Allows(target,DateTimeOffset.UtcNow)!=true)
            {controlStarted=true;atcRequestSent=true;await driver.ApplySystemFlightAsync(new(SystemNavigationPhase.RequestingDocking,phase,null,null,SystemNavigationAction.RequestDocking|SystemNavigationAction.ReleaseFlightAxes),atcFrame,target,atcFrame.ObservedAt.AddMilliseconds(250),token);}
            await Log(new{at=DateTimeOffset.UtcNow,phase,atcRequestSent,sourceSequence=atcFrame.Sequence,entity=atcEntity,target,atcRequestAt,atcDeadline=atcRequestAt+OriginalAtcPendingWindow,originalTaskWindowSeconds=OriginalAtcPendingWindow.TotalSeconds,requestReplayed=false});
            while(true)
            {
                var frame=await Read();await Log(new{at=DateTimeOffset.UtcNow,phase,frame,local=driver.LastTelemetry});
                JsonElement? refusal;lock(atcEventLock)refusal=atcRefusal;
                if(OriginalGrantReady(frame,target,atcEntity,atcRequestAt.Value,DateTimeOffset.UtcNow,refusal))
                {
                    grantAt=(frame.Clearance??throw new InvalidDataException("Original ATC grant disappeared.")).ObservedAt;phase="original-reset-and-ai";Console.WriteLine(phase);
                    var observer=await new NavigationPipeClient(pid,NativeSystemFlightControlDriver.OriginalDockingService).CallAsync(new{op="read"},token);
                    publishedBefore=observer.GetProperty("mapperNodes").EnumerateArray().Single(x=>x.GetProperty("node").GetString()==node&&x.GetProperty("source").GetString()==output&&x.GetProperty("registry").GetString()==registry).Clone();
                    if(ulong.Parse(inputProvenance.Value.GetProperty("entity").GetString()!)!=frame.ShipEntity)throw new InvalidDataException("Original mapper owner changed before reset.");
                    controlStarted=true;await driver.ApplySystemFlightAsync(new(SystemNavigationPhase.NativeLanding,phase,null,null,SystemNavigationAction.EnableDockingAssist|SystemNavigationAction.ReleaseFlightAxes),frame,target,frame.ObservedAt.AddMilliseconds(250),token);
                    receipt=driver.LastOriginalDockingResetReceipt;
                    originalStart=receipt is {} startedDocking&&startedDocking.TryGetProperty("originalDockingStartCalled",out var called)&&called.ValueKind==JsonValueKind.True;
                    resetZero=receipt is {} verified&&DockingValidationProof.OriginalResetZero(verified,frame.ShipEntity,target.MarketId!.Value,frame.ObservedAt,DateTimeOffset.UtcNow);
                    meaningfulNonzero=receipt is {} nonzero&&DockingValidationProof.OriginalResetZero(nonzero,frame.ShipEntity,target.MarketId!.Value,frame.ObservedAt,DateTimeOffset.UtcNow,requireNonzeroBefore:true)
                        &&DockingValidationProof.PriorPublishedResetSource(nonzero,publishedBefore.Value,node,output,registry);
                    await Log(new{at=DateTimeOffset.UtcNow,phase,resetZero,meaningfulNonzero,receipt});break;
                }
                await Task.Delay(25,token);
            }
            while(true)
            {
                var frame=await Read();ai=frame.DockingAssist==NativeAssistState.Engaged;await Log(new{at=DateTimeOffset.UtcNow,phase,frame,local=driver.LastTelemetry,receipt});
                if(resetZero&&ai)break;
                if(DateTimeOffset.UtcNow-grantAt>TimeSpan.FromSeconds(2))throw new InvalidOperationException("Original source reset and real AI takeover were not confirmed inside two seconds.");
                await Task.Delay(25,token);
            }
            phase="native-ai-docking";Console.WriteLine(phase);var dockLimit=DateTimeOffset.UtcNow.AddMinutes(5);
            while(true)
            {
                token.ThrowIfCancellationRequested();CheckSession();
                if(journal.State.Docked&&journal.State.MarketId==target.MarketId)
                {
                    dockedEvidence=await new NavigationPipeClient(pid).CallAsync(new{op="commerce.docked.status",marketId=target.MarketId},token);
                    await Log(new{at=DateTimeOffset.UtcNow,phase,dockedEvidence});
                    var actual=dockedEvidence.Value.GetProperty("actualMarketId").GetInt64();
                    if(actual!=0&&actual!=target.MarketId)throw new InvalidDataException("Original docked market differs from the selected station.");
                    if(dockedEvidence.Value.GetProperty("ready").GetBoolean()&&actual==target.MarketId){docked=true;break;}
                }
                else{var frame=await Read();await Log(new{at=DateTimeOffset.UtcNow,phase,frame,local=driver.LastTelemetry});}
                if(DateTimeOffset.UtcNow>dockLimit)throw new TimeoutException("Original AI has not confirmed actual docking.");await Task.Delay(100,token);
            }
            if(!docked||(requireNonzero?!meaningfulNonzero:!originalStart))throw new InvalidDataException("Actual docking/source or original start evidence is incomplete; an initially zero source is not a reset reproduction.");
            phase=requireNonzero?"passed-nonzero-reset-selected-station":"passed-original-ai-docking-selected-station";
        }
        catch(Exception error)
        {
            failure=error.ToString();Console.WriteLine(error.Message);await Log(new{at=DateTimeOffset.UtcNow,phase,failure});
            try{await driver.ReleaseSystemFlightAsync();}catch(Exception release){await Log(new{releaseFailure=release.ToString()});}
            receipt??=driver.LastOriginalDockingResetReceipt;await Save();
            if(protection is not null){await protection.DisposeAsync();protection=null;}
            exclusivity?.Dispose();exclusivity=null;
            if(controlStarted&&!File.Exists(sharedStopFile??report+".stop")&&!journal.State.Docked&&!process.HasExited&&journal.State.CurrentShipId==5&&journal.State.CargoCount==0)
            {
                try
                {
                    phase="same-process-original-menu-recovery";await EnterPauseAsync(pid,guardian,report+".pause.json");
                    await SessionRecoveryAcceptance.RunAsync(pid,recoveryPackage,report+".menu-recovery.json",leave:true,protectionWorkRoot:protectionWorkRoot);
                    await GameSessionAcceptance.StartAsync(settings.PluginGameExecutable,report+".solo-reentry.json",pid);
                    recovered=journal.State.InGame&&journal.State.LoadGameAt>initial.LoadGameAt&&journal.State.CurrentShipId==5&&journal.State.CargoCount==0;
                }
                catch(Exception recovery){await Log(new{recoveryFailure=recovery.ToString(),clientTerminated=false});}
            }
        }
        finally
        {
            journal.JournalEvent-=ObserveAtc;
            try{await driver.ReleaseSystemFlightAsync();}catch(Exception release){await Log(new{finalReleaseFailure=release.ToString()});}
            if(protection is not null)await protection.DisposeAsync();exclusivity?.Dispose();PersistentProtectionProcess.Current=null;await Save();
        }
        if(failure is not null)throw new InvalidOperationException("Original reset diagnostic did not pass; original failure was preserved.");
    }
    internal static int SelfTestAtcWait()
    {
        var at=DateTimeOffset.FromUnixTimeSeconds(1700000000);var target=new SystemNavigationTarget(1,2,"Original station",SystemTargetKind.Station,7);const ulong entity=42;
        SystemFlightFrame Frame(double seconds,DockingClearance? permission=null,ulong ship=entity)=>new(){ObservedAt=at.AddSeconds(seconds),ShipEntity=ship,SelectedTarget=target,TargetDistance=6400,Clearance=permission};
        DockingClearance Grant(double seconds,long market=7)=>new(market,DockingPermission.Granted,5,at.AddSeconds(seconds),at.AddSeconds(seconds+1));
        bool Refused(Action action){try{action();return false;}catch(Exception error)when(error is InvalidOperationException or InvalidDataException or TimeoutException){return true;}}
        var refusal=JsonSerializer.SerializeToElement(new{ @event="DockingDenied",MarketID=7,StationName="Original station",Reason="NoSpace"});
        var checks=new[]{!OriginalGrantReady(Frame(6),target,entity,at,at.AddSeconds(6),null),OriginalGrantReady(Frame(6,Grant(6)),target,entity,at,at.AddSeconds(6),null),
            Refused(()=>OriginalGrantReady(Frame(15),target,entity,at,at.AddSeconds(15),null)),Refused(()=>OriginalGrantReady(Frame(6,Grant(6,8)),target,entity,at,at.AddSeconds(6),null)),
            Refused(()=>OriginalGrantReady(Frame(6),target,entity,at,at.AddSeconds(6),refusal)),Refused(()=>OriginalGrantReady(Frame(6,ship:43),target,entity,at,at.AddSeconds(6),null)),
            Refused(()=>OriginalGrantReady(Frame(6),target,entity,at,at.AddSeconds(6).AddMilliseconds(251),null))};
        if(checks.Any(value=>!value))throw new InvalidDataException("Original ATC task-window, late-grant or refusal/identity regression failed.");return checks.Length;
    }
    internal static async Task EnterPauseAsync(int pid,PersistentProtectionProcess guardian,string report)
    {
        var channel=new NativeRelogChannel(pid,NativeRelogChannel.SupportedGameSha256,t=>{t.ThrowIfCancellationRequested();guardian.RequireReady(pid);return Task.CompletedTask;},returnToMenuTrial:true);
        var owner=Guid.NewGuid().ToString("N");var request=Guid.NewGuid().ToString("N");var submitted=false;var paused=false;string? failure=null;
        using var stop=new CancellationTokenSource(TimeSpan.FromSeconds(8));
        try
        {
            await channel.BeginAsync(owner,stop.Token);
            while(true)
            {
                var frame=await channel.ReadAsync(owner,stop.Token);
                if(frame.Stable&&frame.State=="Pause"){paused=true;break;}
                if(frame.Stable&&frame.State=="MainMenu"){paused=true;break;}
                if(!submitted&&frame.Stable&&frame.State=="InGame")
                {
                    submitted=true;
                    try{await channel.SubmitAsync(owner,request,frame,"@pause",stop.Token);}catch(Exception error)when(error is IOException or OperationCanceledException&&!stop.IsCancellationRequested){}
                }
                await Task.Delay(40,stop.Token);
            }
        }
        catch(Exception error){failure=error.ToString();throw;}
        finally{await channel.EndAsync(owner);await File.WriteAllTextAsync(report,JsonSerializer.Serialize(new{pid,paused,submitted,request,failure,clientTerminated=false},ToolboxSettings.JsonOptions));}
    }
}