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 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 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."); } }