using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using static XFEEDToolBox.Plugin.JsonWire;
namespace XFEEDToolBox.Plugin;
internal static unsafe partial class Navigation
{
// One-use, fixed-value empty-ship diagnostic, not a general throttle API.
// Only the original node may write its accumulator and computed output.
private struct DockingFixture
{
public string? id,issue;
public LocalActionRequest command;
public uint stage,unseededFrames;
public nuint node,binding,source;
public ulong until,seedSequence,lastSequence,utc;
public float before,accumulator,sourceValue,commandValue;
public bool restored,zeroVerified;
}
private struct DockingFixturePulse
{
public nuint node,binding,source;
public float beforeAccumulator;
public bool applied;
}
private static DockingFixture dockingFixture;
private static readonly HashSet<string> dockingFixtureUsed=new(StringComparer.Ordinal);
private static bool FixtureGeometryEligible(double distance,double speed,double outwardCosine)
=>double.IsFinite(distance)&&distance is >=4500 and <=7200&&double.IsFinite(speed)&&speed is >=0 and <=15&&double.IsFinite(outwardCosine)&&outwardCosine>=.98;
private static void ExpireDockingFixture(){lock(localLock){dockingFixture.command.expires=0;dockingFixture.until=0;}}
private static JsonObject DockingFixtureRequest(string op,JsonElement request,string owner,in Snapshot frame)
{
var now=GetTickCount64();var id=request.Str("trialId");
if(id.Length!=32||id.Any(c=>!"0123456789abcdef".Contains(c)))throw new InvalidOperationException("Explicit one-use docking trial identity required");
LocalSelection selection;lock(localLock)selection=localSelection;
if(clientOwner!=owner||request.U64("generation")!=selection.generation||localConfirmed!=selection.generation)throw new InvalidOperationException("Docking trial must belong to the current selected controller");
lock(localLock)
{
if(op=="docking.validation.arm")
{
if(dockingFixture.id==id)return DockingFixtureDescription(now);
if(dockingFixtureUsed.Contains(id)||dockingFixtureUsed.Count>=32||dockingFixture.until>now)throw new InvalidOperationException("Docking trial consumed or another trial active");
if(!dockingInputMapperReady||!Enabled()||!inGame||!frame.valid||!frame.healthValid||frame.healthEmergency||frame.hullFraction<.95||overridden||frame.rawFsdState is not(1 or 2)||fsdOwned||selection.entity!=frame.entity||selection.life!=frame.life)throw new InvalidOperationException("Fresh healthy normal-space main ship required for empty docking trial");
var reference=Reference(request.U64("sequence"),frame,now);dockingFixtureUsed.Add(id);
dockingFixtureDiagnostics=default;
dockingFixture=new(){id=id,stage=1,until=now+15000,command=new(){expires=Math.Min(now+250,reference.tick+300),sequence=reference.sequence,selection=selection.generation,entity=frame.entity,life=frame.life,inputRoot=frame.inputRoot,world=frame.referenceWorld,epoch=controllerEpoch}};
}
else
{
if(dockingFixture.id!=id)throw new InvalidOperationException("No matching docking diagnostic trial");
if(op=="docking.validation.cancel"){dockingFixture.command.expires=0;dockingFixture.until=0;}
}
return DockingFixtureDescription(now);
}
}
private static JsonObject DockingFixtureDescription(ulong now)
{
if(dockingFixture.stage==1&&dockingFixture.command.expires<=now){dockingFixture.stage=6;dockingFixture.issue??="Eligible original mapper did not consume the diagnostic before its deadline";}
return Obj(("trialId",dockingFixture.id),("stage",dockingFixture.stage),("issue",dockingFixture.issue),("oneUse",true),("fixedCommand",.75),("seedSequence",dockingFixture.seedSequence),("lastSequence",dockingFixture.lastSequence),("unseededFrames",dockingFixture.unseededFrames),("observedAtUnixMilliseconds",dockingFixture.utc),("accumulatorBefore",dockingFixture.before),("accumulatorAfter",dockingFixture.accumulator),("sourceAfter",dockingFixture.sourceValue),("commandAfter",dockingFixture.commandValue),("commandRestored",dockingFixture.restored),("zeroVerified",dockingFixture.zeroVerified),("persistent075",dockingFixture.unseededFrames>=3),("gameAccumulatorWrittenBySdk",false),("gameOutputWrittenBySdk",false),("diagnostics",DockingFixtureDiagnosticsDescription()));
}
private static nuint ResolveFixtureBinding(nuint actions,nuint name)
{
var scratch=stackalloc byte[47];var temporary=AlignServiceScratch(scratch);new Span<byte>(temporary,32).Clear();
try{Call(image+0x8FEDC0,actions,(ulong)(nuint)temporary,name);return Pointer((nuint)temporary);}
finally{Call(image+0x204010,(ulong)(nuint)temporary);}
}
private static bool FixtureBindingName(nuint binding,string expected)
{
if(binding<0x10000||Read<int>(binding)<=0)return false;
var text=Pointer(binding+8);if(text<0x10000||Read<ulong>(text)!=(ulong)expected.Length)return false;
return Platform.ReadBytes(text+0x14,expected.Length).AsSpan().SequenceEqual(Encoding.ASCII.GetBytes(expected));
}
private static bool BeginDockingFixturePulse(nuint node,nuint binding,nuint source,out DockingFixturePulse pulse)
{
pulse=default;if(node<0x10000||binding<0x10000||source<0x10000||binding==source||Pointer(node+0xB0)!=binding||Pointer(node+0x78)!=source||Read<int>(binding)<=0||Read<uint>(binding+0x18)!=0)return false;
var before=Read<float>(node+0xD0);if(!float.IsFinite(before)||Math.Abs(before)>1.001f)return false;
pulse=new(){node=node,binding=binding,source=source,beforeAccumulator=before,applied=true};Write(binding+0x18,.75f);return true;
}
private static void EndDockingFixturePulse(in DockingFixturePulse pulse)
{
if(pulse.applied&&Pointer(pulse.node+0xB0)==pulse.binding&&Pointer(pulse.node+0x78)==pulse.source&&Read<uint>(pulse.binding+0x18)==0x3F400000)Write(pulse.binding+0x18,0u);
}
private static bool PrepareDockingFixturePulse(nuint node,float dt,out DockingFixturePulse pulse)
{
pulse=default;DockingFixture wanted;LocalSelection selection;LocalFlightObservation observed;
lock(localLock){wanted=dockingFixture;selection=localSelection;observed=localObservation;}
var now=GetTickCount64();if(wanted.stage!=1||wanted.command.expires<=now)return false;
BeginFixtureDiagnostics(node,dt,now);
if(!dockingInputMapperReady)return RefuseFixture(FixtureRefusal.MapperReady);
if(GetCurrentThreadId()!=owningThread)return RefuseFixture(FixtureRefusal.Thread);
if(!float.IsFinite(dt)||dt<=0||dt>.25f)return RefuseFixture(FixtureRefusal.DeltaTime);
var frame=Current();var command=wanted.command;
lock(localLock){dockingFixtureDiagnostics.frameAge=now>=frame.tick?now-frame.tick:ulong.MaxValue;dockingFixtureDiagnostics.observationAge=now>=observed.tick?now-observed.tick:ulong.MaxValue;}
if(!Enabled())return RefuseFixture(FixtureRefusal.Enabled);
if(!inGame)return RefuseFixture(FixtureRefusal.InGame);
if(!frame.valid)return RefuseFixture(FixtureRefusal.FrameValid);
if(!frame.healthValid||frame.healthEmergency||frame.hullFraction<.95)return RefuseFixture(FixtureRefusal.Health);
if(overridden)return RefuseFixture(FixtureRefusal.Override);
if(fsdOwned)return RefuseFixture(FixtureRefusal.FsdOwned);
if(frame.rawFsdState is not(1 or 2))return RefuseFixture(FixtureRefusal.FsdState);
if(now<frame.tick||now-frame.tick>200)return RefuseFixture(FixtureRefusal.FrameFresh);
if(command.entity!=frame.entity)return RefuseFixture(FixtureRefusal.Entity);
if(command.life!=frame.life)return RefuseFixture(FixtureRefusal.Life);
if(command.inputRoot!=frame.inputRoot)return RefuseFixture(FixtureRefusal.InputRoot);
if(command.world!=frame.referenceWorld)return RefuseFixture(FixtureRefusal.World);
if(command.epoch!=controllerEpoch)return RefuseFixture(FixtureRefusal.Epoch);
if(command.selection!=selection.generation)return RefuseFixture(FixtureRefusal.Selection);
if(localConfirmed!=selection.generation)return RefuseFixture(FixtureRefusal.Confirmation);
if(!BoundFsd(frame,frame.fsd))return RefuseFixture(FixtureRefusal.FsdBinding);
if(!observed.valid)return RefuseFixture(FixtureRefusal.ObservationValid);
if(observed.entity!=frame.entity)return RefuseFixture(FixtureRefusal.ObservationEntity);
if(now<observed.tick||now-observed.tick>200)return RefuseFixture(FixtureRefusal.ObservationFresh);
if(observed.inSupercruise)return RefuseFixture(FixtureRefusal.Supercruise);
if(!SameLocal(observed.target,selection.target))return RefuseFixture(FixtureRefusal.Target);
if(observed.nativeDockingActive)return RefuseFixture(FixtureRefusal.DockingDuty);
if(observed.nativeLaunchActive)return RefuseFixture(FixtureRefusal.LaunchDuty);
if(observed.nativeLaunchRequested)return RefuseFixture(FixtureRefusal.LaunchRequest);
if(observed.cruiseAssistEnabled)return RefuseFixture(FixtureRefusal.ScaEnabled);
if(observed.cruiseAssistEngaged)return RefuseFixture(FixtureRefusal.ScaEngaged);
var outward=Subtract(observed.ship.position,observed.destination.position);var length=Math.Sqrt(Dot(outward,outward));
if(length<=0||!FixtureGeometryEligible(observed.distance,observed.speed,Dot(outward,observed.ship.forward)/length))return RefuseFixture(FixtureRefusal.Geometry);
bool released;ulong flightUntil,fsdUntil;lock(leaseLock){flightUntil=lease.expires;fsdUntil=fsdLease.expires;released=flightUntil<=now&&fsdUntil<=now;}
lock(localLock){dockingFixtureDiagnostics.leaseUntil=flightUntil;dockingFixtureDiagnostics.fsdLeaseUntil=fsdUntil;}
if(!released)return RefuseFixture(FixtureRefusal.Lease);
var inventory=ReadCommerceShipInventory(frame);
lock(localLock){dockingFixtureDiagnostics.inventoryVerified=inventory.verified;dockingFixtureDiagnostics.cargoCount=inventory.count;dockingFixtureDiagnostics.cargoCapacity=inventory.capacity;}
if(!inventory.verified||inventory.count!=0||inventory.capacity!=192)return RefuseFixture(FixtureRefusal.Inventory);
var computer=LocalDockingComputer(frame);if(computer==0||!LocalDockingAvailable(computer))return RefuseFixture(FixtureRefusal.Computer);
var source=Pointer(computer+0x260);lock(localLock)dockingFixtureDiagnostics.source=source;
if(source==0||Pointer(node+0x78)!=source)return RefuseFixture(FixtureRefusal.SourceAlias);
var actions=Pointer(computer+0x230);var active=Pointer(node+0x90);
lock(localLock){dockingFixtureDiagnostics.registry=Pointer(node+0x40);dockingFixtureDiagnostics.active=active;dockingFixtureDiagnostics.activeFlag=active>=0x10000?Read<byte>(active+0x24):(byte)0;}
if(actions==0||Pointer(actions)!=image+0x512FD90||Pointer(Pointer(actions)+0x40)!=image+0x8FEDC0||Pointer(node+0x40)!=actions)return RefuseFixture(FixtureRefusal.Registry);
if(active<0x10000||Read<byte>(active+0x24)==0)return RefuseFixture(FixtureRefusal.ActiveBinding);
if((byte)Call(image+0xED6620,computer)!=0)return RefuseFixture(FixtureRefusal.AiDuty);
var dock=LocalDocking(frame);if(dock==0||Read<uint>(dock+0x4C0)is 5 or 7)return RefuseFixture(FixtureRefusal.DockState);
if(Read<uint>(dock+0x614)==2)return RefuseFixture(FixtureRefusal.RequestState);
var absolute=ResolveFixtureBinding(actions,image+0x532FD58);var zero=ResolveFixtureBinding(actions,image+0x531A568);
lock(localLock){dockingFixtureDiagnostics.absolute=Pointer(node+0xB0);dockingFixtureDiagnostics.zero=Pointer(node+0xB8);var a=Read<float>(node+0xD0);var b=Read<float>(absolute+0x18);dockingFixtureDiagnostics.accumulator=float.IsFinite(a)?a:0;dockingFixtureDiagnostics.absoluteValue=float.IsFinite(b)?b:0;}
if(!FixtureBindingName(absolute,"ForwardReverseThrust_Set"))return RefuseFixture(FixtureRefusal.AbsoluteName);
if(!FixtureBindingName(zero,"SetSpeedZero"))return RefuseFixture(FixtureRefusal.ZeroName);
if(Pointer(node+0xB0)!=absolute)return RefuseFixture(FixtureRefusal.AbsoluteAlias);
if(!DockingMapperNodeMatches(node,zero,source))return RefuseFixture(FixtureRefusal.ZeroAlias);
if(Read<byte>(zero+0x24)!=0)return RefuseFixture(FixtureRefusal.ZeroAlreadySet);
if(!BeginDockingFixturePulse(node,absolute,source,out pulse))return RefuseFixture(FixtureRefusal.Pulse);
lock(localLock){dockingFixture.node=node;dockingFixture.binding=absolute;dockingFixture.source=source;}return true;
}
private static void ObserveDockingFixtureMapper(nuint node,in DockingFixturePulse pulse,in DockingMapperPulse zeroPulse)
{
DockingFixture wanted;lock(localLock)wanted=dockingFixture;
if(wanted.node!=node||wanted.until<=GetTickCount64()||wanted.stage is not(1 or 2 or 3)||Pointer(node+0xB0)!=wanted.binding||Pointer(node+0x78)!=wanted.source)return;
var frame=Current();if(frame.entity!=wanted.command.entity||frame.life!=wanted.command.life)return;
var accumulator=Read<float>(node+0xD0);var source=Read<float>(wanted.source+0x18);var command=Read<float>(wanted.binding+0x18);var finite=float.IsFinite(accumulator)&&float.IsFinite(source)&&float.IsFinite(command);
lock(localLock)
{
dockingFixture.utc=UtcMilliseconds();dockingFixture.accumulator=finite?accumulator:0;dockingFixture.sourceValue=finite?source:0;dockingFixture.commandValue=finite?command:0;
if(pulse.applied)
{
dockingFixture.before=pulse.beforeAccumulator;dockingFixture.seedSequence=frame.sequence;dockingFixture.lastSequence=frame.sequence;dockingFixture.restored=command==0;
dockingFixture.stage=finite&&Math.Abs(accumulator-.75f)<.001f&&Math.Abs(source-.75f)<.001f&&command==0?2u:6u;
if(dockingFixture.stage==6)dockingFixture.issue="Original absolute command did not produce retained .75 state";
}
else if(zeroPulse.applied&&wanted.unseededFrames>=3&&Math.Abs(zeroPulse.beforeAccumulator-.75f)<.001f&&finite&&Math.Abs(accumulator)<.001f&&Math.Abs(source)<.001f)
{dockingFixture.zeroVerified=true;dockingFixture.stage=4;dockingFixture.lastSequence=frame.sequence;}
else if(frame.sequence>wanted.lastSequence)
{
dockingFixture.lastSequence=frame.sequence;
if(finite&&Math.Abs(accumulator-.75f)<.001f&&Math.Abs(source-.75f)<.001f&&command==0){dockingFixture.unseededFrames++;if(dockingFixture.unseededFrames>=3)dockingFixture.stage=3;}
else{dockingFixture.stage=6;dockingFixture.issue="Unseeded original graph did not retain .75; not a valid reproduction";}
}
}
}
}
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using static XFEEDToolBox.Plugin.JsonWire;
namespace XFEEDToolBox.Plugin;
internal static unsafe partial class Navigation
{
// One-use, fixed-value empty-ship diagnostic, not a general throttle API.
// Only the original node may write its accumulator and computed output.
private struct DockingFixture
{
public string? id,issue;
public LocalActionRequest command;
public uint stage,unseededFrames;
public nuint node,binding,source;
public ulong until,seedSequence,lastSequence,utc;
public float before,accumulator,sourceValue,commandValue;
public bool restored,zeroVerified;
}
private struct DockingFixturePulse
{
public nuint node,binding,source;
public float beforeAccumulator;
public bool applied;
}
private static DockingFixture dockingFixture;
private static readonly HashSet<string> dockingFixtureUsed=new(StringComparer.Ordinal);
private static bool FixtureGeometryEligible(double distance,double speed,double outwardCosine)
=>double.IsFinite(distance)&&distance is >=4500 and <=7200&&double.IsFinite(speed)&&speed is >=0 and <=15&&double.IsFinite(outwardCosine)&&outwardCosine>=.98;
private static void ExpireDockingFixture(){lock(localLock){dockingFixture.command.expires=0;dockingFixture.until=0;}}
private static JsonObject DockingFixtureRequest(string op,JsonElement request,string owner,in Snapshot frame)
{
var now=GetTickCount64();var id=request.Str("trialId");
if(id.Length!=32||id.Any(c=>!"0123456789abcdef".Contains(c)))throw new InvalidOperationException("Explicit one-use docking trial identity required");
LocalSelection selection;lock(localLock)selection=localSelection;
if(clientOwner!=owner||request.U64("generation")!=selection.generation||localConfirmed!=selection.generation)throw new InvalidOperationException("Docking trial must belong to the current selected controller");
lock(localLock)
{
if(op=="docking.validation.arm")
{
if(dockingFixture.id==id)return DockingFixtureDescription(now);
if(dockingFixtureUsed.Contains(id)||dockingFixtureUsed.Count>=32||dockingFixture.until>now)throw new InvalidOperationException("Docking trial consumed or another trial active");
if(!dockingInputMapperReady||!Enabled()||!inGame||!frame.valid||!frame.healthValid||frame.healthEmergency||frame.hullFraction<.95||overridden||frame.rawFsdState is not(1 or 2)||fsdOwned||selection.entity!=frame.entity||selection.life!=frame.life)throw new InvalidOperationException("Fresh healthy normal-space main ship required for empty docking trial");
var reference=Reference(request.U64("sequence"),frame,now);dockingFixtureUsed.Add(id);
dockingFixtureDiagnostics=default;
dockingFixture=new(){id=id,stage=1,until=now+15000,command=new(){expires=Math.Min(now+250,reference.tick+300),sequence=reference.sequence,selection=selection.generation,entity=frame.entity,life=frame.life,inputRoot=frame.inputRoot,world=frame.referenceWorld,epoch=controllerEpoch}};
}
else
{
if(dockingFixture.id!=id)throw new InvalidOperationException("No matching docking diagnostic trial");
if(op=="docking.validation.cancel"){dockingFixture.command.expires=0;dockingFixture.until=0;}
}
return DockingFixtureDescription(now);
}
}
private static JsonObject DockingFixtureDescription(ulong now)
{
if(dockingFixture.stage==1&&dockingFixture.command.expires<=now){dockingFixture.stage=6;dockingFixture.issue??="Eligible original mapper did not consume the diagnostic before its deadline";}
return Obj(("trialId",dockingFixture.id),("stage",dockingFixture.stage),("issue",dockingFixture.issue),("oneUse",true),("fixedCommand",.75),("seedSequence",dockingFixture.seedSequence),("lastSequence",dockingFixture.lastSequence),("unseededFrames",dockingFixture.unseededFrames),("observedAtUnixMilliseconds",dockingFixture.utc),("accumulatorBefore",dockingFixture.before),("accumulatorAfter",dockingFixture.accumulator),("sourceAfter",dockingFixture.sourceValue),("commandAfter",dockingFixture.commandValue),("commandRestored",dockingFixture.restored),("zeroVerified",dockingFixture.zeroVerified),("persistent075",dockingFixture.unseededFrames>=3),("gameAccumulatorWrittenBySdk",false),("gameOutputWrittenBySdk",false),("diagnostics",DockingFixtureDiagnosticsDescription()));
}
private static nuint ResolveFixtureBinding(nuint actions,nuint name)
{
var scratch=stackalloc byte[47];var temporary=AlignServiceScratch(scratch);new Span<byte>(temporary,32).Clear();
try{Call(image+0x8FEDC0,actions,(ulong)(nuint)temporary,name);return Pointer((nuint)temporary);}
finally{Call(image+0x204010,(ulong)(nuint)temporary);}
}
private static bool FixtureBindingName(nuint binding,string expected)
{
if(binding<0x10000||Read<int>(binding)<=0)return false;
var text=Pointer(binding+8);if(text<0x10000||Read<ulong>(text)!=(ulong)expected.Length)return false;
return Platform.ReadBytes(text+0x14,expected.Length).AsSpan().SequenceEqual(Encoding.ASCII.GetBytes(expected));
}
private static bool BeginDockingFixturePulse(nuint node,nuint binding,nuint source,out DockingFixturePulse pulse)
{
pulse=default;if(node<0x10000||binding<0x10000||source<0x10000||binding==source||Pointer(node+0xB0)!=binding||Pointer(node+0x78)!=source||Read<int>(binding)<=0||Read<uint>(binding+0x18)!=0)return false;
var before=Read<float>(node+0xD0);if(!float.IsFinite(before)||Math.Abs(before)>1.001f)return false;
pulse=new(){node=node,binding=binding,source=source,beforeAccumulator=before,applied=true};Write(binding+0x18,.75f);return true;
}
private static void EndDockingFixturePulse(in DockingFixturePulse pulse)
{
if(pulse.applied&&Pointer(pulse.node+0xB0)==pulse.binding&&Pointer(pulse.node+0x78)==pulse.source&&Read<uint>(pulse.binding+0x18)==0x3F400000)Write(pulse.binding+0x18,0u);
}
private static bool PrepareDockingFixturePulse(nuint node,float dt,out DockingFixturePulse pulse)
{
pulse=default;DockingFixture wanted;LocalSelection selection;LocalFlightObservation observed;
lock(localLock){wanted=dockingFixture;selection=localSelection;observed=localObservation;}
var now=GetTickCount64();if(wanted.stage!=1||wanted.command.expires<=now)return false;
BeginFixtureDiagnostics(node,dt,now);
if(!dockingInputMapperReady)return RefuseFixture(FixtureRefusal.MapperReady);
if(GetCurrentThreadId()!=owningThread)return RefuseFixture(FixtureRefusal.Thread);
if(!float.IsFinite(dt)||dt<=0||dt>.25f)return RefuseFixture(FixtureRefusal.DeltaTime);
var frame=Current();var command=wanted.command;
lock(localLock){dockingFixtureDiagnostics.frameAge=now>=frame.tick?now-frame.tick:ulong.MaxValue;dockingFixtureDiagnostics.observationAge=now>=observed.tick?now-observed.tick:ulong.MaxValue;}
if(!Enabled())return RefuseFixture(FixtureRefusal.Enabled);
if(!inGame)return RefuseFixture(FixtureRefusal.InGame);
if(!frame.valid)return RefuseFixture(FixtureRefusal.FrameValid);
if(!frame.healthValid||frame.healthEmergency||frame.hullFraction<.95)return RefuseFixture(FixtureRefusal.Health);
if(overridden)return RefuseFixture(FixtureRefusal.Override);
if(fsdOwned)return RefuseFixture(FixtureRefusal.FsdOwned);
if(frame.rawFsdState is not(1 or 2))return RefuseFixture(FixtureRefusal.FsdState);
if(now<frame.tick||now-frame.tick>200)return RefuseFixture(FixtureRefusal.FrameFresh);
if(command.entity!=frame.entity)return RefuseFixture(FixtureRefusal.Entity);
if(command.life!=frame.life)return RefuseFixture(FixtureRefusal.Life);
if(command.inputRoot!=frame.inputRoot)return RefuseFixture(FixtureRefusal.InputRoot);
if(command.world!=frame.referenceWorld)return RefuseFixture(FixtureRefusal.World);
if(command.epoch!=controllerEpoch)return RefuseFixture(FixtureRefusal.Epoch);
if(command.selection!=selection.generation)return RefuseFixture(FixtureRefusal.Selection);
if(localConfirmed!=selection.generation)return RefuseFixture(FixtureRefusal.Confirmation);
if(!BoundFsd(frame,frame.fsd))return RefuseFixture(FixtureRefusal.FsdBinding);
if(!observed.valid)return RefuseFixture(FixtureRefusal.ObservationValid);
if(observed.entity!=frame.entity)return RefuseFixture(FixtureRefusal.ObservationEntity);
if(now<observed.tick||now-observed.tick>200)return RefuseFixture(FixtureRefusal.ObservationFresh);
if(observed.inSupercruise)return RefuseFixture(FixtureRefusal.Supercruise);
if(!SameLocal(observed.target,selection.target))return RefuseFixture(FixtureRefusal.Target);
if(observed.nativeDockingActive)return RefuseFixture(FixtureRefusal.DockingDuty);
if(observed.nativeLaunchActive)return RefuseFixture(FixtureRefusal.LaunchDuty);
if(observed.nativeLaunchRequested)return RefuseFixture(FixtureRefusal.LaunchRequest);
if(observed.cruiseAssistEnabled)return RefuseFixture(FixtureRefusal.ScaEnabled);
if(observed.cruiseAssistEngaged)return RefuseFixture(FixtureRefusal.ScaEngaged);
var outward=Subtract(observed.ship.position,observed.destination.position);var length=Math.Sqrt(Dot(outward,outward));
if(length<=0||!FixtureGeometryEligible(observed.distance,observed.speed,Dot(outward,observed.ship.forward)/length))return RefuseFixture(FixtureRefusal.Geometry);
bool released;ulong flightUntil,fsdUntil;lock(leaseLock){flightUntil=lease.expires;fsdUntil=fsdLease.expires;released=flightUntil<=now&&fsdUntil<=now;}
lock(localLock){dockingFixtureDiagnostics.leaseUntil=flightUntil;dockingFixtureDiagnostics.fsdLeaseUntil=fsdUntil;}
if(!released)return RefuseFixture(FixtureRefusal.Lease);
var inventory=ReadCommerceShipInventory(frame);
lock(localLock){dockingFixtureDiagnostics.inventoryVerified=inventory.verified;dockingFixtureDiagnostics.cargoCount=inventory.count;dockingFixtureDiagnostics.cargoCapacity=inventory.capacity;}
if(!inventory.verified||inventory.count!=0||inventory.capacity!=192)return RefuseFixture(FixtureRefusal.Inventory);
var computer=LocalDockingComputer(frame);if(computer==0||!LocalDockingAvailable(computer))return RefuseFixture(FixtureRefusal.Computer);
var source=Pointer(computer+0x260);lock(localLock)dockingFixtureDiagnostics.source=source;
if(source==0||Pointer(node+0x78)!=source)return RefuseFixture(FixtureRefusal.SourceAlias);
var actions=Pointer(computer+0x230);var active=Pointer(node+0x90);
lock(localLock){dockingFixtureDiagnostics.registry=Pointer(node+0x40);dockingFixtureDiagnostics.active=active;dockingFixtureDiagnostics.activeFlag=active>=0x10000?Read<byte>(active+0x24):(byte)0;}
if(actions==0||Pointer(actions)!=image+0x512FD90||Pointer(Pointer(actions)+0x40)!=image+0x8FEDC0||Pointer(node+0x40)!=actions)return RefuseFixture(FixtureRefusal.Registry);
if(active<0x10000||Read<byte>(active+0x24)==0)return RefuseFixture(FixtureRefusal.ActiveBinding);
if((byte)Call(image+0xED6620,computer)!=0)return RefuseFixture(FixtureRefusal.AiDuty);
var dock=LocalDocking(frame);if(dock==0||Read<uint>(dock+0x4C0)is 5 or 7)return RefuseFixture(FixtureRefusal.DockState);
if(Read<uint>(dock+0x614)==2)return RefuseFixture(FixtureRefusal.RequestState);
var absolute=ResolveFixtureBinding(actions,image+0x532FD58);var zero=ResolveFixtureBinding(actions,image+0x531A568);
lock(localLock){dockingFixtureDiagnostics.absolute=Pointer(node+0xB0);dockingFixtureDiagnostics.zero=Pointer(node+0xB8);var a=Read<float>(node+0xD0);var b=Read<float>(absolute+0x18);dockingFixtureDiagnostics.accumulator=float.IsFinite(a)?a:0;dockingFixtureDiagnostics.absoluteValue=float.IsFinite(b)?b:0;}
if(!FixtureBindingName(absolute,"ForwardReverseThrust_Set"))return RefuseFixture(FixtureRefusal.AbsoluteName);
if(!FixtureBindingName(zero,"SetSpeedZero"))return RefuseFixture(FixtureRefusal.ZeroName);
if(Pointer(node+0xB0)!=absolute)return RefuseFixture(FixtureRefusal.AbsoluteAlias);
if(!DockingMapperNodeMatches(node,zero,source))return RefuseFixture(FixtureRefusal.ZeroAlias);
if(Read<byte>(zero+0x24)!=0)return RefuseFixture(FixtureRefusal.ZeroAlreadySet);
if(!BeginDockingFixturePulse(node,absolute,source,out pulse))return RefuseFixture(FixtureRefusal.Pulse);
lock(localLock){dockingFixture.node=node;dockingFixture.binding=absolute;dockingFixture.source=source;}return true;
}
private static void ObserveDockingFixtureMapper(nuint node,in DockingFixturePulse pulse,in DockingMapperPulse zeroPulse)
{
DockingFixture wanted;lock(localLock)wanted=dockingFixture;
if(wanted.node!=node||wanted.until<=GetTickCount64()||wanted.stage is not(1 or 2 or 3)||Pointer(node+0xB0)!=wanted.binding||Pointer(node+0x78)!=wanted.source)return;
var frame=Current();if(frame.entity!=wanted.command.entity||frame.life!=wanted.command.life)return;
var accumulator=Read<float>(node+0xD0);var source=Read<float>(wanted.source+0x18);var command=Read<float>(wanted.binding+0x18);var finite=float.IsFinite(accumulator)&&float.IsFinite(source)&&float.IsFinite(command);
lock(localLock)
{
dockingFixture.utc=UtcMilliseconds();dockingFixture.accumulator=finite?accumulator:0;dockingFixture.sourceValue=finite?source:0;dockingFixture.commandValue=finite?command:0;
if(pulse.applied)
{
dockingFixture.before=pulse.beforeAccumulator;dockingFixture.seedSequence=frame.sequence;dockingFixture.lastSequence=frame.sequence;dockingFixture.restored=command==0;
dockingFixture.stage=finite&&Math.Abs(accumulator-.75f)<.001f&&Math.Abs(source-.75f)<.001f&&command==0?2u:6u;
if(dockingFixture.stage==6)dockingFixture.issue="Original absolute command did not produce retained .75 state";
}
else if(zeroPulse.applied&&wanted.unseededFrames>=3&&Math.Abs(zeroPulse.beforeAccumulator-.75f)<.001f&&finite&&Math.Abs(accumulator)<.001f&&Math.Abs(source)<.001f)
{dockingFixture.zeroVerified=true;dockingFixture.stage=4;dockingFixture.lastSequence=frame.sequence;}
else if(frame.sequence>wanted.lastSequence)
{
dockingFixture.lastSequence=frame.sequence;
if(finite&&Math.Abs(accumulator-.75f)<.001f&&Math.Abs(source-.75f)<.001f&&command==0){dockingFixture.unseededFrames++;if(dockingFixture.unseededFrames>=3)dockingFixture.stage=3;}
else{dockingFixture.stage=6;dockingFixture.issue="Unseeded original graph did not retain .75; not a valid reproduction";}
}
}
}
}