fix(battle-node): respond to InitBattle/Loaded, not InitNetwork
Pushing Matched in response to InitNetwork lands it before
MatchingInitBattle() finishes wiring up the OnReceivedEvent handler
and setting status=Connect. The client's Matched-case in
ReactionReceiveUri only transitions to StartLoad when status is
Connect at the moment of receipt; otherwise the frame is silently
dropped at the state machine and the matchmaking UI never advances.
The real connect-handshake sequence (per MatchingNetworkConnectChecker
+ Matching.cs):
1. WS opens.
2. Client emits InitNetwork (cat=general).
3. Server replies InitNetwork ack → _initNetworkSuccess = true.
4. MatchingInitBattle: status=Connect; emit InitBattle; subscribe
OnReceivedEvent matching handler.
5. Server replies Matched → status=StartLoad, StartBattleLoad.
6. Asset load done → client emits Loaded.
7. Server replies BattleStart + Deal → status=Prepared, GotoBattle.
Add AwaitingInitBattle phase, gate Matched on InitBattle receipt, and
gate BattleStart+Deal on Loaded receipt. Update dispatch and
integration tests to walk the new sequence; InitBattle's wire cat is
Matching(2), not Battle(1).
Caught during v1 smoke walkthrough — battle-traffic.ndjson showed the
client receiving Matched/BattleStart at sub-millisecond gaps after
InitNetwork ack, but never advancing past matchmaking.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -40,21 +40,21 @@ public class BattleNodeFlowTests
|
||||
await using var client = new RawSocketIoTestClient(ws);
|
||||
await client.ConsumeHandshakeAsync(ct);
|
||||
|
||||
// 1. InitNetwork → expect InitNetwork ack push, then Matched, then BattleStart.
|
||||
// 1. InitNetwork → expect InitNetwork ack push only.
|
||||
await client.SendMsgAsync(MakeEnvelope(NetworkBattleUri.InitNetwork, pubSeq: 1), key, ct);
|
||||
var f1 = await client.ReceiveSynchronizeAsync(ct);
|
||||
var f2 = await client.ReceiveSynchronizeAsync(ct);
|
||||
var f3 = await client.ReceiveSynchronizeAsync(ct);
|
||||
Assert.That(f1.Uri, Is.EqualTo(NetworkBattleUri.InitNetwork));
|
||||
Assert.That(f2.Uri, Is.EqualTo(NetworkBattleUri.Matched));
|
||||
Assert.That(f3.Uri, Is.EqualTo(NetworkBattleUri.BattleStart));
|
||||
Assert.That((await client.ReceiveSynchronizeAsync(ct)).Uri, Is.EqualTo(NetworkBattleUri.InitNetwork));
|
||||
|
||||
// 2. Loaded → expect Deal.
|
||||
await client.SendMsgAsync(MakeEnvelope(NetworkBattleUri.Loaded, pubSeq: 2), key, ct);
|
||||
// 2. InitBattle → expect Matched (handler is now subscribed on the client side).
|
||||
await client.SendMsgAsync(MakeEnvelope(NetworkBattleUri.InitBattle, pubSeq: 2), key, ct);
|
||||
Assert.That((await client.ReceiveSynchronizeAsync(ct)).Uri, Is.EqualTo(NetworkBattleUri.Matched));
|
||||
|
||||
// 3. Loaded → expect BattleStart + Deal.
|
||||
await client.SendMsgAsync(MakeEnvelope(NetworkBattleUri.Loaded, pubSeq: 3), key, ct);
|
||||
Assert.That((await client.ReceiveSynchronizeAsync(ct)).Uri, Is.EqualTo(NetworkBattleUri.BattleStart));
|
||||
Assert.That((await client.ReceiveSynchronizeAsync(ct)).Uri, Is.EqualTo(NetworkBattleUri.Deal));
|
||||
|
||||
// 3. Swap with empty idxList → expect Swap response + Ready.
|
||||
await client.SendMsgAsync(MakeEnvelope(NetworkBattleUri.Swap, pubSeq: 3,
|
||||
// 4. Swap with empty idxList → expect Swap response + Ready.
|
||||
await client.SendMsgAsync(MakeEnvelope(NetworkBattleUri.Swap, pubSeq: 4,
|
||||
body: new Dictionary<string, object?> { ["idxList"] = new List<object?>() }), key, ct);
|
||||
Assert.That((await client.ReceiveSynchronizeAsync(ct)).Uri, Is.EqualTo(NetworkBattleUri.Swap));
|
||||
Assert.That((await client.ReceiveSynchronizeAsync(ct)).Uri, Is.EqualTo(NetworkBattleUri.Ready));
|
||||
@@ -62,7 +62,10 @@ public class BattleNodeFlowTests
|
||||
|
||||
private static MsgEnvelope MakeEnvelope(NetworkBattleUri uri, long pubSeq, Dictionary<string, object?>? body = null) =>
|
||||
new(uri, ViewerId: 906243102, Uuid: "udid-test", Bid: null, Try: 0,
|
||||
Cat: uri == NetworkBattleUri.InitNetwork ? EmitCategory.General : EmitCategory.Battle,
|
||||
// EmitMsgPack: InitNetwork → general(99); other matching URIs → matching(2); else battle(1).
|
||||
Cat: uri == NetworkBattleUri.InitNetwork ? EmitCategory.General
|
||||
: uri == NetworkBattleUri.InitBattle ? EmitCategory.Matching
|
||||
: EmitCategory.Battle,
|
||||
PubSeq: pubSeq, PlaySeq: null, Body: body ?? new Dictionary<string, object?>());
|
||||
|
||||
private static string MakeKey()
|
||||
|
||||
Reference in New Issue
Block a user