XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using XFEEDToolBox.Core.Galaxy;
using XFEEDToolBox.Core.Services;
using System.Text.Json;

namespace XFEEDToolBox.ViewModels;

public partial class ToolboxViewModel
{
    // Capture while the owner is alive. Background metadata continuations may
    // still observe cancellation after Dispose has released the source itself.
    private readonly CancellationToken galaxyLifetimeToken;
    [ObservableProperty] public partial GalaxyMapFilter GalaxyFilter { get; set; }=GalaxyMapFilter.Default;
    [ObservableProperty] public partial string GalaxyFilterInformationStatus { get; set; }="恒星类型来自游戏目录;其他数据仅使用实际观测记录。";
    [ObservableProperty] public partial string GalaxyOriginalSettingsStatus { get; set; }="可读取游戏已保存的星图筛选和显示开关。";
    [ObservableProperty] public partial string GalaxyOriginalSettingsDetails { get; set; }="";
    [ObservableProperty] public partial IReadOnlyDictionary<string,string> GalaxyOriginalOptions { get; set; }=new Dictionary<string,string>();
    public string GalaxyOriginalSettingsPath { get; private set; }="";
    internal Task ImportOriginalForProbe()=>ImportOriginalGalaxySettingsAsync();
    [RelayCommand] private async Task ImportOriginalGalaxySettingsAsync()
    {
        try
        {
            var directory=Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"Frontier Developments","Elite Dangerous","Options","Player");
            var start=Path.Combine(directory,"StartPreset.start");var preset=File.Exists(start)&&new FileInfo(start).Length<=128?(await File.ReadAllTextAsync(start,galaxyLifetimeToken)).Trim():"Custom";
            if(preset.Length is <1 or >64||preset.Any(c=>!char.IsAsciiLetterOrDigit(c)&&c is not('_' or '-')))throw new InvalidDataException("游戏预设名称无效。");
            var file=Directory.EnumerateFiles(directory,preset+".*.misc").OrderByDescending(File.GetLastWriteTimeUtc).FirstOrDefault()??throw new FileNotFoundException("没有找到游戏保存的玩家星图配置。");
            var imported=await Task.Run(()=>GalaxyOriginalSettings.Read(file,GalaxyFilter),galaxyLifetimeToken);
            GalaxyOriginalSettingsPath=file;GalaxyOriginalOptions=imported.OriginalOptions;GalaxyOriginalSettingsDetails=string.Join("\n",imported.Limitations);GalaxyOriginalSettingsStatus=Path.GetFileName(file)+" · "+imported.Summary;GalaxyFilter=imported.Filter;
        }
        catch(OperationCanceledException){ }
        catch(Exception error)when(error is IOException or UnauthorizedAccessException or System.Xml.XmlException or InvalidDataException)
        {GalaxyOriginalSettingsStatus="读取原游戏星图配置失败:"+error.Message;}
    }
    public double GalaxyShipJumpRange=>CurrentPlayer.MaxJumpRange is >0 and <1000?CurrentPlayer.MaxJumpRange.Value:0;
    private DateTimeOffset galaxyMetadataReadAt;
    private readonly HashSet<long> galaxyMetadataEvents=[];
    private readonly Dictionary<string,GalaxySystemData> galaxyObservedFacts=new(StringComparer.OrdinalIgnoreCase);
    private readonly GalaxyJournalMarkers galaxyJournalMarkers=new();
    private readonly Dictionary<string,IReadOnlyList<string>> galaxyPersonalMarkers=new(StringComparer.OrdinalIgnoreCase);
    private OriginalVisitedStarsSnapshot? galaxyOriginalVisited;
    private IReadOnlyDictionary<long,OriginalTradeDataSnapshot> galaxyOriginalTrades=new Dictionary<long,OriginalTradeDataSnapshot>();
    private IReadOnlyDictionary<long,IReadOnlyList<string>> galaxyAccountMarkers=new Dictionary<long,IReadOnlyList<string>>();
    private IReadOnlyDictionary<long,IReadOnlyList<GalaxyBookmarkStyle>> galaxyBookmarkStyles=new Dictionary<long,IReadOnlyList<GalaxyBookmarkStyle>>();
    private IReadOnlyDictionary<long,IReadOnlyList<string>> galaxySocialMarkers=new Dictionary<long,IReadOnlyList<string>>();
    private string galaxySocialMarkerStatus="原游戏好友/队伍位置接口尚未连接;时效未确认的位置不作为实时标记。";
    private string galaxyAccountMarkerStatus="原游戏书签接口尚未连接;缺少记录保持未知。";
    private IReadOnlyDictionary<long,bool> galaxyColonyOwnership=new Dictionary<long,bool>();
    private string galaxyOwnedColonisationStatus="原游戏殖民归属接口尚未连接;缺少记录保持未知。";
    private DateTimeOffset galaxyNativeAccountReadAt;
    private long galaxyNativeFactsVersion;
    private string galaxyMetadataAccount="";
    private Task? galaxyMetadataReadTask;
    [ObservableProperty] public partial IReadOnlyList<GalaxyFilterChoice> GalaxyQueryChoices { get; set; }=[];
    [ObservableProperty] public partial IReadOnlyList<GalacticSystem> GalaxyQuerySystems { get; set; }=[];
    [ObservableProperty] public partial bool GalaxyQueryBusy { get; set; }
    [ObservableProperty] public partial GalaxyQueryBounds? GalaxyActiveQueryBounds { get; set; }
    public event Action<GalaxyQueryBounds,long>? GalaxyQueryStarted;
    public event Action<GalaxyQueryResult,long>? GalaxyQueryProgress;
    private GalaxyQueryService? galaxyQueries;
    private CancellationTokenSource? galaxyFilterInformationStop;
    private long galaxyFilterQueryGeneration;
    private string galaxyQueuedQuery="";
    private string galaxyRunningQuery="",galaxyRunningPredicate="";
    private GalaxyQueryBounds? galaxyRunningBounds;
    private GalaxyViewport? galaxyDesiredViewport;
    private bool galaxyQueryPublished;
    private readonly Dictionary<string,GalacticSystem> galaxyQueryEvidence=new(StringComparer.OrdinalIgnoreCase);
    partial void OnGalaxyFilterChanged(GalaxyMapFilter oldValue,GalaxyMapFilter newValue)
    {if(oldValue.Category!=newValue.Category)GalaxyQueryChoices=[];LocateGalaxyPowerHeadquartersCommand.NotifyCanExecuteChanged();PublishGalaxy();QueueGalaxyFilterInformation();}
    private static bool GalaxyUsesAccountFacts(GalaxyMapFilter filter)=>filter.RequireOriginalCivilizationFacts||filter.RequireOriginalServiceFacts||filter.RequireOriginalCarrierFacts||filter.RequireOriginalStateFacts||filter.RequireOriginalColonisationOwnershipFacts||filter.RequireOriginalBasicSystemFacts||filter.RequiresOriginalCommodity||filter.SizeCategory==GalaxyFilterCategory.Commodity||filter.RequestsOriginalColonisationSize||filter.UsesPowerOuter||filter.UsesPowerActivityCounter||filter.Category is GalaxyFilterCategory.Markers or GalaxyFilterCategory.ThargoidWar or GalaxyFilterCategory.Colonisation or GalaxyFilterCategory.Visited or GalaxyFilterCategory.Exploration or GalaxyFilterCategory.TradeData or GalaxyFilterCategory.PowerActivityEligibility;
    private void QueueGalaxyFilterInformation()
    {
        QueueGalaxyHistoricalTradeFlows();
        QueueGalaxyCurrentTradeFlows();
        var filter=GalaxyFilter;
        if(!filter.IsQueryActive)
        {galaxyFilterInformationStop?.Cancel();galaxyQueuedQuery="";galaxyDesiredViewport=null;GalaxyQueryBusy=false;GalaxyQuerySystems=[];QueueGalaxyOriginalFilterFacts();return;}
        var focus=galaxyViewport?.Focus??GalaxyCentre??CurrentPlayer.Position;if(focus is not {IsValid:true})return;
        var radius=Math.Max(5,GalaxyRadius);
        var viewport=galaxyViewport??new(focus,new(1,0,0),new(0,1,0),new(0,0,1),radius,radius,radius);
        QueueGalaxyOriginalFilterFacts(viewport);
        var bounds=GalaxyQueryBounds.FromViewport(viewport);var quantum=Math.Max(.01,bounds.Radius/128);
        var accountVersion=GalaxyUsesAccountFacts(filter)?galaxyNativeFactsVersion:0;
        var key=filter.QueryKey+FormattableString.Invariant($"|{Math.Round(bounds.Minimum.X/quantum)}:{Math.Round(bounds.Minimum.Y/quantum)}:{Math.Round(bounds.Minimum.Z/quantum)}:{Math.Round(bounds.Maximum.X/quantum)}:{Math.Round(bounds.Maximum.Y/quantum)}:{Math.Round(bounds.Maximum.Z/quantum)}:{Math.Round(Math.Log2(bounds.Radius)*64)}:{Math.Round((viewport.Camera?.Yaw??0)*180/Math.PI)}:{Math.Round((viewport.Camera?.Pitch??0)*180/Math.PI)}:{filter.IncludeUnknown}:{filter.SizeCategory}:{filter.PopulationMinimum:R}:{filter.PopulationMaximum:R}:{accountVersion}");
        if(galaxyQueuedQuery==key)return;
        galaxyDesiredViewport=viewport;GalaxyActiveQueryBounds=bounds;
        bool Overlaps(GalaxyQueryBounds a,GalaxyQueryBounds b)=>a.Minimum.X<=b.Maximum.X&&a.Maximum.X>=b.Minimum.X&&a.Minimum.Y<=b.Maximum.Y&&a.Maximum.Y>=b.Minimum.Y&&a.Minimum.Z<=b.Maximum.Z&&a.Maximum.Z>=b.Minimum.Z;
        if(GalaxyQueryBusy&&galaxyRunningPredicate==filter.QueryKey&&galaxyRunningBounds is {} running&&Overlaps(bounds,running)&&(GalaxyQueryService.UsesPublicCatalog(filter.Category)||filter.SizeCategory==GalaxyFilterCategory.Population))
        {
            galaxyQueuedQuery=key;GalaxyQueryStarted?.Invoke(bounds,galaxyFilterQueryGeneration);
            // Reuse the first in-flight page while panning. Once actual rows have
            // arrived, switch to the latest overlapping view without starvation.
            if(galaxyQueryPublished)galaxyFilterInformationStop?.Cancel();
            return;
        }
        if(galaxyRunningPredicate!=filter.QueryKey)galaxyQueryEvidence.Clear();
        galaxyQueuedQuery=key;galaxyFilterInformationStop?.Cancel();galaxyFilterInformationStop?.Dispose();
        galaxyFilterInformationStop=CancellationTokenSource.CreateLinkedTokenSource(galaxyLifetimeToken);
        var generation=++galaxyFilterQueryGeneration;galaxyRunningQuery=key;galaxyRunningPredicate=filter.QueryKey;galaxyRunningBounds=bounds;galaxyQueryPublished=false;GalaxyQueryBusy=true;GalaxyQueryStarted?.Invoke(bounds,generation);
        _=QueryGalaxyViewportAsync(filter,viewport,generation,galaxyFilterInformationStop.Token);
    }
    private async Task QueryGalaxyViewportAsync(GalaxyMapFilter filter,GalaxyViewport viewport,long generation,CancellationToken token)
    {
        var focus=viewport.Focus;var bounds=GalaxyQueryBounds.FromViewport(viewport);
        bool Latest()=>generation==galaxyFilterQueryGeneration&&GalaxyFilter.IsQueryActive&&GalaxyFilter.QueryKey==filter.QueryKey;
        try
        {
            await Task.Delay(120,token);if(!Latest())return;
            if(filter.Category==GalaxyFilterCategory.PowerActivityEligibility)
            {await ReadGalaxyPowerOptionsAsync(token);if(!Latest())return;if(GalaxyFilter.OriginalPowerOptions.Count==0){galaxyQueries??=new(galaxyHttp);var powers=await galaxyQueries.ChoicesAsync(GalaxyFilterCategory.Power,token);if(!Latest())return;GalaxyQueryChoices=new[]{new GalaxyFilterChoice("__allpowers","全部势力(原活动选项)")}.Concat(powers.Where(p=>p.Key!="none")).ToArray();}}
            var metadataFilter=!filter.UsesPowerOuter&&(!GalaxyQueryService.UsesPublicCatalog(filter.Category)||filter.Category==GalaxyFilterCategory.Commodity&&filter.RequiresOriginalCommodity)&&filter.SizeCategory==GalaxyFilterCategory.Population
                ?new GalaxyMapFilter{Mode=GalaxyMapMode.Map,Category=GalaxyFilterCategory.Population,PopulationMinimum=filter.PopulationMinimum,PopulationMaximum=filter.PopulationMaximum}:filter;
            if(filter.UsesPowerOuter||metadataFilter==filter&&filter.Category==GalaxyFilterCategory.Commodity&&filter.RequiresOriginalCommodity||!GalaxyQueryService.UsesPublicCatalog(metadataFilter.Category))
            {
                await RefreshGalaxyMetadataAsync();token.ThrowIfCancellationRequested();if(!Latest())return;
                GalaxyQuerySystems=galaxyCache.Values.Where(s=>viewport.Contains(s.Position,1.000001)&&filter.Matches(s)).ToArray();
                if(filter.Category!=GalaxyFilterCategory.PowerActivityEligibility)GalaxyQueryChoices=[];PublishGalaxy();
                GalaxyFilterInformationStatus=filter.Category==GalaxyFilterCategory.PowerActivityEligibility?galaxyPowerOptionsStatus+"\n"+(!filter.UseMinimumActivity?"未启用最低近期强度;当前不增加活动阈值过滤。":!filter.ActivityNativeQueryReady?"请选择原势力或全部势力后读取对应近期强度。":galaxySystemFactsStatus.Length>0?galaxySystemFactsStatus:"原近期活动计数尚未就绪;缺缓存保持未知。"):
                    GalaxyUsesOriginalFilterFacts(filter)&&galaxySystemFactsStatus.Length>0?galaxySystemFactsStatus:$"当前视野已知记录 · {GalaxyQuerySystems.Count:N0} 个匹配;尚未确认的账号属性保持未知。";return;
            }
            galaxyQueries??=new(galaxyHttp);GalaxyFilterInformationStatus="正在按当前完整视锥查询真实目录…";
            if(filter.Category!=GalaxyFilterCategory.PowerActivityEligibility)
            {var options=await galaxyQueries.ChoicesAsync(filter.Category,token);token.ThrowIfCancellationRequested();if(!Latest())return;GalaxyQueryChoices=filter.Category==GalaxyFilterCategory.Power?options.Concat(GalaxyFilter.OriginalPowerOptions.Select(p=>new GalaxyFilterChoice(GalaxyMapFilter.Normalize(p.Name),p.Name))).DistinctBy(c=>c.Key).ToArray():options;}
            await RefreshGalaxyMetadataAsync();token.ThrowIfCancellationRequested();if(!Latest())return;
            var additional=new List<GalacticSystem>();
            if(galaxyThargoidContext==ThargoidContext()&&(filter.Category==GalaxyFilterCategory.ThargoidWar||filter.Category==GalaxyFilterCategory.Markers&&(filter.Values is null||filter.Values.Contains("titan"))))
            {
                var known=galaxyCache.Values.Where(s=>s.SystemAddress is not null).Select(s=>s.SystemAddress!.Value).ToHashSet();
                foreach(var address in galaxyThargoidObservedStates.Keys.Where(a=>!known.Contains(a)).ToArray())
                {
                    token.ThrowIfCancellationRequested();if(runtime.FlightDriver is not XFEEDToolBox.Core.Navigation.NativeFlightControlDriver{SupportsGalaxyResolveAddress:true}||NativeGalaxy() is not {} catalog)break;
                    try{var resolved=await catalog.ResolveAddressAsync(address,token);token.ThrowIfCancellationRequested();if(!Latest())return;RememberGalaxy(resolved);}
                    catch(Exception error)when(error is KeyNotFoundException or InvalidDataException or IOException){runtime.Log("星图 Thargoid 缓存",error.Message);}
                }
                additional.AddRange(galaxyCache.Values.Where(s=>viewport.Contains(s.Position,1.000001)&&filter.Matches(s)));
            }
            if(filter.Category==GalaxyFilterCategory.Services&&(filter.Values is null||filter.Values.Contains("engineer"))||filter.Category==GalaxyFilterCategory.Markers&&(filter.Values is null||filter.Values.Contains("engineer")))
                additional.AddRange(EngineerDirectory.All.Where(e=>viewport.Contains(e.Position,1.000001)).Select(e=>new GalacticSystem(e.SystemName,e.Position,Source:"工程师驻地目录"){Data=new(){Services=["engineer"],Markers=["engineer"]}}));
            if(filter.Category==GalaxyFilterCategory.Markers)
            {
                if(filter.Values is null||filter.Values.Any(k=>k is "bookmark" or "squadronbookmark" or "friend" or "wing" or "communitygoal" or "ship" or "mission"))
                {
                    var knownAddresses=galaxyCache.Values.Where(s=>s.SystemAddress is not null).Select(s=>s.SystemAddress!.Value).ToHashSet();
                    foreach(var address in galaxyAccountMarkers.Keys.Concat(galaxySocialMarkers.Keys).Concat(galaxyCommunityGoalsContext==ThargoidContext()?galaxyCommunityGoalAddresses:[]).Concat(galaxyFleetContext==ThargoidContext()?galaxyFleetAddresses:[]).Concat(galaxyMissionsContext==ThargoidContext()?galaxyMissionAddresses:[]).Distinct().Where(a=>!knownAddresses.Contains(a)).ToArray())
                    {
                        token.ThrowIfCancellationRequested();if(runtime.FlightDriver is not XFEEDToolBox.Core.Navigation.NativeFlightControlDriver{SupportsGalaxyResolveAddress:true}||NativeGalaxy() is not {} catalog)break;
                        try{var resolved=await catalog.ResolveAddressAsync(address,token);token.ThrowIfCancellationRequested();if(!Latest())return;RememberGalaxy(resolved);}
                        catch(Exception e)when(e is KeyNotFoundException or InvalidDataException or IOException){runtime.Log("星图书签",e.Message);}
                    }
                }
                foreach(var name in galaxyPersonalMarkers.Keys.ToArray())
                {
                    token.ThrowIfCancellationRequested();
                    var candidate=galaxyCache.GetValueOrDefault(name);
                    if(candidate is null)try{candidate=await FindGalacticSystemAsync(name,token);}catch(Exception e)when(e is HttpRequestException or InvalidDataException or JsonException){runtime.Log("星图标记",name+":"+e.Message);}
                    if(candidate is not null)RememberGalaxy(candidate);
                }
                additional.AddRange(galaxyCache.Values.Where(s=>viewport.Contains(s.Position,1.000001)&&filter.Matches(s)));
            }
            if(filter.Category==GalaxyFilterCategory.Colonisation&&(filter.Values is null||filter.Values.Contains("localplayerclaimed")))
            {
                var knownAddresses=galaxyCache.Values.Where(s=>s.SystemAddress is not null).Select(s=>s.SystemAddress!.Value).ToHashSet();
                foreach(var address in galaxyColonyOwnership.Where(c=>c.Value&&!knownAddresses.Contains(c.Key)).Select(c=>c.Key).ToArray())
                {
                    token.ThrowIfCancellationRequested();if(runtime.FlightDriver is not XFEEDToolBox.Core.Navigation.NativeFlightControlDriver{SupportsGalaxyResolveAddress:true}||NativeGalaxy() is not {} catalog)break;
                    try{var resolved=await catalog.ResolveAddressAsync(address,token);token.ThrowIfCancellationRequested();if(!Latest())return;RememberGalaxy(resolved);}
                    catch(Exception error)when(error is KeyNotFoundException or InvalidDataException or IOException){runtime.Log("星图殖民归属",error.Message);}
                }
                additional.AddRange(galaxyCache.Values.Where(s=>viewport.Contains(s.Position,1.000001)&&filter.Matches(s)));
            }
            void Commit(GalaxyQueryResult result)
            {
                if(!Latest()||token.IsCancellationRequested)return;
                var accepted=new List<GalacticSystem>();
                foreach(var system in result.Systems.Concat(additional).DistinctBy(s=>s.Name,StringComparer.OrdinalIgnoreCase))
                {
                    if(galaxyCache.TryGetValue(system.Name,out var original))
                    {if(original.Position.DistanceTo(system.Position)>.25)continue;var merged=original with{Data=original.Data.Merge(system.Data),StarClass=original.StarClass.Length>0?original.StarClass:system.StarClass};galaxyCache[original.Name]=merged;accepted.Add(merged);}
                    else accepted.Add(RememberGalaxy(system));
                }
                foreach(var s in accepted)galaxyQueryEvidence[s.Name]=s;
                var desired=galaxyDesiredViewport??viewport;
                var visible=galaxyQueryEvidence.Values.Where(s=>desired.Contains(s.Position,1.000001)&&GalaxyFilter.Matches(s)).ToArray();
                GalaxyQuerySystems=visible;galaxyQueryPublished=true;PublishGalaxy();GalaxyQueryProgress?.Invoke(result with{Systems=visible,CoverageComplete=result.CoverageComplete&&!filter.UsesPowerActivityCounter&&galaxyQueuedQuery==galaxyRunningQuery&&metadataFilter.Category==filter.Category},generation);GalaxyFilterInformationStatus=$"当前视锥 · 已返回 {visible.Length:N0} 个匹配 · 待查 {result.PendingQueries:N0} 个分区。";
                if(galaxyQueuedQuery!=galaxyRunningQuery)galaxyFilterInformationStop?.Cancel();
            }
            var result=await galaxyQueries.QueryViewportAsync(metadataFilter,viewport,token,Commit);token.ThrowIfCancellationRequested();if(!Latest())return;Commit(result);
            GalaxyFilterInformationStatus=$"当前视野 {GalaxyQuerySystems.Count:N0} 个匹配 · {result.Source}"+(metadataFilter.Category!=filter.Category?" · 人口资料已返回,原筛选属性仍需游戏确认。":filter.UsesPowerActivityCounter?" · 目录已返回,原近期活动条件持续刷新;未知隐藏。":result.CoverageComplete?" · 分区查询已完成。":" · 仍有覆盖待确认。")+(filter.Category==GalaxyFilterCategory.Markers?"\n"+GalaxyMarkerSources.Describe(filter.Values)+(filter.Values is null||filter.Values.Contains("bookmark")||filter.Values.Contains("squadronbookmark")?"\n"+galaxyAccountMarkerStatus:""):"");
            if(filter.Category==GalaxyFilterCategory.Colonisation&&(filter.Values is null||filter.Values.Contains("localplayerclaimed")))GalaxyFilterInformationStatus+="\n"+galaxyOwnedColonisationStatus;
            if(filter.Category==GalaxyFilterCategory.Commodity)GalaxyFilterInformationStatus+="\n"+galaxyCommodityOptionsStatus;
            if(filter.Category==GalaxyFilterCategory.Markers&&(filter.Values is null||filter.Values.Contains("friend")||filter.Values.Contains("wing")))GalaxyFilterInformationStatus+="\n"+galaxySocialMarkerStatus;
            if(filter.Category==GalaxyFilterCategory.Markers&&(filter.Values is null||filter.Values.Contains("communitygoal")))GalaxyFilterInformationStatus+="\n"+GalaxyCommunityGoalStatus;
        }
        catch(OperationCanceledException){ }
        catch(Exception error){if(Latest()){galaxyQueuedQuery="";GalaxyFilterInformationStatus="星图查询失败:"+error.Message;}}
        finally
        {
            if(generation==galaxyFilterQueryGeneration)
            {
                var follow=galaxyDesiredViewport is not null&&GalaxyFilter.IsQueryActive&&galaxyQueuedQuery!=galaxyRunningQuery&&GalaxyFilter.QueryKey==filter.QueryKey;
                GalaxyQueryBusy=false;
                if(follow){galaxyQueuedQuery="";QueueGalaxyFilterInformation();}
            }
        }
    }
    private GalacticSystem RememberGalaxy(GalacticSystem system,int? appearanceProcessId=null)
    {
        var nativeProcess=runtime.FlightDriver is XFEEDToolBox.Core.Navigation.NativeFlightControlDriver driver?(int?)driver.ProcessId:null;
        system=ApplyGalaxyAppearanceBinding(system,appearanceProcessId,nativeProcess,galaxyRegionQueryForProbe is not null);
        var origin=CurrentPlayer.Navigation.CurrentSystemAddress;
        system=system with{Data=system.Data.ForNativeContext(CurrentPlayer.CommanderId,nativeProcess,origin)};
        if(string.Equals(galaxyMetadataAccount,CurrentPlayer.CommanderId,StringComparison.OrdinalIgnoreCase)&&galaxyObservedFacts.TryGetValue(system.Name,out var facts))system=system with{Data=system.Data.Merge(facts)};
        if(galaxyCache.TryGetValue(system.Name,out var old))
        {
            var retainOriginal=old.Source=="游戏银河目录"&&system.Source!="游戏银河目录";
            system=system with
            {Position=retainOriginal?old.Position:system.Position,Source=retainOriginal?old.Source:system.Source,SystemAddress=retainOriginal?old.SystemAddress??system.SystemAddress:system.SystemAddress??old.SystemAddress,
                OriginalAppearance=retainOriginal?old.OriginalAppearance:system.OriginalAppearance,
                StarClass=retainOriginal&&old.StarClass.Length>0?old.StarClass:system.StarClass.Length>0?system.StarClass:old.StarClass,Data=old.Data.ForNativeContext(CurrentPlayer.CommanderId,nativeProcess,origin).Merge(system.Data)};
        }
        system=system with{Data=GalaxyPersonalFacts(system)};
        if(old is not null&&old==system)return old;
        galaxyCache[system.Name]=system;return system;
    }
    private GalaxySystemData GalaxyPersonalFacts(GalacticSystem system)
    {
        var data=system.Data;if(!string.Equals(galaxyMetadataAccount,CurrentPlayer.CommanderId,StringComparison.OrdinalIgnoreCase))return data;
        var markers=galaxyPersonalMarkers.GetValueOrDefault(system.Name)??[];
        var visited=system.SystemAddress is {} address&&galaxyOriginalVisited?.Contains(address)==true?true:(bool?)null;
        var trade=system.SystemAddress is {} tradeAddress?galaxyOriginalTrades.GetValueOrDefault(tradeAddress):null;
        bool? owned=system.SystemAddress is {} colonyAddress&&galaxyColonyOwnership.TryGetValue(colonyAddress,out var owner)?owner:null;
        if(trade is {Routes.Count:>0})markers=markers.Concat(new[]{"traderoute"}).Distinct().ToArray();
        if(system.SystemAddress is {} markerAddress&&galaxyAccountMarkers.TryGetValue(markerAddress,out var accountMarkers))markers=markers.Concat(accountMarkers).Distinct().ToArray();
        if(system.SystemAddress is {} socialAddress&&galaxySocialMarkers.TryGetValue(socialAddress,out var socialMarkers))markers=markers.Concat(socialMarkers).Distinct().ToArray();
        var runtimeMarkerKeys=new List<string>();
        if(galaxyCommunityGoalsContext==ThargoidContext()&&system.SystemAddress is {} communityAddress&&galaxyCommunityGoalAddresses.Contains(communityAddress))runtimeMarkerKeys.Add("communitygoal");
        if(galaxyFleetContext==ThargoidContext()&&system.SystemAddress is {} fleetAddress&&galaxyFleetAddresses.Contains(fleetAddress))runtimeMarkerKeys.Add("ship");
        IReadOnlyList<int> missionMarkerTypes=[];
        if(galaxyMissionsContext==ThargoidContext()&&system.SystemAddress is {} missionAddress&&galaxyMissionAddresses.Contains(missionAddress))
        {runtimeMarkerKeys.Add("mission");missionMarkerTypes=galaxyMissionMarkerTypes.GetValueOrDefault(missionAddress)??[];}
        GalaxyNativeAccountMarkerState? runtimeMarkers=null;
        if(runtimeMarkerKeys.Count>0&&runtime.FlightDriver is XFEEDToolBox.Core.Navigation.NativeFlightControlDriver markerDriver)
            runtimeMarkers=data.NativeAccountMarkers is {} previous&&previous.AccountFid==CurrentPlayer.CommanderId&&previous.ProcessId==markerDriver.ProcessId&&previous.Keys.SequenceEqual(runtimeMarkerKeys)&&previous.MissionMarkerTypes.SequenceEqual(missionMarkerTypes)
                ?previous:new(CurrentPlayer.CommanderId,markerDriver.ProcessId,runtimeMarkerKeys){MissionMarkerTypes=missionMarkerTypes};
        if(data.NativeAccountMarkers!=runtimeMarkers)data=data with{NativeAccountMarkers=runtimeMarkers};
        var war=galaxyThargoidContext==ThargoidContext()&&runtime.FlightDriver is XFEEDToolBox.Core.Navigation.NativeFlightControlDriver warDriver&&system.SystemAddress is {} warAddress&&galaxyThargoidObservedStates.TryGetValue(warAddress,out var state)?new GalaxyNativeThargoidState(CurrentPlayer.CommanderId,warDriver.ProcessId,state):null;
        if(data.NativeThargoid!=war)data=data with{NativeThargoid=war};
        var styles=system.SystemAddress is {} styleAddress?galaxyBookmarkStyles.GetValueOrDefault(styleAddress)??[]:[];
        if(markers.Count==0&&data.PersonalMarkers is null&&styles.Count==0&&data.BookmarkStyles is null&&visited is null&&trade is null&&owned is null&&data.OwnedColony is null)return data;
        var enriched=data.Merge(new(){AccountFid=CurrentPlayer.CommanderId,Visited=visited,PersonalMarkers=markers,BookmarkStyles=styles.Count>0||data.BookmarkStyles is not null?styles:null,TradeDataRecordedAt=trade?.Timestamp});
        return enriched.OwnedColony==owned?enriched:enriched with{OwnedColony=owned};
    }
    private Task RefreshGalaxyMetadataAsync()
    {
        if(galaxyLifetimeToken.IsCancellationRequested)return Task.FromCanceled(galaxyLifetimeToken);
        QueueGalaxyOriginalFilterFacts();QueueGalaxyHistoricalTradeFlows();QueueGalaxyCurrentTradeFlows();
        return galaxyMetadataReadTask is {IsCompleted:false} running?running:galaxyMetadataReadTask=RefreshGalaxyMetadataCoreAsync();
    }
    private async Task RefreshGalaxyMetadataCoreAsync()
    {
        galaxyLifetimeToken.ThrowIfCancellationRequested();
        // Page through the complete indexed local journal once, then stop at already observed rows.
        var account=CurrentPlayer.CommanderId;if(string.IsNullOrWhiteSpace(account))return;
        if(galaxyMetadataAccount!=account)
        {galaxyMetadataAccount=account;galaxyMetadataReadAt=default;galaxyNativeAccountReadAt=default;galaxyNativeFactsVersion++;galaxyMetadataEvents.Clear();galaxyObservedFacts.Clear();galaxyJournalMarkers.Clear();galaxyPersonalMarkers.Clear();galaxyOriginalVisited=null;galaxyOriginalTrades=new Dictionary<long,OriginalTradeDataSnapshot>();galaxyAccountMarkers=new Dictionary<long,IReadOnlyList<string>>();galaxyBookmarkStyles=new Dictionary<long,IReadOnlyList<GalaxyBookmarkStyle>>();galaxySocialMarkers=new Dictionary<long,IReadOnlyList<string>>();galaxyColonyOwnership=new Dictionary<long,bool>();foreach(var s in galaxyCache.Values.ToArray())RememberGalaxy(s);}
        if(DateTimeOffset.UtcNow-galaxyMetadataReadAt<TimeSpan.FromSeconds(20))
        {
            var previousVersion=galaxyNativeFactsVersion;await ReadNativeGalaxyAccountAsync(account);if(CurrentPlayer.CommanderId!=account)return;
            if(RefreshGalaxyPersonalMarkers()||previousVersion!=galaxyNativeFactsVersion)foreach(var s in galaxyCache.Values.ToArray())RememberGalaxy(s);
            if(previousVersion!=galaxyNativeFactsVersion&&GalaxyUsesAccountFacts(GalaxyFilter))QueueGalaxyFilterInformation();return;
        }
        galaxyMetadataReadAt=DateTimeOffset.UtcNow;
        var rows=await Task.Run(()=>
        {
            var observations=new List<XFEEDToolBox.Core.Data.GameEventEntry>();
            var owners=new Dictionary<string,HashSet<string>>(StringComparer.OrdinalIgnoreCase);
            foreach(var ownerEvent in new[]{"LoadGame","Commander","NewCommander"})
            {
                XFEEDToolBox.Core.Data.GameEventCursor? before=null;
                while(true)
                {
                    galaxyLifetimeToken.ThrowIfCancellationRequested();var page=runtime.GameEvents.Query(before:before,limit:500,eventName:ownerEvent);
                    foreach(var row in page.Entries)try{using var doc=JsonDocument.Parse(row.Json);if(doc.RootElement.TryGetProperty("FID",out var fid)&&fid.ValueKind==JsonValueKind.String&&fid.GetString() is {Length:>0} id){if(!owners.TryGetValue(row.Source,out var set))owners[row.Source]=set=new(StringComparer.OrdinalIgnoreCase);set.Add(id);}}catch(JsonException){ }
                    if(!page.HasMore||page.Entries.Count==0)break;before=page.Entries[^1].Cursor;
                }
            }
            foreach(var eventName in new[]{"Location","FSDJump","Docked","BuyExplorationData","BuyTradeData","MissionAccepted","MissionRedirected","MissionCompleted","MissionFailed","MissionAbandoned","Missions","CommunityGoal","CommunityGoalDiscard","CommunityGoalReward"})
            {
                XFEEDToolBox.Core.Data.GameEventCursor? cursor=null;
                while(true)
                {
                    galaxyLifetimeToken.ThrowIfCancellationRequested();var page=runtime.GameEvents.Query(before:cursor,limit:500,eventName:eventName);var known=false;
                    foreach(var row in page.Entries){if(galaxyMetadataEvents.Contains(row.Id)){known=true;continue;}if(owners.TryGetValue(row.Source,out var fids)&&fids.Count==1&&fids.Contains(account))observations.Add(row);}
                    if(known||!page.HasMore||page.Entries.Count==0)break;cursor=page.Entries[^1].Cursor;
                }
            }
            return observations.OrderBy(e=>e.Time).ThenBy(e=>e.Id).ToArray();
        },galaxyLifetimeToken);
        galaxyLifetimeToken.ThrowIfCancellationRequested();
        if(CurrentPlayer.CommanderId!=account)return;
        foreach(var row in rows)
        {
            galaxyMetadataEvents.Add(row.Id);
            try
            {
                using var json=JsonDocument.Parse(row.Json);var e=json.RootElement;
                galaxyJournalMarkers.Apply(e,row.Time);
                if(GalaxySystemData.FromAcquisition(e) is {} acquisition&&e.GetProperty("System").GetString() is {} acquiredName)
                {
                    acquisition=acquisition with{AccountFid=account};galaxyObservedFacts[acquiredName]=(galaxyObservedFacts.GetValueOrDefault(acquiredName)??new()).Merge(acquisition);
                    if(galaxyCache.TryGetValue(acquiredName,out var acquiredSystem))RememberGalaxy(acquiredSystem with{Data=acquiredSystem.Data.Merge(acquisition)});
                    continue;
                }
                if(!e.TryGetProperty("StarSystem",out var n)||n.ValueKind!=JsonValueKind.String||n.GetString() is not {Length:>0} name)continue;
                GalacticSystem? s=galaxyCache.GetValueOrDefault(name);
                if(s is null&&e.TryGetProperty("StarPos",out var p)&&p.ValueKind==JsonValueKind.Array&&p.GetArrayLength()==3)
                {var position=new XFEEDToolBox.Core.Models.Coordinates(p[0].GetDouble(),p[1].GetDouble(),p[2].GetDouble());if(position.IsValid)s=new(name,position,Source:"本机 Journal");}
                var data=GalaxySystemData.FromJournal(e) with{AccountFid=account};
                var previous=galaxyObservedFacts.GetValueOrDefault(name)??s?.Data??new();
                // A station's inventory proves that service exists here, not an exhaustive system inventory.
                if(data.Services is {} services)data=data with{Services=(previous.Services??[]).Concat(services).Distinct(StringComparer.OrdinalIgnoreCase).ToArray()};
                if(e.TryGetProperty("StationType",out var stationType)&&stationType.ValueKind==JsonValueKind.String&&stationType.GetString()=="FleetCarrier")data=data with{FleetCarrier=true};
                galaxyObservedFacts[name]=previous.Merge(data);
                if(s is null)continue;
                RememberGalaxy(s with{Data=s.Data.Merge(data)});
            }
            catch(Exception error)when(error is JsonException or InvalidOperationException or FormatException){runtime.Log("星图元数据",error.Message);}
        }
        await ReadGalaxyAccountCachesAsync(account);
        if(CurrentPlayer.CommanderId!=account)return;RefreshGalaxyPersonalMarkers();
        foreach(var s in galaxyCache.Values.ToArray())RememberGalaxy(s);
        OnPropertyChanged(nameof(GalaxyShipJumpRange));
    }
    private bool RefreshGalaxyPersonalMarkers()
    {
        var next=new Dictionary<string,IReadOnlyList<string>>(StringComparer.OrdinalIgnoreCase);
        void Marker(string name,string key){if(string.IsNullOrWhiteSpace(name))return;next[name]=(next.GetValueOrDefault(name)??[]).Append(key).Distinct().ToArray();}
        foreach(var ship in CurrentPlayer.Ships.Where(s=>!s.InTransit))Marker(ship.SystemName,"ship");
        foreach(var waypoint in CurrentPlayer.Navigation.Route)Marker(waypoint.Name,"navigation");
        foreach(var name in galaxyJournalMarkers.MissionSystems(DateTimeOffset.UtcNow))Marker(name,"mission");
        foreach(var name in galaxyJournalMarkers.CommunityGoalSystems(DateTimeOffset.UtcNow))Marker(name,"communitygoal");
        if(next.Count==galaxyPersonalMarkers.Count&&next.All(x=>galaxyPersonalMarkers.TryGetValue(x.Key,out var old)&&old.SequenceEqual(x.Value)))return false;
        galaxyPersonalMarkers.Clear();foreach(var entry in next)galaxyPersonalMarkers.Add(entry.Key,entry.Value);return true;
    }
    private async Task ReadGalaxyAccountCachesAsync(string account)
    {
        var root=Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"Frontier Developments","Elite Dangerous");
        var snapshots=await Task.Run(()=>
        {
            OriginalVisitedStarsSnapshot? visited=null;var trades=new Dictionary<long,OriginalTradeDataSnapshot>();
            try
            {
                visited=OriginalVisitedStarsCache.ReadForUser(root,account);
                runtime.ResolveDiagnostic("galaxy.visited-cache:"+account,"星图账号缓存","原游戏访问缓存已恢复读取。");
                var folder=Path.Combine(root,visited.CommanderId.ToString(System.Globalization.CultureInfo.InvariantCulture));
                if(Directory.Exists(folder))foreach(var path in Directory.EnumerateFiles(folder,"*.trd"))
                {
                    galaxyLifetimeToken.ThrowIfCancellationRequested();if(!long.TryParse(Path.GetFileNameWithoutExtension(path),out var address)||address<=0)continue;
                    var diagnostic="galaxy.trade-cache:"+account+":"+address;
                    try
                    {
                        trades[address]=OriginalTradeDataCache.Read(path,visited.CommanderId,address);
                        runtime.ResolveDiagnostic(diagnostic,"星图账号缓存","原游戏贸易缓存已恢复读取。");
                    }
                    catch(Exception e)when(e is IOException or UnauthorizedAccessException or InvalidDataException or JsonException or KeyNotFoundException or FormatException or ArgumentOutOfRangeException)
                    {runtime.ReportDiagnostic(diagnostic,"星图账号缓存",e,ActivitySeverity.Warning,"原游戏贸易缓存暂不可用:"+e.Message);}
                }
            }
            catch(Exception e)when(e is IOException or UnauthorizedAccessException or InvalidDataException or ArgumentException)
            {
                var expected=e is FileNotFoundException or DirectoryNotFoundException or EndOfStreamException or OriginalCacheUpdatingException
                    ||e is IOException io&&(io.HResult&0xffff) is 32 or 33;
                runtime.ReportDiagnostic("galaxy.visited-cache:"+account,"星图账号缓存",e,expected?ActivitySeverity.Status:ActivitySeverity.Warning,
                    expected?"原游戏访问缓存尚未就绪,等待游戏生成或更新。":"原游戏访问缓存读取失败:"+e.Message);
            }
            return(visited,trades);
        },galaxyLifetimeToken);
        if(CurrentPlayer.CommanderId!=account)return;galaxyOriginalVisited=snapshots.visited;galaxyOriginalTrades=snapshots.trades;
        await ReadNativeGalaxyAccountAsync(account);
    }
    private void SetGalaxyNativeBookmarks(IReadOnlyDictionary<long,IReadOnlyList<string>> markers,IReadOnlyDictionary<long,IReadOnlyList<GalaxyBookmarkStyle>> styles)
    {
        if(markers.Count==galaxyAccountMarkers.Count&&styles.Count==galaxyBookmarkStyles.Count&&markers.All(x=>galaxyAccountMarkers.TryGetValue(x.Key,out var old)&&old.SequenceEqual(x.Value))&&styles.All(x=>galaxyBookmarkStyles.TryGetValue(x.Key,out var old)&&old.SequenceEqual(x.Value)))return;
        galaxyAccountMarkers=markers;galaxyBookmarkStyles=styles;galaxyNativeFactsVersion++;
    }
    private void SetGalaxyColonyOwnership(IReadOnlyDictionary<long,bool> colonies)
    {if(galaxyColonyOwnership.Count==colonies.Count&&galaxyColonyOwnership.All(c=>colonies.TryGetValue(c.Key,out var own)&&own==c.Value))return;galaxyColonyOwnership=colonies;galaxyNativeFactsVersion++;}
    private void SetGalaxyNativeSocial(IReadOnlyDictionary<long,IReadOnlyList<string>> markers)
    {if(markers.Count==galaxySocialMarkers.Count&&markers.All(x=>galaxySocialMarkers.TryGetValue(x.Key,out var old)&&old.SequenceEqual(x.Value)))return;galaxySocialMarkers=markers;galaxyNativeFactsVersion++;}
    private async Task ReadNativeGalaxyAccountAsync(string account)
    {
        if(DateTimeOffset.UtcNow-galaxyNativeAccountReadAt<TimeSpan.FromSeconds(2))return;galaxyNativeAccountReadAt=DateTimeOffset.UtcNow;
        try
        {
            if(runtime.FlightDriver is XFEEDToolBox.Core.Navigation.NativeFlightControlDriver{SupportsGalaxyAccountBookmarks:true} driver)
            {
                var snapshot=await new NativeGalaxyAccountCatalog(driver.ProcessId).ReadAsync(galaxyLifetimeToken);
                if(CurrentPlayer.CommanderId!=account||runtime.FlightDriver is not XFEEDToolBox.Core.Navigation.NativeFlightControlDriver current||current.ProcessId!=driver.ProcessId)return;
                // Private custom bookmark names and UIDs remain inside the native reply;
                // only actual system addresses and marker kinds enter the map cache.
                var markers=snapshot.Available?snapshot.Bookmarks.GroupBy(b=>b.SystemAddress).ToDictionary(g=>g.Key,g=>(IReadOnlyList<string>)g.Select(b=>b.Squadron?"squadronbookmark":"bookmark").Distinct().Order(StringComparer.Ordinal).ToArray()):new Dictionary<long,IReadOnlyList<string>>();
                var styles=snapshot.Available?snapshot.Bookmarks.GroupBy(b=>b.SystemAddress).ToDictionary(g=>g.Key,g=>(IReadOnlyList<GalaxyBookmarkStyle>)g.Select(b=>new GalaxyBookmarkStyle(b.Icon,b.Squadron)).Distinct().OrderBy(s=>s.Squadron).ThenBy(s=>s.Icon).ToArray()):new Dictionary<long,IReadOnlyList<GalaxyBookmarkStyle>>();
                SetGalaxyNativeBookmarks(markers,styles);
                galaxyAccountMarkerStatus=snapshot.Available?$"原游戏书签管理器 · {snapshot.Bookmarks.Count:N0} 条实际记录;完整加载状态尚未确认。":"原游戏书签服务尚未就绪;缺少记录保持未知。";
            }
            else{SetGalaxyNativeBookmarks(new Dictionary<long,IReadOnlyList<string>>(),new Dictionary<long,IReadOnlyList<GalaxyBookmarkStyle>>());galaxyAccountMarkerStatus="原游戏书签接口尚未连接;缺少记录保持未知。";}
        }
        catch(OperationCanceledException)when(!lifetime.IsCancellationRequested)
        {galaxyAccountMarkerStatus="原游戏书签读取超时;完整加载状态仍未知。";}
        catch(Exception error)when(error is IOException or TimeoutException or InvalidDataException or InvalidOperationException or JsonException or KeyNotFoundException or FormatException)
        {galaxyAccountMarkerStatus="原游戏书签读取尚未确认:"+error.Message;}
        try
        {
            if(runtime.FlightDriver is XFEEDToolBox.Core.Navigation.NativeFlightControlDriver{SupportsGalaxyAccountColonisation:true} driver&&galaxyOriginalVisited is {} verified)
            {
                var snapshot=await new NativeGalaxyAccountCatalog(driver.ProcessId).ReadColonisationAsync(galaxyLifetimeToken);
                if(CurrentPlayer.CommanderId!=account||runtime.FlightDriver is not XFEEDToolBox.Core.Navigation.NativeFlightControlDriver current||current.ProcessId!=driver.ProcessId)return;
                // An actual active claim proves its owner; an absent row proves neither owner.
                SetGalaxyColonyOwnership(snapshot.Available?snapshot.Claims.Where(c=>c.Active).GroupBy(c=>c.SystemAddress).Where(g=>g.Select(c=>c.CommanderId).Distinct().Count()==1).ToDictionary(g=>g.Key,g=>g.First().CommanderId==verified.CommanderId):new Dictionary<long,bool>());
                galaxyOwnedColonisationStatus=snapshot.Available?$"已匹配当前账号原CommanderId · {galaxyColonyOwnership.Values.Count(v=>v):N0} 个本人活动殖民地;原注册表完整性未知。":"原游戏殖民注册表尚未就绪;归属保持未知。";
            }
            else{SetGalaxyColonyOwnership(new Dictionary<long,bool>());galaxyOwnedColonisationStatus=galaxyOriginalVisited is null?"未取得当前账号原CommanderId证明;殖民归属保持未知。":"原游戏殖民归属接口尚未连接;缺少记录保持未知。";}
        }
        catch(OperationCanceledException)when(!lifetime.IsCancellationRequested)
        {galaxyOwnedColonisationStatus="原游戏殖民归属读取超时;完整加载状态仍未知。";}
        catch(Exception error)when(error is IOException or TimeoutException or InvalidDataException or InvalidOperationException or JsonException or KeyNotFoundException or FormatException)
        {galaxyOwnedColonisationStatus="原游戏殖民归属尚未确认:"+error.Message;}
        try
        {
            if(CurrentPlayer.InGame&&runtime.FlightDriver is XFEEDToolBox.Core.Navigation.NativeFlightControlDriver{SupportsGalaxyAccountSocial:true} driver)
            {
                var session=CurrentPlayer.LoadGameAt;var snapshot=await new NativeGalaxyAccountCatalog(driver.ProcessId).ReadSocialAsync(galaxyLifetimeToken);
                if(CurrentPlayer.CommanderId!=account||CurrentPlayer.LoadGameAt!=session||!CurrentPlayer.InGame||runtime.FlightDriver is not XFEEDToolBox.Core.Navigation.NativeFlightControlDriver current||current.ProcessId!=driver.ProcessId)return;
                var markers=snapshot.Available?snapshot.Locations.Where(l=>l.LocationCurrent==true).GroupBy(l=>l.SystemAddress).ToDictionary(g=>g.Key,g=>(IReadOnlyList<string>)g.Select(l=>l.Kind).Distinct().Order(StringComparer.Ordinal).ToArray()):new Dictionary<long,IReadOnlyList<string>>();
                SetGalaxyNativeSocial(markers);
                var uncertain=snapshot.Locations.Count(l=>l.LocationCurrent is null);
                galaxySocialMarkerStatus=snapshot.Available?$"原游戏社交位置 · {markers.Count:N0} 个确认实时星系"+(uncertain>0?$" · {uncertain:N0} 条时效未确认,不显示为实时位置。":";位置快照不证明全部好友/队伍覆盖。"):"原游戏社交位置尚未就绪;缺少资料保持未知。";
            }
            else{SetGalaxyNativeSocial(new Dictionary<long,IReadOnlyList<string>>());galaxySocialMarkerStatus="原游戏好友/队伍位置接口尚未连接;时效未确认的位置不作为实时标记。";}
        }
        catch(OperationCanceledException)when(!lifetime.IsCancellationRequested)
        {SetGalaxyNativeSocial(new Dictionary<long,IReadOnlyList<string>>());galaxySocialMarkerStatus="原游戏社交位置读取超时;旧实时标记已清除,时效保持未知。";}
        catch(Exception error)when(error is IOException or TimeoutException or InvalidDataException or InvalidOperationException or JsonException or KeyNotFoundException or FormatException)
        {SetGalaxyNativeSocial(new Dictionary<long,IReadOnlyList<string>>());galaxySocialMarkerStatus="原游戏社交位置尚未确认:"+error.Message;}
        await ReadGalaxyPowerOptionsAsync(galaxyLifetimeToken);
        await ReadGalaxyCommodityOptionsAsync(galaxyLifetimeToken);
        await ReadGalaxyThargoidAsync(galaxyLifetimeToken);
        await ReadGalaxyCommunityGoalsAsync(galaxyLifetimeToken);
        await ReadGalaxyFleetAsync(galaxyLifetimeToken);
        await ReadGalaxyMissionsAsync(galaxyLifetimeToken);
    }
}