using System.Text.Json;
using XFEEDToolBox.Core.Plugins;
namespace XFEEDToolBox.Core.Navigation;
public sealed partial class NativeSystemFlightControlDriver
{
private NavigationPipeClient? dockingResetPipe;
private string? dockingResetRequestId;
private DateTimeOffset dockingResetDeadline;
private bool requestOriginalDockingStart;
private bool emptyDockingDiagnostic=true;
public JsonElement? LastOriginalDockingResetReceipt{get;private set;}
/// <summary>Explicit empty-ship validation retains its narrower ship/cargo limits.</summary>
public Task EnableOriginalDockingResetTestAsync(CancellationToken token=default)=>EnableOriginalDockingResetTestAsync(false,token);
public async Task EnableOriginalDockingResetTestAsync(bool startDockingAfterZero,CancellationToken token=default)
=>await ConfigureOriginalDockingAsync(startDockingAfterZero,true,token);
public async Task ConfigureOriginalDockingControlAsync(string packageDirectory,CancellationToken token=default)
{
var native=new NavigationPipeClient(processId,OriginalDockingService);
try{await native.CallAsync(new{op="describe"},token);}
catch(Exception error)when(!token.IsCancellationRequested&&error is IOException or OperationCanceledException)
{
var main=await pipe.CallAsync(new{op="describe"},token);
if(main.GetProperty("processId").GetInt32()!=processId||main.GetProperty("gameSha256").GetString()!=gameHash)
throw new InvalidDataException("Original docking package belongs to another game.");
await RuntimeInteractionModule.LoadAsync(processId,gameHash,main.GetProperty("owningThread").GetInt32(),packageDirectory,token);
}
await ConfigureOriginalDockingAsync(true,false,token);
}
private async Task ConfigureOriginalDockingAsync(bool startDockingAfterZero,bool diagnostic,CancellationToken token)
{
if(dockingResetRequestId is not null)throw new InvalidOperationException("An original docking test request is already immutable.");
const string service=OriginalDockingService;
var candidate=new NavigationPipeClient(processId,service);var hello=await candidate.CallAsync(new{op="describe"},token);
if(hello.GetProperty("processId").GetInt32()!=processId||hello.GetProperty("gameSha256").GetString()!=gameHash
||hello.GetProperty("service").GetString()!=service||!Flag(hello,"nativeDockingReset")||!Flag(hello,"enabled")
||hello.GetProperty("owningThread").GetInt32()<=0||hello.GetProperty("state").GetString()!="InGame"||hello.GetProperty("nextState").GetString()!="InGame"
||DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()-hello.GetProperty("observedAtUnixMilliseconds").GetInt64() is <0 or >200
||startDockingAfterZero&&!Flag(hello,"nativeDockingStart"))
throw new NotSupportedException("Verified original docking reset test extension is not loaded.");
dockingResetPipe=candidate;requestOriginalDockingStart=startDockingAfterZero;emptyDockingDiagnostic=diagnostic;
}
private void AdoptOriginalResetReceipt(JsonElement value)
{
if(value.GetProperty("requestId").GetString()!=dockingResetRequestId)throw new InvalidDataException("Original reset receipt identity differs.");
if(value.TryGetProperty("nativeGeneration",out var selectedGeneration)&&selectedGeneration.ValueKind==JsonValueKind.Number&&selectedGeneration.GetInt64()>0)
generation=selectedGeneration.GetInt64();
LastOriginalDockingResetReceipt=value.Clone();
}
private async Task OriginalDockingResetAsync(SystemFlightFrame frame,DateTimeOffset deadline,CancellationToken token)
{
var service=dockingResetPipe??throw new InvalidOperationException("Original docking reset is not configured.");
if(frame.Mode!=LocalFlightMode.Normal||frame.PlayerOverride||frame.ExternalThreat||selected is null||frame.Clearance?.Allows(selected,DateTimeOffset.UtcNow)!=true
||frame.TargetDistance>7500||player().CurrentShipId is null
||emptyDockingDiagnostic&&(player().CargoCount!=0||player().CurrentShipId!=5||player().CargoCapacity!=192))
throw new InvalidOperationException("Original reset requires the same selected main ship, current ATC and the configured validation scope.");
if(dockingResetRequestId is not null)
{
if(LastOriginalDockingResetReceipt is {} prior&&Flag(prior,"applied")
&&LastTelemetry is {} current&&NativeSystemFlightObservation.DockingInputZeroConfirmed(current))return;
throw new InvalidOperationException("This docking grant already used its one original reset attempt; it will not be replayed.");
}
if(ensureFlightProtection is not null)await ensureFlightProtection(token);
await pipe.CallAsync(new{op="local.axes.release",owner,generation},token);
var remaining=(int)Math.Floor((deadline-DateTimeOffset.UtcNow).TotalMilliseconds);
if(remaining is <1 or >250)throw new FlightTelemetryUnavailableException("Original reset intent frame is expired.");
dockingResetRequestId=Guid.NewGuid().ToString("N");dockingResetDeadline=deadline;
try
{
var receipt=await service.CallAsync(new{op="docking.reset.arm",requestId=dockingResetRequestId,owner,generation,
sequence=frame.Sequence,marketId=selected.MarketId,ttlMilliseconds=remaining,expiresAtUnixMilliseconds=deadline.ToUnixTimeMilliseconds(),startDockingAfterZero=requestOriginalDockingStart},token);
AdoptOriginalResetReceipt(receipt);
}
catch(Exception original)when(original is IOException or InvalidOperationException or OperationCanceledException)
{
// Resolve an uncertain acknowledgement by the same immutable ID;
// arm is never sent again, even if a native reselect already happened.
JsonElement reconciled;
try
{
using var reconciliation=new CancellationTokenSource(TimeSpan.FromMilliseconds(500));
reconciled=await service.CallAsync(new{op="docking.reset.status",requestId=dockingResetRequestId,owner},reconciliation.Token);
}
catch(Exception status)when(!emptyDockingDiagnostic&&!token.IsCancellationRequested
&&(NavigationRecovery.CanRetry(status,CancellationToken.None)||status is InvalidOperationException&&status.Message=="No matching original reset request"))
{throw new DockingResetObservationPendingException(dockingResetRequestId,selected.MarketId!.Value,frame.ObservedAt,new AggregateException(original,status));}
catch(Exception status){throw new InvalidOperationException("Original reset outcome is unknown; the arm request was not replayed.",new AggregateException(original,status));}
AdoptOriginalResetReceipt(reconciled);
}
while(true)
{
token.ThrowIfCancellationRequested();var receipt=LastOriginalDockingResetReceipt!.Value;
if(Flag(receipt,"failed")||Flag(receipt,"expired"))
{
var error=new InvalidOperationException("Original reset was rejected: "+(receipt.TryGetProperty("issue",out var issue)?issue.GetString():"no detail"));
if(!emptyDockingDiagnostic&&DockingValidationProof.RejectedBeforeResetDispatch(receipt))
throw new DockingResetObservationPendingException(dockingResetRequestId,selected.MarketId!.Value,frame.ObservedAt,error);
throw error;
}
if(Flag(receipt,"applied"))
{
if(!DockingValidationProof.OriginalResetZero(receipt,frame.ShipEntity,selected.MarketId!.Value,frame.ObservedAt,DateTimeOffset.UtcNow))
throw new InvalidDataException("Original reset did not verify the selected main ship's actual accumulator and source zero.");
if(!requestOriginalDockingStart)return;
if(!Flag(receipt,"dockingStartRequested"))throw new InvalidDataException("Explicit original docking start was not accepted by the pinned test extension.");
if(receipt.TryGetProperty("dockingStartFailure",out var startFailure)&&startFailure.ValueKind==JsonValueKind.String&&!string.IsNullOrEmpty(startFailure.GetString()))
throw new InvalidOperationException("Original docking start was rejected: "+startFailure.GetString());
if(Flag(receipt,"originalDockingStartCalled"))return;
// A status read can overlap the one original native call.
// Reconcile that same request until its original deadline;
// neither an in-progress call nor an unknown reply is retried.
}
if(DateTimeOffset.UtcNow>deadline.AddMilliseconds(350))throw new InvalidOperationException("Original reset receipt remains unresolved; no request will be replayed.");
await Task.Delay(10,token);
AdoptOriginalResetReceipt(await service.CallAsync(new{op="docking.reset.status",requestId=dockingResetRequestId,owner},token));
}
}
private async Task CancelOriginalDockingResetAsync()
{
if(dockingResetPipe is null||dockingResetRequestId is null)return;
try{AdoptOriginalResetReceipt(await dockingResetPipe.CallAsync(new{op="docking.reset.cancel",requestId=dockingResetRequestId,owner}));}
catch
{
// The extension enforces this original absolute deadline. Waiting
// it out does not extend the input or create a replacement request.
var remaining=dockingResetDeadline-DateTimeOffset.UtcNow;if(remaining>TimeSpan.Zero)await Task.Delay(remaining);
}
}
}
using System.Text.Json;
using XFEEDToolBox.Core.Plugins;
namespace XFEEDToolBox.Core.Navigation;
public sealed partial class NativeSystemFlightControlDriver
{
private NavigationPipeClient? dockingResetPipe;
private string? dockingResetRequestId;
private DateTimeOffset dockingResetDeadline;
private bool requestOriginalDockingStart;
private bool emptyDockingDiagnostic=true;
public JsonElement? LastOriginalDockingResetReceipt{get;private set;}
/// <summary>Explicit empty-ship validation retains its narrower ship/cargo limits.</summary>
public Task EnableOriginalDockingResetTestAsync(CancellationToken token=default)=>EnableOriginalDockingResetTestAsync(false,token);
public async Task EnableOriginalDockingResetTestAsync(bool startDockingAfterZero,CancellationToken token=default)
=>await ConfigureOriginalDockingAsync(startDockingAfterZero,true,token);
public async Task ConfigureOriginalDockingControlAsync(string packageDirectory,CancellationToken token=default)
{
var native=new NavigationPipeClient(processId,OriginalDockingService);
try{await native.CallAsync(new{op="describe"},token);}
catch(Exception error)when(!token.IsCancellationRequested&&error is IOException or OperationCanceledException)
{
var main=await pipe.CallAsync(new{op="describe"},token);
if(main.GetProperty("processId").GetInt32()!=processId||main.GetProperty("gameSha256").GetString()!=gameHash)
throw new InvalidDataException("Original docking package belongs to another game.");
await RuntimeInteractionModule.LoadAsync(processId,gameHash,main.GetProperty("owningThread").GetInt32(),packageDirectory,token);
}
await ConfigureOriginalDockingAsync(true,false,token);
}
private async Task ConfigureOriginalDockingAsync(bool startDockingAfterZero,bool diagnostic,CancellationToken token)
{
if(dockingResetRequestId is not null)throw new InvalidOperationException("An original docking test request is already immutable.");
const string service=OriginalDockingService;
var candidate=new NavigationPipeClient(processId,service);var hello=await candidate.CallAsync(new{op="describe"},token);
if(hello.GetProperty("processId").GetInt32()!=processId||hello.GetProperty("gameSha256").GetString()!=gameHash
||hello.GetProperty("service").GetString()!=service||!Flag(hello,"nativeDockingReset")||!Flag(hello,"enabled")
||hello.GetProperty("owningThread").GetInt32()<=0||hello.GetProperty("state").GetString()!="InGame"||hello.GetProperty("nextState").GetString()!="InGame"
||DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()-hello.GetProperty("observedAtUnixMilliseconds").GetInt64() is <0 or >200
||startDockingAfterZero&&!Flag(hello,"nativeDockingStart"))
throw new NotSupportedException("Verified original docking reset test extension is not loaded.");
dockingResetPipe=candidate;requestOriginalDockingStart=startDockingAfterZero;emptyDockingDiagnostic=diagnostic;
}
private void AdoptOriginalResetReceipt(JsonElement value)
{
if(value.GetProperty("requestId").GetString()!=dockingResetRequestId)throw new InvalidDataException("Original reset receipt identity differs.");
if(value.TryGetProperty("nativeGeneration",out var selectedGeneration)&&selectedGeneration.ValueKind==JsonValueKind.Number&&selectedGeneration.GetInt64()>0)
generation=selectedGeneration.GetInt64();
LastOriginalDockingResetReceipt=value.Clone();
}
private async Task OriginalDockingResetAsync(SystemFlightFrame frame,DateTimeOffset deadline,CancellationToken token)
{
var service=dockingResetPipe??throw new InvalidOperationException("Original docking reset is not configured.");
if(frame.Mode!=LocalFlightMode.Normal||frame.PlayerOverride||frame.ExternalThreat||selected is null||frame.Clearance?.Allows(selected,DateTimeOffset.UtcNow)!=true
||frame.TargetDistance>7500||player().CurrentShipId is null
||emptyDockingDiagnostic&&(player().CargoCount!=0||player().CurrentShipId!=5||player().CargoCapacity!=192))
throw new InvalidOperationException("Original reset requires the same selected main ship, current ATC and the configured validation scope.");
if(dockingResetRequestId is not null)
{
if(LastOriginalDockingResetReceipt is {} prior&&Flag(prior,"applied")
&&LastTelemetry is {} current&&NativeSystemFlightObservation.DockingInputZeroConfirmed(current))return;
throw new InvalidOperationException("This docking grant already used its one original reset attempt; it will not be replayed.");
}
if(ensureFlightProtection is not null)await ensureFlightProtection(token);
await pipe.CallAsync(new{op="local.axes.release",owner,generation},token);
var remaining=(int)Math.Floor((deadline-DateTimeOffset.UtcNow).TotalMilliseconds);
if(remaining is <1 or >250)throw new FlightTelemetryUnavailableException("Original reset intent frame is expired.");
dockingResetRequestId=Guid.NewGuid().ToString("N");dockingResetDeadline=deadline;
try
{
var receipt=await service.CallAsync(new{op="docking.reset.arm",requestId=dockingResetRequestId,owner,generation,
sequence=frame.Sequence,marketId=selected.MarketId,ttlMilliseconds=remaining,expiresAtUnixMilliseconds=deadline.ToUnixTimeMilliseconds(),startDockingAfterZero=requestOriginalDockingStart},token);
AdoptOriginalResetReceipt(receipt);
}
catch(Exception original)when(original is IOException or InvalidOperationException or OperationCanceledException)
{
// Resolve an uncertain acknowledgement by the same immutable ID;
// arm is never sent again, even if a native reselect already happened.
JsonElement reconciled;
try
{
using var reconciliation=new CancellationTokenSource(TimeSpan.FromMilliseconds(500));
reconciled=await service.CallAsync(new{op="docking.reset.status",requestId=dockingResetRequestId,owner},reconciliation.Token);
}
catch(Exception status)when(!emptyDockingDiagnostic&&!token.IsCancellationRequested
&&(NavigationRecovery.CanRetry(status,CancellationToken.None)||status is InvalidOperationException&&status.Message=="No matching original reset request"))
{throw new DockingResetObservationPendingException(dockingResetRequestId,selected.MarketId!.Value,frame.ObservedAt,new AggregateException(original,status));}
catch(Exception status){throw new InvalidOperationException("Original reset outcome is unknown; the arm request was not replayed.",new AggregateException(original,status));}
AdoptOriginalResetReceipt(reconciled);
}
while(true)
{
token.ThrowIfCancellationRequested();var receipt=LastOriginalDockingResetReceipt!.Value;
if(Flag(receipt,"failed")||Flag(receipt,"expired"))
{
var error=new InvalidOperationException("Original reset was rejected: "+(receipt.TryGetProperty("issue",out var issue)?issue.GetString():"no detail"));
if(!emptyDockingDiagnostic&&DockingValidationProof.RejectedBeforeResetDispatch(receipt))
throw new DockingResetObservationPendingException(dockingResetRequestId,selected.MarketId!.Value,frame.ObservedAt,error);
throw error;
}
if(Flag(receipt,"applied"))
{
if(!DockingValidationProof.OriginalResetZero(receipt,frame.ShipEntity,selected.MarketId!.Value,frame.ObservedAt,DateTimeOffset.UtcNow))
throw new InvalidDataException("Original reset did not verify the selected main ship's actual accumulator and source zero.");
if(!requestOriginalDockingStart)return;
if(!Flag(receipt,"dockingStartRequested"))throw new InvalidDataException("Explicit original docking start was not accepted by the pinned test extension.");
if(receipt.TryGetProperty("dockingStartFailure",out var startFailure)&&startFailure.ValueKind==JsonValueKind.String&&!string.IsNullOrEmpty(startFailure.GetString()))
throw new InvalidOperationException("Original docking start was rejected: "+startFailure.GetString());
if(Flag(receipt,"originalDockingStartCalled"))return;
// A status read can overlap the one original native call.
// Reconcile that same request until its original deadline;
// neither an in-progress call nor an unknown reply is retried.
}
if(DateTimeOffset.UtcNow>deadline.AddMilliseconds(350))throw new InvalidOperationException("Original reset receipt remains unresolved; no request will be replayed.");
await Task.Delay(10,token);
AdoptOriginalResetReceipt(await service.CallAsync(new{op="docking.reset.status",requestId=dockingResetRequestId,owner},token));
}
}
private async Task CancelOriginalDockingResetAsync()
{
if(dockingResetPipe is null||dockingResetRequestId is null)return;
try{AdoptOriginalResetReceipt(await dockingResetPipe.CallAsync(new{op="docking.reset.cancel",requestId=dockingResetRequestId,owner}));}
catch
{
// The extension enforces this original absolute deadline. Waiting
// it out does not extend the input or create a replacement request.
var remaining=dockingResetDeadline-DateTimeOffset.UtcNow;if(remaining>TimeSpan.Zero)await Task.Delay(remaining);
}
}
}