XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 0
UTF-8
using System.Numerics;
using System.Security.Cryptography;

namespace XFEEliteDangerous.NativeFormat.Replication;

public readonly record struct EdServerSoloIslandRequest(uint Correlation,ulong Address,bool FrameFlag,uint FlightMode);

/// <summary>Pinned RPC2 reader958EE0/968380/967E50 and RPC3 reader959600.
/// Supports the explicit zero-peer Solo request and creates two local sessions,
/// Island and Wake. It does not set native object or world readiness.</summary>
public static class EdServerIslandMessageCodec
{
    public static EdServerSoloIslandRequest ReadSoloRequest(ReadOnlySpan<byte> payload,int bits,bool alignedValues)
    {
        if(bits is <1000 or >8192||payload.Length!=(bits+7)/8)throw new InvalidDataException("Invalid native island request bound.");
        var p=0;
        if(Read(payload,ref p,8,bits)!=2)throw new InvalidDataException("Invalid native island request method.");
        var correlation=(uint)Read(payload,ref p,32,bits);
        var address=Read(payload,ref p,64,bits);
        for(var i=0;i<6;i++)FiniteDouble(payload,ref p,bits);
        var frame=Read(payload,ref p,1,bits)!=0;Align(payload,ref p,bits,alignedValues);
        //967E50: two IDs,3-bit flight mode, scalar, three IDs, two flags,
        //6-bit classification, ID, two scalars,2-bit classification, optional
        //spatial values (always serialized), radius, flag. Its caller aligns once.
        Read(payload,ref p,64,bits);Read(payload,ref p,64,bits);
        var mode=(uint)Read(payload,ref p,3,bits);Read(payload,ref p,32,bits);
        for(var i=0;i<3;i++)Read(payload,ref p,64,bits);
        Read(payload,ref p,1,bits);Read(payload,ref p,1,bits);Read(payload,ref p,6,bits);
        Read(payload,ref p,64,bits);Read(payload,ref p,32,bits);Read(payload,ref p,32,bits);Read(payload,ref p,2,bits);
        for(var i=0;i<3;i++)FiniteDouble(payload,ref p,bits);
        var radius=BitConverter.Int32BitsToSingle((int)Read(payload,ref p,32,bits));
        if(!float.IsFinite(radius))throw new InvalidDataException("Invalid native island radius.");
        Read(payload,ref p,1,bits);Align(payload,ref p,bits,alignedValues);
        var exponent=(int)Read(payload,ref p,5,bits);
        var peers=(1UL<<exponent)+Read(payload,ref p,exponent,bits)-1;
        if(peers!=0)throw new NotSupportedException("This local island service admits only the zero-peer Solo request.");
        Align(payload,ref p,bits,alignedValues);
        if(p!=bits||bits%8!=0&&(payload[^1]>>(bits%8))!=0||address==0||address>0x007F_FFFF_FFFF_FFFF)
            throw new InvalidDataException("Noncanonical native island request.");
        return new(correlation,address,frame,mode);
    }
    public static EdServerCallEnvelope CreateSoloAssignment(ulong controller,uint correlation,ulong island,ulong wake)
        =>CreateAssignment(controller,correlation,island,wake,[]);
    // 626EA0's elements call4C18CD0: each is a U48 machine reference, not
    // an IP string. The original manager resolves it through RPC13/14.
    public static EdServerCallEnvelope CreateAssignment(ulong controller,uint correlation,ulong island,ulong wake,IReadOnlyList<ulong> peers)
    {
        if(island==0||wake==0||island==wake||island>0xFFFF_FFFF_FFFF||wake>0xFFFF_FFFF_FFFF)
            throw new ArgumentException("Distinct fresh local island and wake sessions are required.");
        if(peers.Count>32||peers.Any(id=>id==0||id>0xFFFF_FFFF_FFFF)||peers.Distinct().Count()!=peers.Count)
            throw new ArgumentException("Invalid native island peers.");
        Span<byte> payload=stackalloc byte[43+peers.Count*6+1];payload.Clear();var p=0;
        Write(payload,ref p,3,8);Write(payload,ref p,correlation,32);
        Write(payload,ref p,island,64);Write(payload,ref p,wake,64);
        Write(payload,ref p,0,64);Write(payload,ref p,0,64); // no extra or ProvingGround session
        var exponent=BitOperations.Log2((uint)peers.Count+1);
        Write(payload,ref p,(uint)exponent,5);Write(payload,ref p,(uint)peers.Count+1-(1U<<exponent),exponent);
        foreach(var peer in peers)Write(payload,ref p,peer,48);
        Write(payload,ref p,0,5); // no additional U64 callback data
        Write(payload,ref p,0,5); // no additional U32 callback data
        Write(payload,ref p,0,32); // empty local diagnostic string
        try{return new(controller,false,p,payload[..((p+7)/8)]);}finally{CryptographicOperations.ZeroMemory(payload);}
    }
    private static void FiniteDouble(ReadOnlySpan<byte> data,ref int p,int bits)
    {if(!double.IsFinite(BitConverter.Int64BitsToDouble((long)Read(data,ref p,64,bits))))throw new InvalidDataException("Invalid native island coordinates.");}
    private static void Align(ReadOnlySpan<byte> data,ref int p,int bits,bool enabled)
    {if(enabled&&p%8!=0&&Read(data,ref p,8-p%8,bits)!=0)throw new InvalidDataException("Nonzero native island alignment padding.");}
    private static ulong Read(ReadOnlySpan<byte> data,ref int p,int count,int limit)
    {
        if(count is <0 or >64||p<0||p+count>limit)throw new InvalidDataException("Truncated native island request.");
        ulong value=0;for(var bit=0;bit<count;bit++,p++)value|=(ulong)((data[p/8]>>(p%8))&1)<<bit;return value;
    }
    private static void Write(Span<byte> data,ref int p,ulong value,int count)
    {for(var bit=0;bit<count;bit++,p++)if(((value>>bit)&1)!=0)data[p/8]|=(byte)(1<<(p%8));}
}