using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using XFEEDToolBox.Core.Galaxy;
using XFEEDToolBox.Core.Navigation;
namespace XFEEDToolBox.ViewModels;
public partial class ToolboxViewModel
{
[ObservableProperty] public partial IReadOnlyList<GalaxyMissionMapTarget> GalaxyMissionTargets {get;set;}=[];
[ObservableProperty] public partial string GalaxyMissionsStatus {get;set;}="原游戏任务发布列表尚未连接。";
private IReadOnlyList<GalaxyMissionItem> galaxyMissionItems=[];
private IReadOnlySet<long> galaxyMissionAddresses=new HashSet<long>();
private IReadOnlyDictionary<long,IReadOnlyList<int>> galaxyMissionMarkerTypes=new Dictionary<long,IReadOnlyList<int>>();
private string galaxyMissionsContext="";
private DateTimeOffset galaxyMissionsReadAt;
private void SetGalaxyMissions(IReadOnlyList<GalaxyMissionItem> missions)
{
if(galaxyMissionItems.Count!=missions.Count||!galaxyMissionItems.Zip(missions).All(p=>p.First.MissionId==p.Second.MissionId&&p.First.MarkerType==p.Second.MarkerType&&p.First.SelectedSystemAddress==p.Second.SelectedSystemAddress&&p.First.SystemAddresses.SequenceEqual(p.Second.SystemAddresses)))
{galaxyMissionItems=missions;galaxyMissionAddresses=missions.SelectMany(m=>m.SystemAddresses).ToHashSet();galaxyMissionMarkerTypes=GalaxyMissionMarkerStyles.BySystem(missions);galaxyNativeFactsVersion++;}
var names=galaxyCache.Values.Where(s=>s.SystemAddress is {} address&&galaxyMissionAddresses.Contains(address)&&s.Position.IsValid).GroupBy(s=>s.SystemAddress!.Value).ToDictionary(g=>g.Key,g=>g.First().Name);
var targets=missions.Select(m=>new GalaxyMissionMapTarget(m.MissionId,m.SelectedSystemAddress,m.MarkerType,"任务 #"+m.MissionId+" · "+(m.SelectedSystemAddress is {} address?names.GetValueOrDefault(address)??"地址 "+address:"暂无星系目标"))).ToArray();
if(!GalaxyMissionTargets.SequenceEqual(targets))GalaxyMissionTargets=targets;
}
private async Task ReadGalaxyMissionsAsync(CancellationToken token)
{
var context=ThargoidContext();
if(galaxyMissionsContext.Length>0&&galaxyMissionsContext!=context){SetGalaxyMissions([]);galaxyMissionsContext="";galaxyMissionsReadAt=default;}
if(DateTimeOffset.UtcNow-galaxyMissionsReadAt<TimeSpan.FromSeconds(2))return;galaxyMissionsReadAt=DateTimeOffset.UtcNow;
if(context.Length==0||runtime.FlightDriver is not NativeFlightControlDriver{SupportsGalaxyMissions:true} driver)
{SetGalaxyMissions([]);galaxyMissionsContext="";GalaxyMissionsStatus="原游戏任务发布列表尚未连接;Journal 有效目的地另行保留。";return;}
bool Latest()=>context==ThargoidContext();
try
{
var snapshot=await new NativeGalaxyAccountCatalog(driver.ProcessId).ReadMissionsAsync(token);token.ThrowIfCancellationRequested();if(!Latest())return;
galaxyMissionsContext=snapshot.Available?context:"";SetGalaxyMissions(snapshot.Available?snapshot.Missions:[]);
GalaxyMissionsStatus=snapshot.Available?$"原发布列表 · {snapshot.Missions.Count:N0} 个任务 · {galaxyMissionAddresses.Count:N0} 个实际目标星系。按原完整集合最后地址定位;完整性和刷新时效尚未确认。":"原游戏任务列表尚未就绪;不把空缓存当无任务。";
}
catch(OperationCanceledException)when(!token.IsCancellationRequested)
{if(Latest()){SetGalaxyMissions([]);galaxyMissionsContext="";GalaxyMissionsStatus="原游戏任务读取超时;旧发布列表已清除。";}}
catch(Exception error)when(error is IOException or TimeoutException or InvalidDataException or InvalidOperationException or System.Text.Json.JsonException or KeyNotFoundException or FormatException)
{if(Latest()){SetGalaxyMissions([]);galaxyMissionsContext="";GalaxyMissionsStatus="原游戏任务列表尚未确认:"+error.Message;}}
}
[RelayCommand] private async Task SelectGalaxyMissionAsync(GalaxyMissionMapTarget? target)
{
if(target?.SystemAddress is not {} address||galaxyMissionsContext.Length==0||galaxyMissionsContext!=ThargoidContext())return;
var context=galaxyMissionsContext;var request=++galaxyMapTargetSelectionVersion;
bool Latest()=>request==galaxyMapTargetSelectionVersion&&context==ThargoidContext()&&context==galaxyMissionsContext&&galaxyMissionItems.Any(m=>m.MissionId==target.MissionId&&m.SelectedSystemAddress==address&&m.MarkerType==target.MarkerType);
if(!Latest())return;
try
{
var system=galaxyCache.Values.FirstOrDefault(s=>s.SystemAddress==address&&s.Position.IsValid&&!string.IsNullOrWhiteSpace(s.Name));
if(system is null)
{
if(runtime.FlightDriver is not NativeFlightControlDriver{SupportsGalaxyResolveAddress:true}||NativeGalaxy() is not {} catalog)
{GalaxyMissionsStatus="已读取任务实际星系地址,尚未连接精确地址解析。";return;}
system=await catalog.ResolveAddressAsync(address,lifetime.Token);
}
if(!Latest())return;if(system.SystemAddress!=address||!system.Position.IsValid)throw new InvalidDataException("任务目标与实际目录地址不一致。");
system=RememberGalaxy(system);GalaxySelection=system;GalaxyCentre=system.Position;PublishGalaxy();GalaxyMissionsStatus=$"已定位原任务 #{target.MissionId} · {system.Name}";
}
catch(OperationCanceledException){ }
catch(Exception error)when(error is IOException or TimeoutException or InvalidDataException or InvalidOperationException or KeyNotFoundException or System.Text.Json.JsonException)
{if(Latest())GalaxyMissionsStatus="任务定位尚未完成:"+error.Message;}
}
}
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using XFEEDToolBox.Core.Galaxy;
using XFEEDToolBox.Core.Navigation;
namespace XFEEDToolBox.ViewModels;
public partial class ToolboxViewModel
{
[ObservableProperty] public partial IReadOnlyList<GalaxyMissionMapTarget> GalaxyMissionTargets {get;set;}=[];
[ObservableProperty] public partial string GalaxyMissionsStatus {get;set;}="原游戏任务发布列表尚未连接。";
private IReadOnlyList<GalaxyMissionItem> galaxyMissionItems=[];
private IReadOnlySet<long> galaxyMissionAddresses=new HashSet<long>();
private IReadOnlyDictionary<long,IReadOnlyList<int>> galaxyMissionMarkerTypes=new Dictionary<long,IReadOnlyList<int>>();
private string galaxyMissionsContext="";
private DateTimeOffset galaxyMissionsReadAt;
private void SetGalaxyMissions(IReadOnlyList<GalaxyMissionItem> missions)
{
if(galaxyMissionItems.Count!=missions.Count||!galaxyMissionItems.Zip(missions).All(p=>p.First.MissionId==p.Second.MissionId&&p.First.MarkerType==p.Second.MarkerType&&p.First.SelectedSystemAddress==p.Second.SelectedSystemAddress&&p.First.SystemAddresses.SequenceEqual(p.Second.SystemAddresses)))
{galaxyMissionItems=missions;galaxyMissionAddresses=missions.SelectMany(m=>m.SystemAddresses).ToHashSet();galaxyMissionMarkerTypes=GalaxyMissionMarkerStyles.BySystem(missions);galaxyNativeFactsVersion++;}
var names=galaxyCache.Values.Where(s=>s.SystemAddress is {} address&&galaxyMissionAddresses.Contains(address)&&s.Position.IsValid).GroupBy(s=>s.SystemAddress!.Value).ToDictionary(g=>g.Key,g=>g.First().Name);
var targets=missions.Select(m=>new GalaxyMissionMapTarget(m.MissionId,m.SelectedSystemAddress,m.MarkerType,"任务 #"+m.MissionId+" · "+(m.SelectedSystemAddress is {} address?names.GetValueOrDefault(address)??"地址 "+address:"暂无星系目标"))).ToArray();
if(!GalaxyMissionTargets.SequenceEqual(targets))GalaxyMissionTargets=targets;
}
private async Task ReadGalaxyMissionsAsync(CancellationToken token)
{
var context=ThargoidContext();
if(galaxyMissionsContext.Length>0&&galaxyMissionsContext!=context){SetGalaxyMissions([]);galaxyMissionsContext="";galaxyMissionsReadAt=default;}
if(DateTimeOffset.UtcNow-galaxyMissionsReadAt<TimeSpan.FromSeconds(2))return;galaxyMissionsReadAt=DateTimeOffset.UtcNow;
if(context.Length==0||runtime.FlightDriver is not NativeFlightControlDriver{SupportsGalaxyMissions:true} driver)
{SetGalaxyMissions([]);galaxyMissionsContext="";GalaxyMissionsStatus="原游戏任务发布列表尚未连接;Journal 有效目的地另行保留。";return;}
bool Latest()=>context==ThargoidContext();
try
{
var snapshot=await new NativeGalaxyAccountCatalog(driver.ProcessId).ReadMissionsAsync(token);token.ThrowIfCancellationRequested();if(!Latest())return;
galaxyMissionsContext=snapshot.Available?context:"";SetGalaxyMissions(snapshot.Available?snapshot.Missions:[]);
GalaxyMissionsStatus=snapshot.Available?$"原发布列表 · {snapshot.Missions.Count:N0} 个任务 · {galaxyMissionAddresses.Count:N0} 个实际目标星系。按原完整集合最后地址定位;完整性和刷新时效尚未确认。":"原游戏任务列表尚未就绪;不把空缓存当无任务。";
}
catch(OperationCanceledException)when(!token.IsCancellationRequested)
{if(Latest()){SetGalaxyMissions([]);galaxyMissionsContext="";GalaxyMissionsStatus="原游戏任务读取超时;旧发布列表已清除。";}}
catch(Exception error)when(error is IOException or TimeoutException or InvalidDataException or InvalidOperationException or System.Text.Json.JsonException or KeyNotFoundException or FormatException)
{if(Latest()){SetGalaxyMissions([]);galaxyMissionsContext="";GalaxyMissionsStatus="原游戏任务列表尚未确认:"+error.Message;}}
}
[RelayCommand] private async Task SelectGalaxyMissionAsync(GalaxyMissionMapTarget? target)
{
if(target?.SystemAddress is not {} address||galaxyMissionsContext.Length==0||galaxyMissionsContext!=ThargoidContext())return;
var context=galaxyMissionsContext;var request=++galaxyMapTargetSelectionVersion;
bool Latest()=>request==galaxyMapTargetSelectionVersion&&context==ThargoidContext()&&context==galaxyMissionsContext&&galaxyMissionItems.Any(m=>m.MissionId==target.MissionId&&m.SelectedSystemAddress==address&&m.MarkerType==target.MarkerType);
if(!Latest())return;
try
{
var system=galaxyCache.Values.FirstOrDefault(s=>s.SystemAddress==address&&s.Position.IsValid&&!string.IsNullOrWhiteSpace(s.Name));
if(system is null)
{
if(runtime.FlightDriver is not NativeFlightControlDriver{SupportsGalaxyResolveAddress:true}||NativeGalaxy() is not {} catalog)
{GalaxyMissionsStatus="已读取任务实际星系地址,尚未连接精确地址解析。";return;}
system=await catalog.ResolveAddressAsync(address,lifetime.Token);
}
if(!Latest())return;if(system.SystemAddress!=address||!system.Position.IsValid)throw new InvalidDataException("任务目标与实际目录地址不一致。");
system=RememberGalaxy(system);GalaxySelection=system;GalaxyCentre=system.Position;PublishGalaxy();GalaxyMissionsStatus=$"已定位原任务 #{target.MissionId} · {system.Name}";
}
catch(OperationCanceledException){ }
catch(Exception error)when(error is IOException or TimeoutException or InvalidDataException or InvalidOperationException or KeyNotFoundException or System.Text.Json.JsonException)
{if(Latest())GalaxyMissionsStatus="任务定位尚未完成:"+error.Message;}
}
}