fix(battle-node): inject resultCode=1 into every scripted synchronize push

The client's OnReceived routing drops any synchronize push whose
resultCode != Success(1) — and absent counts as 0(None), which is
also dropped. Our InitNetwork ack and BattleFinish already included
resultCode=1, but the five lifecycle bodies (Matched, BattleStart,
Deal, Swap response, Ready) didn't, so the client silently dropped
every one of them.

Symptom: battle-traffic.ndjson capture showed the client receiving
InitNetwork/Matched/BattleStart, but the UI stayed at the matchmaking
screen until timeout — Matched/BattleStart were dropped at the
routing layer before they ever reached the state machine. Move the
resultCode injection into the shared EnvelopeForPush helper so every
scripted push gets it.

Caught during v1 smoke walkthrough.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-06-01 01:55:35 -04:00
parent e7dac31d52
commit 01b0c64a63

View File

@@ -149,8 +149,14 @@ public static class ScriptedLifecycle
return deck;
}
private static MsgEnvelope EnvelopeForPush(NetworkBattleUri uri, Dictionary<string, object?> body, string? bid = null) =>
new(uri,
private static MsgEnvelope EnvelopeForPush(NetworkBattleUri uri, Dictionary<string, object?> body, string? bid = null)
{
// Synchronize-push routing in the client's OnReceived drops any frame whose
// resultCode != Success (1). Absent counts as 0 (None) and is also dropped — so we
// MUST include it on every scripted push, not just InitNetwork ack / BattleFinish.
// See server-to-client.md §"Routing in OnReceived" and the matching prod captures.
body["resultCode"] = (int)ReceiveNodeResultCode.Success;
return new MsgEnvelope(uri,
ViewerId: FakeOpponentViewerId,
Uuid: "node-stub",
Bid: bid,
@@ -159,4 +165,5 @@ public static class ScriptedLifecycle
PubSeq: null,
PlaySeq: null, // OutboundSequencer.AssignAndArchive stamps this
Body: body);
}
}