diff --git a/PlatformSampleGameServer.csproj b/PlatformSampleGameServer.csproj index 4b78c4c..39db74f 100644 --- a/PlatformSampleGameServer.csproj +++ b/PlatformSampleGameServer.csproj @@ -22,6 +22,6 @@ - + diff --git a/Services/EnjinService.cs b/Services/EnjinService.cs index ca0b9b3..6749f3e 100644 --- a/Services/EnjinService.cs +++ b/Services/EnjinService.cs @@ -20,7 +20,9 @@ namespace PlatformSampleGameServer.Services; // // - There are no event subscriptions in v3. After submitting a transaction // we poll GetTransaction until State is terminal -// (Finalized | Failed | Abandoned | Timeout). +// (Finalized | Failed | Abandoned | Timeout). Finalized alone does not mean +// success: a batch extrinsic still finalizes on-chain when wrapped items +// fail to dispatch, so we also require Error to be null. // // - The daemon wallet (configured DaemonWalletAddress) owns the collection // and mints tokens to player wallets. Per-player melts and transfers are @@ -625,7 +627,11 @@ CancellationToken ct ct.ThrowIfCancellationRequested(); var query = new QueryQueryBuilder().WithGetTransaction( - new TransactionQueryBuilder().WithUuid().WithState(), + new TransactionQueryBuilder() + .WithUuid() + .WithState() + .WithError() + .WithFailedItemIndexes(), _network, _chain, uuid: uuid @@ -642,6 +648,17 @@ CancellationToken ct switch (txn.State) { case TransactionStateEnum.Finalized: + // A non-null Error under Finalized means the extrinsic made it + // on-chain but one or more wrapped batch items did not dispatch, + // so the requested work did not fully happen. + if (!string.IsNullOrEmpty(txn.Error)) + { + throw new InvalidOperationException( + $"Transaction {uuid} ({description}) finalized but reported an error: " + + $"{txn.Error}{DescribeFailedItems(txn.FailedItemIndexes)}" + ); + } + _log.LogInformation( "Transaction {Uuid} ({Desc}) finalized after {Elapsed:F0}s.", uuid, @@ -655,6 +672,12 @@ CancellationToken ct case TransactionStateEnum.Timeout: throw new InvalidOperationException( $"Transaction {uuid} ({description}) ended in terminal state {txn.State}." + + ( + string.IsNullOrEmpty(txn.Error) + ? string.Empty + : $" Error: {txn.Error}" + ) + + DescribeFailedItems(txn.FailedItemIndexes) ); default: @@ -700,6 +723,13 @@ await Task.Delay( // Helpers // ------------------------------------------------------------------ + // Zero-based positions of the batch items that failed to dispatch, when the + // platform reports them (CONTINUE_ON_ERROR batches only). + private static string DescribeFailedItems(ICollection? failedItemIndexes) => + failedItemIndexes is { Count: > 0 } + ? $" Failed batch items: {string.Join(", ", failedItemIndexes)}." + : string.Empty; + private BigInteger RequireCollectionId() => _state.CollectionId ?? throw new InvalidOperationException(