using System.Diagnostics;
using System.Security.Cryptography;
using System.Text.Json;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Navigation;
/// <summary>Bounded SC orbit/scoop experiment. Does not send FSD input or certify complete navigation.</summary>
internal static class StarOrbitAcceptance
{
private static JsonDocument ReadShared(string path)
{
for(var attempt=0;;attempt++)
{
try
{
using var stream=new FileStream(path,FileMode.Open,FileAccess.Read,FileShare.ReadWrite|FileShare.Delete);
return JsonDocument.Parse(stream);
}
catch(Exception error)when(attempt<5&&error is IOException or JsonException)
{Thread.Sleep(10);}
}
}
private static IEnumerable<JsonElement> Events(string path)
{
using var stream=new FileStream(path,FileMode.Open,FileAccess.Read,FileShare.ReadWrite|FileShare.Delete);
using var reader=new StreamReader(stream);
while(reader.ReadLine() is {} line)
{
if(!line.TrimEnd().EndsWith('}'))continue;
using var document=JsonDocument.Parse(line);yield return document.RootElement.Clone();
}
}
private static Vector3d V(JsonElement row,string name){var v=row.GetProperty(name);return new(v[0].GetDouble(),v[1].GetDouble(),v[2].GetDouble());}
public static async Task<bool> RunAsync(int pid,string profilePath,string reportPath)
{
if(File.Exists(reportPath)||File.Exists(reportPath+".jsonl"))throw new IOException("Use a new evidence path.");
using var profile=ReadShared(profilePath);var image=profile.RootElement.GetProperty("image").GetString()!;
var hash=profile.RootElement.GetProperty("sha256").GetString();
await using(var input=File.OpenRead(image))if(Convert.ToHexStringLower(await SHA256.HashDataAsync(input))!=hash)throw new InvalidDataException("Build changed.");
using var game=Process.GetProcessById(pid);var processStart=game.StartTime.ToUniversalTime().Ticks;
if(!string.Equals(game.MainModule?.FileName,image,StringComparison.OrdinalIgnoreCase))throw new InvalidDataException("Selected game changed.");
var settingsPath=Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox","settings.json");
using var settings=ReadShared(settingsPath);var journalDirectory=settings.RootElement.GetProperty("journalDirectory").GetString()!;
var journal=Directory.GetFiles(journalDirectory,"Journal.*.log").OrderBy(File.GetLastWriteTimeUtc).Last();
var statusPath=Path.Combine(journalDirectory,"Status.json");
var start=DateTimeOffset.UtcNow;var clock=Stopwatch.StartNew();var owner=Guid.NewGuid().ToString("N");var pipe=new NavigationPipeClient(pid);
await using var log=new StreamWriter(new FileStream(reportPath+".jsonl",FileMode.CreateNew,FileAccess.Write,FileShare.Read));
async Task Log(object row){await log.WriteLineAsync(JsonSerializer.Serialize(row));await log.FlushAsync();}
var frames=0;var peak=0d;var minimumRadius=double.PositiveInfinity;var rotated=0d;var released=false;var cleared=false;var scooped=0d;var journalScooped=0d;var fuelGain=0d;var initialFuel=0d;var sawScooping=false;
var samples=new List<object>();string? failure=null;ulong target=0;long world=0;double exclusion=0,initialRadius=0;bool thermalEscape=false;
async Task Release()
{
var timeout=Stopwatch.StartNew();
while(timeout.Elapsed.TotalSeconds<3)
{
var reply=await pipe.CallAsync(new{op="release",owner});
if(reply.GetProperty("released").GetBoolean()){released=true;return;}
await Task.Delay(40);
}
throw new TimeoutException("Orbit inputs were not released.");
}
try
{
var hello=await pipe.CallAsync(new{op="describe"});
if(hello.GetProperty("version").GetString()!="0.3.1-dev"||hello.GetProperty("gameSha256").GetString()!=hash||hello.GetProperty("processId").GetInt32()!=pid)throw new InvalidDataException("Native identity changed.");
using(var status=ReadShared(statusPath))
{
var flags=status.RootElement.GetProperty("Flags").GetUInt64();
if((flags&16)==0||(flags&(1UL|65536UL|131072UL|1048576UL|4194304UL))!=0||status.RootElement.GetProperty("GuiFocus").GetInt32()!=0)throw new InvalidOperationException("Requires idle, safe main-ship supercruise.");
initialFuel=status.RootElement.GetProperty("Fuel").GetProperty("FuelMain").GetDouble();
}
var loadout=Events(journal).Last(e=>e.GetProperty("event").GetString()=="Loadout");
if(!loadout.GetProperty("Modules").EnumerateArray().Any(m=>m.GetProperty("Item").GetString()!.Contains("fuelscoop",StringComparison.OrdinalIgnoreCase)&&m.GetProperty("On").GetBoolean()))throw new InvalidOperationException("A powered fuel scoop is required.");
var frame=await pipe.CallAsync(new{op="read"});var drive=await pipe.CallAsync(new{op="fsd.describe"});var geometry=await pipe.CallAsync(new{op="geometry.describe"});
if(!frame.GetProperty("localPlayerShipVerified").GetBoolean()||drive.GetProperty("entityId").GetString()!=frame.GetProperty("entityId").GetString()||!drive.GetProperty("fresh").GetBoolean()||drive.GetProperty("state").GetInt32()!=2||!drive.GetProperty("targetValid").GetBoolean()||!geometry.GetProperty("fresh").GetBoolean()||geometry.GetProperty("bodyKind").GetInt32()!=1)throw new InvalidDataException("Coherent local-ship, idle FSD, real target and primary-star geometry are required.");
world=long.Parse(geometry.GetProperty("worldAddress").GetString()!);target=ulong.Parse(drive.GetProperty("targetAddress").GetString()!);
var starType=geometry.GetProperty("starType").GetInt32();
if(starType is <0 or >6)throw new InvalidOperationException("This primary star is not a verified OBAFGKM type.");
exclusion=geometry.GetProperty("exclusionRadius").GetDouble();var star=new StarEnvelope(world,"OBAFGKM"[starType].ToString(),Vector3d.Zero,exclusion,null,null);
var position=V(frame,"position");var destination=position+V(drive,"targetDirection")*drive.GetProperty("targetDistance").GetDouble();
initialRadius=position.Length;
if(!FlightGeometry.Obscures(star,position,destination)||initialRadius<exclusion*1.4||frame.GetProperty("heatPercent").GetDouble()>=86)throw new InvalidOperationException("Requires a real star-obscured target, exterior approach and initial heat below 86 percent.");
await Log(new{kind="preflight",start,pid,target,world,frame,drive,geometry,loadoutShip=loadout.GetProperty("Ship").GetString()});
var previousRadial=position.Unit();var orbitRadius=Math.Max(exclusion*1.25,initialRadius*.9);var phase="prealign";double previousHeat=frame.GetProperty("heatPercent").GetDouble(),previousSeconds=0,lastBandChange=0;
while(clock.Elapsed.TotalSeconds<300)
{
using(var current=Process.GetProcessById(pid))if(current.StartTime.ToUniversalTime().Ticks!=processStart)throw new InvalidOperationException("Process identity changed.");
frame=await pipe.CallAsync(new{op="read"});drive=await pipe.CallAsync(new{op="fsd.describe"});geometry=await pipe.CallAsync(new{op="geometry.describe"});frames++;
if(!frame.GetProperty("inGame").GetBoolean()||frame.GetProperty("playerOverride").GetBoolean()||!drive.GetProperty("fresh").GetBoolean()||drive.GetProperty("state").GetInt32()!=2||drive.GetProperty("entityId").GetString()!=frame.GetProperty("entityId").GetString()||!geometry.GetProperty("fresh").GetBoolean()||long.Parse(geometry.GetProperty("worldAddress").GetString()!)!=world||ulong.Parse(drive.GetProperty("targetAddress").GetString()!)!=target)throw new InvalidOperationException("Orbit state or ownership changed.");
using var status=ReadShared(statusPath);var flags=status.RootElement.GetProperty("Flags").GetUInt64();
if((flags&16)==0||(flags&(1UL|131072UL|1048576UL|4194304UL))!=0)throw new InvalidOperationException("Supercruise or safety state changed.");
var activeScoop=(flags&2048)!=0;
foreach(var e in Events(journal))if(e.GetProperty("event").GetString()=="FuelScoop"&&e.GetProperty("timestamp").GetDateTimeOffset()>=start.AddSeconds(-1))journalScooped=Math.Max(journalScooped,e.GetProperty("Scooped").GetDouble());
fuelGain=Math.Max(fuelGain,status.RootElement.GetProperty("Fuel").GetProperty("FuelMain").GetDouble()-initialFuel);
scooped=Math.Max(journalScooped,fuelGain);
position=V(frame,"position");var radius=position.Length;var heat=frame.GetProperty("heatPercent").GetDouble();var seconds=clock.Elapsed.TotalSeconds;
var slope=(heat-previousHeat)/Math.Max(.02,seconds-previousSeconds);previousHeat=heat;previousSeconds=seconds;
peak=Math.Max(peak,heat);minimumRadius=Math.Min(minimumRadius,radius);rotated+=Vector3d.AngleDegrees(previousRadial,position);previousRadial=position.Unit();
destination=position+V(drive,"targetDirection")*drive.GetProperty("targetDistance").GetDouble();cleared=!FlightGeometry.Obscures(star,position,destination);
if(activeScoop)
{
// Anchor at the first actual scoop boundary rather than continue toward a lower search radius.
if(!sawScooping)orbitRadius=Math.Max(exclusion*1.25,radius*.999);
sawScooping=true;samples.Add(new{radius,heat,scooped,seconds});
}
if(heat>=88||heat+Math.Max(0,slope)*8>=94||radius<exclusion*1.18)thermalEscape=true;
var desired=thermalEscape?position.Unit():FlightGeometry.OrbitAtRadius(star,position,destination,V(frame,"up"),orbitRadius);
var aim=AttitudeController.Aim(V(frame,"forward"),V(frame,"up"),desired,.6);
double throttle=0;var speed=frame.GetProperty("speedMetresPerSecond").GetDouble();
if(aim.ErrorDegrees<=8)
{
phase=thermalEscape?"thermal-escape":"orbit";
var fraction=drive.GetProperty("speedFraction").GetDouble();
var maximum=fraction>1e-7?speed/fraction:double.NaN;
if(!double.IsFinite(maximum)||maximum<=0)throw new InvalidDataException("Native speed scale is unavailable.");
throttle=speed>12_000_000?0:Math.Clamp(10_000_000/maximum,0,.15);
}
if(thermalEscape&&radius>=initialRadius*1.05&&heat<75)throw new InvalidOperationException("Thermal guard completed an outward escape; scoop/orbit acceptance is incomplete.");
if(heat>=95)throw new InvalidOperationException("Reached bounded orbit heat ceiling.");
await Log(new{kind="sample",seconds,phase,orbitRadius,thermalEscape,aim,throttle,activeScoop,scooped,cleared,frame,drive,geometry});
if(cleared&&sawScooping&&scooped>0&&rotated>=15){await Release();break;}
if(!activeScoop&&!thermalEscape&&seconds-lastBandChange>15&&phase=="orbit")
{orbitRadius=Math.Max(exclusion*1.25,orbitRadius-exclusion*.02);lastBandChange=seconds;}
await pipe.CallAsync(new{op="calibrate.axes",owner,sequence=frame.GetProperty("sequence").GetInt64(),ttlMilliseconds=250,throttle,
axes=new[]{0d,0,0,aim.Yaw,aim.Pitch,aim.Roll}});
await Task.Delay(60);
}
if(!cleared||!sawScooping||scooped<=0||rotated<15)throw new InvalidOperationException("Actual orbit, scoop and target clearance were not all observed within the bound.");
}
catch(Exception error){failure=error.ToString();}
finally{if(!released)try{await Release();}catch(Exception error){failure=(failure??"")+" Release: "+error.Message;}}
var passed=failure is null&&released&&cleared&&sawScooping&&scooped>0&&rotated>=15;
await File.WriteAllTextAsync(reportPath,JsonSerializer.Serialize(new{pid,hash,start,target,world,exclusion,initialRadius,minimumRadius,frames,peak,rotated,cleared,sawScooping,scooped,journalScooped,initialFuel,fuelGain,thermalEscape,released,passed,failure,scoopSamples=samples,screenUse=false,osInputSent=false,fsdInputsSent=0,completeAutomaticNavigationAccepted=false},new JsonSerializerOptions{WriteIndented=true}));
Console.WriteLine(JsonSerializer.Serialize(new{passed,frames,peak,rotated,cleared,sawScooping,scooped,thermalEscape,released,failure}));return passed;
}
}
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text.Json;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Navigation;
/// <summary>Bounded SC orbit/scoop experiment. Does not send FSD input or certify complete navigation.</summary>
internal static class StarOrbitAcceptance
{
private static JsonDocument ReadShared(string path)
{
for(var attempt=0;;attempt++)
{
try
{
using var stream=new FileStream(path,FileMode.Open,FileAccess.Read,FileShare.ReadWrite|FileShare.Delete);
return JsonDocument.Parse(stream);
}
catch(Exception error)when(attempt<5&&error is IOException or JsonException)
{Thread.Sleep(10);}
}
}
private static IEnumerable<JsonElement> Events(string path)
{
using var stream=new FileStream(path,FileMode.Open,FileAccess.Read,FileShare.ReadWrite|FileShare.Delete);
using var reader=new StreamReader(stream);
while(reader.ReadLine() is {} line)
{
if(!line.TrimEnd().EndsWith('}'))continue;
using var document=JsonDocument.Parse(line);yield return document.RootElement.Clone();
}
}
private static Vector3d V(JsonElement row,string name){var v=row.GetProperty(name);return new(v[0].GetDouble(),v[1].GetDouble(),v[2].GetDouble());}
public static async Task<bool> RunAsync(int pid,string profilePath,string reportPath)
{
if(File.Exists(reportPath)||File.Exists(reportPath+".jsonl"))throw new IOException("Use a new evidence path.");
using var profile=ReadShared(profilePath);var image=profile.RootElement.GetProperty("image").GetString()!;
var hash=profile.RootElement.GetProperty("sha256").GetString();
await using(var input=File.OpenRead(image))if(Convert.ToHexStringLower(await SHA256.HashDataAsync(input))!=hash)throw new InvalidDataException("Build changed.");
using var game=Process.GetProcessById(pid);var processStart=game.StartTime.ToUniversalTime().Ticks;
if(!string.Equals(game.MainModule?.FileName,image,StringComparison.OrdinalIgnoreCase))throw new InvalidDataException("Selected game changed.");
var settingsPath=Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox","settings.json");
using var settings=ReadShared(settingsPath);var journalDirectory=settings.RootElement.GetProperty("journalDirectory").GetString()!;
var journal=Directory.GetFiles(journalDirectory,"Journal.*.log").OrderBy(File.GetLastWriteTimeUtc).Last();
var statusPath=Path.Combine(journalDirectory,"Status.json");
var start=DateTimeOffset.UtcNow;var clock=Stopwatch.StartNew();var owner=Guid.NewGuid().ToString("N");var pipe=new NavigationPipeClient(pid);
await using var log=new StreamWriter(new FileStream(reportPath+".jsonl",FileMode.CreateNew,FileAccess.Write,FileShare.Read));
async Task Log(object row){await log.WriteLineAsync(JsonSerializer.Serialize(row));await log.FlushAsync();}
var frames=0;var peak=0d;var minimumRadius=double.PositiveInfinity;var rotated=0d;var released=false;var cleared=false;var scooped=0d;var journalScooped=0d;var fuelGain=0d;var initialFuel=0d;var sawScooping=false;
var samples=new List<object>();string? failure=null;ulong target=0;long world=0;double exclusion=0,initialRadius=0;bool thermalEscape=false;
async Task Release()
{
var timeout=Stopwatch.StartNew();
while(timeout.Elapsed.TotalSeconds<3)
{
var reply=await pipe.CallAsync(new{op="release",owner});
if(reply.GetProperty("released").GetBoolean()){released=true;return;}
await Task.Delay(40);
}
throw new TimeoutException("Orbit inputs were not released.");
}
try
{
var hello=await pipe.CallAsync(new{op="describe"});
if(hello.GetProperty("version").GetString()!="0.3.1-dev"||hello.GetProperty("gameSha256").GetString()!=hash||hello.GetProperty("processId").GetInt32()!=pid)throw new InvalidDataException("Native identity changed.");
using(var status=ReadShared(statusPath))
{
var flags=status.RootElement.GetProperty("Flags").GetUInt64();
if((flags&16)==0||(flags&(1UL|65536UL|131072UL|1048576UL|4194304UL))!=0||status.RootElement.GetProperty("GuiFocus").GetInt32()!=0)throw new InvalidOperationException("Requires idle, safe main-ship supercruise.");
initialFuel=status.RootElement.GetProperty("Fuel").GetProperty("FuelMain").GetDouble();
}
var loadout=Events(journal).Last(e=>e.GetProperty("event").GetString()=="Loadout");
if(!loadout.GetProperty("Modules").EnumerateArray().Any(m=>m.GetProperty("Item").GetString()!.Contains("fuelscoop",StringComparison.OrdinalIgnoreCase)&&m.GetProperty("On").GetBoolean()))throw new InvalidOperationException("A powered fuel scoop is required.");
var frame=await pipe.CallAsync(new{op="read"});var drive=await pipe.CallAsync(new{op="fsd.describe"});var geometry=await pipe.CallAsync(new{op="geometry.describe"});
if(!frame.GetProperty("localPlayerShipVerified").GetBoolean()||drive.GetProperty("entityId").GetString()!=frame.GetProperty("entityId").GetString()||!drive.GetProperty("fresh").GetBoolean()||drive.GetProperty("state").GetInt32()!=2||!drive.GetProperty("targetValid").GetBoolean()||!geometry.GetProperty("fresh").GetBoolean()||geometry.GetProperty("bodyKind").GetInt32()!=1)throw new InvalidDataException("Coherent local-ship, idle FSD, real target and primary-star geometry are required.");
world=long.Parse(geometry.GetProperty("worldAddress").GetString()!);target=ulong.Parse(drive.GetProperty("targetAddress").GetString()!);
var starType=geometry.GetProperty("starType").GetInt32();
if(starType is <0 or >6)throw new InvalidOperationException("This primary star is not a verified OBAFGKM type.");
exclusion=geometry.GetProperty("exclusionRadius").GetDouble();var star=new StarEnvelope(world,"OBAFGKM"[starType].ToString(),Vector3d.Zero,exclusion,null,null);
var position=V(frame,"position");var destination=position+V(drive,"targetDirection")*drive.GetProperty("targetDistance").GetDouble();
initialRadius=position.Length;
if(!FlightGeometry.Obscures(star,position,destination)||initialRadius<exclusion*1.4||frame.GetProperty("heatPercent").GetDouble()>=86)throw new InvalidOperationException("Requires a real star-obscured target, exterior approach and initial heat below 86 percent.");
await Log(new{kind="preflight",start,pid,target,world,frame,drive,geometry,loadoutShip=loadout.GetProperty("Ship").GetString()});
var previousRadial=position.Unit();var orbitRadius=Math.Max(exclusion*1.25,initialRadius*.9);var phase="prealign";double previousHeat=frame.GetProperty("heatPercent").GetDouble(),previousSeconds=0,lastBandChange=0;
while(clock.Elapsed.TotalSeconds<300)
{
using(var current=Process.GetProcessById(pid))if(current.StartTime.ToUniversalTime().Ticks!=processStart)throw new InvalidOperationException("Process identity changed.");
frame=await pipe.CallAsync(new{op="read"});drive=await pipe.CallAsync(new{op="fsd.describe"});geometry=await pipe.CallAsync(new{op="geometry.describe"});frames++;
if(!frame.GetProperty("inGame").GetBoolean()||frame.GetProperty("playerOverride").GetBoolean()||!drive.GetProperty("fresh").GetBoolean()||drive.GetProperty("state").GetInt32()!=2||drive.GetProperty("entityId").GetString()!=frame.GetProperty("entityId").GetString()||!geometry.GetProperty("fresh").GetBoolean()||long.Parse(geometry.GetProperty("worldAddress").GetString()!)!=world||ulong.Parse(drive.GetProperty("targetAddress").GetString()!)!=target)throw new InvalidOperationException("Orbit state or ownership changed.");
using var status=ReadShared(statusPath);var flags=status.RootElement.GetProperty("Flags").GetUInt64();
if((flags&16)==0||(flags&(1UL|131072UL|1048576UL|4194304UL))!=0)throw new InvalidOperationException("Supercruise or safety state changed.");
var activeScoop=(flags&2048)!=0;
foreach(var e in Events(journal))if(e.GetProperty("event").GetString()=="FuelScoop"&&e.GetProperty("timestamp").GetDateTimeOffset()>=start.AddSeconds(-1))journalScooped=Math.Max(journalScooped,e.GetProperty("Scooped").GetDouble());
fuelGain=Math.Max(fuelGain,status.RootElement.GetProperty("Fuel").GetProperty("FuelMain").GetDouble()-initialFuel);
scooped=Math.Max(journalScooped,fuelGain);
position=V(frame,"position");var radius=position.Length;var heat=frame.GetProperty("heatPercent").GetDouble();var seconds=clock.Elapsed.TotalSeconds;
var slope=(heat-previousHeat)/Math.Max(.02,seconds-previousSeconds);previousHeat=heat;previousSeconds=seconds;
peak=Math.Max(peak,heat);minimumRadius=Math.Min(minimumRadius,radius);rotated+=Vector3d.AngleDegrees(previousRadial,position);previousRadial=position.Unit();
destination=position+V(drive,"targetDirection")*drive.GetProperty("targetDistance").GetDouble();cleared=!FlightGeometry.Obscures(star,position,destination);
if(activeScoop)
{
// Anchor at the first actual scoop boundary rather than continue toward a lower search radius.
if(!sawScooping)orbitRadius=Math.Max(exclusion*1.25,radius*.999);
sawScooping=true;samples.Add(new{radius,heat,scooped,seconds});
}
if(heat>=88||heat+Math.Max(0,slope)*8>=94||radius<exclusion*1.18)thermalEscape=true;
var desired=thermalEscape?position.Unit():FlightGeometry.OrbitAtRadius(star,position,destination,V(frame,"up"),orbitRadius);
var aim=AttitudeController.Aim(V(frame,"forward"),V(frame,"up"),desired,.6);
double throttle=0;var speed=frame.GetProperty("speedMetresPerSecond").GetDouble();
if(aim.ErrorDegrees<=8)
{
phase=thermalEscape?"thermal-escape":"orbit";
var fraction=drive.GetProperty("speedFraction").GetDouble();
var maximum=fraction>1e-7?speed/fraction:double.NaN;
if(!double.IsFinite(maximum)||maximum<=0)throw new InvalidDataException("Native speed scale is unavailable.");
throttle=speed>12_000_000?0:Math.Clamp(10_000_000/maximum,0,.15);
}
if(thermalEscape&&radius>=initialRadius*1.05&&heat<75)throw new InvalidOperationException("Thermal guard completed an outward escape; scoop/orbit acceptance is incomplete.");
if(heat>=95)throw new InvalidOperationException("Reached bounded orbit heat ceiling.");
await Log(new{kind="sample",seconds,phase,orbitRadius,thermalEscape,aim,throttle,activeScoop,scooped,cleared,frame,drive,geometry});
if(cleared&&sawScooping&&scooped>0&&rotated>=15){await Release();break;}
if(!activeScoop&&!thermalEscape&&seconds-lastBandChange>15&&phase=="orbit")
{orbitRadius=Math.Max(exclusion*1.25,orbitRadius-exclusion*.02);lastBandChange=seconds;}
await pipe.CallAsync(new{op="calibrate.axes",owner,sequence=frame.GetProperty("sequence").GetInt64(),ttlMilliseconds=250,throttle,
axes=new[]{0d,0,0,aim.Yaw,aim.Pitch,aim.Roll}});
await Task.Delay(60);
}
if(!cleared||!sawScooping||scooped<=0||rotated<15)throw new InvalidOperationException("Actual orbit, scoop and target clearance were not all observed within the bound.");
}
catch(Exception error){failure=error.ToString();}
finally{if(!released)try{await Release();}catch(Exception error){failure=(failure??"")+" Release: "+error.Message;}}
var passed=failure is null&&released&&cleared&&sawScooping&&scooped>0&&rotated>=15;
await File.WriteAllTextAsync(reportPath,JsonSerializer.Serialize(new{pid,hash,start,target,world,exclusion,initialRadius,minimumRadius,frames,peak,rotated,cleared,sawScooping,scooped,journalScooped,initialFuel,fuelGain,thermalEscape,released,passed,failure,scoopSamples=samples,screenUse=false,osInputSent=false,fsdInputsSent=0,completeAutomaticNavigationAccepted=false},new JsonSerializerOptions{WriteIndented=true}));
Console.WriteLine(JsonSerializer.Serialize(new{passed,frames,peak,rotated,cleared,sawScooping,scooped,thermalEscape,released,failure}));return passed;
}
}