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{}}
}
}
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{}}
}
}