docs(battle-node): project README + docstrings on hosting/lifecycle

Add a per-project README in SVSim.BattleNode/ that covers:
- Architecture (the six concern folders)
- The connect-handshake sequence verified end-to-end at smoke
- A wire-format-gotchas table for the spec divergences caught during
  v1 (headers vs query for credentials, schemeless node URL with
  /socket.io/ path, required card_master_id, required resultCode=1,
  Matched in response to InitBattle not InitNetwork, EIO3 0x04 prefix
  on binary frames, FromJson conditional-expression number-boxing)
- What the v1 scripted opponent does and what is hardcoded
- A "where to extend" table for v2 work
- The full test layout and cross-references to specs/plans

Fill in XML docs on the public surface that previously had none:
- BattleNodeExtensions.AddBattleNode / UseBattleNode (DI + middleware
  wiring, including the pipeline-order note that auth runs before
  UseWebSockets)
- BattleNodeWebSocketHandler class + HandleAsync (the validation chain)
- BattleSession.ComputeResponses (the lifecycle state machine, with
  the NoStock flag's meaning)
- ScriptedLifecycle class (v1 scope, resultCode injection rule,
  pointer to the "where to extend" section)
- MatchingBridge class (mint-id + register flow)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-06-01 08:57:15 -04:00
parent 9e8ebd1b2b
commit c279b811ad
6 changed files with 240 additions and 6 deletions

View File

@@ -178,6 +178,18 @@ public sealed class BattleSession
}
}
/// <summary>
/// Pure-logic lifecycle state machine: given an inbound <see cref="MsgEnvelope"/> and the
/// current <see cref="Phase"/>, return the envelopes the session should push back AND
/// transition <see cref="Phase"/>. Extracted as an internal method so unit tests can drive
/// the state machine without standing up a real WebSocket.
/// </summary>
/// <returns>
/// Ordered list of (envelope, no-stock) tuples. <c>NoStock = true</c> means the push is a
/// control frame (ack / BattleFinish) and bypasses <see cref="OutboundSequencer"/>'s
/// playSeq assignment + Resume archive. <c>NoStock = false</c> means the push is part of
/// the ordered stream and gets a fresh playSeq.
/// </returns>
internal IReadOnlyList<(MsgEnvelope Envelope, bool NoStock)> ComputeResponses(MsgEnvelope env)
{
var result = new List<(MsgEnvelope Envelope, bool NoStock)>();