using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Channels;
using XFEEDToolBox.Core.Data;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Native;
using XFEEDToolBox.Core.Navigation;
using XFEEDToolBox.Core.Session;
using XFEEDToolBox.NativeSupport;
/// <summary>One sustained SCO interval on the already selected, clear SC leg. No launch, trade, target replacement or scene transition.</summary>
internal static class SCOContinuityAcceptance
{
private const double LightSecond = 299792458;
private const double RequestedActiveSeconds = 20;
private static readonly JsonSerializerOptions EvidenceJson = new(ToolboxSettings.JsonOptions)
{ NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals };
private static readonly JsonSerializerOptions EventJson = new(EvidenceJson) { WriteIndented = false };
private static bool True(JsonElement value, string key) => value.TryGetProperty(key, out var item) && item.ValueKind == JsonValueKind.True;
private static Vector3d Vector(JsonElement value, string key)
{ var array = value.GetProperty(key); return new(array[0].GetDouble(), array[1].GetDouble(), array[2].GetDouble()); }
private static string Cargo(PlayerState state) => JsonSerializer.Serialize(state.Inventory
.OrderBy(item => item.Key, StringComparer.Ordinal).ThenBy(item => item.MissionId)
.Select(item => new { item.Key, item.Count, item.Stolen, item.MissionId, item.Group }), ToolboxSettings.JsonOptions);
internal static async Task RunAsync(int pid, string directory)
{
directory = Path.GetFullPath(directory);
if (Directory.Exists(directory)) throw new IOException("Use a fresh SCO continuity evidence directory.");
var parent = Path.GetDirectoryName(directory)!; Directory.CreateDirectory(parent);
using var exclusive = new FileStream(Path.Combine(parent, "docking-fixture-wait.lock"), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
Directory.CreateDirectory(directory);
var stopFile = Path.Combine(directory, "stop");
using var timeout = new CancellationTokenSource(TimeSpan.FromMinutes(3));
using var sentinel = new Timer(_ => { if (File.Exists(stopFile)) timeout.Cancel(); }, null, 100, 100);
var token = timeout.Token;
using var process = Process.GetProcessById(pid); var processStarted = process.StartTime.ToUniversalTime().Ticks;
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(token);
var initial = journal.State; var initialCargo = Cargo(initial); var start = DateTimeOffset.UtcNow;
FlightTestProtectionScope? protection = null; NativeSystemFlightControlDriver? driver = null; SystemNavigationTarget? target = null;
var previousGuardian = PersistentProtectionProcess.Current;
string phase = "preflight", stopReason = "not-started"; string? failure = null, writerFailure = null, releaseFailure = null;
var commands = new List<JsonElement>(); var stops = new List<JsonElement>(); var active = new SortedDictionary<long, JsonElement>();
JsonElement? firstEnable = null, actualOff = null; SystemFlightFrame? firstFrame = null, lastFrame = null;
JsonElement? initialInputOwnership = null, finalInputOwnership = null;
var enableCalls = 0; var inputSubmitted = false; var zeroThrottleObserved = false; var targetPreserved = false; var overallPassed = false;
double initialSpeed = 0, maximumSpeed = 0, maximumHeat = 0, reserve = double.NaN;
var events = Channel.CreateUnbounded<object>(new() { SingleReader = true });
await using var log = new StreamWriter(new FileStream(Path.Combine(directory, "frames.jsonl"), FileMode.CreateNew, FileAccess.Write, FileShare.Read));
var writer = Task.Run(async () =>
{
try
{
await foreach (var value in events.Reader.ReadAllAsync())
{ await log.WriteLineAsync(JsonSerializer.Serialize(value, EventJson)); await log.FlushAsync(); }
}
catch (Exception error) { writerFailure = error.ToString(); timeout.Cancel(); }
});
void Record(object value)
{ if (!events.Writer.TryWrite(value)) throw new IOException("SCO evidence writer stopped."); }
void Session()
{
token.ThrowIfCancellationRequested(); process.Refresh(); var state = journal.State;
if (process.HasExited || process.StartTime.ToUniversalTime().Ticks != processStarted || !state.InGame
|| state.LoadGameAt != initial.LoadGameAt || state.CommanderId != initial.CommanderId || state.CurrentShipId != initial.CurrentShipId
|| state.Navigation.CurrentSystemAddress != initial.Navigation.CurrentSystemAddress || state.Docked
|| state.CargoCount != initial.CargoCount || state.CargoCapacity != initial.CargoCapacity || Cargo(state) != initialCargo
|| NavigationSafety.HasExternalThreat(state) || NavigationSafety.UnhealthyHull(state))
throw new InvalidOperationException("SCO proof session, cargo, ship, system or safety state changed.");
if (writerFailure is not null) throw new IOException(writerFailure);
}
Task Protection(CancellationToken cancellation)
{
cancellation.ThrowIfCancellationRequested(); Session();
if (protection?.Stopped == true) throw new InvalidOperationException(protection.LastIssue ?? "Flight protection stopped.");
NativeFlightTestProtection.CheckBeforeInput(pid); return Task.CompletedTask;
}
async Task<SystemFlightFrame> Read(CancellationToken cancellation)
{
Session(); var frame = await driver!.ReadSystemFlightAsync(cancellation); await Protection(cancellation);
if (frame.Mode != LocalFlightMode.Supercruise || frame.PlayerOverride || frame.ExternalThreat || frame.TargetObscured || frame.CollisionCourse
|| frame.HeatPercent is not { } heat || !double.IsFinite(heat) || heat >= 90
|| frame.SupercruiseAssist is NativeAssistState.Engaged or NativeAssistState.Requested or NativeAssistState.Aligning)
{
Record(new { at = DateTimeOffset.UtcNow, phase, kind = "flight-guard-refused", frame, local = driver.LastTelemetry,
raw = await new NavigationPipeClient(pid).CallAsync(new { op = "read" }, cancellation) });
throw new InvalidOperationException($"The existing clear, healthy SC leg is no longer available: mode={frame.Mode}, override={frame.PlayerOverride}, threat={frame.ExternalThreat}, obscured={frame.TargetObscured}, collision={frame.CollisionCourse}, heat={frame.HeatPercent}, assist={frame.SupercruiseAssist}.");
}
var assist = driver.LastTelemetry!.Value.GetProperty("supercruiseAssist");
if (True(assist, "enabled") || True(assist, "engaged")) throw new InvalidDataException("SCA must remain explicitly disabled throughout the proof.");
lastFrame = frame; maximumSpeed = Math.Max(maximumSpeed, frame.Speed); maximumHeat = Math.Max(maximumHeat, heat);
return frame;
}
async Task Apply(SystemFlightFrame frame, SystemFlightDecision decision, CancellationToken cancellation)
{
if (DateTimeOffset.UtcNow - frame.ObservedAt > TimeSpan.FromMilliseconds(150))
throw new FlightTelemetryUnavailableException("Proof input source is older than 150ms.");
inputSubmitted = true;
await driver!.ApplySystemFlightAsync(decision, frame, target!, frame.ObservedAt.AddMilliseconds(250), cancellation);
}
try
{
if (!initial.InGame || initial.Docked || initial.CurrentShipId is null || initial.GameMode != "Solo" || !initial.CargoReliable
|| initial.Navigation.CurrentSystemAddress <= 0 || NavigationSafety.HasExternalThreat(initial) || NavigationSafety.UnhealthyHull(initial)
|| TravelPreparation.HasDeployedEquipment(initial))
throw new InvalidOperationException("Enter healthy Solo supercruise with known unchanged cargo and all flight equipment retracted before this proof.");
using var source = new NativeCurrentSystemSource(pid, NativeSystemLocationReader.GameSha256, () => journal.State);
var catalogue = await source.ReadLocationsAsync(token);
var row = catalogue.Locations.SingleOrDefault(location => location.Selected)
?? throw new InvalidOperationException("An existing selected local target is required; this proof will not choose another one.");
if (row.DistanceMetres is not { } distance || !double.IsFinite(distance) || distance < 50 * LightSecond
|| row.Category is not (SystemLocationCategory.Station or SystemLocationCategory.Carrier or SystemLocationCategory.Star or SystemLocationCategory.Planet or SystemLocationCategory.LandablePlanet))
throw new InvalidOperationException("The already selected target must be a station or celestial body at least 50ls away.");
target = new(catalogue.SystemAddress, row.WorldAddress, row.Name,
row.Category is SystemLocationCategory.Station or SystemLocationCategory.Carrier ? SystemTargetKind.Station : SystemTargetKind.Orbit, row.MarketId)
{ NativeKey = row.Key, NativeKind = row.NativeKind, NativeIdentity = row.Identity };
var rawPipe = new NavigationPipeClient(pid);
var raw = await rawPipe.CallAsync(new { op = "read" }, token);
if (!True(raw, "inGame") || raw.GetProperty("rawFsdState").GetInt32() != 2)
throw new InvalidOperationException("This proof requires a stable existing flight scene; SC is confirmed from the bound local frame before any flight input.");
// The generic production protection owns its continuous health monitor.
// The older research guardian is intentionally not used: it pins ShipId 5.
PersistentProtectionProcess.Current = null;
protection = await FlightTestProtectionScope.ArmAsync(pid, exitForFailedHandoff: false);
driver = new(pid, NativeSystemLocationReader.GameSha256, () => journal.State, ensureFlightProtection: Protection, useNativeSupercruiseAssist: false);
await driver.SelectAsync(target, token); // Bind the same already-selected identity, without replacing the destination.
firstFrame = await Read(token); initialSpeed = firstFrame.Speed;
var preparedRaw = await rawPipe.CallAsync(new { op = "read" }, token);
if (preparedRaw.TryGetProperty("inputOwnershipObservation", out var initialOwnership)) initialInputOwnership = initialOwnership.Clone();
ClearRay(firstFrame, preparedRaw);
var localAssist = driver.LastTelemetry!.Value.GetProperty("supercruiseAssist");
if (True(localAssist, "enabled") || True(localAssist, "engaged")) throw new InvalidOperationException("Disable game SCA before verification.");
await driver.PrepareAsync(token);
phase = "align-existing-target-at-zero-throttle"; var alignmentUntil = DateTimeOffset.UtcNow.AddSeconds(40);
while (true)
{
var frame = await Read(token); ClearRay(frame, await rawPipe.CallAsync(new { op = "read" }, token));
var direction = (frame.TargetPosition - frame.Position).Unit();
Record(new { at = DateTimeOffset.UtcNow, phase, frame });
await Apply(frame, new(SystemNavigationPhase.Waiting, phase, direction, 0), token);
if (Vector3d.AngleDegrees(frame.Forward, direction) <= 2 && Math.Abs(frame.Throttle) < .001) break;
if (DateTimeOffset.UtcNow >= alignmentUntil) throw new TimeoutException("Existing target alignment did not settle.");
await Task.Delay(40, token);
}
var prepared = new NativeCruiseControlChannel(pid, NativeSystemLocationReader.GameSha256);
await prepared.PrepareAsync(Path.Combine(NativeSupportPackage.Directory, "CruiseControl"), token);
var ship = initial.Ships.Single(ship => ship.ShipId == initial.CurrentShipId);
if (ship.FuelMain is not > 8 || !double.IsFinite(ship.FuelMain.Value)) throw new InvalidDataException("A verified main-tank capacity above the required reserve is required.");
reserve = Math.Max(8, ship.FuelMain.Value * .25);
await driver.ConfigureOriginalCruiseControlAsync(() => reserve, token: token);
var control = driver.CruiseControl!;
control.Observed += value =>
{
Record(new { at = DateTimeOffset.UtcNow, kind = "original-sco-status", value });
if (!True(value, "sampleReady") || !value.TryGetProperty("sample", out var sample) || sample.ValueKind != JsonValueKind.Object) return;
if (!True(sample, "verified") || sample.GetProperty("entityId").GetString() != firstFrame.ShipEntity.ToString()
|| sample.GetProperty("referenceWorldAddress").GetString() != firstFrame.ReferenceWorld.ToString()) return;
var at = sample.GetProperty("observedAtUnixMilliseconds").GetInt64();
if (firstEnable is { } enabled && sample.GetProperty("state").GetInt32() == 2 && at >= enabled.GetProperty("observedAtUnixMilliseconds").GetInt64())
active.TryAdd(at, sample.Clone());
if (stops.Count > 0 && !True(value, "owned") && sample.GetProperty("state").GetInt32() is 0 or 3
&& at > stops[^1].GetProperty("observedAtUnixMilliseconds").GetInt64()) actualOff = sample.Clone();
};
control.OriginalReceipt += value =>
{
commands.Add(value.Clone()); Record(new { at = DateTimeOffset.UtcNow, kind = "original-sco-receipt", value });
if (True(value, "nativeMethodCalled") && True(value, "argument"))
{ enableCalls++; firstEnable ??= value.Clone(); }
};
control.OriginalStopReceipt += value => { stops.Add(value.Clone()); Record(new { at = DateTimeOffset.UtcNow, kind = "original-sco-stop", value }); };
phase = "sustained-production-sco"; var activationUntil = DateTimeOffset.UtcNow.AddSeconds(10); var navigator = new SystemFlightNavigator();
while (true)
{
var frame = await Read(token);
Record(new { at = DateTimeOffset.UtcNow, phase, frame, decision = control.LastDecision, observation = control.LastObservation });
if (enableCalls > 1) throw new InvalidDataException("A second SCO enable is forbidden in this one-interval proof.");
if (stops.Count > 0) { stopReason = stops[^1].GetProperty("reason").GetString() ?? "native-stop"; break; }
if (active.Count > 1 && active.Keys.Last() - active.Keys.First() >= RequestedActiveSeconds * 1000)
{ stopReason = "requested-active-duration-complete"; break; }
if (active.Count == 0 && DateTimeOffset.UtcNow >= activationUntil) throw new TimeoutException("No real Active SCO sample was observed within ten seconds.");
var decision = navigator.Decide(frame, target, DateTimeOffset.UtcNow);
if (decision.Phase is not (SystemNavigationPhase.Supercruise or SystemNavigationPhase.Aligning)
|| (decision.Actions & ~SystemNavigationAction.CancelOwnedAssists) != SystemNavigationAction.None)
throw new InvalidOperationException("The one-interval proof reached an approach or scene-change boundary.");
await Apply(frame, decision, token);
if (firstEnable is not null && control.LastDecision is { UseOvercharge: false } off)
{ stopReason = "production-policy-" + off.Reason; break; }
await Task.Delay(40, token);
}
}
catch (Exception error) { failure = error.ToString(); Record(new { at = DateTimeOffset.UtcNow, phase, failure }); }
finally
{
phase = "release-sco-and-zero-throttle";
using var cleanup = new CancellationTokenSource(TimeSpan.FromSeconds(8));
try
{
if (driver is not null)
{
if (driver.CruiseControl is { } control) await control.ReleaseAsync(cleanup.Token);
if (!token.IsCancellationRequested && protection?.Stopped == false)
{
var frame = await Read(cleanup.Token);
await Apply(frame, new(SystemNavigationPhase.Waiting, phase, null, 0), cleanup.Token);
await Task.Delay(80, cleanup.Token);
var stopped = await Read(cleanup.Token); zeroThrottleObserved = Math.Abs(stopped.Throttle) < .001;
}
await driver.ReleaseSystemFlightAsync();
}
using var finalSource = new NativeCurrentSystemSource(pid, NativeSystemLocationReader.GameSha256, () => journal.State);
var finalRaw = await new NavigationPipeClient(pid).CallAsync(new { op = "read" }, cleanup.Token);
if (finalRaw.TryGetProperty("inputOwnershipObservation", out var finalOwnership)) finalInputOwnership = finalOwnership.Clone();
var final = await finalSource.ReadLocationsAsync(cleanup.Token);
var selected = final.Locations.SingleOrDefault(location => location.Selected);
targetPreserved = target is not null && selected?.WorldAddress == target.BodyAddress && selected.NativeKind == target.NativeKind
&& selected.Name == target.Name && selected.MarketId == target.MarketId;
}
catch (Exception error) { releaseFailure = error.ToString(); Record(new { at = DateTimeOffset.UtcNow, phase, releaseFailure }); }
try { if (protection is not null) await protection.DisposeAsync(); }
catch (Exception error) { releaseFailure ??= error.ToString(); }
PersistentProtectionProcess.Current = previousGuardian;
events.Writer.TryComplete(); await writer;
var activeSeconds = active.Count > 1 ? (active.Keys.Last() - active.Keys.First()) / 1000d : 0;
var cargoPreserved = journal.State.CargoCount == initial.CargoCount && journal.State.CargoCapacity == initial.CargoCapacity && Cargo(journal.State) == initialCargo;
var firstActiveSpeed = active.Count > 0 ? active.First().Value.GetProperty("speedMetresPerSecond").GetDouble() : 0;
var maximumActiveSpeed = active.Count > 0 ? active.Values.Max(sample => sample.GetProperty("speedMetresPerSecond").GetDouble()) : 0;
var continuityVerified = activeSeconds >= 6 && maximumActiveSpeed > firstActiveSpeed;
var originalOff = firstEnable is { } enable && stops.Any(stop => True(stop, "nativeMethodCalled") && !True(stop, "argument")
&& stop.GetProperty("nativeMethodRva").GetString() == "2eaacd0" && stop.GetProperty("owner").GetString() == enable.GetProperty("owner").GetString()
&& stop.GetProperty("startRequestId").GetString() == enable.GetProperty("requestId").GetString()
&& stop.GetProperty("selectionGeneration").GetUInt64() == enable.GetProperty("selectionGeneration").GetUInt64()
&& stop.GetProperty("entityId").GetString() == enable.GetProperty("entityId").GetString()
&& stop.GetProperty("referenceWorldAddress").GetString() == enable.GetProperty("referenceWorldAddress").GetString()
&& stop.GetProperty("owningThread").GetInt32() == enable.GetProperty("owningThread").GetInt32()
&& (active.Count == 0 || stop.GetProperty("observedAtUnixMilliseconds").GetInt64() >= active.Keys.Last())
&& stop.GetProperty("stateAfter").GetInt32() is 0 or 3);
var passed = failure is null && releaseFailure is null && writerFailure is null && continuityVerified && enableCalls == 1
&& originalOff && actualOff is not null && cargoPreserved && targetPreserved && zeroThrottleObserved;
overallPassed = passed;
var report = new
{
passed, pid, processStarted, start, at = DateTimeOffset.UtcNow, phase, stopReason, failure, releaseFailure, writerFailure,
initialShipId = initial.CurrentShipId, initial.Ship, initial.CargoCount, initial.CargoCapacity, initialCargo, finalCargo = Cargo(journal.State), cargoPreserved,
target, targetPreserved, reserveFuelTonnes = reserve, requestedActiveSeconds = RequestedActiveSeconds, activeSeconds, continuityVerified,
requestedDurationReached = activeSeconds >= RequestedActiveSeconds, activeSamples = active.Count, enableCalls, initialSpeed, maximumSpeed, firstActiveSpeed, maximumActiveSpeed, maximumHeat,
originalOff, actualOff, zeroThrottleObserved, firstFrame, lastFrame, initialInputOwnership, finalInputOwnership, commands, stops,
inputSubmitted, tradeOrdersSent = 0, serviceOrdersSent = 0, launchCommandsSent = 0, scEntryCommandsSent = 0, targetReplaced = false,
gameSupercruiseAssistUsed = false, crossSystemFlight = false, clientRestarted = false
};
await File.WriteAllTextAsync(Path.Combine(directory, "result.json"), JsonSerializer.Serialize(report, EvidenceJson));
Console.WriteLine(JsonSerializer.Serialize(new { passed, activeSeconds, maximumSpeed, maximumHeat, stopReason, cargoPreserved, targetPreserved, directory }));
}
if (!overallPassed)
throw new InvalidOperationException("SCO continuity proof stopped; see the preserved report.");
}
private static void ClearRay(SystemFlightFrame frame, JsonElement raw)
{
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); var geometry = raw.GetProperty("geometry");
var at = raw.GetProperty("observedAtUnixMilliseconds").GetInt64();
if (now - at is < 0 or > 250 || !True(geometry, "fresh") || !True(geometry, "referenceFrameVerified")
|| geometry.GetProperty("worldAddress").GetString() != frame.ReferenceWorld.ToString())
throw new InvalidDataException("A fresh matching reference frame is required for the current-target clearance check.");
var offset = frame.Position - Vector(geometry, "position"); var direction = (frame.TargetPosition - frame.Position).Unit();
var horizon = frame.TargetDistance - 10 * LightSecond;
if (horizon <= 0) throw new InvalidDataException("The selected target is already inside SCO approach range.");
var bodies = geometry.GetProperty("nearbyBodies");
if (bodies.ValueKind != JsonValueKind.Array || bodies.GetArrayLength() == 0) throw new InvalidDataException("Original body clearance geometry is unavailable.");
foreach (var body in bodies.EnumerateArray())
{
var centre = Vector(body, "centre") + offset; var radius = body.GetProperty("exclusionRadius").GetDouble();
if (!centre.Finite || !double.IsFinite(radius) || radius <= 0) throw new InvalidDataException("Invalid original exclusion geometry.");
var relative = centre - frame.Position; var projected = Math.Clamp(Vector3d.Dot(relative, direction), 0, horizon);
if ((relative - direction * projected).Length <= radius * 1.2)
throw new InvalidOperationException("The existing target ray crosses an original exclusion envelope; no alternate target will be selected.");
}
}
}
using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Channels;
using XFEEDToolBox.Core.Data;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Native;
using XFEEDToolBox.Core.Navigation;
using XFEEDToolBox.Core.Session;
using XFEEDToolBox.NativeSupport;
/// <summary>One sustained SCO interval on the already selected, clear SC leg. No launch, trade, target replacement or scene transition.</summary>
internal static class SCOContinuityAcceptance
{
private const double LightSecond = 299792458;
private const double RequestedActiveSeconds = 20;
private static readonly JsonSerializerOptions EvidenceJson = new(ToolboxSettings.JsonOptions)
{ NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals };
private static readonly JsonSerializerOptions EventJson = new(EvidenceJson) { WriteIndented = false };
private static bool True(JsonElement value, string key) => value.TryGetProperty(key, out var item) && item.ValueKind == JsonValueKind.True;
private static Vector3d Vector(JsonElement value, string key)
{ var array = value.GetProperty(key); return new(array[0].GetDouble(), array[1].GetDouble(), array[2].GetDouble()); }
private static string Cargo(PlayerState state) => JsonSerializer.Serialize(state.Inventory
.OrderBy(item => item.Key, StringComparer.Ordinal).ThenBy(item => item.MissionId)
.Select(item => new { item.Key, item.Count, item.Stolen, item.MissionId, item.Group }), ToolboxSettings.JsonOptions);
internal static async Task RunAsync(int pid, string directory)
{
directory = Path.GetFullPath(directory);
if (Directory.Exists(directory)) throw new IOException("Use a fresh SCO continuity evidence directory.");
var parent = Path.GetDirectoryName(directory)!; Directory.CreateDirectory(parent);
using var exclusive = new FileStream(Path.Combine(parent, "docking-fixture-wait.lock"), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
Directory.CreateDirectory(directory);
var stopFile = Path.Combine(directory, "stop");
using var timeout = new CancellationTokenSource(TimeSpan.FromMinutes(3));
using var sentinel = new Timer(_ => { if (File.Exists(stopFile)) timeout.Cancel(); }, null, 100, 100);
var token = timeout.Token;
using var process = Process.GetProcessById(pid); var processStarted = process.StartTime.ToUniversalTime().Ticks;
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(token);
var initial = journal.State; var initialCargo = Cargo(initial); var start = DateTimeOffset.UtcNow;
FlightTestProtectionScope? protection = null; NativeSystemFlightControlDriver? driver = null; SystemNavigationTarget? target = null;
var previousGuardian = PersistentProtectionProcess.Current;
string phase = "preflight", stopReason = "not-started"; string? failure = null, writerFailure = null, releaseFailure = null;
var commands = new List<JsonElement>(); var stops = new List<JsonElement>(); var active = new SortedDictionary<long, JsonElement>();
JsonElement? firstEnable = null, actualOff = null; SystemFlightFrame? firstFrame = null, lastFrame = null;
JsonElement? initialInputOwnership = null, finalInputOwnership = null;
var enableCalls = 0; var inputSubmitted = false; var zeroThrottleObserved = false; var targetPreserved = false; var overallPassed = false;
double initialSpeed = 0, maximumSpeed = 0, maximumHeat = 0, reserve = double.NaN;
var events = Channel.CreateUnbounded<object>(new() { SingleReader = true });
await using var log = new StreamWriter(new FileStream(Path.Combine(directory, "frames.jsonl"), FileMode.CreateNew, FileAccess.Write, FileShare.Read));
var writer = Task.Run(async () =>
{
try
{
await foreach (var value in events.Reader.ReadAllAsync())
{ await log.WriteLineAsync(JsonSerializer.Serialize(value, EventJson)); await log.FlushAsync(); }
}
catch (Exception error) { writerFailure = error.ToString(); timeout.Cancel(); }
});
void Record(object value)
{ if (!events.Writer.TryWrite(value)) throw new IOException("SCO evidence writer stopped."); }
void Session()
{
token.ThrowIfCancellationRequested(); process.Refresh(); var state = journal.State;
if (process.HasExited || process.StartTime.ToUniversalTime().Ticks != processStarted || !state.InGame
|| state.LoadGameAt != initial.LoadGameAt || state.CommanderId != initial.CommanderId || state.CurrentShipId != initial.CurrentShipId
|| state.Navigation.CurrentSystemAddress != initial.Navigation.CurrentSystemAddress || state.Docked
|| state.CargoCount != initial.CargoCount || state.CargoCapacity != initial.CargoCapacity || Cargo(state) != initialCargo
|| NavigationSafety.HasExternalThreat(state) || NavigationSafety.UnhealthyHull(state))
throw new InvalidOperationException("SCO proof session, cargo, ship, system or safety state changed.");
if (writerFailure is not null) throw new IOException(writerFailure);
}
Task Protection(CancellationToken cancellation)
{
cancellation.ThrowIfCancellationRequested(); Session();
if (protection?.Stopped == true) throw new InvalidOperationException(protection.LastIssue ?? "Flight protection stopped.");
NativeFlightTestProtection.CheckBeforeInput(pid); return Task.CompletedTask;
}
async Task<SystemFlightFrame> Read(CancellationToken cancellation)
{
Session(); var frame = await driver!.ReadSystemFlightAsync(cancellation); await Protection(cancellation);
if (frame.Mode != LocalFlightMode.Supercruise || frame.PlayerOverride || frame.ExternalThreat || frame.TargetObscured || frame.CollisionCourse
|| frame.HeatPercent is not { } heat || !double.IsFinite(heat) || heat >= 90
|| frame.SupercruiseAssist is NativeAssistState.Engaged or NativeAssistState.Requested or NativeAssistState.Aligning)
{
Record(new { at = DateTimeOffset.UtcNow, phase, kind = "flight-guard-refused", frame, local = driver.LastTelemetry,
raw = await new NavigationPipeClient(pid).CallAsync(new { op = "read" }, cancellation) });
throw new InvalidOperationException($"The existing clear, healthy SC leg is no longer available: mode={frame.Mode}, override={frame.PlayerOverride}, threat={frame.ExternalThreat}, obscured={frame.TargetObscured}, collision={frame.CollisionCourse}, heat={frame.HeatPercent}, assist={frame.SupercruiseAssist}.");
}
var assist = driver.LastTelemetry!.Value.GetProperty("supercruiseAssist");
if (True(assist, "enabled") || True(assist, "engaged")) throw new InvalidDataException("SCA must remain explicitly disabled throughout the proof.");
lastFrame = frame; maximumSpeed = Math.Max(maximumSpeed, frame.Speed); maximumHeat = Math.Max(maximumHeat, heat);
return frame;
}
async Task Apply(SystemFlightFrame frame, SystemFlightDecision decision, CancellationToken cancellation)
{
if (DateTimeOffset.UtcNow - frame.ObservedAt > TimeSpan.FromMilliseconds(150))
throw new FlightTelemetryUnavailableException("Proof input source is older than 150ms.");
inputSubmitted = true;
await driver!.ApplySystemFlightAsync(decision, frame, target!, frame.ObservedAt.AddMilliseconds(250), cancellation);
}
try
{
if (!initial.InGame || initial.Docked || initial.CurrentShipId is null || initial.GameMode != "Solo" || !initial.CargoReliable
|| initial.Navigation.CurrentSystemAddress <= 0 || NavigationSafety.HasExternalThreat(initial) || NavigationSafety.UnhealthyHull(initial)
|| TravelPreparation.HasDeployedEquipment(initial))
throw new InvalidOperationException("Enter healthy Solo supercruise with known unchanged cargo and all flight equipment retracted before this proof.");
using var source = new NativeCurrentSystemSource(pid, NativeSystemLocationReader.GameSha256, () => journal.State);
var catalogue = await source.ReadLocationsAsync(token);
var row = catalogue.Locations.SingleOrDefault(location => location.Selected)
?? throw new InvalidOperationException("An existing selected local target is required; this proof will not choose another one.");
if (row.DistanceMetres is not { } distance || !double.IsFinite(distance) || distance < 50 * LightSecond
|| row.Category is not (SystemLocationCategory.Station or SystemLocationCategory.Carrier or SystemLocationCategory.Star or SystemLocationCategory.Planet or SystemLocationCategory.LandablePlanet))
throw new InvalidOperationException("The already selected target must be a station or celestial body at least 50ls away.");
target = new(catalogue.SystemAddress, row.WorldAddress, row.Name,
row.Category is SystemLocationCategory.Station or SystemLocationCategory.Carrier ? SystemTargetKind.Station : SystemTargetKind.Orbit, row.MarketId)
{ NativeKey = row.Key, NativeKind = row.NativeKind, NativeIdentity = row.Identity };
var rawPipe = new NavigationPipeClient(pid);
var raw = await rawPipe.CallAsync(new { op = "read" }, token);
if (!True(raw, "inGame") || raw.GetProperty("rawFsdState").GetInt32() != 2)
throw new InvalidOperationException("This proof requires a stable existing flight scene; SC is confirmed from the bound local frame before any flight input.");
// The generic production protection owns its continuous health monitor.
// The older research guardian is intentionally not used: it pins ShipId 5.
PersistentProtectionProcess.Current = null;
protection = await FlightTestProtectionScope.ArmAsync(pid, exitForFailedHandoff: false);
driver = new(pid, NativeSystemLocationReader.GameSha256, () => journal.State, ensureFlightProtection: Protection, useNativeSupercruiseAssist: false);
await driver.SelectAsync(target, token); // Bind the same already-selected identity, without replacing the destination.
firstFrame = await Read(token); initialSpeed = firstFrame.Speed;
var preparedRaw = await rawPipe.CallAsync(new { op = "read" }, token);
if (preparedRaw.TryGetProperty("inputOwnershipObservation", out var initialOwnership)) initialInputOwnership = initialOwnership.Clone();
ClearRay(firstFrame, preparedRaw);
var localAssist = driver.LastTelemetry!.Value.GetProperty("supercruiseAssist");
if (True(localAssist, "enabled") || True(localAssist, "engaged")) throw new InvalidOperationException("Disable game SCA before verification.");
await driver.PrepareAsync(token);
phase = "align-existing-target-at-zero-throttle"; var alignmentUntil = DateTimeOffset.UtcNow.AddSeconds(40);
while (true)
{
var frame = await Read(token); ClearRay(frame, await rawPipe.CallAsync(new { op = "read" }, token));
var direction = (frame.TargetPosition - frame.Position).Unit();
Record(new { at = DateTimeOffset.UtcNow, phase, frame });
await Apply(frame, new(SystemNavigationPhase.Waiting, phase, direction, 0), token);
if (Vector3d.AngleDegrees(frame.Forward, direction) <= 2 && Math.Abs(frame.Throttle) < .001) break;
if (DateTimeOffset.UtcNow >= alignmentUntil) throw new TimeoutException("Existing target alignment did not settle.");
await Task.Delay(40, token);
}
var prepared = new NativeCruiseControlChannel(pid, NativeSystemLocationReader.GameSha256);
await prepared.PrepareAsync(Path.Combine(NativeSupportPackage.Directory, "CruiseControl"), token);
var ship = initial.Ships.Single(ship => ship.ShipId == initial.CurrentShipId);
if (ship.FuelMain is not > 8 || !double.IsFinite(ship.FuelMain.Value)) throw new InvalidDataException("A verified main-tank capacity above the required reserve is required.");
reserve = Math.Max(8, ship.FuelMain.Value * .25);
await driver.ConfigureOriginalCruiseControlAsync(() => reserve, token: token);
var control = driver.CruiseControl!;
control.Observed += value =>
{
Record(new { at = DateTimeOffset.UtcNow, kind = "original-sco-status", value });
if (!True(value, "sampleReady") || !value.TryGetProperty("sample", out var sample) || sample.ValueKind != JsonValueKind.Object) return;
if (!True(sample, "verified") || sample.GetProperty("entityId").GetString() != firstFrame.ShipEntity.ToString()
|| sample.GetProperty("referenceWorldAddress").GetString() != firstFrame.ReferenceWorld.ToString()) return;
var at = sample.GetProperty("observedAtUnixMilliseconds").GetInt64();
if (firstEnable is { } enabled && sample.GetProperty("state").GetInt32() == 2 && at >= enabled.GetProperty("observedAtUnixMilliseconds").GetInt64())
active.TryAdd(at, sample.Clone());
if (stops.Count > 0 && !True(value, "owned") && sample.GetProperty("state").GetInt32() is 0 or 3
&& at > stops[^1].GetProperty("observedAtUnixMilliseconds").GetInt64()) actualOff = sample.Clone();
};
control.OriginalReceipt += value =>
{
commands.Add(value.Clone()); Record(new { at = DateTimeOffset.UtcNow, kind = "original-sco-receipt", value });
if (True(value, "nativeMethodCalled") && True(value, "argument"))
{ enableCalls++; firstEnable ??= value.Clone(); }
};
control.OriginalStopReceipt += value => { stops.Add(value.Clone()); Record(new { at = DateTimeOffset.UtcNow, kind = "original-sco-stop", value }); };
phase = "sustained-production-sco"; var activationUntil = DateTimeOffset.UtcNow.AddSeconds(10); var navigator = new SystemFlightNavigator();
while (true)
{
var frame = await Read(token);
Record(new { at = DateTimeOffset.UtcNow, phase, frame, decision = control.LastDecision, observation = control.LastObservation });
if (enableCalls > 1) throw new InvalidDataException("A second SCO enable is forbidden in this one-interval proof.");
if (stops.Count > 0) { stopReason = stops[^1].GetProperty("reason").GetString() ?? "native-stop"; break; }
if (active.Count > 1 && active.Keys.Last() - active.Keys.First() >= RequestedActiveSeconds * 1000)
{ stopReason = "requested-active-duration-complete"; break; }
if (active.Count == 0 && DateTimeOffset.UtcNow >= activationUntil) throw new TimeoutException("No real Active SCO sample was observed within ten seconds.");
var decision = navigator.Decide(frame, target, DateTimeOffset.UtcNow);
if (decision.Phase is not (SystemNavigationPhase.Supercruise or SystemNavigationPhase.Aligning)
|| (decision.Actions & ~SystemNavigationAction.CancelOwnedAssists) != SystemNavigationAction.None)
throw new InvalidOperationException("The one-interval proof reached an approach or scene-change boundary.");
await Apply(frame, decision, token);
if (firstEnable is not null && control.LastDecision is { UseOvercharge: false } off)
{ stopReason = "production-policy-" + off.Reason; break; }
await Task.Delay(40, token);
}
}
catch (Exception error) { failure = error.ToString(); Record(new { at = DateTimeOffset.UtcNow, phase, failure }); }
finally
{
phase = "release-sco-and-zero-throttle";
using var cleanup = new CancellationTokenSource(TimeSpan.FromSeconds(8));
try
{
if (driver is not null)
{
if (driver.CruiseControl is { } control) await control.ReleaseAsync(cleanup.Token);
if (!token.IsCancellationRequested && protection?.Stopped == false)
{
var frame = await Read(cleanup.Token);
await Apply(frame, new(SystemNavigationPhase.Waiting, phase, null, 0), cleanup.Token);
await Task.Delay(80, cleanup.Token);
var stopped = await Read(cleanup.Token); zeroThrottleObserved = Math.Abs(stopped.Throttle) < .001;
}
await driver.ReleaseSystemFlightAsync();
}
using var finalSource = new NativeCurrentSystemSource(pid, NativeSystemLocationReader.GameSha256, () => journal.State);
var finalRaw = await new NavigationPipeClient(pid).CallAsync(new { op = "read" }, cleanup.Token);
if (finalRaw.TryGetProperty("inputOwnershipObservation", out var finalOwnership)) finalInputOwnership = finalOwnership.Clone();
var final = await finalSource.ReadLocationsAsync(cleanup.Token);
var selected = final.Locations.SingleOrDefault(location => location.Selected);
targetPreserved = target is not null && selected?.WorldAddress == target.BodyAddress && selected.NativeKind == target.NativeKind
&& selected.Name == target.Name && selected.MarketId == target.MarketId;
}
catch (Exception error) { releaseFailure = error.ToString(); Record(new { at = DateTimeOffset.UtcNow, phase, releaseFailure }); }
try { if (protection is not null) await protection.DisposeAsync(); }
catch (Exception error) { releaseFailure ??= error.ToString(); }
PersistentProtectionProcess.Current = previousGuardian;
events.Writer.TryComplete(); await writer;
var activeSeconds = active.Count > 1 ? (active.Keys.Last() - active.Keys.First()) / 1000d : 0;
var cargoPreserved = journal.State.CargoCount == initial.CargoCount && journal.State.CargoCapacity == initial.CargoCapacity && Cargo(journal.State) == initialCargo;
var firstActiveSpeed = active.Count > 0 ? active.First().Value.GetProperty("speedMetresPerSecond").GetDouble() : 0;
var maximumActiveSpeed = active.Count > 0 ? active.Values.Max(sample => sample.GetProperty("speedMetresPerSecond").GetDouble()) : 0;
var continuityVerified = activeSeconds >= 6 && maximumActiveSpeed > firstActiveSpeed;
var originalOff = firstEnable is { } enable && stops.Any(stop => True(stop, "nativeMethodCalled") && !True(stop, "argument")
&& stop.GetProperty("nativeMethodRva").GetString() == "2eaacd0" && stop.GetProperty("owner").GetString() == enable.GetProperty("owner").GetString()
&& stop.GetProperty("startRequestId").GetString() == enable.GetProperty("requestId").GetString()
&& stop.GetProperty("selectionGeneration").GetUInt64() == enable.GetProperty("selectionGeneration").GetUInt64()
&& stop.GetProperty("entityId").GetString() == enable.GetProperty("entityId").GetString()
&& stop.GetProperty("referenceWorldAddress").GetString() == enable.GetProperty("referenceWorldAddress").GetString()
&& stop.GetProperty("owningThread").GetInt32() == enable.GetProperty("owningThread").GetInt32()
&& (active.Count == 0 || stop.GetProperty("observedAtUnixMilliseconds").GetInt64() >= active.Keys.Last())
&& stop.GetProperty("stateAfter").GetInt32() is 0 or 3);
var passed = failure is null && releaseFailure is null && writerFailure is null && continuityVerified && enableCalls == 1
&& originalOff && actualOff is not null && cargoPreserved && targetPreserved && zeroThrottleObserved;
overallPassed = passed;
var report = new
{
passed, pid, processStarted, start, at = DateTimeOffset.UtcNow, phase, stopReason, failure, releaseFailure, writerFailure,
initialShipId = initial.CurrentShipId, initial.Ship, initial.CargoCount, initial.CargoCapacity, initialCargo, finalCargo = Cargo(journal.State), cargoPreserved,
target, targetPreserved, reserveFuelTonnes = reserve, requestedActiveSeconds = RequestedActiveSeconds, activeSeconds, continuityVerified,
requestedDurationReached = activeSeconds >= RequestedActiveSeconds, activeSamples = active.Count, enableCalls, initialSpeed, maximumSpeed, firstActiveSpeed, maximumActiveSpeed, maximumHeat,
originalOff, actualOff, zeroThrottleObserved, firstFrame, lastFrame, initialInputOwnership, finalInputOwnership, commands, stops,
inputSubmitted, tradeOrdersSent = 0, serviceOrdersSent = 0, launchCommandsSent = 0, scEntryCommandsSent = 0, targetReplaced = false,
gameSupercruiseAssistUsed = false, crossSystemFlight = false, clientRestarted = false
};
await File.WriteAllTextAsync(Path.Combine(directory, "result.json"), JsonSerializer.Serialize(report, EvidenceJson));
Console.WriteLine(JsonSerializer.Serialize(new { passed, activeSeconds, maximumSpeed, maximumHeat, stopReason, cargoPreserved, targetPreserved, directory }));
}
if (!overallPassed)
throw new InvalidOperationException("SCO continuity proof stopped; see the preserved report.");
}
private static void ClearRay(SystemFlightFrame frame, JsonElement raw)
{
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); var geometry = raw.GetProperty("geometry");
var at = raw.GetProperty("observedAtUnixMilliseconds").GetInt64();
if (now - at is < 0 or > 250 || !True(geometry, "fresh") || !True(geometry, "referenceFrameVerified")
|| geometry.GetProperty("worldAddress").GetString() != frame.ReferenceWorld.ToString())
throw new InvalidDataException("A fresh matching reference frame is required for the current-target clearance check.");
var offset = frame.Position - Vector(geometry, "position"); var direction = (frame.TargetPosition - frame.Position).Unit();
var horizon = frame.TargetDistance - 10 * LightSecond;
if (horizon <= 0) throw new InvalidDataException("The selected target is already inside SCO approach range.");
var bodies = geometry.GetProperty("nearbyBodies");
if (bodies.ValueKind != JsonValueKind.Array || bodies.GetArrayLength() == 0) throw new InvalidDataException("Original body clearance geometry is unavailable.");
foreach (var body in bodies.EnumerateArray())
{
var centre = Vector(body, "centre") + offset; var radius = body.GetProperty("exclusionRadius").GetDouble();
if (!centre.Finite || !double.IsFinite(radius) || radius <= 0) throw new InvalidDataException("Invalid original exclusion geometry.");
var relative = centre - frame.Position; var projected = Math.Clamp(Vector3d.Dot(relative, direction), 0, horizon);
if ((relative - direction * projected).Length <= radius * 1.2)
throw new InvalidOperationException("The existing target ray crosses an original exclusion envelope; no alternate target will be selected.");
}
}
}