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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

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

XFEstudio/XFEEDToolBox

Add expiring original SCO control and coherent live qualification

Dispatch original enable and disable only on the bound main-ship update, retain heat/fuel/geometry/deadline guards, and require five distinct samples plus one matching original stop and later Off state. Correct sampling-before-clock evaluation found by the first empty SC trial; live SCO qualification remains required before production use. Validation: 10 native guard regressions, 9 cycle qualification counterexamples, 7 fuel conversion regressions, isolated NativeAOT and deployment CLI builds. The initial live trial reached supercruise but was refused before SCO activation; its failure and same-process recovery remain preserved.

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

代码差异

18 个文件 +752 -10
Added XFEEDToolBox.Core/Navigation/CruiseOverchargeNativeGuard.cs +51 -0
@@ -0,0 +1,51 @@
1 namespace XFEEDToolBox.Core.Navigation;
2
3 public sealed record CruiseOverchargeGateFrame
4 {
5 public bool IdentityVerified{get;init;}
6 public bool GeometryVerified{get;init;}
7 public bool TargetVerified{get;init;}
8 public bool InSupercruise{get;init;}
9 public bool AssistEnabled{get;init;}
10 public bool CollisionCourse{get;init;}
11 public bool Emergency{get;init;}
12 public bool NativeCanEngage{get;init;}
13 public int RawFsdState{get;init;}
14 public int OverchargeState{get;init;}=-1;
15 public ulong ObservedAt{get;init;}
16 public double Hull{get;init;}=double.NaN;
17 public double Heat{get;init;}=double.NaN;
18 public double HeatRise{get;init;}=double.NaN;
19 public double MainFuel{get;init;}=double.NaN;
20 public double FuelCapacity{get;init;}=double.NaN;
21 public double FuelRateUpper{get;init;}=double.NaN;
22 public double RequiredFuelReserve{get;init;}=double.NaN;
23 public double Distance{get;init;}=double.NaN;
24 public double Speed{get;init;}=double.NaN;
25 public double AimErrorDegrees{get;init;}=double.NaN;
26 }
27
28 /// <summary>Additional per-update SCO interlock, evaluated inside the original owning thread.</summary>
29 public static class CruiseOverchargeNativeGuard
30 {
31 public static string? Refusal(CruiseOverchargeGateFrame value,ulong now,ulong deadline)
32 {
33 if(!value.IdentityVerified||!value.TargetVerified)return "identity-or-target";
34 if(value.ObservedAt>now||now-value.ObservedAt>200||deadline<=now||deadline-now>250)return "expired";
35 if(!value.InSupercruise||value.RawFsdState!=2||value.OverchargeState is <0 or >2)return "flight-state";
36 if(!value.GeometryVerified||value.CollisionCourse||value.AssistEnabled)return "geometry-or-assist";
37 if(value.Emergency||!double.IsFinite(value.Hull)||value.Hull<.5)return "health";
38 if(!value.NativeCanEngage)return "native-refusal";
39 var active=value.OverchargeState is 1 or 2;
40 if(!double.IsFinite(value.Distance)||!double.IsFinite(value.Speed)||value.Speed<0||!double.IsFinite(value.AimErrorDegrees)
41 ||value.Distance<(active?10:15)*299792458d||value.Distance/Math.Max(1,value.Speed)<=(active?30:45)
42 ||value.AimErrorDegrees<0||value.AimErrorDegrees>(active?6:3))return "approach-or-alignment";
43 if(!double.IsFinite(value.Heat)||!double.IsFinite(value.HeatRise)||value.Heat<0||Math.Abs(value.HeatRise)>1000
44 ||value.Heat>=75||!active&&value.Heat>60||value.Heat+Math.Max(0,value.HeatRise)*2+3>=75)return "heat";
45 if(!double.IsFinite(value.MainFuel)||!double.IsFinite(value.FuelCapacity)||value.FuelCapacity<=0||value.MainFuel<0||value.MainFuel>value.FuelCapacity+.001
46 ||!double.IsFinite(value.RequiredFuelReserve)||value.RequiredFuelReserve<=0||value.RequiredFuelReserve>=value.FuelCapacity
47 ||!double.IsFinite(value.FuelRateUpper)||value.FuelRateUpper<=0
48 ||value.MainFuel-value.RequiredFuelReserve-value.FuelRateUpper*2-Math.Max(.25,value.FuelCapacity*.01)<=0)return "fuel";
49 return null;
50 }
51 }
Added XFEEDToolBox.Core/Navigation/NativeCruiseControlChannel.cs +119 -0
@@ -0,0 +1,119 @@
1 using System.Globalization;
2 using System.Text.Json;
3
4 namespace XFEEDToolBox.Core.Navigation;
5
6 public sealed record NativeCruiseControlCapabilities(bool Available,bool Qualified,string Service,int OwningThread);
7
8 /// <summary>Program-owned SCO policy and bounded original-command receipts. Never steers or selects a target.</summary>
9 public sealed class NativeCruiseControlChannel(int processId,string gameSha256)
10 {
11 public const string Service="CruiseControl0514c";
12 private readonly NavigationPipeClient pipe=new(processId,Service);
13 private readonly CruiseOverchargeSampleHistory history=new(gameSha256);
14 private readonly CruiseOverchargePolicy policy=new();
15 private ulong entity,world;
16 private bool leaseRequested,originalStartWitness;
17 private string owner="";
18 private string? pendingStopId,lastStopIdentity;
19 public JsonElement? LastStatus{get;private set;}
20 public CruiseOverchargeObservation? LastObservation{get;private set;}
21 public CruiseOverchargeDecision? LastDecision{get;private set;}
22 public event Action<JsonElement>? Observed;
23 public event Action<JsonElement>? OriginalReceipt;
24 public event Action<JsonElement>? OriginalStopReceipt;
25 private static bool Flag(JsonElement value,string key)=>value.TryGetProperty(key,out var found)&&found.ValueKind==JsonValueKind.True;
26 private static ulong Identity(JsonElement value,string key)=>ulong.Parse(value.GetProperty(key).GetString()!,CultureInfo.InvariantCulture);
27 private void CheckIdentity(JsonElement result)
28 {
29 if(result.GetProperty("schema").GetInt32()!=1||result.GetProperty("processId").GetInt32()!=processId
30 ||result.GetProperty("gameSha256").GetString()!=gameSha256||result.GetProperty("service").GetString()!=Service
31 ||!Flag(result,"overchargeControl")||!Flag(result,"enabled"))throw new InvalidDataException("Original SCO controller does not match this client.");
32 }
33 private void Capture(JsonElement result)
34 {
35 CheckIdentity(result);LastStatus=result.Clone();
36 if(result.TryGetProperty("lastOriginalStop",out var stop)&&stop.ValueKind==JsonValueKind.Object&&stop.GetProperty("owner").GetString()==owner)
37 {
38 var id=stop.GetRawText();if(id!=lastStopIdentity){lastStopIdentity=id;OriginalStopReceipt?.Invoke(stop.Clone());}
39 }
40 // A real Off sample in this response follows its original stop receipt.
41 Observed?.Invoke(result.Clone());
42 }
43 public async Task<NativeCruiseControlCapabilities> InspectAsync(CancellationToken token)
44 {
45 var result=await pipe.CallAsync(new{op="describe"},token);CheckIdentity(result);
46 return new(true,Flag(result,"overchargeControlQualified"),Service,result.GetProperty("owningThread").GetInt32());
47 }
48 public async Task UpdateAsync(string controllerOwner,ulong selectionGeneration,SystemFlightFrame flight,SystemFlightDecision decision,
49 SystemNavigationTarget target,double reserve,DateTimeOffset deadline,CancellationToken token)
50 {
51 if(owner.Length!=0&&owner!=controllerOwner)throw new InvalidOperationException("SCO channel is already bound to another controller.");
52 owner=controllerOwner;
53 var mayBoost=flight.Mode==LocalFlightMode.Supercruise&&decision.Actions==SystemNavigationAction.None
54 &&decision.Phase is SystemNavigationPhase.Supercruise or SystemNavigationPhase.Aligning;
55 if(!mayBoost){await ReleaseAsync(token);return;}
56 if(flight.GameSha256!=gameSha256||flight.ShipEntity==0||flight.ReferenceWorld==0||selectionGeneration==0)
57 throw new InvalidDataException("SCO requires the current original flight target and controller.");
58 if(entity!=flight.ShipEntity||world!=flight.ReferenceWorld)
59 {
60 await ReleaseAsync(token);var bound=await pipe.CallAsync(new{op="watch"},token);Capture(bound);
61 entity=flight.ShipEntity;world=flight.ReferenceWorld;history.Reset();originalStartWitness=false;
62 }
63 var status=await pipe.CallAsync(new{op="read"},token);Capture(status);
64 var observed=history.Read(status,flight,DateTimeOffset.UtcNow);LastObservation=observed;
65 if(!Flag(status,"owned")&&status.TryGetProperty("sample",out var sample)&&sample.ValueKind==JsonValueKind.Object&&!Flag(sample,"active"))originalStartWitness=false;
66 var selected=policy.Decide(flight,observed,reserve,DateTimeOffset.UtcNow);LastDecision=selected;
67 if(!selected.UseOvercharge){await ReleaseAsync(token);return;}
68 var requestId=Guid.NewGuid().ToString("N");
69 // Mark before IPC: a lost reply may still have queued an original call.
70 leaseRequested=true;
71 var result=await pipe.CallAsync(new{op="cruise.set",owner,requestId,selectionGeneration,sequence=flight.Sequence,
72 expiresAtUnixMilliseconds=deadline.ToUnixTimeMilliseconds(),requiredFuelReserveTonnes=reserve,
73 target=new{key=target.NativeKey,bodyAddress=target.BodyAddress.ToString(CultureInfo.InvariantCulture),kind=target.NativeKind,marketId=target.MarketId??uint.MaxValue,name=target.Name}},token);
74 var command=result.GetProperty("generation").GetUInt64();
75 while(true)
76 {
77 Capture(result);
78 if(result.TryGetProperty("receipt",out var receipt)&&receipt.ValueKind==JsonValueKind.Object&&receipt.GetProperty("requestId").GetString()==requestId)
79 {
80 if(Flag(receipt,"failed"))throw new FlightTelemetryUnavailableException("Original SCO interlock refused: "+(receipt.TryGetProperty("reason",out var why)?why.GetString():"unknown"));
81 if(Flag(receipt,"applied"))
82 {
83 if(receipt.GetProperty("owner").GetString()!=owner||receipt.GetProperty("generation").GetUInt64()!=command
84 ||receipt.GetProperty("selectionGeneration").GetUInt64()!=selectionGeneration||Identity(receipt,"entityId")!=entity||Identity(receipt,"referenceWorldAddress")!=world)
85 throw new InvalidDataException("Original SCO receipt identity differs.");
86 var called=Flag(receipt,"nativeMethodCalled");
87 if(called)
88 {
89 if(receipt.GetProperty("nativeMethodRva").GetString()!="2eaacd0"||!Flag(receipt,"argument")||receipt.GetProperty("actualStateAfterCall").GetInt32() is not(1 or 2))
90 throw new InvalidDataException("Original SCO enable was not confirmed.");
91 originalStartWitness=true;
92 }
93 else if(!originalStartWitness)throw new InvalidDataException("SCO renewal has no original enable witness.");
94 OriginalReceipt?.Invoke(receipt.Clone());return;
95 }
96 }
97 if(DateTimeOffset.UtcNow>=deadline)throw new FlightTelemetryUnavailableException("Original SCO command expired without a matching receipt.");
98 await Task.Delay(10,token);result=await pipe.CallAsync(new{op="read"},token);
99 }
100 }
101 public async Task ReleaseAsync(CancellationToken token=default)
102 {
103 if(!leaseRequested)return;
104 pendingStopId??=Guid.NewGuid().ToString("N");
105 var result=await pipe.CallAsync(new{op="cruise.release",owner,requestId=pendingStopId},token);
106 var until=DateTimeOffset.UtcNow.AddSeconds(2);
107 while(true)
108 {
109 var status=await pipe.CallAsync(new{op="read"},token);Capture(status);
110 if(!Flag(status,"owned")&&Flag(status,"sampleReady")&&status.TryGetProperty("sample",out var sample)&&sample.ValueKind==JsonValueKind.Object
111 &&Identity(sample,"entityId")==entity&&Identity(sample,"referenceWorldAddress")==world&&sample.GetProperty("state").GetInt32() is 0 or 3)
112 {leaseRequested=false;originalStartWitness=false;pendingStopId=null;return;}
113 if(!Flag(status,"owned")&&result.TryGetProperty("released",out var released)&&released.ValueKind==JsonValueKind.True&&!originalStartWitness)
114 {leaseRequested=false;pendingStopId=null;return;}
115 if(DateTimeOffset.UtcNow>=until)throw new FlightTelemetryUnavailableException("Waiting for the owned SCO to report its real stopped state.");
116 await Task.Delay(15,token);
117 }
118 }
119 }
Modified XFEEDToolBox.Core/Navigation/NativeFlightControlDriver.cs +2 -2
@@ -9,7 +9,7 @@ namespace XFEEDToolBox.Core.Navigation;
9 9 public sealed class NavigationPipeClient(int processId,string service="Navigation")
10 10 {
11 11 public int ProcessId { get; }=processId>0?processId:throw new ArgumentOutOfRangeException(nameof(processId));
12 private readonly string serviceName=service is "Navigation" or "Interaction" or "SessionRecovery0514" or "SessionRecovery0514c" or "SessionRecovery0514d" or "SessionRecovery0514e" or "SessionRecovery0514f" or "SessionRecovery0514g" or "CruiseObservation0514a" or "CruiseObservation0514b" or "CruiseObservation0514c"?service:throw new ArgumentException("Unknown game service.",nameof(service));
12 private readonly string serviceName=service is "Navigation" or "Interaction" or "SessionRecovery0514" or "SessionRecovery0514c" or "SessionRecovery0514d" or "SessionRecovery0514e" or "SessionRecovery0514f" or "SessionRecovery0514g" or "CruiseObservation0514a" or "CruiseObservation0514b" or "CruiseObservation0514c" or "CruiseControl0514a" or "CruiseControl0514b" or "CruiseControl0514c"?service:throw new ArgumentException("Unknown game service.",nameof(service));
13 13 private readonly SemaphoreSlim gate=new(1,1);
14 14 public async Task<JsonElement> CallAsync(object request,CancellationToken token=default)
15 15 {
@@ -19,7 +19,7 @@ public sealed class NavigationPipeClient(int processId,string service="Navigatio
19 19 using var deadline=CancellationTokenSource.CreateLinkedTokenSource(token);deadline.CancelAfter(TimeSpan.FromMilliseconds(500));
20 20 var data=JsonSerializer.SerializeToUtf8Bytes(request,ToolboxSettings.JsonOptions);
21 21 using(var guardRequest=JsonDocument.Parse(data))
22 if(guardRequest.RootElement.TryGetProperty("op",out var operation)&&operation.GetString() is "apply" or "local.apply" or "local.action" or "fsd" or "fsd.command" or "input.set" or "calibrate.axes" or "calibrate.fsd" or "inputs.set" or "commerce.trade" or "commerce.trade.batch" or "cg.join" or "station.services" or "docking.validation.arm" or "session.recovery.leave" or "docking.reset.arm")
22 if(guardRequest.RootElement.TryGetProperty("op",out var operation)&&operation.GetString() is "apply" or "local.apply" or "local.action" or "fsd" or "fsd.command" or "input.set" or "calibrate.axes" or "calibrate.fsd" or "inputs.set" or "commerce.trade" or "commerce.trade.batch" or "cg.join" or "station.services" or "docking.validation.arm" or "session.recovery.leave" or "docking.reset.arm" or "cruise.set")
23 23 XFEEDToolBox.Core.Session.NativeFlightTestProtection.CheckBeforeInput(ProcessId);
24 24 if(data.Length>=65535)throw new InvalidDataException("导航指令超出消息上限。");
25 25 using var pipe=new NamedPipeClientStream(".",$"XFEEDToolBox.{serviceName}.{ProcessId}",PipeDirection.InOut,PipeOptions.Asynchronous);
Added XFEEDToolBox.Core/Navigation/NativeSystemFlightControlDriver.CruiseControl.cs +18 -0
@@ -0,0 +1,18 @@
1 namespace XFEEDToolBox.Core.Navigation;
2
3 public sealed partial class NativeSystemFlightControlDriver
4 {
5 private NativeCruiseControlChannel? originalCruiseControl;
6 private Func<double>? originalCruiseFuelReserve;
7 public NativeCruiseControlChannel? CruiseControl=>originalCruiseControl;
8 public async Task ConfigureOriginalCruiseControlAsync(Func<double> fuelReserve,bool allowUnqualifiedEmptyTrial=false,CancellationToken token=default)
9 {
10 if(useNativeSupercruiseAssist)throw new InvalidOperationException("Original SCO requires toolbox navigation with game SCA disabled.");
11 var state=player();if(allowUnqualifiedEmptyTrial&&(!state.InGame||!state.Docked||state.CurrentShipId is null||state.CargoCount!=0))throw new InvalidOperationException("Unqualified SCO testing requires the empty ship to remain docked during setup.");
12 var channel=new NativeCruiseControlChannel(processId,gameHash);var capabilities=await channel.InspectAsync(token);
13 if(!capabilities.Available||!allowUnqualifiedEmptyTrial&&!capabilities.Qualified)throw new NotSupportedException("Original SCO controls have not completed live qualification.");
14 originalCruiseControl=channel;originalCruiseFuelReserve=fuelReserve;
15 }
16 private Task UpdateOriginalCruiseAsync(SystemFlightDecision decision,SystemFlightFrame frame,SystemNavigationTarget target,DateTimeOffset deadline,CancellationToken token)
17 =>originalCruiseControl?.UpdateAsync(owner,checked((ulong)generation),frame,decision,target,originalCruiseFuelReserve!(),deadline,token)??Task.CompletedTask;
18 }
Modified XFEEDToolBox.Core/Navigation/NativeSystemFlightControlDriver.cs +3 -1
@@ -264,6 +264,7 @@ public sealed partial class NativeSystemFlightControlDriver(int processId,string
264 264 if(ensureFlightProtection is not null)await ensureFlightProtection(token);
265 265 if(selected is null||!selected.SameIdentity(target)||last?.Sequence!=reference.Sequence)throw new InvalidOperationException("驾驶指令与目标或飞行帧不一致。");
266 266 if(expiresAt<=DateTimeOffset.UtcNow)throw new FlightTelemetryUnavailableException("正在刷新星系内飞行数据。");
267 await UpdateOriginalCruiseAsync(decision,reference,target,expiresAt,token);
267 268 if(decision.Actions.HasFlag(SystemNavigationAction.ExitForFailedHandoff))
268 269 {
269 270 emergencyExited=await Session.NativeFlightTestProtection.HandleFailedHandoffAsync(processId);
@@ -405,9 +406,10 @@ public sealed partial class NativeSystemFlightControlDriver(int processId,string
405 406 }
406 407 }
407 408 public async Task SuspendSystemFlightAsync()
408 {attitude.Reset();await pipe.CallAsync(new{op="local.suspend",owner});}
409 {if(originalCruiseControl is not null)await originalCruiseControl.ReleaseAsync();attitude.Reset();await pipe.CallAsync(new{op="local.suspend",owner});}
409 410 public async Task ReleaseSystemFlightAsync()
410 411 {
412 if(originalCruiseControl is not null)await originalCruiseControl.ReleaseAsync();
411 413 await CancelOriginalDockingResetAsync();
412 414 if(emergencyExited){attitude.Reset();ownsCruiseAssist=ownsDockingAssist=false;ownsLaunch=false;return;}
413 415 try
Added XFEEDToolBox.Core/Navigation/OriginalCruiseCycleQualification.cs +42 -0
@@ -0,0 +1,42 @@
1 namespace XFEEDToolBox.Core.Navigation;
2
3 public sealed record OriginalCruiseCycleIdentity(string Owner,string StartRequestId,ulong SelectionGeneration,ulong Entity,ulong World);
4 public sealed record OriginalCruiseCycleEvidence(OriginalCruiseCycleIdentity Identity,int ActiveSamples,ulong EnableAt,ulong LastActiveAt,string StopRequestId,ulong StopAt,ulong OffAt);
5
6 /// <summary>A native qualification belongs to one original enable, five distinct updates and an explicit original stop.</summary>
7 public sealed class OriginalCruiseCycleQualification
8 {
9 private OriginalCruiseCycleIdentity? current;
10 private ulong enableAt,lastActiveAt,stopAt;
11 private string stopRequestId="";
12 public int ActiveSamples{get;private set;}
13 public OriginalCruiseCycleEvidence? Completed{get;private set;}
14 public bool Qualified=>Completed is not null;
15 private static bool Key(string value)=>value.Length==32&&value.All(c=>c is >= '0' and <= '9' or >= 'a' and <= 'f');
16 public void ForgetCurrent(){current=null;enableAt=lastActiveAt=stopAt=0;stopRequestId="";ActiveSamples=0;}
17 public bool Begin(OriginalCruiseCycleIdentity identity,ulong at,bool originalCalled,int stateAfter)
18 {
19 ForgetCurrent();
20 if(!originalCalled||stateAfter is not(1 or 2)||at==0||!Key(identity.Owner)||!Key(identity.StartRequestId)
21 ||identity.SelectionGeneration==0||identity.Entity==0||identity.World==0)return false;
22 current=identity;enableAt=at;return true;
23 }
24 public bool Stop(OriginalCruiseCycleIdentity identity,string requestId,ulong at,bool originalCalled,int stateBefore,int stateAfter,bool explicitlyReleased)
25 {
26 stopAt=0;stopRequestId="";
27 if(current!=identity||!originalCalled||!explicitlyReleased||!Key(requestId)||stateBefore is not(1 or 2)||stateAfter is not(0 or 3)
28 ||at<enableAt||at<lastActiveAt||ActiveSamples<5)return false;
29 stopRequestId=requestId;stopAt=at;return true;
30 }
31 public void Observe(ulong entity,ulong world,ulong at,int actualState)
32 {
33 if(current is null||entity!=current.Entity||world!=current.World||at<enableAt)return;
34 if(stopAt==0)
35 {
36 if(actualState==2&&at>lastActiveAt){lastActiveAt=at;ActiveSamples++;}
37 return;
38 }
39 if(actualState is 0 or 3&&at>stopAt)
40 Completed=new(current,ActiveSamples,enableAt,lastActiveAt,stopRequestId,stopAt,at);
41 }
42 }
Modified XFEEDToolBox.Core/Navigation/OriginalFuelConsumptionBounds.cs +10 -0
@@ -7,6 +7,16 @@ public static class OriginalFuelConsumptionBounds
7 7 // This is an energy conversion, not an assumed kg/tonne or hours/second factor.
8 8 public const double OriginalEnergyConversion=45000;
9 9
10 public static bool TryConvertRange(OriginalCurveRange energyCurve,double moduleMultiplier,double fuelConversion,
11 out OriginalCurveRange tonnesPerSecond)
12 {
13 tonnesPerSecond=default;
14 if(!TryConvert(energyCurve,moduleMultiplier,fuelConversion,out var upper))return false;
15 var lower=Math.Max(0,energyCurve.Minimum*moduleMultiplier/(fuelConversion*OriginalEnergyConversion)*(1-1e-5)-1e-10);
16 if(!double.IsFinite(lower)||lower>upper)return false;
17 tonnesPerSecond=new(lower,upper);return true;
18 }
19
10 20 public static bool TryConvert(OriginalCurveRange energyCurve,double moduleMultiplier,double fuelConversion,
11 21 out double upperTonnesPerSecond)
12 22 {
Added XFEEDToolBox.Test/CruiseOverchargeNativeGuardTests.cs +36 -0
@@ -0,0 +1,36 @@
1 using XFEEDToolBox.Core.Navigation;
2 using XFEExtension.NetCore.XUnit.Attributes;
3 namespace XFEEDToolBox.Test;
4
5 [TestFixture]
6 public class CruiseOverchargeNativeGuardTests
7 {
8 private static CruiseOverchargeGateFrame Good=>new(){IdentityVerified=true,GeometryVerified=true,TargetVerified=true,InSupercruise=true,NativeCanEngage=true,
9 RawFsdState=2,OverchargeState=0,ObservedAt=1000,Hull=1,Heat=40,HeatRise=0,MainFuel=32,FuelCapacity=32,FuelRateUpper=1,RequiredFuelReserve=8,Distance=1e12,Speed=1e6,AimErrorDegrees=0};
10 private static string? Refuse(CruiseOverchargeGateFrame? frame=null,ulong now=1100,ulong deadline=1250)=>CruiseOverchargeNativeGuard.Refusal(frame??Good,now,deadline);
11 [Test]public void VerifiedOriginalDistantAlignedShipIsAllowed()=>Check.That(Refuse() is null);
12 [Test]public void ALeaseCannotOutliveItsOriginalDeadline()
13 {Check.Equal("expired",Refuse(now:1250));Check.Equal("expired",Refuse(deadline:1351));Check.Equal("expired",Refuse(Good with{ObservedAt=1101}));}
14 [Test]public void IdentityTargetAndAssistChangesStopBoost()
15 {Check.That(Refuse(Good with{TargetVerified=false}) is not null);Check.That(Refuse(Good with{IdentityVerified=false}) is not null);Check.That(Refuse(Good with{AssistEnabled=true}) is not null);}
16 [Test]public void NormalSpaceUnknownFsdAndCoolingCannotStart()
17 {Check.That(Refuse(Good with{InSupercruise=false}) is not null);Check.That(Refuse(Good with{RawFsdState=3}) is not null);Check.That(Refuse(Good with{OverchargeState=3}) is not null);}
18 [Test]public void AcknowledgementDoesNotReplaceOriginalReadiness()
19 {Check.That(Refuse(Good with{NativeCanEngage=false}) is not null);Check.That(Refuse(Good with{GeometryVerified=false}) is not null);}
20 [Test]public void DamageAndCollisionStopBoostImmediately()
21 {Check.That(Refuse(Good with{Hull=.49}) is not null);Check.That(Refuse(Good with{Emergency=true}) is not null);Check.That(Refuse(Good with{CollisionCourse=true}) is not null);}
22 [Test]public void RisingHeatAndUnknownSlopeStopBeforeCeiling()
23 {Check.Equal("heat",Refuse(Good with{Heat=50,HeatRise=12}));Check.Equal("heat",Refuse(Good with{HeatRise=double.NaN}));Check.Equal("heat",Refuse(Good with{Heat=75,OverchargeState=2}));}
24 [Test]public void LargeShipsUseTheirOwnFuelRateAndReserve()
25 {Check.That(Refuse(Good with{MainFuel=128,FuelCapacity=128,FuelRateUpper=12,RequiredFuelReserve=70}) is null);Check.Equal("fuel",Refuse(Good with{MainFuel=14,FuelRateUpper=4}));}
26 [Test]public void UnknownFuelOrGeometryCannotEnable()
27 {Check.Equal("fuel",Refuse(Good with{FuelRateUpper=double.NaN}));Check.Equal("approach-or-alignment",Refuse(Good with{Distance=1e8}));Check.Equal("approach-or-alignment",Refuse(Good with{AimErrorDegrees=4}));}
28 [Test]public void ANewFrameIsEvaluatedAgainstTheTimeAfterItWasSampled()
29 {
30 var observed=Good with{ObservedAt=1101};
31 Check.Equal("expired",Refuse(observed,now:1100));
32 Check.That(Refuse(observed,now:1102) is null);
33 Check.Equal("expired",Refuse(observed,now:1302,deadline:1350));
34 Check.Equal("expired",Refuse(observed,now:1250));
35 }
36 }
Added XFEEDToolBox.Test/OriginalCruiseCycleQualificationTests.cs +30 -0
@@ -0,0 +1,30 @@
1 using XFEEDToolBox.Core.Navigation;
2 using XFEExtension.NetCore.XUnit.Attributes;
3 namespace XFEEDToolBox.Test;
4
5 [TestFixture]
6 public class OriginalCruiseCycleQualificationTests
7 {
8 private static readonly OriginalCruiseCycleIdentity Id=new(new('a',32),new('b',32),4,12,24);
9 private static readonly string StopId=new('c',32);
10 private static OriginalCruiseCycleQualification Started(int count=5)
11 {var proof=new OriginalCruiseCycleQualification();Check.That(proof.Begin(Id,1000,true,1));for(var i=1;i<=count;i++)proof.Observe(12,24,(ulong)(1000+i*20),2);return proof;}
12 [Test]public void OneOriginalCycleRequiresFiveUpdatesAndLaterOff()
13 {var proof=Started();Check.That(proof.Stop(Id,StopId,1120,true,2,3,true));Check.That(!proof.Qualified);proof.Observe(12,24,1140,3);Check.That(proof.Qualified);Check.Equal(5,proof.Completed!.ActiveSamples);}
14 [Test]public void DuplicateAndReversedTimestampsCannotCountAsFiveUpdates()
15 {var proof=Started(0);for(var i=0;i<8;i++){proof.Observe(12,24,1020,2);proof.Observe(12,24,1010,2);}Check.Equal(1,proof.ActiveSamples);Check.That(!proof.Stop(Id,StopId,1120,true,2,0,true));}
16 [Test]public void AnotherShipOrReferenceCannotSupplyActiveFrames()
17 {var proof=Started(0);for(var i=1;i<8;i++){proof.Observe(13,24,(ulong)(1000+i*20),2);proof.Observe(12,25,(ulong)(1000+i*20),2);}Check.Equal(0,proof.ActiveSamples);}
18 [Test]public void ASecondEnableCannotBorrowTheEarlierCyclesFrames()
19 {var proof=Started();var next=Id with{StartRequestId=new('d',32)};Check.That(proof.Begin(next,1200,true,1));Check.Equal(0,proof.ActiveSamples);Check.That(!proof.Stop(next,StopId,1250,true,2,0,true));}
20 [Test]public void AcknowledgedEnableWithoutOriginalCallCannotQualify()
21 {var proof=new OriginalCruiseCycleQualification();Check.That(!proof.Begin(Id,1000,false,1));for(var i=1;i<8;i++)proof.Observe(12,24,(ulong)(1000+i*20),2);Check.That(!proof.Stop(Id,StopId,1200,true,2,0,true));}
22 [Test]public void StopMustMatchOwnerRequestSelectionAndShip()
23 {foreach(var different in new[]{Id with{Owner=new('d',32)},Id with{StartRequestId=new('d',32)},Id with{SelectionGeneration=5},Id with{Entity=13},Id with{World=25}}){var proof=Started();Check.That(!proof.Stop(different,StopId,1120,true,2,0,true));proof.Observe(12,24,1140,0);Check.That(!proof.Qualified);}}
24 [Test]public void IdleObservationAndGlobalDisableCountsCannotReplaceAnOriginalStop()
25 {var proof=Started();Check.That(!proof.Stop(Id,StopId,1120,false,0,0,true));proof.Observe(12,24,1140,0);Check.That(!proof.Qualified);}
26 [Test]public void AutomaticExpiryIsNotTheExplicitReleaseQualification()
27 {var proof=Started();Check.That(!proof.Stop(Id,StopId,1120,true,2,3,false));proof.Observe(12,24,1140,3);Check.That(!proof.Qualified);}
28 [Test]public void OffMustFollowTheCallAndBelongToTheSameMainShip()
29 {var proof=Started();Check.That(proof.Stop(Id,StopId,1120,true,2,3,true));proof.Observe(12,24,1120,3);proof.Observe(13,24,1140,3);proof.Observe(12,25,1140,3);Check.That(!proof.Qualified);proof.Observe(12,24,1160,0);Check.That(proof.Qualified);}
30 }
Modified XFEEDToolBox.Test/OriginalFuelConsumptionBoundsTests.cs +7 -0
@@ -6,6 +6,13 @@ namespace XFEEDToolBox.Test;
6 6 [TestFixture]
7 7 public class OriginalFuelConsumptionBoundsTests
8 8 {
9 [Test]public void OriginalCurveLowerAndUpperAreBothConverted()
10 {
11 Check.That(OriginalFuelConsumptionBounds.TryConvertRange(new(450,900),2,.5,out var range));
12 Check.That(range.Minimum>.03999&&range.Minimum<.04&&range.Maximum>.08&&range.Maximum<.08001);
13 }
14 [Test]public void RoundingCannotTurnAZeroCurveMinimumIntoNegativeFuel()
15 {Check.That(OriginalFuelConsumptionBounds.TryConvertRange(new(-1e-5,1),45000,1,out var range));Check.That(range.Minimum==0&&range.Maximum>1);}
9 16 [Test]public void OriginalEnergyUnitsAreConvertedUsingTheShipsFuelParameter()
10 17 {
11 18 Check.That(OriginalFuelConsumptionBounds.TryConvert(new(450,900),2,.5,out var rate));
Modified docs/TRADE-WORK-COORDINATION.md +13 -0
@@ -2,6 +2,19 @@
2 2
3 3 更新:2026-10-24(Asia/Shanghai)。最新状态以紧接下方的记录为准,后文保留历史故障及验收过程。
4 4
5 ## SCO首次实飞完成SC入口,原期限检查顺序已修正(2026-10-24 16:38)
6
7 - b(SHAd74dcdd766e36cfee679ee8e4e36108216ad0c614de9f15f349a6d1a2cc2c868)已实际加载且8次2047自测/协议通过;a仅停用自身hook。b将qualification限定同owner/startRequest/selection/实体/参考系内五个不同post-update采样、显式原false回执及之后Off,9项反例通过,不使用全局启停累计量拼接验收。
8 - 子智能体唯一95792/新ticks639284275904552388执行sco-control-0514b-01:修正后的原bodyAddress→已统一系centre预检通过、原维护0Cr、真实起飞、SC入口及对准A3通过;首SCO请求被expired拒绝,enableCalls=0/Active未成立。原失败保留;原菜单与同PID Solo真实恢复已通过,原recovery-solo.json为证据,不冒充已停靠。恢复新实体11823869650069/空舱/健康100%、Homer约295.6km,正在完成同轮软件SC/g返站及维护,不发第二脉冲。
9 - 拒绝根因已定位为PollControl先取now,再采样得到更新的frame.utcMilliseconds,误触发observedAt>now;实际时间线只过约43ms,非250ms预算耗尽。候选c在完整采样后重新取now,同时保留前置过期、200ms样本、250ms意图及调用前截止复检;10项守卫回归和AOT/CLI构建通过。c已构建SHA78547c29142a9e0d2729f2b76865277a421d0dfe7593a4290162ac5697daf941,尚未部署,不覆盖已加载b。
10 - 首a轮预检的误拒因目录Position在站体系、geometry在统一星系系,现以同bodyAddress原centre及原distance核对,50ls/原误差/排除裕量不变。原数据仅诊断重放找到21个清晰远目标,明确不能用拟合坐标驾驶或将重建header当实机证据。
11 - 子智能体另修本次CLI局部日志未知NaN导致writer fault/最后Save未执行的问题;真实恢复将写独立reconciliation,不覆盖旧失败。保护87996持续,游戏88060未终止。持久.75、Polyakov连续、SCO真实启停、原逃逸矢量与生产自主贸易均仍待验收,禁止新采购;无定时任务/键鼠。
12 ## SCO原生控制已部署,首次空载预检拒绝(2026-10-24 16:26)
13
14 - 仍为游戏88060/同启动身份及唯一独立保护87996;交回前原始Homer ready/serviceReady/fresh/healthy、state5、空舱实体11823841535223均确认,保护ready126ms。c仅停用自身hook,g和基础14包保持。新独立CruiseControl0514a已实际装入,SHA478b841ae633862dd918c53f1d3ca519bafb6a15c4e808955094b8f535c02eea;8次1023原生自测及ABI/实际只读协议通过,控制未qualified,尚无SCO输入。
15 - 原生控制只在本主船更新线程、匹配控制owner/原目标/实体/参考系、250ms以内意图下调用原2EAACD0;热量预测、原始燃油区间/实际储备、避障、受损及5秒单段上限保持。独立原false调用回执与之后实际Off采样分开记录,Core先发布回执再发布同响应观测。没有SCA、直接修改SCO状态或账户字段。
16 - 子智能体唯一runner97292已执行sco-control-0514a-01并终止,原result.passed=false保留:停靠预检未找到通过原目录/距离/排除区核验的至少50ls目标。maintainedBefore/launched/entered/activeFive均false,没有起飞、SCO脉冲或新增订单;不把准备或自测当实飞。正在离线分析已存catalogue/geometry拒绝原因,不能原样重飞或臆造坐标。
17 - 75%持久油门及Polyakov连续衔接仍未通过;f的.05归零与g另轮初始0接管不得拼接为该验收。SCO实际启停、拦截原逃逸矢量、生产工具箱自主跑商也仍未完整验收。财务仍净14716005Cr/钱包40108122Cr、CG384,001/002不重放。没有定时任务。
5 18 ## 空载超巡至原生停靠完整通过,SCO控制接入仍待完成(2026-10-24 16:01)
6 19
7 20 - c已实际加载到88060,SHA db6ceea2a3bcc7b1da571bbae7f176701b5574e17941d8a7b255e9f1f6aeb297;b仅停用自身SCO hook,g未改。新c隔离原生自测8次255、实际加载/只读协议通过,没有SCO启停能力。
Added native/ManagedPlugin/CruiseControl/CruiseControl.csproj +10 -0
@@ -0,0 +1,10 @@
1 <Project Sdk="Microsoft.NET.Sdk">
2 <PropertyGroup><AssemblyName>XFEEDInteraction</AssemblyName><PluginSymbol>PLUGIN_CRUISE_CONTROL</PluginSymbol></PropertyGroup>
3 <Import Project="../NativePlugin.props" />
4 <ItemGroup>
5 <Compile Include="../../../XFEEDToolBox.Core/Navigation/OriginalCurveBounds.cs" Link="OriginalCurveBounds.cs" />
6 <Compile Include="../../../XFEEDToolBox.Core/Navigation/OriginalFuelConsumptionBounds.cs" Link="OriginalFuelConsumptionBounds.cs" />
7 <Compile Include="../../../XFEEDToolBox.Core/Navigation/CruiseOverchargeNativeGuard.cs" Link="CruiseOverchargeNativeGuard.cs" />
8 <Compile Include="../../../XFEEDToolBox.Core/Navigation/OriginalCruiseCycleQualification.cs" Link="OriginalCruiseCycleQualification.cs" />
9 </ItemGroup>
10 </Project>
Added native/ManagedPlugin/CruiseControlRuntime.cs +294 -0
Added native/ManagedPlugin/Navigation.CruiseControl.cs +54 -0
Modified native/ManagedPlugin/Navigation.CruiseObservation.cs +10 -6
Modified native/ManagedPlugin/Navigation.RecoveryResetProof.cs +1 -1
Added research/NativeAcceptance/CruiseControlAcceptance.cs +46 -0
Modified research/NativeAcceptance/Program.cs +6 -0