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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Text.Json;
using XFEEDToolBox.Core.Data;
using XFEEDToolBox.Core.Execution;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Navigation;

internal static class DockedMaintenance
{
    internal static async Task RunAsync(int pid,string directory,CancellationToken externalToken=default)
    {
        directory=Path.GetFullPath(directory);Directory.CreateDirectory(directory);
        using var lease=new FileStream(Path.Combine(directory,"runner.lock"),FileMode.OpenOrCreate,FileAccess.ReadWrite,FileShare.None);
        var settings=ToolboxSettings.Load(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox","settings.json"));
        await using var journal=new JournalMonitor(settings.JournalDirectory);await journal.StartAsync();var initial=journal.State;
        if(!initial.InGame||!initial.Docked||initial.CurrentShipId!=5||initial.CargoCount!=0||initial.CargoCapacity!=192||!initial.StationServicesKnown
            ||initial.MarketId is not (3228792064 or 4210924803)||string.IsNullOrWhiteSpace(initial.CommanderId))
            throw new InvalidOperationException("Expected the empty, docked CG ship at its verified route station.");
        await using var protection=await FlightTestProtectionScope.ArmAsync(pid);
        var channel=new NativeCommerceChannel(pid);using var ledger=new TradeProfitLedger(Path.Combine(directory,"ledger"));
        NativeWalletObservation? wallet=null;PlayerState Player()=>wallet?.Project(journal.State)??journal.State;
        async Task Check(CancellationToken token)
        {
            var player=Player();token.ThrowIfCancellationRequested();
            if(!player.InGame||!player.Docked||player.MarketId!=initial.MarketId||player.CommanderId!=initial.CommanderId||player.CurrentShipId!=initial.CurrentShipId
                ||player.LoadGameAt!=initial.LoadGameAt||player.CargoCount!=0||protection.Stopped||NavigationSafety.HasExternalThreat(player))
                throw new InvalidOperationException("Docked maintenance ship, cargo or protection changed.");
            var h=(await channel.CallAsync(new{op="health.read"},token)).GetProperty("shipHealth");
            if(!h.GetProperty("verified").GetBoolean()||h.GetProperty("hullFraction").GetDouble()<.5)throw new InvalidOperationException("Fresh original ship health required.");
        }
        var driver=new NativeTradeCommandDriver(channel,Player,Path.Combine(directory,"operations"),(_,_,_)=>throw new InvalidOperationException("No travel in docked maintenance."),Check);
        driver.WalletObserved+=value=>wallet=value;driver.ServiceConfirmed+=receipt=>ledger.Record(receipt.ToCashEntry());driver.Status+=Console.WriteLine;
        using var stop=CancellationTokenSource.CreateLinkedTokenSource(externalToken);stop.CancelAfter(TimeSpan.FromMinutes(3));
        using var sentinel=new Timer(_=>{if(File.Exists(Path.Combine(directory,"stop")))stop.Cancel();},null,100,100);
        string? failure=null;long? opening=null,closing=null;
        try
        {
            await driver.PreflightAsync(stop.Token);var path=Path.Combine(directory,"opening-wallet.json");
            if(File.Exists(path)){using var saved=JsonDocument.Parse(await File.ReadAllTextAsync(path,stop.Token));opening=saved.RootElement.GetProperty("credits").GetInt64();}
            else
            {
                opening=wallet!.Credits;await using var file=new FileStream(path,FileMode.CreateNew,FileAccess.Write,FileShare.Read);
                await JsonSerializer.SerializeAsync(file,new{credits=opening},cancellationToken:stop.Token);
            }
            await driver.ServiceDockedAsync(new(initial.MarketId!.Value,192),Check,stop.Token);
            await Check(stop.Token);var inventory=await channel.CallAsync(new{op="commerce.inventory"},stop.Token);closing=inventory.GetProperty("credits").GetInt64();
            if(inventory.GetProperty("count").GetInt32()!=0||opening+ledger.Snapshot.CashChange!=closing)
                throw new InvalidDataException("Docked maintenance wallet does not reconcile with confirmed service debits.");
        }
        catch(Exception error){failure=error.ToString();}
        finally
        {
            await driver.ReleaseAllAsync();
            await File.WriteAllTextAsync(Path.Combine(directory,"result.json"),JsonSerializer.Serialize(new{passed=failure is null,pid,finished=DateTimeOffset.UtcNow,
                initial.MarketId,opening,closing,profit=ledger.Snapshot,failure,cargoCount=0,tradeOrdersSent=0,screenUse=false},ToolboxSettings.JsonOptions));
        }
        if(failure is not null)throw new InvalidOperationException("Docked maintenance requires reconciliation: "+failure);
        Console.WriteLine(JsonSerializer.Serialize(new{passed=true,opening,closing,profit=ledger.Snapshot}));
    }
}