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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using XFEEDToolBox.Core.Galaxy;
using XFEEDToolBox.Core.Navigation;

namespace XFEEDToolBox.ViewModels;

public partial class ToolboxViewModel
{
    private NativeGalaxyFilterFactsCatalog? galaxySystemFactsClient;
    private int galaxySystemFactsProcess;
    private CancellationTokenSource? galaxySystemFactsStop;
    private Task? galaxySystemFactsTask;
    private long galaxySystemFactsEpoch;
    private string galaxySystemFactsBinding="";
    private GalaxyViewport? galaxySystemFactsViewport;
    private readonly Dictionary<long,DateTimeOffset> galaxySystemFactsReadAt=[];
    private readonly Dictionary<long,GalacticSystem> galaxySystemFactsTargets=[];
    private string galaxySystemFactsStatus="";
    private static bool GalaxyUsesOriginalFilterFacts(GalaxyMapFilter filter)=>filter.IsQueryActive&&(filter.RequireOriginalBasicSystemFacts&&filter.RequestsOriginalBasicSystemFacts||filter.RequireOriginalCivilizationFacts&&filter.RequestsOriginalCivilizationFacts||filter.RequireOriginalServiceFacts&&filter.RequestsOriginalServices||filter.RequireOriginalCarrierFacts&&filter.RequestsOriginalCarrierFacts||filter.RequireOriginalStateFacts&&filter.RequestsOriginalStateFacts||filter.RequireOriginalColonisationOwnershipFacts&&filter.RequestsOriginalColonisationOwnershipFacts||filter.RequiresOriginalCommodity||filter.SizeCategory==GalaxyFilterCategory.Commodity||filter.RequestsOriginalColonisationSize||filter.UsesPowerStatus||filter.UsesPowerOuter||filter.UsesPowerTerritory||filter.UsesPowerActivityCounter||filter.Category is GalaxyFilterCategory.Visited or GalaxyFilterCategory.Exploration or GalaxyFilterCategory.TradeData or GalaxyFilterCategory.Colonisation);
    private string GalaxySystemFactsBinding()
    {
        if(!GalaxyUsesOriginalFilterFacts(GalaxyFilter)||NavigationActive||!CurrentPlayer.InGame||CurrentPlayer.Navigation.JumpInProgress||runtime.FlightDriver is not NativeFlightControlDriver{SupportsGalaxyFilterFacts:true} driver
            ||string.IsNullOrWhiteSpace(CurrentPlayer.CommanderId)||CurrentPlayer.Navigation.CurrentSystemAddress is not >0)return "";
        if((GalaxyFilter.Category==GalaxyFilterCategory.Colonisation||GalaxyFilter.RequestsOriginalColonisationSize)&&!driver.SupportsGalaxyColonisationEligibility)return "";
        if(GalaxyFilter.RequireOriginalCivilizationFacts&&!driver.SupportsGalaxyCivilizationFacts)return "";
        if(GalaxyFilter.RequireOriginalServiceFacts&&!driver.SupportsGalaxyServiceFacts)return "";
        if(GalaxyFilter.RequireOriginalCarrierFacts&&!driver.SupportsGalaxyCarrierFacts)return "";
        if(GalaxyFilter.RequireOriginalStateFacts&&!driver.SupportsGalaxyStateFacts)return "";
        if(GalaxyFilter.RequireOriginalColonisationOwnershipFacts&&!driver.SupportsGalaxyColonisationOwnership)return "";
        if(GalaxyFilter.UsesPowerActivityCounter&&(!driver.SupportsGalaxyPowerActivityFacts||!GalaxyFilter.ActivityNativeQueryReady))return "";
        if(GalaxyFilter.UsesPowerOuter&&!driver.SupportsGalaxyPowerOuterFacts)return "";
        if(GalaxyFilter.UsesPowerTerritory&&!driver.SupportsGalaxyPowerTerritoryFacts)return "";
        if(GalaxyFilter.UsesPowerStatus&&!driver.SupportsGalaxyPowerStatusFacts)return "";
        if(GalaxyFilter.RequiresOriginalCommodity&&(!driver.SupportsGalaxyCommodityFilterFacts||GalaxyFilter.CommodityRequest is null))return "";
        return FormattableString.Invariant($"{driver.ProcessId}|{CurrentPlayer.CommanderId}|{CurrentPlayer.LoadGameAt:O}|{CurrentPlayer.Navigation.CurrentSystemAddress}|{GalaxyFilter.QueryKey}|{GalaxyFilter.IncludeUnknown}|{GalaxyFilter.SizeCategory}|{GalaxyFilter.PopulationMinimum:R}|{GalaxyFilter.PopulationMaximum:R}");
    }
    // Catalogue streaming and attribute refresh have separate lifetimes. Panning
    // updates the wanted addresses without cancelling the first in-flight batch.
    private void QueueGalaxyOriginalFilterFacts(GalaxyViewport? viewport=null)
    {
        var requireBasic=GalaxyFilter.RequestsOriginalBasicSystemFacts&&(GalaxyFilter.Category!=GalaxyFilterCategory.State||GalaxyFilter.SizeCategory==GalaxyFilterCategory.Population)&&!NavigationActive&&CurrentPlayer.InGame&&
            !string.IsNullOrWhiteSpace(CurrentPlayer.CommanderId)&&CurrentPlayer.Navigation.CurrentSystemAddress is >0&&
            runtime.FlightDriver is NativeFlightControlDriver{SupportsGalaxyFilterFacts:true,SupportsGalaxyBasicSystemFacts:true};
        var originalContext=!NavigationActive&&CurrentPlayer.InGame&&!string.IsNullOrWhiteSpace(CurrentPlayer.CommanderId)&&
            CurrentPlayer.Navigation.CurrentSystemAddress is >0&&runtime.FlightDriver is NativeFlightControlDriver{SupportsGalaxyFilterFacts:true};
        var requireCivilization=originalContext&&GalaxyFilter.RequestsOriginalCivilizationFacts;
        var requireServices=originalContext&&GalaxyFilter.RequestsOriginalServices;
        var requireCarrier=originalContext&&GalaxyFilter.RequestsOriginalCarrierFacts;
        var requireState=originalContext&&GalaxyFilter.RequestsOriginalStateFacts;
        var requireOwnership=originalContext&&GalaxyFilter.RequestsOriginalColonisationOwnershipFacts;
        if(GalaxyFilter.RequireOriginalBasicSystemFacts!=requireBasic||GalaxyFilter.RequireOriginalCivilizationFacts!=requireCivilization||GalaxyFilter.RequireOriginalServiceFacts!=requireServices||GalaxyFilter.RequireOriginalCarrierFacts!=requireCarrier||GalaxyFilter.RequireOriginalStateFacts!=requireState||GalaxyFilter.RequireOriginalColonisationOwnershipFacts!=requireOwnership)
        {GalaxyFilter=GalaxyFilter with{RequireOriginalBasicSystemFacts=requireBasic,RequireOriginalCivilizationFacts=requireCivilization,RequireOriginalServiceFacts=requireServices,RequireOriginalCarrierFacts=requireCarrier,RequireOriginalStateFacts=requireState,RequireOriginalColonisationOwnershipFacts=requireOwnership};return;}
        galaxySystemFactsViewport=viewport??galaxyViewport??galaxyDesiredViewport;
        var binding=GalaxySystemFactsBinding();
        if(binding!=galaxySystemFactsBinding)
        {
            galaxySystemFactsStop?.Cancel();galaxySystemFactsStop?.Dispose();galaxySystemFactsStop=null;
            galaxySystemFactsBinding=binding;galaxySystemFactsEpoch++;galaxySystemFactsReadAt.Clear();galaxySystemFactsTargets.Clear();
            // A disconnected process or changed account/origin cannot retain
            // its previous original filter classifications in the static cache.
            ClearGalaxyOriginalFilterFacts();
        }
        if(binding.Length==0)
        {
            if(GalaxyUsesOriginalFilterFacts(GalaxyFilter))
            {
                galaxySystemFactsStatus=NavigationActive?"导航中 · 原游戏账号属性刷新已暂停;仅显示当前账号已知记录。":GalaxyFilter.RequiresOriginalCommodity&&GalaxyFilter.CommodityRequest is null?"原商品选择尚未与实际UID定义绑定;缺少资料保持未知。":"原游戏星图属性接口尚未连接 · 缺少资料保持未知。";
                GalaxyFilterInformationStatus=galaxySystemFactsStatus;
            }
            return;
        }
        UpdateGalaxySystemFactsTargets();
        if(galaxySystemFactsTargets.Count==0)
        {galaxySystemFactsStatus="当前视野尚无可读取的原生系统属性 · 缺少资料保持未知。";GalaxyFilterInformationStatus=galaxySystemFactsStatus;}
        if(galaxySystemFactsTask is {IsCompleted:false}&&galaxySystemFactsStop?.IsCancellationRequested==false)return;
        galaxySystemFactsStop=CancellationTokenSource.CreateLinkedTokenSource(lifetime.Token);
        var epoch=galaxySystemFactsEpoch;var stop=galaxySystemFactsStop;
        galaxySystemFactsTask=RefreshGalaxyOriginalFilterFactsAsync(binding,epoch,stop.Token);
    }
    private void UpdateGalaxySystemFactsTargets()
    {
        galaxySystemFactsTargets.Clear();if(galaxySystemFactsViewport is not {} viewport)return;
        var rangeLimited=GalaxyFilter.Category is GalaxyFilterCategory.Exploration or GalaxyFilterCategory.TradeData;
        var origin=CurrentPlayer.Position;
        foreach(var system in galaxyCache.Values)
        {
            if(system.SystemAddress is not >0||system.Source!="游戏银河目录"||!viewport.Contains(system.Position,1.000001))continue;
            // This only avoids unnecessary reads. A skipped/missing address is
            // never classified as noDataAvailable; the native predicate owns it.
            if(rangeLimited&&origin is {IsValid:true}&&origin.DistanceTo(system.Position)>20.000001)continue;
            galaxySystemFactsTargets[system.SystemAddress.Value]=system;
        }
    }
    private void ClearGalaxyOriginalFilterFacts(IReadOnlySet<long>? addresses=null)
    {
        var changed=false;
        foreach(var system in galaxyCache.Values.ToArray())
        {
            if(addresses is not null&&(system.SystemAddress is not {} address||!addresses.Contains(address)))continue;
            var data=system.Data;if(data.NativeFilterFacts is null&&data.Exploration is null&&data.TradeData is null)continue;
            galaxyCache[system.Name]=system with{Data=data with{NativeFilterFacts=null,Exploration=null,TradeData=null}};changed=true;
        }
        if(changed)PublishGalaxyOriginalFilterFacts();
    }
    private void PublishGalaxyOriginalFilterFacts()
    {
        if(GalaxyUsesOriginalFilterFacts(GalaxyFilter)&&galaxySystemFactsViewport is {} viewport)
            GalaxyQuerySystems=galaxyCache.Values.Where(s=>viewport.Contains(s.Position,1.000001)&&GalaxyFilter.Matches(s)).ToArray();
        PublishGalaxy();
    }
    private async Task RefreshGalaxyOriginalFilterFactsAsync(string binding,long epoch,CancellationToken token)
    {
        bool Latest()=>!token.IsCancellationRequested&&epoch==galaxySystemFactsEpoch&&binding==galaxySystemFactsBinding&&binding==GalaxySystemFactsBinding();
        // Yield before assigning the task so a completed/empty initial batch
        // cannot make a second caller start another worker synchronously.
        await Task.Yield();
        try
        {
            while(Latest())
            {
                UpdateGalaxySystemFactsTargets();var now=DateTimeOffset.UtcNow;
                var batch=galaxySystemFactsTargets.Values
                    .Where(s=>!galaxySystemFactsReadAt.TryGetValue(s.SystemAddress!.Value,out var at)||now-at>=TimeSpan.FromSeconds(2))
                    // Never-read rows precede refreshes: large views make forward
                    // progress rather than continually revisiting the first page.
                    .OrderBy(s=>galaxySystemFactsReadAt.GetValueOrDefault(s.SystemAddress!.Value))
                    .ThenBy(s=>s.Position.DistanceTo(galaxySystemFactsViewport!.Focus))
                    .Take(256).Select(s=>s.SystemAddress!.Value).ToArray();
                if(batch.Length==0)
                {
                    if(galaxySystemFactsTargets.Count==0)
                    {galaxySystemFactsStatus="当前视野尚无可读取的原生系统属性 · 缺少资料保持未知。";GalaxyFilterInformationStatus=galaxySystemFactsStatus;}
                    await Task.Delay(500,token);continue;
                }
                if(runtime.FlightDriver is not NativeFlightControlDriver driver)break;
                var process=driver.ProcessId;var fid=CurrentPlayer.CommanderId;var origin=CurrentPlayer.Navigation.CurrentSystemAddress!.Value;
                if(galaxySystemFactsClient is null||galaxySystemFactsProcess!=process)
                {galaxySystemFactsProcess=process;galaxySystemFactsClient=new(process);}
                try
                {
                    var powerRequest=GalaxyFilter.UsesPowerActivityCounter?new GalaxyNativePowerActivityRequest(GalaxyFilter.ActivityAllPowers,GalaxyFilter.ActivityAllPowers?null:GalaxyFilter.ActivityInternalPowerId,GalaxyPowerActivities.OriginalUid(GalaxyFilter.Activity)):null;
                    var result=await galaxySystemFactsClient.ReadAsync(batch,token,includeColonisation:GalaxyFilter.Category==GalaxyFilterCategory.Colonisation||GalaxyFilter.RequestsOriginalColonisationSize,powerActivity:powerRequest,includePowerOuter:GalaxyFilter.UsesPowerOuter,powerTerritory:GalaxyFilter.PowerTerritoryRequest,includePowerStatus:GalaxyFilter.UsesPowerStatus,commodity:GalaxyFilter.CommodityRequest,includeBasicSystem:GalaxyFilter.RequireOriginalBasicSystemFacts&&GalaxyFilter.RequestsOriginalBasicSystemFacts,
                        includeCivilization:GalaxyFilter.RequireOriginalCivilizationFacts&&GalaxyFilter.RequestsOriginalCivilizationFacts,includeServices:GalaxyFilter.RequireOriginalServiceFacts&&GalaxyFilter.RequestsOriginalServices,includeCarrier:GalaxyFilter.RequireOriginalCarrierFacts&&GalaxyFilter.RequestsOriginalCarrierFacts,includeState:GalaxyFilter.RequireOriginalStateFacts&&GalaxyFilter.RequestsOriginalStateFacts,includeColonisationOwnership:GalaxyFilter.RequireOriginalColonisationOwnershipFacts&&GalaxyFilter.RequestsOriginalColonisationOwnershipFacts);if(!Latest())break;
                    if(result.OriginSystemAddress!=origin)throw new InvalidDataException("原游戏筛选资料的参照星系与当前玩家不一致。");
                    UpdateGalaxySystemFactsTargets();
                    foreach(var row in result.Rows)
                    {
                        galaxySystemFactsReadAt[row.SystemAddress]=DateTimeOffset.UtcNow;
                        if(!galaxySystemFactsTargets.TryGetValue(row.SystemAddress,out var target)||!galaxyCache.TryGetValue(target.Name,out var current)||current.SystemAddress!=row.SystemAddress)continue;
                        var activity=row.PowerActivityCounter is {} counter?new GalaxyPowerActivityEvidence(counter.AllPowers?"":GalaxyFilter.ActivityPowerName??"",GalaxyPowerActivities.FromOriginalUid(counter.ActionId)!.Value,counter.RecentActivity,counter.InternalPowerId,counter.AllPowers):null;
                        var ownerId=row.PowerStatus?.OwnerInternalPowerId??row.PowerTerritory?.Style?.OwnerInternalPowerId;
                        var owner=ownerId is {} id?GalaxyFilter.OriginalPowerOptions.FirstOrDefault(p=>p.InternalPowerId==id)?.Name:null;
                        var facts=new GalaxyNativeSystemFacts(fid,process,origin,row.Visited,row.Exploration,row.TradeData,row.WithinDataRange,row.MetadataStatus,row.ColonisationStatus,activity){PowerOuter=row.PowerOuter,PowerTerritory=row.PowerTerritory,PowerTerritoryOwnerName=owner,PowerStatus=row.PowerStatus,Commodity=row.Commodity,BasicSystem=row.BasicSystem,BasicSystemRequested=row.BasicSystemRequested,
                            Civilization=row.Civilization,CivilizationRequested=row.CivilizationRequested,ServiceMask=row.ServiceMask,ServicesRequested=row.ServicesRequested,ServicesDefaulted=row.ServicesDefaulted,CarrierDockable=row.CarrierDockable,CarrierRequested=row.CarrierRequested,State=row.State,StateRequested=row.StateRequested,ColonisationOwnership=row.ColonisationOwnership,ColonisationOwnershipRequested=row.ColonisationOwnershipRequested,
                            ColonisationHighlight=row.ColonisationHighlight,ColonisationHighlightRequested=row.ColonisationHighlightRequested,
                            ColonisationNormalStatus=row.ColonisationNormalStatus,ColonisationNormalStatusRequested=row.ColonisationNormalStatusRequested};
                        RememberGalaxy(current with{Data=current.Data.Merge(new(){NativeFilterFacts=facts})});
                    }
                    if(!Latest())break;
                    PublishGalaxyOriginalFilterFacts();
                    var category=GalaxyFilter.Category;
                    var known=galaxySystemFactsTargets.Values.Count(s=>galaxyCache.TryGetValue(s.Name,out var current)&&!GalaxyFilter.IsUnknown(current));
                    var pending=galaxySystemFactsTargets.Keys.Count(a=>!galaxySystemFactsReadAt.ContainsKey(a));
                    galaxySystemFactsStatus=$"原游戏星图属性 · 视野已核实 {known:N0} / {galaxySystemFactsTargets.Count:N0} · 匹配 {GalaxyQuerySystems.Count:N0}"+
                        (pending>0?$" · 待查 {pending:N0}":" · 每 2 秒刷新")+(GalaxyFilter.RequiresOriginalCommodity?"\n原商品分类使用实际定义及已缓存市场,保留原中心/范围和价格规则;未就绪或缺上下文保持未知。":GalaxyFilter.UsesPowerOuter?"\n原活动视图按原缓存中心/半径筛选;已就绪点用原配色与尺寸,缺样式保留回退。圈层与完整淡出仍待验收。":GalaxyFilter.UsesPowerTerritory?"\n领土区域仅用原metadata/实际势力定义/原counter返回的半径和样式;活动开关与最低强度独立,未知样式不画圈。":GalaxyFilter.RequireOriginalStateFacts?"\n原状态包含游戏特殊覆盖;分类资料尚未就绪时保持未知。":GalaxyFilter.RequireOriginalCarrierFacts?"\n按原游戏航母缓存区分可停靠/无可停靠记录;缺行分类不代表全球无航母。":GalaxyFilter.RequireOriginalServiceFacts?"\n使用原游戏十项服务缓存,已知无匹配严格隐藏,未知保持未知。":GalaxyFilter.RequireOriginalCivilizationFacts?"\n使用原文明分类与实际定义ID,缺少原资料保持未知。":GalaxyFilter.RequireOriginalColonisationOwnershipFacts?"\n殖民归属以游戏当前指挥官资料为准;尚未取得归属资料时保持未知。":GalaxyFilter.RequireOriginalBasicSystemFacts?"\n使用游戏已载入的原分类;资料表就绪且确认无条目时采用原分类默认值,未就绪或未知编号保持未知。":category is GalaxyFilterCategory.Exploration or GalaxyFilterCategory.TradeData?"\n原游戏仅在当前系统 20 ly 范围内判定;范围外保持未知。":category==GalaxyFilterCategory.Colonisation?"\n申领资格取原游戏候选规则;本人归属另外核对 CommanderID。未就绪保持未知。":GalaxyFilter.UsesPowerActivityCounter?"\n领土附加的原近期活动计数;0为有效值,缺缓存保持未知。":"\n未就绪的原游戏访问表保持未知;本机访问日志只补充已访问记录。");
                    GalaxyFilterInformationStatus=galaxySystemFactsStatus;
                }
                catch(OperationCanceledException)when(token.IsCancellationRequested){break;}
                catch(Exception error)when(error is IOException or TimeoutException or InvalidOperationException or System.Text.Json.JsonException or KeyNotFoundException or FormatException)
                {
                    if(!Latest())break;
                    foreach(var id in batch)galaxySystemFactsReadAt[id]=DateTimeOffset.UtcNow;
                    ClearGalaxyOriginalFilterFacts(batch.ToHashSet());
                    galaxySystemFactsStatus="原游戏星图属性暂未就绪 · 缺少资料保持未知:"+error.Message;GalaxyFilterInformationStatus=galaxySystemFactsStatus;
                    await Task.Delay(2000,token);
                }
            }
        }
        catch(OperationCanceledException)when(token.IsCancellationRequested){ }
        finally
        {
            if(epoch==galaxySystemFactsEpoch&&binding==galaxySystemFactsBinding&&GalaxyUsesOriginalFilterFacts(GalaxyFilter)&&binding!=GalaxySystemFactsBinding())
                QueueGalaxyOriginalFilterFacts();
        }
    }
}