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
{
    sealed class GalaxyColonisationLoad
    {
        internal required GalaxyFilterFactsWork work;
        internal nuint obj,expectedImage;
        internal ulong address,entity,currentSystem,started,readyAt;
    }
    static readonly Dictionary<ulong,GalaxyColonisationLoad> galaxyColonisationLoads=[];
    static int galaxyColonisationLoadCursor;
    static bool GalaxyColonisationLoadRetired(GalaxyColonisationLoad load,Snapshot frame,ulong now)
        =>Volatile.Read(ref load.work.cancelled)||Volatile.Read(ref load.work.error).Length!=0||!frame.valid||frame.entity!=load.entity||
          (frame.referenceWorld&0x007FFFFFFFFFFFFFUL)!=load.currentSystem||now-load.started>(load.readyAt==0?2000UL:5000UL);

    static void DropGalaxyColonisationLoad(GalaxyColonisationLoad load)
    {
        var obj=load.obj;load.obj=0;
        if(obj!=0)try{DropGalaxyObject(obj);}catch{}
        galaxyColonisationLoads.Remove(load.address);
    }

    // 3C331E0's object tree does not itself establish a strong reference. Keep
    // the actual returned reference until generation completes, rather than
    // reconstructing and destroying a pending object on every physics tick.
    static void PollGalaxyColonisationLoads(Snapshot frame)
    {
        if(galaxyColonisationLoads.Count==0)return;
        var rows=galaxyColonisationLoads.Values.ToArray();var now=GetTickCount64();
        foreach(var load in rows)
            if(!Enabled()||!inGame||GalaxyColonisationLoadRetired(load,frame,now))DropGalaxyColonisationLoad(load);
        for(var step=0;step<Math.Min(rows.Length,16);step++)
        {
            var load=rows[(galaxyColonisationLoadCursor+step)%rows.Length];
            if(load.obj==0||load.readyAt!=0)continue;
            try
            {
                if(Read<ulong>(load.obj+0x28)!=load.address){DropGalaxyColonisationLoad(load);continue;}
                Call(load.expectedImage+0x3C35900,load.obj);
                if(GalaxyColonisationLoadedObject(load.obj,load.address))load.readyAt=now;
            }
            catch{DropGalaxyColonisationLoad(load);}
        }
        galaxyColonisationLoadCursor=(galaxyColonisationLoadCursor+16)%Math.Max(1,rows.Length);
    }

    static void RetireGalaxyColonisationLoads(Snapshot frame)
    {
        if(owningThread==0||GetCurrentThreadId()!=owningThread||galaxyColonisationLoads.Count==0)return;
        var now=GetTickCount64();
        foreach(var load in galaxyColonisationLoads.Values.ToArray())
            if(!Enabled()||!inGame||GalaxyColonisationLoadRetired(load,frame,now))DropGalaxyColonisationLoad(load);
    }

    static bool GalaxyColonisationQuerySource(GalaxyFilterContext context,GalaxyServices services)
    {
        try
        {
            var source=context.colonisation;
            return source>=0x10058&&services.galaxy>=0x10000&&
                Pointer(source)==context.expectedImage+0x519D720&&Pointer(source-0x58)==context.expectedImage+0x519D5F0&&
                Pointer(source-0x50)==context.expectedImage+0x519D620&&Pointer(Pointer(source)+0x38)==context.expectedImage+0x119A8D0&&
                Pointer(services.galaxy)==context.expectedImage+0x5539C28;
        }
        catch{return false;}
    }

    static bool GalaxyColonisationLoadedObject(nuint obj,ulong address)
    {
        try
        {
            if(obj<0x10000||Read<uint>(obj+0x20)!=2||Read<ulong>(obj+0x28)!=address)return false;
            var compact=Pointer(obj+0x38);
            return compact>=0x10000&&Read<ulong>(compact)==address;
        }
        catch{return false;}
    }

    static bool GalaxyColonisationPending(int? status)=>status is 12 or 13 or 14;

    // This invokes the ORIGINAL candidate predicate, not the claim operation.
    // Its complete unwind assembly proves ABI uint(iface,GalaxyObject**,int).
    // mode=1 checks the original account/permit/cooldown/deny/radius rules.
    // Missing candidate configuration only sets base+F61: 118F3A0 then uses
    // the dedicated base+1D8 candidate_filters request with its own TTL/inflight
    // gate; 11A0750 receives the data. The claimsystem object at base+2D0 and
    // the manager's whole Update are never invoked by this adapter.
    static bool TryReadGalaxyColonisationEligibility(GalaxyFilterFactsWork work,GalaxyFilterContext context,GalaxyServices services,ulong address,out int? status,out int? normalStatus,out GalaxyColonisationHighlight? highlight)
    {
        status=null;normalStatus=null;highlight=null;nuint obj=0;
        if(!Enabled()||!inGame||context.thread==0||context.thread!=owningThread||GetCurrentThreadId()!=context.thread||
           GetTickCount64()-context.tick>500||!galaxyFactsPositions.ContainsKey(address))return false;
        var frame=Current();
        if(!frame.valid||frame.entity!=context.entity||(frame.referenceWorld&0x007FFFFFFFFFFFFFUL)!=context.currentSystem||
           !GalaxyColonisationQuerySource(context,services))return false;
        try
        {
            // Same reference-owning lookup/load/release path as CopyGalaxyItem.
            // Only an address already copied from a genuine original compact
            // star may reach it. Unknown client IDs never load game objects.
            if(galaxyColonisationLoads.TryGetValue(address,out var existing))
            {
                if(GalaxyColonisationLoadRetired(existing,frame,GetTickCount64()))DropGalaxyColonisationLoad(existing);
                else
                {
                    existing.work=work;
                    if(!GalaxyColonisationLoadedObject(existing.obj,address)){status=14;return true;}
                    obj=existing.obj;existing.obj=0;galaxyColonisationLoads.Remove(address);
                }
            }
            if(obj==0)
            {
                if(galaxyColonisationLoads.Count>=64)return false;
                Call(context.expectedImage+0x3C33140,services.galaxy,(nuint)(&obj),address);
            }
            if(obj==0)return false;
            if(Read<uint>(obj+0x20)==1)Call(context.expectedImage+0x3C35900,obj);
            if(!GalaxyColonisationLoadedObject(obj,address))
            {
                galaxyColonisationLoads[address]=new(){work=work,obj=obj,expectedImage=context.expectedImage,address=address,
                    entity=context.entity,currentSystem=context.currentSystem,started=GetTickCount64()};
                obj=0;status=14;return true;
            }
            var result=(uint)Call(context.expectedImage+0x119A8D0,context.colonisation,(nuint)(&obj),1);
            if(result>14||!GalaxyColonisationLoadedObject(obj,address))return false;
            // Original3B26390 and the non-highlight branch of3B26BD0 call
            // classifier mode0.3B27F00's independent size also uses mode0.
            // Candidate eligibility/mode1 must not replace that classification.
            var normal=(uint)Call(context.expectedImage+0x119A8D0,context.colonisation,(nuint)(&obj),0);
            if(!GalaxyColonisationLoadedObject(obj,address))return false;
            frame=Current();
            if(!frame.valid||frame.entity!=context.entity||(frame.referenceWorld&0x007FFFFFFFFFFFFFUL)!=context.currentSystem)return false;
            status=(int)result;
            if(normal<=11)normalStatus=(int)normal;
            if(result<=11)TryReadGalaxyColonisationHighlight(context,services,obj,address,(int)result,out highlight);
            return true;
        }
        catch{return false;}
        finally{if(obj!=0)try{DropGalaxyObject(obj);}catch{}}
    }
}