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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
namespace XFEEDToolBox.Plugin;

internal static unsafe partial class Navigation
{
    // Original input-graph node. Its SetSpeedZero branch clears its own
    // throttle accumulator before producing ForwardReverseThrust. A pulse
    // delivered after the FSD update misses this node's legal evaluation.
    private const nuint DockingInputMapper=0x8F3500;
    private static nuint originalDockingInputMapper;
    private static bool dockingInputMapperReady;
    private static ulong dockingInputMapperCalls,dockingInputMapperMatched,dockingInputMapperApplied;
    private struct DockingMapperReceipt
    {
        public ulong sequence,entity,utc;
        public float before,after,source;
        public bool verified;
    }
    private static DockingMapperReceipt dockingInputMapperReceipt;
    private struct DockingMapperPulse
    {
        public nuint binding,source,node;
        public uint beforeCount,afterCount;
        public byte beforeFlags,afterFlags;
        public float beforeAccumulator;
        public ulong entity,sequence;
        public bool applied;
    }
    private static bool DockingMapperNodeMatches(nuint node,nuint binding,nuint source)
    {
        if(node<0x10000||binding<0x10000||source<0x10000)return false;
        // Both input and output must alias the exact current ship's original
        // registry bindings. Matching a node type or offset alone is unsafe.
        return Pointer(node+0xB8)==binding&&Pointer(node+0x78)==source;
    }
    private static bool BeginDockingMapperPulse(nuint node,nuint binding,nuint source,out DockingMapperPulse pulse)
    {
        pulse=default;
        if(!DockingMapperNodeMatches(node,binding,source)||Read<int>(binding)<=0)return false;
        var count=Read<uint>(binding+0x1C);var flags=Read<byte>(binding+0x24);
        var accumulator=Read<float>(node+0xD0);
        if(count==uint.MaxValue||!float.IsFinite(accumulator)||Math.Abs(accumulator)>1.001f)return false;
        pulse=new(){node=node,binding=binding,source=source,beforeCount=count,afterCount=count+1,beforeFlags=flags,afterFlags=(byte)(flags|1),beforeAccumulator=accumulator,applied=true};
        Write(binding+0x1C,pulse.afterCount);Write(binding+0x24,pulse.afterFlags);
        return true;
    }
    private static void EndDockingMapperPulse(in DockingMapperPulse pulse)
    {
        if(!pulse.applied||!DockingMapperNodeMatches(pulse.node,pulse.binding,pulse.source))return;
        // Do not erase a later physical input or another original consumer's
        // update. Only restore the temporary logical pulse we actually wrote.
        if(Read<uint>(pulse.binding+0x1C)!=pulse.afterCount||Read<byte>(pulse.binding+0x24)!=pulse.afterFlags)return;
        Write(pulse.binding+0x1C,pulse.beforeCount);Write(pulse.binding+0x24,pulse.beforeFlags);
    }
    private static bool PrepareDockingMapperPulse(nuint node,float dt,out DockingMapperPulse pulse)
    {
        pulse=default;dockingInputMapperCalls++;
        if(!dockingInputMapperReady||!float.IsFinite(dt)||dt<=0||dt>.25f||GetCurrentThreadId()!=owningThread)return false;
        var now=GetTickCount64();if(Volatile.Read(ref dockingZeroUpdate.command.expires)<=now)return false;
        var frame=Current();DockingZeroUpdate wanted;LocalSelection selection;LocalFlightObservation observed;
        lock(localLock){wanted=dockingZeroUpdate;selection=localSelection;observed=localObservation;}
        if(wanted.command.action!=10)return false;
        if(!DockingZeroUpdateMatches(wanted,frame,selection,wanted.computer,now,controllerEpoch,
            clientOwner.Length!=0,Enabled(),inGame,overridden)||localConfirmed!=selection.generation||fsdOwned||!BoundFsd(frame,frame.fsd))return false;
        var computer=LocalDockingComputer(frame);var receipt=dockingZeroInputReceipt;
        if(computer!=wanted.computer||computer==0||!LocalDockingAvailable(computer)||Pointer(computer+0x240)!=wanted.controller
            ||receipt.entity!=frame.entity||receipt.life!=frame.life||receipt.market!=wanted.market||receipt.inputService!=Pointer(computer+0x230)
            ||now<receipt.tick||now-receipt.tick>=30000||!observed.valid||observed.entity!=frame.entity||observed.inSupercruise
            ||observed.distance>7500||now<observed.tick||now-observed.tick>200||!SameLocal(observed.target,selection.target))return false;
        if((byte)Call(image+0xED6620,computer)!=0)
        {lock(localLock)dockingZeroUpdate.command.expires=0;return false;}
        var source=Pointer(computer+0x260);
        if(!DockingMapperNodeMatches(node,receipt.binding,source))return false;
        // The registry clears this frame's aggregate before evaluating the
        // node. Its zero at entry does not describe the retained accumulator;
        // let the original SetSpeedZero branch handle that state itself.
        if(!ReadOriginalDockingThrottle(computer,out _))return false;
        var active=Pointer(node+0x90);
        if(active<0x10000||Read<byte>(active+0x24)==0)return false;
        var dock=LocalDocking(frame);
        if(dock==0||Read<uint>(dock+0x614)!=2||Read<uint>(dock+0x4C0)is not(3 or 4)||Read<byte>(dock+0x4E9)==255)return false;
        var station=Pointer(dock+0x4E0);
        if(station==0||Read<uint>(station+0xC0)!=8||Read<ulong>(station+0xB0)!=wanted.market||!LocalRecordMatches(Pointer(frame.fsd+0x338),selection.target))return false;
        bool released;lock(leaseLock)released=lease.expires<=now&&fsdLease.expires<=now;
        if(!released)return false;
        dockingInputMapperMatched++;
        if(!BeginDockingMapperPulse(node,receipt.binding,source,out pulse))return false;
        pulse.entity=frame.entity;pulse.sequence=frame.sequence;return true;
    }
    [UnmanagedCallersOnly]
    private static ulong DockingInputMapperHook(nuint node,float dt)
    {
        DockingMapperPulse pulse=default;
        DockingFixturePulse fixture=default;
        try{if(!PrepareDockingFixturePulse(node,dt,out fixture))PrepareDockingMapperPulse(node,dt,out pulse);}catch{try{FixtureExceptionEvidence();}catch{}}
        try{return Call(originalDockingInputMapper,(ulong)node,F32(dt));}
        catch{return 0;}
        finally
        {
            try
            {
                if(pulse.applied&&DockingMapperNodeMatches(pulse.node,pulse.binding,pulse.source))
                {
                    var accumulator=Read<float>(pulse.node+0xD0);var source=Read<float>(pulse.source+0x18);
                    var valid=float.IsFinite(accumulator)&&Math.Abs(accumulator)<=1.001f&&float.IsFinite(source)&&Math.Abs(source)<=1.001f;
                    lock(localLock)dockingInputMapperReceipt=new(){sequence=pulse.sequence,entity=pulse.entity,utc=UtcMilliseconds(),before=pulse.beforeAccumulator,
                        after=valid?accumulator:0,source=valid?source:0,verified=valid};
                    if(valid&&Math.Abs(accumulator)<=.001f&&Math.Abs(source)<=.001f)dockingInputMapperApplied++;
                }
            }
            catch{}
            finally
            {
                try{EndDockingMapperPulse(pulse);}catch{}
                try{EndDockingFixturePulse(fixture);}catch{}
                try{ObserveDockingFixtureMapper(node,fixture,pulse);}catch{}
            }
        }
    }
}