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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFEEDToolBox

Preserve failed docking scenes while keeping damage exits armed

Separate the CG workflow's failed handoff exit policy from actual ship damage protection. A two-second handoff timeout still stops control and fails acceptance, while the game stays available for inspection. Add a recovery docking command with the same behavior and retain full read-only diagnostic failures. Advance the plugin and workflow pins to 0.5.12 for the docking action TTL fix. Record the background return-leg failure after the earlier visual success; require fresh continuous validation before another purchase. Validation: 12 protection regressions including real damage after a preserved handoff, 728 native self-tests with ABI/PE/export checks, 20 status and 3 lifecycle handshake replies, isolated CLI publish. New plugin installed; live recovery and repeat acceptance remain pending.

7ff9727
XFE工作室室长 <mail@xfegzs.com>
提交于

代码差异

10 个文件 +53 -24
Modified XFEEDToolBox.Core/Session/NativeFlightTestProtection.cs +6 -5
@@ -17,6 +17,7 @@ public sealed class NativeFlightTestProtection:IAsyncDisposable
17 17 private readonly Action stopAutomation;
18 18 private readonly bool ownsMonitor;
19 19 private readonly bool automaticExit;
20 private readonly bool exitForFailedHandoff;
20 21 private readonly IGameSessionDriver sessionDriver;
21 22 private bool independentHealth;
22 23 private Task? polling;
@@ -27,9 +28,9 @@ public sealed class NativeFlightTestProtection:IAsyncDisposable
27 28 public string? LastIssue{get;private set;}
28 29 public bool Stopped=>Volatile.Read(ref unsafeState)!=0;
29 30 public bool Fresh=>!Stopped&&DateTimeOffset.UtcNow.UtcTicks-Interlocked.Read(ref lastHealthTicks)<=TimeSpan.FromMilliseconds(500).Ticks;
30 private NativeFlightTestProtection(int pid,GameSessionTarget target,IGameSessionDriver sessions,Func<PlayerState> player,Action stopAutomation,ShipProtectionMonitor? sharedMonitor,bool automaticExit)
31 private NativeFlightTestProtection(int pid,GameSessionTarget target,IGameSessionDriver sessions,Func<PlayerState> player,Action stopAutomation,ShipProtectionMonitor? sharedMonitor,bool automaticExit,bool exitForFailedHandoff)
31 32 {
32 pipe=new(pid);this.target=target;this.player=player;this.stopAutomation=stopAutomation;sessionDriver=sessions;this.automaticExit=automaticExit;
33 pipe=new(pid);this.target=target;this.player=player;this.stopAutomation=stopAutomation;sessionDriver=sessions;this.automaticExit=automaticExit;this.exitForFailedHandoff=exitForFailedHandoff;
33 34 ownsMonitor=sharedMonitor is null;
34 35 monitor=sharedMonitor??new(sessions,player,()=>{Interlocked.Exchange(ref unsafeState,1);StopAutomation();},automaticExit);
35 36 }
@@ -41,12 +42,12 @@ public sealed class NativeFlightTestProtection:IAsyncDisposable
41 42 Interlocked.Exchange(ref protection.unsafeState,1);
42 43 protection.LastIssue="自动降落未接管,飞船仍在移动;已停止操作并保护退出。";
43 44 protection.StopAutomation();
44 if(!protection.automaticExit){protection.LastIssue="人工观察模式:自动降落未接管,已停止自动输入,不退出游戏。";return;}
45 if(!protection.automaticExit||!protection.exitForFailedHandoff){protection.LastIssue="自动降落未接管,已停止自动输入并保留游戏现场。";return;}
45 46 // Do not restart into the same unresolved approach or let cancelling
46 47 // the navigation worker interrupt the already required protective exit.
47 48 await new GameSessionController(protection.sessionDriver,()=>false).EmergencyExitAsync(protection.target,CancellationToken.None);
48 49 }
49 public static async Task<NativeFlightTestProtection> ArmAsync(int pid,string hash,IGameSessionDriver sessions,Func<PlayerState> player,Action stopAutomation,CancellationToken token=default,ShipProtectionMonitor? sharedMonitor=null,bool automaticExit=true)
50 public static async Task<NativeFlightTestProtection> ArmAsync(int pid,string hash,IGameSessionDriver sessions,Func<PlayerState> player,Action stopAutomation,CancellationToken token=default,ShipProtectionMonitor? sharedMonitor=null,bool automaticExit=true,bool exitForFailedHandoff=true)
50 51 {
51 52 if(Volatile.Read(ref active) is not null)throw new InvalidOperationException("已有飞行测试保护正在运行。");
52 53 var pipe=new NavigationPipeClient(pid);var hello=await pipe.CallAsync(new{op="describe"},token);
@@ -58,7 +59,7 @@ public sealed class NativeFlightTestProtection:IAsyncDisposable
58 59 var target=await sessions.FindTargetAsync(token);if(target.ProcessId!=pid)throw new InvalidOperationException("飞行测试与受保护游戏实例不一致。");
59 60 if(sharedMonitor is not null&&sharedMonitor.BoundTarget!=target)throw new InvalidOperationException("连续血量监测与受保护游戏实例不一致。");
60 61 if(sharedMonitor is not null&&sharedMonitor.AutomaticExitEnabled!=automaticExit)throw new InvalidOperationException("连续血量监测的退出模式不一致。");
61 var protection=new NativeFlightTestProtection(pid,target,sessions,player,stopAutomation,sharedMonitor,automaticExit);
62 var protection=new NativeFlightTestProtection(pid,target,sessions,player,stopAutomation,sharedMonitor,automaticExit,exitForFailedHandoff);
62 63 protection.independentHealth=hello.TryGetProperty("independentShipHealth",out var independent)&&independent.ValueKind==JsonValueKind.True;
63 64 try
64 65 {
Modified XFEEDToolBox.Test/NativeFlightTestProtectionTests.cs +20 -0
@@ -147,6 +147,26 @@ public class NativeFlightTestProtectionTests
147 147 }
148 148 finally{stop.Cancel();await server;}
149 149 }
150 [Test] public async Task PreservedHandoffSceneStillExitsForSubsequentRealDamage()
151 {
152 var pid=Random.Shared.Next(300000000,310000000);using var stop=new CancellationTokenSource(TimeSpan.FromSeconds(5));
153 var sessions=new Sessions();sessions.Target=sessions.Target with{ProcessId=pid};var hash=new string('a',64);var now=DateTimeOffset.UtcNow;var damaged=0;
154 var player=new PlayerState{CommanderId="F1",InGame=true,CurrentShipId=7,LoadGameAt=now.AddMinutes(-1),Ships=[new(){ShipId=7,HullHealth=1,HullHealthAt=now,ConfigurationAt=now}],
155 StatusAt=now,Status=new((1L<<24)|8,0,1,1,0,null,null,null,null,[])};
156 var server=ServeHealth(pid,hash,_=>99,stop.Token,_=>Volatile.Read(ref damaged)==0?1:.49,independent:true);
157 try
158 {
159 await using var protection=await NativeFlightTestProtection.ArmAsync(pid,hash,sessions,()=>player,()=>{},stop.Token,exitForFailedHandoff:false);
160 while(protection.LastHealth!.Sequence<3)await Task.Delay(20,stop.Token);
161 await NativeFlightTestProtection.ExitForFailedHandoffAsync(pid);
162 Check.That(protection.Stopped);Check.Throws<InvalidOperationException>(()=>NativeFlightTestProtection.CheckBeforeInput(pid));
163 Check.That(!sessions.Calls.Contains("exit")&&!sessions.Calls.Contains("launch"));
164 Interlocked.Exchange(ref damaged,1);
165 while(!sessions.Calls.Contains("exit"))await Task.Delay(20,stop.Token);
166 Check.Equal(1,sessions.Calls.Count(x=>x=="exit"));
167 }
168 finally{stop.Cancel();await server;}
169 }
150 170 [Test] public async Task ManualVisualDamageStopsInputsWithoutExitOrRestart()
151 171 {
152 172 var pid=Random.Shared.Next(290000000,300000000);using var stop=new CancellationTokenSource(TimeSpan.FromSeconds(5));
Modified docs/CG-TRADING-VALIDATION.md +6 -0
@@ -20,6 +20,10 @@
20 20
21 21 此次加载0.5.11的新增停靠更新点 `dockingZeroUpdateMatched/Applied` 均为0,成功不能归因于它。离线发现该更新点误把选择确认的短TTL当成整个飞行目标的寿命;源码已改为保留目标身份、代次和控制所有权核验,以当前输入命令自己的有效期约束提交,回归覆盖过期确认、过期命令和变化目标。这项后续修正尚未更换到正在运行的游戏,仍需后续独立实测。故障根因与后台持续复现情况尚未确认。
22 22
23 恢复正式流程后的0.5.11返程再次出现实际油门.75、空管已授权而AI未接管,随后保护退出;没有新采购。单次人工观察通过不代表持续自动化稳定。0.5.12已包含上述有效期修正,728次原生自测、原ABI/导出及握手通过,已安装,实机复测仍在进行。
24
25 CG流程现在明确指定 `keepHandoffScene=true`:交接超时仍立即停止本程序输入,保留游戏现场用于诊断;与它分开的真实受损退出继续启用。新增回归证明交接保留现场后,后续真实受损仍触发退出;12项保护回归通过。通用保护接口的默认退出行为保持兼容,人工观察模式仍按用户要求同时关闭两类自动退出。
26
23 27 原始返回菜单路径未通过实机;只有插件明确声明 `nativeReturnToMenuVerified=true` 才允许该路径。从已就绪主菜单进入Solo使用独立入口能力。
24 28
25 29 后续空载连续航程再次完成起飞、跃迁、SCA和空管,但自动降落仍未接管并触发保护退出。实际飞行油门持续为.75,即便控制器零位及原始输入回执已确认。只读来源诊断确认FSD与自动降落使用同一个原始输入注册表,因此不能把来源差异当作已确认原因。可使用 `inspect-docking-input-sources <PID> <新报告>` 读取来源与原始零输入待消费/已消费状态;该命令仅使用QUERY_INFORMATION和VM_READ权限。
@@ -41,3 +45,5 @@
41 45 继续已有货物必须复用原目录。原始回执、钱包、会话身份及日志只存本机,不能提交到仓库。`continuousArrivalAccepted` 必须来自同一完整到站报告,不能用正常空间重试替代。未领取CG奖励不计入净利润。
42 46
43 47 空载诊断停靠可使用 `service-cg-docked <PID> <独立维护目录>`。它仅接受已核对路线站点、空舱及受保护的Corsair/5,不启动贸易或飞行;每笔原始服务费用记入独立账本,结尾必须与原始钱包核对。该路径已在恢复停靠后完成加油与完整性服务实测,诊断费用也应从总收益扣除。
48
49 `accept-local-docking-keep-scene <PID> <站名> <新报告路径>` 用于正常空间恢复停靠,保留真实受损保护,交接失败时停止输入并保留游戏。该报告要求 `requireCruise=false`,不能作为完整连续超巡交接的证明。
Modified native/ManagedPlugin/Platform.cs +1 -1
@@ -13,7 +13,7 @@ internal sealed class NativeAccessException : Exception
13 13 internal static unsafe class Platform
14 14 {
15 15 internal const string GameHash = "ed5a8f8cd10f9a80d3bda5badd7ba1d4fcfc15996c8c7e018852cb170e264b63";
16 internal const string PluginVersion = "0.5.11-dev";
16 internal const string PluginVersion = "0.5.12-dev";
17 17 internal static int ProcessId => Environment.ProcessId;
18 18 internal static nuint ImageBase => (nuint)GetModuleHandleW(null);
19 19 internal static nuint ImageEnd
Modified research/NativeAcceptance/CgCargoJourney.cs +1 -1
@@ -125,7 +125,7 @@ internal static class CgCargoJourney
125 125 }
126 126 await Check(token);Console.WriteLine("原始超巡、空管与自动降落:"+target.Name);
127 127 await LocalNavigationAcceptance.RunAsync(pid,target.Name,Path.Combine(directory,"arrival-"+Guid.NewGuid().ToString("N")+".json"),
128 requireCruise:true,useNativeCruise:true,allowObscuration:true,externalToken:token);
128 requireCruise:true,useNativeCruise:true,allowObscuration:true,externalToken:token,keepHandoffScene:true);
129 129 if(!Player().Docked||Player().MarketId!=target.MarketId)throw new InvalidOperationException("Original docking differs from the CG route.");
130 130 }
131 131 var from=new Station{MarketId=3228792064,Name="Polyakov Station",SystemName="Duamta"};
Modified research/NativeAcceptance/CgTradeLoop.cs +6 -6
@@ -24,8 +24,8 @@ internal static class CgTradeLoop
24 24 if(!initial.InGame||initial.CurrentShipId!=5||initial.CargoCapacity!=192||string.IsNullOrWhiteSpace(initial.CommanderId))
25 25 throw new InvalidOperationException("The verified CG ship and live session are required.");
26 26 var channel=new NativeCommerceChannel(pid);var hello=await channel.CallAsync(new{op="describe"},stop.Token);
27 if(hello.GetProperty("gameSha256").GetString()!=NativeSystemLocationReader.GameSha256||hello.GetProperty("version").GetString()!="0.5.11-dev")
28 throw new InvalidOperationException("This continuous workflow requires the verified 0.5.11 docking handoff implementation.");
27 if(hello.GetProperty("gameSha256").GetString()!=NativeSystemLocationReader.GameSha256||hello.GetProperty("version").GetString()!="0.5.12-dev")
28 throw new InvalidOperationException("This continuous workflow requires the verified 0.5.12 docking handoff implementation.");
29 29 decimal initialWallet;
30 30 using(var first=JsonDocument.Parse(await File.ReadAllTextAsync(Path.Combine(workRoot,"one-ton-journey","result.json"),stop.Token)))
31 31 {
@@ -83,7 +83,7 @@ internal static class CgTradeLoop
83 83 if(!journal.State.SystemName.Equals(target.SystemName,StringComparison.OrdinalIgnoreCase))
84 84 {await State("interstellar");await InterstellarTravelAcceptance.RunAsync(pid,target.SystemName,prefix+"-route.json",stop.Token);}
85 85 await Check(stop.Token);await State("cruise-and-docking");
86 await LocalNavigationAcceptance.RunAsync(pid,target.Name,prefix+"-arrival.json",true,true,true,stop.Token);
86 await LocalNavigationAcceptance.RunAsync(pid,target.Name,prefix+"-arrival.json",true,true,true,stop.Token,keepHandoffScene:true);
87 87 if(!journal.State.Docked||journal.State.MarketId!=target.MarketId)throw new InvalidOperationException("Actual docking differs from the trade route.");
88 88 }
89 89 async Task Membership()
@@ -206,17 +206,17 @@ internal static class CgTradeLoop
206 206 // validate a new complete leg to the opposite route station.
207 207 if(journal.State.Docked&&journal.State.MarketId==target.MarketId)
208 208 target=target.MarketId==Homer.MarketId?Source:Homer;
209 await File.WriteAllTextAsync(planPath+".tmp",JsonSerializer.Serialize(new{marketId=target.MarketId,version="0.5.11-dev"}),stop.Token);
209 await File.WriteAllTextAsync(planPath+".tmp",JsonSerializer.Serialize(new{marketId=target.MarketId,version="0.5.12-dev"}),stop.Token);
210 210 File.Move(planPath+".tmp",planPath,true);
211 211 var prefix=Path.Combine(directory,"validation-"+Guid.NewGuid().ToString("N"));
212 212 await State("empty-handoff-validation");await Travel(target,prefix);
213 213 using var report=JsonDocument.Parse(await File.ReadAllTextAsync(prefix+"-arrival.json",stop.Token));var r=report.RootElement;
214 214 if(!r.GetProperty("passed").GetBoolean()||!r.GetProperty("cruiseEngaged").GetBoolean()||!r.GetProperty("dockingGranted").GetBoolean()||!r.GetProperty("dockingEngaged").GetBoolean())
215 215 throw new InvalidDataException("Continuous original cruise/ATC/docking proof is required before new purchases.");
216 await File.WriteAllTextAsync(proof,JsonSerializer.Serialize(new{version="0.5.11-dev",report=prefix+"-arrival.json",passed=true}),stop.Token);
216 await File.WriteAllTextAsync(proof,JsonSerializer.Serialize(new{version="0.5.12-dev",report=prefix+"-arrival.json",passed=true}),stop.Token);
217 217 await Maintain("validation-after-"+Guid.NewGuid().ToString("N"));await FinancialSummary();
218 218 }
219 else{using var d=JsonDocument.Parse(await File.ReadAllTextAsync(proof,stop.Token));if(!d.RootElement.GetProperty("passed").GetBoolean()||d.RootElement.GetProperty("version").GetString()!="0.5.11-dev")throw new InvalidDataException("Continuous proof belongs to a different implementation.");}
219 else{using var d=JsonDocument.Parse(await File.ReadAllTextAsync(proof,stop.Token));if(!d.RootElement.GetProperty("passed").GetBoolean()||d.RootElement.GetProperty("version").GetString()!="0.5.12-dev")throw new InvalidDataException("Continuous proof belongs to a different implementation.");}
220 220 while(true)
221 221 {
222 222 await Check(stop.Token);activeJourney=Unfinished();
Modified research/NativeAcceptance/FlightTestProtectionScope.cs +2 -2
@@ -7,7 +7,7 @@ using XFEEDToolBox.Services;
7 7 internal sealed class FlightTestProtectionScope(JournalMonitor journal,NativeFlightTestProtection protection):IAsyncDisposable
8 8 {
9 9 public bool Stopped=>protection.Stopped;
10 public static async Task<FlightTestProtectionScope> ArmAsync(int pid,bool automaticExit=true)
10 public static async Task<FlightTestProtectionScope> ArmAsync(int pid,bool automaticExit=true,bool exitForFailedHandoff=true)
11 11 {
12 12 if(!OperatingSystem.IsWindows())throw new PlatformNotSupportedException("原生飞行验收仅支持 Windows。");
13 13 var settings=ToolboxSettings.Load(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox","settings.json"));
@@ -17,7 +17,7 @@ internal sealed class FlightTestProtectionScope(JournalMonitor journal,NativeFli
17 17 await journal.StartAsync();
18 18 var hello=await new NavigationPipeClient(pid).CallAsync(new{op="describe"});
19 19 var protection=await NativeFlightTestProtection.ArmAsync(pid,hello.GetProperty("gameSha256").GetString()!,
20 new WindowsGameSessionDriver(()=>journal.State,()=>settings.PluginGameExecutable),()=>journal.State,()=>{},automaticExit:automaticExit);
20 new WindowsGameSessionDriver(()=>journal.State,()=>settings.PluginGameExecutable),()=>journal.State,()=>{},automaticExit:automaticExit,exitForFailedHandoff:exitForFailedHandoff);
21 21 journal.JournalEvent+=protection.ObserveJournal;
22 22 return new(journal,protection);
23 23 }
Modified research/NativeAcceptance/LocalNavigationAcceptance.cs +8 -8
@@ -6,7 +6,7 @@ using XFEEDToolBox.Core.Navigation;
6 6
7 7 internal static class LocalNavigationAcceptance
8 8 {
9 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)
9 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)
10 10 {
11 11 if(File.Exists(reportPath)||File.Exists(reportPath+".jsonl"))throw new IOException("Use a new report path.");
12 12 var settings=ToolboxSettings.Load(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox","settings.json"));
@@ -28,7 +28,7 @@ internal static class LocalNavigationAcceptance
28 28 {
29 29 if(protection?.Stopped!=true)return;Session();var nav=journal.State.Navigation;
30 30 if(!(nav.SupercruiseEnteredAt>=start.AddSeconds(-1)||nav.SupercruiseExitedAt>=start.AddSeconds(-1)))throw new InvalidOperationException("Unexpected entity change during native-assist navigation.");
31 await protection.DisposeAsync();protection=await FlightTestProtectionScope.ArmAsync(pid,!manualVisual);
31 await protection.DisposeAsync();protection=await FlightTestProtectionScope.ArmAsync(pid,!manualVisual,!keepHandoffScene);
32 32 }
33 33 var driver=new NativeSystemFlightControlDriver(pid,hash,()=>journal.State,new NativeGameInputChannel(pid,hash,Path.GetFullPath("XFEEDToolBox/PluginPackage")),EnsureProtection);
34 34 var policy=new SystemFlightNavigator();var arrived=false;var released=false;var cruiseEngaged=false;var dockingGranted=false;var dockingEngaged=false;string? failure=null;
@@ -40,7 +40,7 @@ internal static class LocalNavigationAcceptance
40 40 async Task Record(object value){await log.WriteLineAsync(JsonSerializer.Serialize(value));await log.FlushAsync();}
41 41 try
42 42 {
43 protection=await FlightTestProtectionScope.ArmAsync(pid,!manualVisual);using var stop=CancellationTokenSource.CreateLinkedTokenSource(externalToken);stop.CancelAfter(TimeSpan.FromMinutes(10));
43 protection=await FlightTestProtectionScope.ArmAsync(pid,!manualVisual,!keepHandoffScene);using var stop=CancellationTokenSource.CreateLinkedTokenSource(externalToken);stop.CancelAfter(TimeSpan.FromMinutes(10));
44 44 await NavigationRecovery.ReadWithRecoveryAsync(async token=>{await driver.SelectAsync(target,token);return true;},null,stop.Token);
45 45 await driver.PrepareAsync(stop.Token);
46 46 while(true)
@@ -102,11 +102,11 @@ internal static class LocalNavigationAcceptance
102 102 {
103 103 if(decision.Actions.HasFlag(SystemNavigationAction.ExitForFailedHandoff))
104 104 {
105 await Record(new{at=DateTimeOffset.UtcNow,protectiveExit=!manualVisual,manualVisualHandoffStop=manualVisual,frame,decision});
105 await Record(new{at=DateTimeOffset.UtcNow,protectiveExit=!(manualVisual||keepHandoffScene),manualVisualHandoffStop=manualVisual,keepHandoffScene,frame,decision});
106 106 await driver.ApplySystemFlightAsync(decision,frame,target,frame.ObservedAt.AddMilliseconds(250),stop.Token);
107 107 }
108 throw new InvalidOperationException(manualVisual&&decision.Actions.HasFlag(SystemNavigationAction.ExitForFailedHandoff)
109 ?"人工观察模式:交接未完成,已停止自动输入,游戏保持运行。":decision.Message);
108 throw new InvalidOperationException((manualVisual||keepHandoffScene)&&decision.Actions.HasFlag(SystemNavigationAction.ExitForFailedHandoff)
109 ?"交接未完成,已停止自动输入,游戏保持运行。":decision.Message);
110 110 }
111 111 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.");
112 112 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;}
@@ -120,7 +120,7 @@ internal static class LocalNavigationAcceptance
120 120 {
121 121 object provenance;
122 122 try{provenance=await NativeDockingInputDiagnostics.ReadAsync(pid,diagnosticToken);}
123 catch(Exception error){provenance=new{failure=error.Message,processAccess="QUERY_INFORMATION | VM_READ",mutationsSent=0};}
123 catch(Exception error){provenance=new{failure=error.ToString(),processAccess="QUERY_INFORMATION | VM_READ",mutationsSent=0};}
124 124 await File.WriteAllTextAsync(reportPath+".input-provenance.json",JsonSerializer.Serialize(provenance,ToolboxSettings.JsonOptions));
125 125 });
126 126 }
@@ -146,7 +146,7 @@ internal static class LocalNavigationAcceptance
146 146 if(dockingDiagnostics is not null)try{await dockingDiagnostics.WaitAsync(TimeSpan.FromSeconds(3));}catch{}
147 147 }
148 148 var passed=arrived&&released&&failure is null;
149 await File.WriteAllTextAsync(reportPath,JsonSerializer.Serialize(new{pid,start,finished=DateTimeOffset.UtcNow,passed,arrived,released,manualVisual,automaticExitEnabled=!manualVisual,requireCruise,useNativeCruise,cruiseObserved,safeDropRequested,cruiseEngaged,dockingGranted,dockingEngaged,inputs,maxHeat,closest,
149 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,
150 150 fullThrottleApproachSamples,peakNormalSpeedFraction,firstAtcSpeed,firstNativeLandingSpeed,
151 151 allowObscuration,obscuredSamples,nativeObscuredSamples,bypassFullThrottleSamples,bypassCleared,clearThrottleZeroSamples,nativeCruiseAfterBypass,nativeCruiseEnabledSamples,cruiseActivationRequests,
152 152 finalMarketId=journal.State.MarketId,finalDocked=journal.State.Docked,failure,tradeOrdersSent=0,completeNavigationAccepted=false},ToolboxSettings.JsonOptions));
Modified research/NativeAcceptance/ManualVisualHandoff.cs +1 -1
@@ -19,7 +19,7 @@ internal static class ManualVisualHandoff
19 19 if(!NavigationAssists.From(state).AutoDock||!NavigationAssists.From(state).Supercruise)
20 20 throw new InvalidOperationException("当前装备不满足原生辅助交接观察。");
21 21 var pipe=new NavigationPipeClient(pid);var hello=await pipe.CallAsync(new{op="describe"});
22 if(hello.GetProperty("gameSha256").GetString()!=NativeSystemLocationReader.GameSha256||hello.GetProperty("version").GetString()!="0.5.11-dev")
22 if(hello.GetProperty("gameSha256").GetString()!=NativeSystemLocationReader.GameSha256||hello.GetProperty("version").GetString()!="0.5.12-dev")
23 23 throw new InvalidDataException("人工观察所绑定的游戏构建或插件候选版本已改变。");
24 24 var health=await pipe.CallAsync(new{op="health.read"});var h=health.GetProperty("shipHealth");
25 25 var at=DateTimeOffset.FromUnixTimeMilliseconds(h.GetProperty("observedAtUnixMilliseconds").GetInt64());
Modified research/NativeAcceptance/Program.cs +2 -0
@@ -176,6 +176,8 @@ if(args.Length==4&&args[0]=="accept-local-software-cruise")
176 176 {await LocalNavigationAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],true,false);return;}
177 177 if(args.Length==4&&args[0]=="accept-local-docking")
178 178 {await LocalNavigationAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],false);return;}
179 if(args.Length==4&&args[0]=="accept-local-docking-keep-scene")
180 {await LocalNavigationAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],requireCruise:false,keepHandoffScene:true);return;}
179 181 if(args.Length==4&&args[0]=="accept-local-launch")
180 182 {await LocalLaunchAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
181 183 if(args.Length==4&&args[0]=="accept-local-launch-cancel")