using XFEEDToolBox.Core.Galaxy;
namespace XFEEDToolBox.ViewModels;
public partial class ToolboxViewModel
{
private (int ProcessId,long Epoch,long Address)? galaxyOverrideResolveCursor;
private Task ResolvePublishedGalaxyOverridesAsync(GalaxyCatalogueSelection selection,NativeGalaxyCatalog native,
Func<bool> currentSource,CancellationToken token)=>
ResolvePublishedGalaxyOverridesAsync(selection,native.ProcessId,native.ResolveAddressAsync,currentSource,token);
private async Task ResolvePublishedGalaxyOverridesAsync(GalaxyCatalogueSelection selection,int processId,
Func<long,CancellationToken,Task<GalacticSystem>> resolve,Func<bool> currentSource,CancellationToken token)
{
using var stop=CancellationTokenSource.CreateLinkedTokenSource(token,lifetime.Token);
var previous=Interlocked.Exchange(ref galaxyOverrideResolveStop,stop);
try{previous?.Cancel();}catch(ObjectDisposedException){}
var epoch=Volatile.Read(ref galaxySourceEpoch);var view=Volatile.Read(ref galaxyViewVersion);
try
{
// Let the initial cell request and the first visible frame proceed first.
await Task.Yield();
if(selection.PointOverrides is not{} points||points.ProcessId!=processId)return;
bool Latest()=>!stop.IsCancellationRequested&&epoch==Volatile.Read(ref galaxySourceEpoch)&&
view==Volatile.Read(ref galaxyViewVersion)&¤tSource()&&ReferenceEquals(selection,GalaxyCatalogueSelection)&&
ReferenceEquals(points,GalaxyCatalogueSelection?.PointOverrides);
if(!Latest())return;
var rows=points.Overrides.Values.Where(row=>row.Name is null).OrderBy(row=>row.SystemAddress).ToArray();
if(rows.Length==0)return;
// Names are immutable catalogue facts. Index exact cached identities once,
// rather than scanning the entire galaxy cache for every queued address.
var cached=new HashSet<long>();
foreach(var system in galaxyCache.Values)
if(system.SystemAddress is{} address&&points.TryGetOverride(address,out var row)&&row?.Position==system.Position)
cached.Add(address);
var cursor=galaxyOverrideResolveCursor is{} old&&old.ProcessId==processId&&old.Epoch==epoch?old.Address:0;
var start=Array.FindIndex(rows,row=>row.SystemAddress>cursor);if(start<0)start=0;
var dirty=false;var published=false;var lastPublish=Environment.TickCount64;
void Flush()
{
if(!dirty||!Latest())return;
dirty=false;published=true;lastPublish=Environment.TickCount64;
PublishGalaxy();if(Latest())QueueGalaxyFilterInformation();
}
for(var index=0;index<rows.Length;index++)
{
if(!Latest())return;
var row=rows[(start+index)%rows.Length];
// Advance before awaiting: repeated view changes or an unavailable
// first address cannot keep all later published objects at the back.
galaxyOverrideResolveCursor=(processId,epoch,row.SystemAddress);
if(!cached.Contains(row.SystemAddress))
{
// A single outstanding resolve shares the native catalogue gate.
// Never enqueue a batch that would hold up subsequent cell work.
await Task.Yield();if(!Latest())return;
GalacticSystem? system=null;
try{system=await resolve(row.SystemAddress,stop.Token);}
catch(Exception error)when(error is IOException or InvalidDataException or TimeoutException or InvalidOperationException or KeyNotFoundException){}
if(!Latest())return;
if(system is not null&&system.SystemAddress==row.SystemAddress&&system.Position==row.Position&&
system.Position.IsValid&&!string.IsNullOrWhiteSpace(system.Name))
{
RememberGalaxy(system,processId);cached.Add(row.SystemAddress);dirty=true;
if(!published||Environment.TickCount64-lastPublish>=100)Flush();
}
}
if((index+1)%32==0)
{
Flush();await Task.Yield();
}
}
Flush();
}
catch(OperationCanceledException){}
finally{Interlocked.CompareExchange(ref galaxyOverrideResolveStop,null,stop);}
}
}
using XFEEDToolBox.Core.Galaxy;
namespace XFEEDToolBox.ViewModels;
public partial class ToolboxViewModel
{
private (int ProcessId,long Epoch,long Address)? galaxyOverrideResolveCursor;
private Task ResolvePublishedGalaxyOverridesAsync(GalaxyCatalogueSelection selection,NativeGalaxyCatalog native,
Func<bool> currentSource,CancellationToken token)=>
ResolvePublishedGalaxyOverridesAsync(selection,native.ProcessId,native.ResolveAddressAsync,currentSource,token);
private async Task ResolvePublishedGalaxyOverridesAsync(GalaxyCatalogueSelection selection,int processId,
Func<long,CancellationToken,Task<GalacticSystem>> resolve,Func<bool> currentSource,CancellationToken token)
{
using var stop=CancellationTokenSource.CreateLinkedTokenSource(token,lifetime.Token);
var previous=Interlocked.Exchange(ref galaxyOverrideResolveStop,stop);
try{previous?.Cancel();}catch(ObjectDisposedException){}
var epoch=Volatile.Read(ref galaxySourceEpoch);var view=Volatile.Read(ref galaxyViewVersion);
try
{
// Let the initial cell request and the first visible frame proceed first.
await Task.Yield();
if(selection.PointOverrides is not{} points||points.ProcessId!=processId)return;
bool Latest()=>!stop.IsCancellationRequested&&epoch==Volatile.Read(ref galaxySourceEpoch)&&
view==Volatile.Read(ref galaxyViewVersion)&¤tSource()&&ReferenceEquals(selection,GalaxyCatalogueSelection)&&
ReferenceEquals(points,GalaxyCatalogueSelection?.PointOverrides);
if(!Latest())return;
var rows=points.Overrides.Values.Where(row=>row.Name is null).OrderBy(row=>row.SystemAddress).ToArray();
if(rows.Length==0)return;
// Names are immutable catalogue facts. Index exact cached identities once,
// rather than scanning the entire galaxy cache for every queued address.
var cached=new HashSet<long>();
foreach(var system in galaxyCache.Values)
if(system.SystemAddress is{} address&&points.TryGetOverride(address,out var row)&&row?.Position==system.Position)
cached.Add(address);
var cursor=galaxyOverrideResolveCursor is{} old&&old.ProcessId==processId&&old.Epoch==epoch?old.Address:0;
var start=Array.FindIndex(rows,row=>row.SystemAddress>cursor);if(start<0)start=0;
var dirty=false;var published=false;var lastPublish=Environment.TickCount64;
void Flush()
{
if(!dirty||!Latest())return;
dirty=false;published=true;lastPublish=Environment.TickCount64;
PublishGalaxy();if(Latest())QueueGalaxyFilterInformation();
}
for(var index=0;index<rows.Length;index++)
{
if(!Latest())return;
var row=rows[(start+index)%rows.Length];
// Advance before awaiting: repeated view changes or an unavailable
// first address cannot keep all later published objects at the back.
galaxyOverrideResolveCursor=(processId,epoch,row.SystemAddress);
if(!cached.Contains(row.SystemAddress))
{
// A single outstanding resolve shares the native catalogue gate.
// Never enqueue a batch that would hold up subsequent cell work.
await Task.Yield();if(!Latest())return;
GalacticSystem? system=null;
try{system=await resolve(row.SystemAddress,stop.Token);}
catch(Exception error)when(error is IOException or InvalidDataException or TimeoutException or InvalidOperationException or KeyNotFoundException){}
if(!Latest())return;
if(system is not null&&system.SystemAddress==row.SystemAddress&&system.Position==row.Position&&
system.Position.IsValid&&!string.IsNullOrWhiteSpace(system.Name))
{
RememberGalaxy(system,processId);cached.Add(row.SystemAddress);dirty=true;
if(!published||Environment.TickCount64-lastPublish>=100)Flush();
}
}
if((index+1)%32==0)
{
Flush();await Task.Yield();
}
}
Flush();
}
catch(OperationCanceledException){}
finally{Interlocked.CompareExchange(ref galaxyOverrideResolveStop,null,stop);}
}
}