using System.Text.Json;
using System.Text.Json.Nodes;
using static XFEEDToolBox.Plugin.JsonWire;
namespace XFEEDToolBox.Plugin;
internal static unsafe partial class Navigation
{
private static string CommerceCommodityName(uint id) => id switch
{
128066403 => "drones", 128049244 => "biowaste", 128049202 => "hydrogenfuel", 128049248 => "scrap",
128049172 => "uranium",128049189 => "coffee",128049197 => "polymers",128673851 => "liquidoxygen", _ => ""
};
private static bool CommerceBatchCommodity(uint commodity)=>commodity is 128049172 or 128049189 or 128049197 or 128673851;
private static bool CommerceExpectedCredits(in CommerceReadRequest work,out ulong expected)
{
expected=0;
if(work.delta==0||work.price==0||(!work.formal&&(work.delta is <-192 or >192||work.price>100000))
||work.formal&&(work.capacityBound<=0||Math.Abs((long)work.delta)>work.capacityBound||!FormalCommodityName(CommerceExpectedCommodity(work))))return false;
var cost=checked((ulong)Math.Abs((long)work.delta)*work.price);
if(work.delta>0){if(work.creditsBefore<cost)return false;expected=work.creditsBefore-cost;return true;}
if(work.creditsBefore>ulong.MaxValue-cost)return false;expected=work.creditsBefore+cost;return true;
}
private static int CommerceInventoryCount(JsonElement response, uint commodity,string? expectedName=null)
{
if (!response.TryGetProperty("inventory", out var inventory) || inventory.ValueKind != JsonValueKind.Array) return -1;
var name = expectedName??CommerceCommodityName(commodity); if (name.Length == 0) return -1; var total = 0; var maximum=expectedName is null?1024:int.MaxValue;
foreach (var row in inventory.EnumerateArray())
{
if (row.ValueKind != JsonValueKind.Object || !row.TryGetProperty("commodity", out var item) || item.ValueKind != JsonValueKind.String || !row.TryGetProperty("qty", out var quantity) || !quantity.TryGetInt32(out var count)) return -1;
if (!item.GetString()!.Equals(name, StringComparison.OrdinalIgnoreCase)) continue;
if (row.Bool("stolen") || row.Int("marked") != 0 || count < 0 || count>maximum || total > maximum - count) return -1;
total += count;
}
return total;
}
private static bool StartCommerceMutation(in Snapshot frame, ref CommerceReadRequest work, nuint context)
{
try
{
if (work.kind != 1 || work.mutationSent || !CommerceExpectedCredits(work,out _)) return false;
var cargo = RegisteredComponent(Pointer(frame.life + 0xC8) - 0x30, Read<uint>(image + 0x5F2F25C));
if (GameRoute(frame, out var pilot) == 0) return false;
var wallet = RegisteredComponent(Pointer(pilot + 0xC8) - 0x30, Read<uint>(image + 0x5F59D90));
if (cargo == 0 || Pointer(cargo) != image + 0x530B858 || Pointer(Pointer(cargo) + 0x140) != image + 0x2A686B0 || wallet == 0 || Pointer(wallet) != image + 0x5295868 || Pointer(Pointer(wallet) + 0xA0) != image + 0x2591C70) return false;
var observed = ReadCommerceShipInventory(frame);
if (!observed.verified || observed.credits != work.creditsBefore || Read<int>(cargo + 0x28C) != work.totalBefore || work.delta > 0 && Read<int>(cargo + 0x288) < work.totalBefore + work.delta) return false;
if (Pointer(Pointer(wallet) + 0x28) != image + 0x25650B0 || work.delta>0&&Call(image + 0x25650B0, wallet) < (ulong)work.delta*work.price) return false;
var storage = (nuint)Call(image + 0x53A690, 0xF8); if (storage == 0) return false;
work.request = (nuint)Call(image + 0x1E85E20, storage, context);
if (work.request == 0 || Pointer(work.request) != image + 0x511F9D0) return false;
var emptyCargoTransfers = stackalloc ulong[4]; new Span<ulong>(emptyCargoTransfers, 4).Clear();
// A dispatched original server transaction is never sent again after an uncertain response.
work.mutationSent = true;
Call(image + 0x3437150, work.request, work.market, work.commodity, 0, 0, work.price,
unchecked((ulong)work.delta), unchecked((ulong)work.after), (ulong)(nuint)emptyCargoTransfers, ulong.MaxValue);
return true;
}
catch (Exception error) { work.error = CommerceExceptionCode(error); return false; }
}
private static bool ValidateCommerceMutationBody(in CommerceReadRequest work, byte* body, nuint length)
{
using var document = JsonDocument.Parse(new ReadOnlySpan<byte>(body, checked((int)length)).ToArray());
var response = document.RootElement;
return CommerceExpectedCredits(work,out var expected)&&!response.TryGetProperty("error", out _) && response.TryGetProperty("credits", out var credits) && credits.GetUInt64() == expected && CommerceInventoryCount(response, work.commodity,CommerceExpectedCommodity(work)) == work.after;
}
private static bool ApplyCommerceMutation(in Snapshot frame, ref CommerceReadRequest work, byte* body, nuint length)
{
try
{
if (GetCurrentThreadId() != owningThread || !frame.valid || !frame.healthValid || frame.healthEmergency || frame.hullFraction<=0 || frame.entity != work.entity || frame.life != work.life || controllerEpoch != work.epoch || !CommerceDocked(frame, work.market)) return false;
var cargo = RegisteredComponent(Pointer(frame.life + 0xC8) - 0x30, Read<uint>(image + 0x5F2F25C));
if (GameRoute(frame, out var pilot) == 0) return false;
var wallet = RegisteredComponent(Pointer(pilot + 0xC8) - 0x30, Read<uint>(image + 0x5F59D90));
if (cargo == 0 || Pointer(cargo) != image + 0x530B858 || wallet == 0 || Pointer(wallet) != image + 0x5295868) return false;
var text = OriginalServiceText(body, length); if (text == 0) return false;
try
{
Call(image + 0x2A686B0, cargo, (ulong)(nuint)(&text), 1, 1, 1, 0);
Call(image + 0x2591C70, wallet, (ulong)(nuint)(&text), unchecked((ulong)(-(long)work.price * work.delta)), 0, 0);
var actual = ReadCommerceShipInventory(frame); if(!CommerceExpectedCredits(work,out var expected))return false;
work.creditsResult = expected; work.serviceReplyVerified = true;
work.clientApplied = actual.verified && actual.count == (uint)(work.totalBefore + work.delta) && actual.credits == expected;
}
finally { Call(image + 0x204010, (ulong)(nuint)(&text)); }
return work.clientApplied;
}
catch (Exception error) { work.error = CommerceExceptionCode(error); return false; }
}
private static JsonObject CommerceTradeCall(JsonElement request, in Snapshot frame,bool batch=false,bool formal=false)
{
var market = request.Required("marketId").GetUInt32(); var commodity = request.Required("commodityId").GetUInt32();
var delta = request.Required("quantityDelta").GetInt32(); var key = request.Required("operationKey").GetString()!;
var invalidOrder=formal?commodity==0||delta==0:batch? !CommerceBatchCommodity(commodity)||delta==0||delta is <-192 or >192:
CommerceCommodityName(commodity).Length==0||CommerceBatchCommodity(commodity)||delta is not(1 or -1);
if (!CommerceOperationKey(key)||invalidOrder)
throw new InvalidOperationException(batch?"Only identified CG commodity batches within 192t are available":"Only identified 1t low-price acceptance orders are available");
if (!Enabled() || !inGame || !frame.valid || !frame.healthValid || frame.healthEmergency || frame.hullFraction<=0 || GetTickCount64() - frame.tick > 200 || !CommerceDocked(frame, market)) throw new InvalidOperationException("Fresh healthy docked ship required for original market order");
lock (commerceReadLock)
{
if (commerceOperationKeys.TryGetValue(key, out var previous))
{
if (previous != commerceRead.generation) throw new InvalidOperationException("Order was already submitted; reconcile its actual result before continuing");
return Obj(("generation", previous), ("duplicate", true));
}
if (commerceReconciliationRequired) throw new InvalidOperationException("An uncertain order requires a fresh game session to reconcile inventory and credits; no order was retried");
if (commerceOperationKeys.Count >= CommerceKeyCapacity(formal,batch) || commerceRead.state != 3 || commerceRead.kind!=0 || commerceRead.market != market || commerceRead.entity != frame.entity || commerceRead.life != frame.life || GetTickCount64() - commerceRead.observed > 60000) throw new InvalidOperationException("Read the current original market and cargo before sending an order");
using var document = JsonDocument.Parse(commerceReadBody); var response = document.RootElement;
var quote = response.Required("commodities").Required(commodity.ToString(System.Globalization.CultureInfo.InvariantCulture));
var observed = ReadCommerceShipInventory(frame); if (!observed.verified) throw new InvalidOperationException("Actual original wallet and cargo are unavailable");
if(!QuotedWalletMatches(response,observed.credits))
throw new InvalidOperationException("Original server wallet differs from the actual client; refresh the original profile before submitting an order");
var price = quote.Required(delta > 0 ? "buyPrice" : "sellPrice").GetUInt32(); var before = CommerceInventoryCount(response, commodity);
var total = 0;
foreach (var row in CommerceRows(response.Required("inventory")))
{
var quantity = row.Required("qty").GetInt32(); if (quantity < 0 || total > int.MaxValue - quantity) throw new InvalidOperationException("Unverified cargo count"); total += quantity;
}
var debit=delta>0?checked((ulong)delta*price):0;
var name=quote.Str("name");
if(formal){if(!FormalCommodityName(name))throw new InvalidOperationException("Original commodity name required");before=CommerceInventoryCount(response,commodity,name);}
if (total != observed.count || observed.credits < debit || before < 0 || (long)before + delta < 0 || (long)total+delta<0 || (long)total+delta>observed.capacity || price == 0 || !formal&&price > (batch?100000u:500u) || quote.Str("legality") != "" || delta > 0 && (!quote.TryGetProperty("stock",out var stock)||!stock.TryGetDouble(out var available)||!double.IsFinite(available)||available<delta)) throw new InvalidOperationException("The verified legal cargo, price or stock does not permit this order");
if (!formal && commodity == 128066403 && (delta < 0 && before != 16 || delta > 0 && before != 15)) throw new InvalidOperationException("Limpet test must preserve the agreed 16t baseline");
var generation = ++commerceReadGeneration; commerceRead = NewCommerceRead(frame, market, generation);
commerceRead.kind = 1; commerceRead.commodity = commodity; commerceRead.price = price; commerceRead.delta = delta; commerceRead.before = before; commerceRead.after = checked(before + delta); commerceRead.totalBefore = total; commerceRead.creditsBefore = observed.credits; commerceRead.queued = GetTickCount64();
commerceRead.formal=formal;commerceRead.capacityBound=checked((int)observed.capacity);if(formal)SetCommerceCommodity(ref commerceRead,name);
commerceOperationKeys.Add(key, generation); commerceReadBody = "";
return Obj(("generation", generation), ("queued", true), ("unitPrice", price));
}
}
private static int CommerceInventoryFixture(string json, uint commodity)
{
using var value = JsonDocument.Parse(json); return CommerceInventoryCount(value.RootElement, commodity);
}
private static bool CommerceReceiptFixture(in CommerceReadRequest work, string json, bool service = false)
{
var bytes = System.Text.Encoding.UTF8.GetBytes(json);
fixed (byte* body = bytes) return service ? ValidateOriginalServiceMutation(work, body, (nuint)bytes.Length) : ValidateCommerceMutationBody(work, body, (nuint)bytes.Length);
}
internal static uint CommerceProtocolSelfTest()
{
// These fixtures exercise the migrated implementation itself, without game memory, network requests, or input.
lock (commerceReadLock)
{
var saved = commerceRead; var savedBody = commerceReadBody; var savedReconciliation = commerceReconciliationRequired; var savedKeys = commerceOperationKeys.ToArray();
uint passed = 0;
try
{
if (CommerceCommodityName(128066403) == "drones" && CommerceCommodityName(0) == "" && CommerceInventoryFixture("{\"inventory\":[{\"commodity\":\"DRONES\",\"qty\":15},{\"commodity\":\"drones\",\"qty\":1},{\"commodity\":\"scrap\",\"qty\":2}]}", 128066403) == 16) passed |= 1;
if (CommerceInventoryFixture("{\"inventory\":[{\"commodity\":\"drones\",\"qty\":16,\"stolen\":true}]}", 128066403) == -1 && CommerceInventoryFixture("{\"inventory\":[{\"commodity\":\"drones\",\"qty\":16,\"marked\":1}]}", 128066403) == -1) passed |= 2;
if (CommerceInventoryFixture("{\"inventory\":[{\"commodity\":\"drones\",\"qty\":1024},{\"commodity\":\"drones\",\"qty\":1}]}", 128066403) == -1 && CommerceInventoryFixture("{\"inventory\":[{\"commodity\":\"drones\",\"qty\":-1}]}", 128066403) == -1 && CommerceInventoryFixture("{\"inventory\":[]}", 0) == -1) passed |= 4;
var work = new CommerceReadRequest { commodity = 128066403, delta = 1, before = 15, after = 16, creditsBefore = 1000, price = 100 };
if (CommerceReceiptFixture(work, "{\"credits\":900,\"inventory\":[{\"commodity\":\"drones\",\"qty\":16}]}") && !CommerceReceiptFixture(work, "{\"credits\":899,\"inventory\":[{\"commodity\":\"drones\",\"qty\":16}]}") && !CommerceReceiptFixture(work, "{\"error\":\"rejected\",\"credits\":900,\"inventory\":[{\"commodity\":\"drones\",\"qty\":16}]}")&&CommerceProfileSelfTest()&&CommerceNonBlockingFixture()&&FormalCommerceFixture()) passed |= 8;
work.delta = -1; work.after = 15;
if (CommerceReceiptFixture(work, "{\"credits\":1100,\"inventory\":[{\"commodity\":\"drones\",\"qty\":15}]}") && !CommerceReceiptFixture(work, "{\"credits\":1100,\"inventory\":[{\"commodity\":\"drones\",\"qty\":16}]}")) passed |= 16;
work.kind = 7; work.rearmCount = 1; var target = new RearmTarget { clip = 8, hopper = 24 }; "Hardpoint1"u8.CopyTo(new Span<byte>(target.slot, 48)); work.rearm[0] = target;
if (CommerceReceiptFixture(work, "{\"credits\":900,\"ship\":{\"modules\":{\"Hardpoint1\":{\"module\":{\"ammo\":{\"clip\":8,\"hopper\":24}}}}}}", true) && !CommerceReceiptFixture(work, "{\"credits\":900,\"ship\":{\"modules\":{\"Hardpoint1\":{\"module\":{\"ammo\":{\"clip\":8,\"hopper\":23}}}}}}", true)) passed |= 32;
commerceRead = new() { generation = 91, market = 42, state = 3, kind = 1, mutationSent = true, clientApplied = true, serviceReplyVerified = true, creditsResult = 900, creditsBefore = 1000, commodity = 128066403, price = 100, delta = 1, before = 15, after = 16, totalBefore = 15 };
commerceReadBody = "{\"credits\":900,\"commodities\":{\"1\":{\"id\":1,\"name\":\"first\",\"buyPrice\":10},\"2\":{\"id\":2,\"name\":\"second\",\"buyPrice\":20}},\"inventory\":[{\"commodity\":\"drones\",\"qty\":15},{\"commodity\":\"drones\",\"qty\":1},{\"commodity\":\"drones\",\"qty\":7,\"stolen\":true}],\"prices\":{\"fuel\":{\"costToRefuel\":100,\"fuelLevel\":3,\"fuelCapacity\":4},\"repair\":{\"costToRepair\":10}}}";
commerceReconciliationRequired = false;
using var statusQuery = JsonDocument.Parse("{\"generation\":91,\"offset\":1,\"maximum\":1}");
var status = CommerceReadCall("commerce.market.status", statusQuery.RootElement, default);
if (status["commodityCount"]!.GetValue<uint>() == 2 && status["commodities"]!.AsArray().Count == 1 && status["commodities"]![0]!["name"]!.GetValue<string>() == "second" && status["cargo"]!["drones"]!.GetValue<int>() == 16 && status["cargoAfter"]!.GetValue<int>() == 16 && status["mutationsSent"]!.GetValue<int>() == 1 && status["fuelPrices"]!["costToRefuel"]!.GetValue<int>() == 100 && status["servicePrices"]!["repair"]!["costToRepair"]!.GetValue<int>() == 10) passed |= 64;
commerceOperationKeys.Clear(); commerceOperationKeys.Add(new string('a', 32), 91); commerceOperationKeys.Add(new string('b', 32), 90);
using var knownQuery = JsonDocument.Parse("{\"operationKey\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"}"); using var staleQuery = JsonDocument.Parse("{\"operationKey\":\"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"}"); using var unknownQuery = JsonDocument.Parse("{\"operationKey\":\"cccccccccccccccccccccccccccccccc\"}");
var known = CommerceReadCall("commerce.operation.status", knownQuery.RootElement, default); var stale = CommerceReadCall("commerce.operation.status", staleQuery.RootElement, default); var unknown = CommerceReadCall("commerce.operation.status", unknownQuery.RootElement, default);
if (known["known"]!.GetValue<bool>() && known["receiptAvailable"]!.GetValue<bool>() && stale["known"]!.GetValue<bool>() && !stale["receiptAvailable"]!.GetValue<bool>() && stale["reconciliationRequired"]!.GetValue<bool>() && !unknown["known"]!.GetValue<bool>() && unknown["mutationsSent"]!.GetValue<int>() == 0) passed |= 128;
}
catch { }
finally { commerceRead = saved; commerceReadBody = savedBody; commerceReconciliationRequired = savedReconciliation; commerceOperationKeys.Clear(); foreach (var item in savedKeys) commerceOperationKeys.Add(item.Key, item.Value); }
return passed;
}
}
}
using System.Text.Json;
using System.Text.Json.Nodes;
using static XFEEDToolBox.Plugin.JsonWire;
namespace XFEEDToolBox.Plugin;
internal static unsafe partial class Navigation
{
private static string CommerceCommodityName(uint id) => id switch
{
128066403 => "drones", 128049244 => "biowaste", 128049202 => "hydrogenfuel", 128049248 => "scrap",
128049172 => "uranium",128049189 => "coffee",128049197 => "polymers",128673851 => "liquidoxygen", _ => ""
};
private static bool CommerceBatchCommodity(uint commodity)=>commodity is 128049172 or 128049189 or 128049197 or 128673851;
private static bool CommerceExpectedCredits(in CommerceReadRequest work,out ulong expected)
{
expected=0;
if(work.delta==0||work.price==0||(!work.formal&&(work.delta is <-192 or >192||work.price>100000))
||work.formal&&(work.capacityBound<=0||Math.Abs((long)work.delta)>work.capacityBound||!FormalCommodityName(CommerceExpectedCommodity(work))))return false;
var cost=checked((ulong)Math.Abs((long)work.delta)*work.price);
if(work.delta>0){if(work.creditsBefore<cost)return false;expected=work.creditsBefore-cost;return true;}
if(work.creditsBefore>ulong.MaxValue-cost)return false;expected=work.creditsBefore+cost;return true;
}
private static int CommerceInventoryCount(JsonElement response, uint commodity,string? expectedName=null)
{
if (!response.TryGetProperty("inventory", out var inventory) || inventory.ValueKind != JsonValueKind.Array) return -1;
var name = expectedName??CommerceCommodityName(commodity); if (name.Length == 0) return -1; var total = 0; var maximum=expectedName is null?1024:int.MaxValue;
foreach (var row in inventory.EnumerateArray())
{
if (row.ValueKind != JsonValueKind.Object || !row.TryGetProperty("commodity", out var item) || item.ValueKind != JsonValueKind.String || !row.TryGetProperty("qty", out var quantity) || !quantity.TryGetInt32(out var count)) return -1;
if (!item.GetString()!.Equals(name, StringComparison.OrdinalIgnoreCase)) continue;
if (row.Bool("stolen") || row.Int("marked") != 0 || count < 0 || count>maximum || total > maximum - count) return -1;
total += count;
}
return total;
}
private static bool StartCommerceMutation(in Snapshot frame, ref CommerceReadRequest work, nuint context)
{
try
{
if (work.kind != 1 || work.mutationSent || !CommerceExpectedCredits(work,out _)) return false;
var cargo = RegisteredComponent(Pointer(frame.life + 0xC8) - 0x30, Read<uint>(image + 0x5F2F25C));
if (GameRoute(frame, out var pilot) == 0) return false;
var wallet = RegisteredComponent(Pointer(pilot + 0xC8) - 0x30, Read<uint>(image + 0x5F59D90));
if (cargo == 0 || Pointer(cargo) != image + 0x530B858 || Pointer(Pointer(cargo) + 0x140) != image + 0x2A686B0 || wallet == 0 || Pointer(wallet) != image + 0x5295868 || Pointer(Pointer(wallet) + 0xA0) != image + 0x2591C70) return false;
var observed = ReadCommerceShipInventory(frame);
if (!observed.verified || observed.credits != work.creditsBefore || Read<int>(cargo + 0x28C) != work.totalBefore || work.delta > 0 && Read<int>(cargo + 0x288) < work.totalBefore + work.delta) return false;
if (Pointer(Pointer(wallet) + 0x28) != image + 0x25650B0 || work.delta>0&&Call(image + 0x25650B0, wallet) < (ulong)work.delta*work.price) return false;
var storage = (nuint)Call(image + 0x53A690, 0xF8); if (storage == 0) return false;
work.request = (nuint)Call(image + 0x1E85E20, storage, context);
if (work.request == 0 || Pointer(work.request) != image + 0x511F9D0) return false;
var emptyCargoTransfers = stackalloc ulong[4]; new Span<ulong>(emptyCargoTransfers, 4).Clear();
// A dispatched original server transaction is never sent again after an uncertain response.
work.mutationSent = true;
Call(image + 0x3437150, work.request, work.market, work.commodity, 0, 0, work.price,
unchecked((ulong)work.delta), unchecked((ulong)work.after), (ulong)(nuint)emptyCargoTransfers, ulong.MaxValue);
return true;
}
catch (Exception error) { work.error = CommerceExceptionCode(error); return false; }
}
private static bool ValidateCommerceMutationBody(in CommerceReadRequest work, byte* body, nuint length)
{
using var document = JsonDocument.Parse(new ReadOnlySpan<byte>(body, checked((int)length)).ToArray());
var response = document.RootElement;
return CommerceExpectedCredits(work,out var expected)&&!response.TryGetProperty("error", out _) && response.TryGetProperty("credits", out var credits) && credits.GetUInt64() == expected && CommerceInventoryCount(response, work.commodity,CommerceExpectedCommodity(work)) == work.after;
}
private static bool ApplyCommerceMutation(in Snapshot frame, ref CommerceReadRequest work, byte* body, nuint length)
{
try
{
if (GetCurrentThreadId() != owningThread || !frame.valid || !frame.healthValid || frame.healthEmergency || frame.hullFraction<=0 || frame.entity != work.entity || frame.life != work.life || controllerEpoch != work.epoch || !CommerceDocked(frame, work.market)) return false;
var cargo = RegisteredComponent(Pointer(frame.life + 0xC8) - 0x30, Read<uint>(image + 0x5F2F25C));
if (GameRoute(frame, out var pilot) == 0) return false;
var wallet = RegisteredComponent(Pointer(pilot + 0xC8) - 0x30, Read<uint>(image + 0x5F59D90));
if (cargo == 0 || Pointer(cargo) != image + 0x530B858 || wallet == 0 || Pointer(wallet) != image + 0x5295868) return false;
var text = OriginalServiceText(body, length); if (text == 0) return false;
try
{
Call(image + 0x2A686B0, cargo, (ulong)(nuint)(&text), 1, 1, 1, 0);
Call(image + 0x2591C70, wallet, (ulong)(nuint)(&text), unchecked((ulong)(-(long)work.price * work.delta)), 0, 0);
var actual = ReadCommerceShipInventory(frame); if(!CommerceExpectedCredits(work,out var expected))return false;
work.creditsResult = expected; work.serviceReplyVerified = true;
work.clientApplied = actual.verified && actual.count == (uint)(work.totalBefore + work.delta) && actual.credits == expected;
}
finally { Call(image + 0x204010, (ulong)(nuint)(&text)); }
return work.clientApplied;
}
catch (Exception error) { work.error = CommerceExceptionCode(error); return false; }
}
private static JsonObject CommerceTradeCall(JsonElement request, in Snapshot frame,bool batch=false,bool formal=false)
{
var market = request.Required("marketId").GetUInt32(); var commodity = request.Required("commodityId").GetUInt32();
var delta = request.Required("quantityDelta").GetInt32(); var key = request.Required("operationKey").GetString()!;
var invalidOrder=formal?commodity==0||delta==0:batch? !CommerceBatchCommodity(commodity)||delta==0||delta is <-192 or >192:
CommerceCommodityName(commodity).Length==0||CommerceBatchCommodity(commodity)||delta is not(1 or -1);
if (!CommerceOperationKey(key)||invalidOrder)
throw new InvalidOperationException(batch?"Only identified CG commodity batches within 192t are available":"Only identified 1t low-price acceptance orders are available");
if (!Enabled() || !inGame || !frame.valid || !frame.healthValid || frame.healthEmergency || frame.hullFraction<=0 || GetTickCount64() - frame.tick > 200 || !CommerceDocked(frame, market)) throw new InvalidOperationException("Fresh healthy docked ship required for original market order");
lock (commerceReadLock)
{
if (commerceOperationKeys.TryGetValue(key, out var previous))
{
if (previous != commerceRead.generation) throw new InvalidOperationException("Order was already submitted; reconcile its actual result before continuing");
return Obj(("generation", previous), ("duplicate", true));
}
if (commerceReconciliationRequired) throw new InvalidOperationException("An uncertain order requires a fresh game session to reconcile inventory and credits; no order was retried");
if (commerceOperationKeys.Count >= CommerceKeyCapacity(formal,batch) || commerceRead.state != 3 || commerceRead.kind!=0 || commerceRead.market != market || commerceRead.entity != frame.entity || commerceRead.life != frame.life || GetTickCount64() - commerceRead.observed > 60000) throw new InvalidOperationException("Read the current original market and cargo before sending an order");
using var document = JsonDocument.Parse(commerceReadBody); var response = document.RootElement;
var quote = response.Required("commodities").Required(commodity.ToString(System.Globalization.CultureInfo.InvariantCulture));
var observed = ReadCommerceShipInventory(frame); if (!observed.verified) throw new InvalidOperationException("Actual original wallet and cargo are unavailable");
if(!QuotedWalletMatches(response,observed.credits))
throw new InvalidOperationException("Original server wallet differs from the actual client; refresh the original profile before submitting an order");
var price = quote.Required(delta > 0 ? "buyPrice" : "sellPrice").GetUInt32(); var before = CommerceInventoryCount(response, commodity);
var total = 0;
foreach (var row in CommerceRows(response.Required("inventory")))
{
var quantity = row.Required("qty").GetInt32(); if (quantity < 0 || total > int.MaxValue - quantity) throw new InvalidOperationException("Unverified cargo count"); total += quantity;
}
var debit=delta>0?checked((ulong)delta*price):0;
var name=quote.Str("name");
if(formal){if(!FormalCommodityName(name))throw new InvalidOperationException("Original commodity name required");before=CommerceInventoryCount(response,commodity,name);}
if (total != observed.count || observed.credits < debit || before < 0 || (long)before + delta < 0 || (long)total+delta<0 || (long)total+delta>observed.capacity || price == 0 || !formal&&price > (batch?100000u:500u) || quote.Str("legality") != "" || delta > 0 && (!quote.TryGetProperty("stock",out var stock)||!stock.TryGetDouble(out var available)||!double.IsFinite(available)||available<delta)) throw new InvalidOperationException("The verified legal cargo, price or stock does not permit this order");
if (!formal && commodity == 128066403 && (delta < 0 && before != 16 || delta > 0 && before != 15)) throw new InvalidOperationException("Limpet test must preserve the agreed 16t baseline");
var generation = ++commerceReadGeneration; commerceRead = NewCommerceRead(frame, market, generation);
commerceRead.kind = 1; commerceRead.commodity = commodity; commerceRead.price = price; commerceRead.delta = delta; commerceRead.before = before; commerceRead.after = checked(before + delta); commerceRead.totalBefore = total; commerceRead.creditsBefore = observed.credits; commerceRead.queued = GetTickCount64();
commerceRead.formal=formal;commerceRead.capacityBound=checked((int)observed.capacity);if(formal)SetCommerceCommodity(ref commerceRead,name);
commerceOperationKeys.Add(key, generation); commerceReadBody = "";
return Obj(("generation", generation), ("queued", true), ("unitPrice", price));
}
}
private static int CommerceInventoryFixture(string json, uint commodity)
{
using var value = JsonDocument.Parse(json); return CommerceInventoryCount(value.RootElement, commodity);
}
private static bool CommerceReceiptFixture(in CommerceReadRequest work, string json, bool service = false)
{
var bytes = System.Text.Encoding.UTF8.GetBytes(json);
fixed (byte* body = bytes) return service ? ValidateOriginalServiceMutation(work, body, (nuint)bytes.Length) : ValidateCommerceMutationBody(work, body, (nuint)bytes.Length);
}
internal static uint CommerceProtocolSelfTest()
{
// These fixtures exercise the migrated implementation itself, without game memory, network requests, or input.
lock (commerceReadLock)
{
var saved = commerceRead; var savedBody = commerceReadBody; var savedReconciliation = commerceReconciliationRequired; var savedKeys = commerceOperationKeys.ToArray();
uint passed = 0;
try
{
if (CommerceCommodityName(128066403) == "drones" && CommerceCommodityName(0) == "" && CommerceInventoryFixture("{\"inventory\":[{\"commodity\":\"DRONES\",\"qty\":15},{\"commodity\":\"drones\",\"qty\":1},{\"commodity\":\"scrap\",\"qty\":2}]}", 128066403) == 16) passed |= 1;
if (CommerceInventoryFixture("{\"inventory\":[{\"commodity\":\"drones\",\"qty\":16,\"stolen\":true}]}", 128066403) == -1 && CommerceInventoryFixture("{\"inventory\":[{\"commodity\":\"drones\",\"qty\":16,\"marked\":1}]}", 128066403) == -1) passed |= 2;
if (CommerceInventoryFixture("{\"inventory\":[{\"commodity\":\"drones\",\"qty\":1024},{\"commodity\":\"drones\",\"qty\":1}]}", 128066403) == -1 && CommerceInventoryFixture("{\"inventory\":[{\"commodity\":\"drones\",\"qty\":-1}]}", 128066403) == -1 && CommerceInventoryFixture("{\"inventory\":[]}", 0) == -1) passed |= 4;
var work = new CommerceReadRequest { commodity = 128066403, delta = 1, before = 15, after = 16, creditsBefore = 1000, price = 100 };
if (CommerceReceiptFixture(work, "{\"credits\":900,\"inventory\":[{\"commodity\":\"drones\",\"qty\":16}]}") && !CommerceReceiptFixture(work, "{\"credits\":899,\"inventory\":[{\"commodity\":\"drones\",\"qty\":16}]}") && !CommerceReceiptFixture(work, "{\"error\":\"rejected\",\"credits\":900,\"inventory\":[{\"commodity\":\"drones\",\"qty\":16}]}")&&CommerceProfileSelfTest()&&CommerceNonBlockingFixture()&&FormalCommerceFixture()) passed |= 8;
work.delta = -1; work.after = 15;
if (CommerceReceiptFixture(work, "{\"credits\":1100,\"inventory\":[{\"commodity\":\"drones\",\"qty\":15}]}") && !CommerceReceiptFixture(work, "{\"credits\":1100,\"inventory\":[{\"commodity\":\"drones\",\"qty\":16}]}")) passed |= 16;
work.kind = 7; work.rearmCount = 1; var target = new RearmTarget { clip = 8, hopper = 24 }; "Hardpoint1"u8.CopyTo(new Span<byte>(target.slot, 48)); work.rearm[0] = target;
if (CommerceReceiptFixture(work, "{\"credits\":900,\"ship\":{\"modules\":{\"Hardpoint1\":{\"module\":{\"ammo\":{\"clip\":8,\"hopper\":24}}}}}}", true) && !CommerceReceiptFixture(work, "{\"credits\":900,\"ship\":{\"modules\":{\"Hardpoint1\":{\"module\":{\"ammo\":{\"clip\":8,\"hopper\":23}}}}}}", true)) passed |= 32;
commerceRead = new() { generation = 91, market = 42, state = 3, kind = 1, mutationSent = true, clientApplied = true, serviceReplyVerified = true, creditsResult = 900, creditsBefore = 1000, commodity = 128066403, price = 100, delta = 1, before = 15, after = 16, totalBefore = 15 };
commerceReadBody = "{\"credits\":900,\"commodities\":{\"1\":{\"id\":1,\"name\":\"first\",\"buyPrice\":10},\"2\":{\"id\":2,\"name\":\"second\",\"buyPrice\":20}},\"inventory\":[{\"commodity\":\"drones\",\"qty\":15},{\"commodity\":\"drones\",\"qty\":1},{\"commodity\":\"drones\",\"qty\":7,\"stolen\":true}],\"prices\":{\"fuel\":{\"costToRefuel\":100,\"fuelLevel\":3,\"fuelCapacity\":4},\"repair\":{\"costToRepair\":10}}}";
commerceReconciliationRequired = false;
using var statusQuery = JsonDocument.Parse("{\"generation\":91,\"offset\":1,\"maximum\":1}");
var status = CommerceReadCall("commerce.market.status", statusQuery.RootElement, default);
if (status["commodityCount"]!.GetValue<uint>() == 2 && status["commodities"]!.AsArray().Count == 1 && status["commodities"]![0]!["name"]!.GetValue<string>() == "second" && status["cargo"]!["drones"]!.GetValue<int>() == 16 && status["cargoAfter"]!.GetValue<int>() == 16 && status["mutationsSent"]!.GetValue<int>() == 1 && status["fuelPrices"]!["costToRefuel"]!.GetValue<int>() == 100 && status["servicePrices"]!["repair"]!["costToRepair"]!.GetValue<int>() == 10) passed |= 64;
commerceOperationKeys.Clear(); commerceOperationKeys.Add(new string('a', 32), 91); commerceOperationKeys.Add(new string('b', 32), 90);
using var knownQuery = JsonDocument.Parse("{\"operationKey\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"}"); using var staleQuery = JsonDocument.Parse("{\"operationKey\":\"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"}"); using var unknownQuery = JsonDocument.Parse("{\"operationKey\":\"cccccccccccccccccccccccccccccccc\"}");
var known = CommerceReadCall("commerce.operation.status", knownQuery.RootElement, default); var stale = CommerceReadCall("commerce.operation.status", staleQuery.RootElement, default); var unknown = CommerceReadCall("commerce.operation.status", unknownQuery.RootElement, default);
if (known["known"]!.GetValue<bool>() && known["receiptAvailable"]!.GetValue<bool>() && stale["known"]!.GetValue<bool>() && !stale["receiptAvailable"]!.GetValue<bool>() && stale["reconciliationRequired"]!.GetValue<bool>() && !unknown["known"]!.GetValue<bool>() && unknown["mutationsSent"]!.GetValue<int>() == 0) passed |= 128;
}
catch { }
finally { commerceRead = saved; commerceReadBody = savedBody; commerceReconciliationRequired = savedReconciliation; commerceOperationKeys.Clear(); foreach (var item in savedKeys) commerceOperationKeys.Add(item.Key, item.Value); }
return passed;
}
}
}