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.Models;
using XFEEDToolBox.Core.Navigation;

internal static class NativeMarketOrderAcceptance
{
    public static async Task RunAsync(int pid,string commodity,int delta,string reportPath)
    {
        if(File.Exists(reportPath))throw new IOException("Use a new report path.");
        var id=commodity switch{"drones"=>128066403u,"biowaste"=>128049244u,"hydrogenfuel"=>128049202u,"scrap"=>128049248u,_=>throw new ArgumentException("Choose a permitted low-price test commodity.")};
        if(delta is not(1 or -1))throw new ArgumentException("The acceptance order must be exactly 1t.");
        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.MarketId is not {} market||!initial.CargoReliable||initial.CurrentShipId!=7)throw new InvalidOperationException("Expected identified docked acceptance ship and reliable reserved cargo.");
        var limpets=initial.Inventory.Where(x=>x.Key=="drones"&&x.Stolen==0&&x.MissionId is null).Sum(x=>x.Count);
        if(commodity=="drones"&&(delta<0?limpets!=16:limpets!=15)||commodity!="drones"&&limpets!=15)
            throw new InvalidOperationException("The agreed limpet baseline is not established.");
        if(delta>0&&initial.FreeCargo<1)throw new InvalidOperationException("Sell the authorized single limpet before the cheap 1t test.");
        using var stop=new CancellationTokenSource(TimeSpan.FromSeconds(80));
        await using var protection=await FlightTestProtectionScope.ArmAsync(pid);var pipe=new NavigationPipeClient(pid);
        async Task<JsonElement> Wait(ulong generation)
        {
            while(true)
            {
                var result=await pipe.CallAsync(new{op="commerce.market.status",generation},stop.Token);
                if(result.GetProperty("state").GetInt32()>=3)return result;
                await Task.Delay(80,stop.Token);
            }
        }
        async Task<JsonElement> ReadMarket()
        {
            var queued=await pipe.CallAsync(new{op="commerce.market.read",marketId=(uint)market},stop.Token);
            var result=await Wait(queued.GetProperty("generation").GetUInt64());
            if(result.GetProperty("state").GetInt32()!=3)throw new InvalidOperationException("Original current-market read failed.");return result;
        }
        var started=DateTimeOffset.UtcNow;JsonElement? receipt=null,after=null,serverAfter=null;var key=Guid.NewGuid().ToString("N");string? failure=null;
        var before=await pipe.CallAsync(new{op="commerce.inventory"},stop.Token);
        try
        {
            await ReadMarket();
            var current=journal.State;
            if(!current.InGame||!current.Docked||current.MarketId!=market||current.CommanderId!=initial.CommanderId||current.CurrentShipId!=initial.CurrentShipId||current.LoadGameAt!=initial.LoadGameAt)
                throw new InvalidOperationException("Acceptance session or docking identity changed before the order.");
            var queued=await pipe.CallAsync(new{op="commerce.trade",marketId=(uint)market,commodityId=id,quantityDelta=delta,operationKey=key},stop.Token);
            receipt=await Wait(queued.GetProperty("generation").GetUInt64());
            if(receipt.Value.GetProperty("state").GetInt32()!=3||!receipt.Value.GetProperty("clientApplied").GetBoolean())throw new InvalidOperationException("Original order result is not confirmed; never resend this operation key.");
            after=await pipe.CallAsync(new{op="commerce.inventory"},stop.Token);
            var price=receipt.Value.GetProperty("unitPrice").GetInt64();
            if(price is <=0 or >500||after.Value.GetProperty("count").GetInt32()!=before.GetProperty("count").GetInt32()+delta
                ||after.Value.GetProperty("credits").GetInt64()!=before.GetProperty("credits").GetInt64()-price*delta)
                throw new InvalidOperationException("The original cargo or wallet does not match the confirmed server order.");
            serverAfter=await ReadMarket();
            var actual=serverAfter.Value.GetProperty("cargo").TryGetProperty(commodity,out var amount)?amount.GetInt32():0;
            if(actual!=receipt.Value.GetProperty("after").GetInt32())throw new InvalidOperationException("A second original server read did not confirm the new inventory.");
            var cargoDeadline=DateTimeOffset.UtcNow.AddSeconds(5);
            while(journal.State.CargoCount!=after.Value.GetProperty("count").GetInt32())
            {
                if(DateTimeOffset.UtcNow>=cargoDeadline)throw new InvalidOperationException("The game's actual Cargo.json has not confirmed the new cargo count.");
                await Task.Delay(100,stop.Token);
            }
        }
        catch(Exception error){failure=error.ToString();}
        var passed=failure is null;
        await File.WriteAllTextAsync(reportPath,JsonSerializer.Serialize(new{pid,started,finished=DateTimeOffset.UtcNow,commodity,delta,market,key,passed,before,receipt,after,serverAfter,
            journalCargoCount=journal.State.CargoCount,journalCargoAt=journal.State.CargoAt,failure,screenUse=false,osInputsSent=0,journalEventsSynthesized=0,completeTradeCycleAccepted=false},ToolboxSettings.JsonOptions));
        Console.WriteLine(JsonSerializer.Serialize(new{passed,commodity,delta,market,failure,screenUse=false,journalEventsSynthesized=0}));
        if(!passed)throw new InvalidOperationException("The original 1t market-order acceptance did not pass.");
    }
}