Commit Graph

8 Commits

Author SHA1 Message Date
gamer147
91472df6fc refactor(battle-node): cut handler over to BattleSessionV2 + participants
Production WS path now constructs RealParticipant + ScriptedBotParticipant
and hands them to BattleSessionV2 instead of the old single-WS
BattleSession. Wire behaviour preserved end-to-end (BattleNodeFlowTests
still pass).

Also fixes a RunAsync bug uncovered by the cutover: WhenAny would
terminate the session as soon as the scripted bot's no-op RunAsync
resolved, killing the live WS read loop before any traffic arrived.
Phase 1 semantics are simpler — wait for ALL participants. Phase 2's
Pvp disconnect propagation will revisit this.
2026-06-01 20:07:45 -04:00
gamer147
d665f88067 refactor(battle-node): unify IMatchingBridge.RegisterBattle signature
Single RegisterBattle(p1, p2?, type) with contract validation throws on
invalid combinations (Pvp requires both; Bot requires p2==null; Scripted
accepts either). PendingBattle carries Type + P1 + nullable P2. Handler
+ controller adapt; v1.2 behaviour preserved because Scripted is the
only type used today (Phase 2 adds Pvp, Phase 3 adds Bot).
2026-06-01 20:00:52 -04:00
gamer147
01f9bb722a feat(battle-node): thread MatchContext through bridge to BattleSession
IMatchingBridge.RegisterPendingBattle now takes a MatchContext; PendingBattle
carries it; BattleSession stores it. ArenaTwoPickBattleController builds ctx
from IMatchContextBuilder. ScriptedLifecycle still uses ScriptedProfiles for
the player half — Tasks 5/6 migrate the lifecycle.

Existing tests updated: MatchingBridgeTests, BattleNodeFlowTests,
InMemoryBattleSessionStoreTests, BattleSessionDispatchTests, BattleSession
PumpTests, ArenaTwoPickBattleControllerTests (which now seeds a TK2 run +
adds a no-active-run 400 case).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 12:44:42 -04:00
gamer147
2588388d9d refactor(battle-node): distinct WS auth status codes + named handler delegate 2026-06-01 11:45:50 -04:00
gamer147
c279b811ad 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>
2026-06-01 08:57:15 -04:00
gamer147
ccc9b41473 fix(battle-node): header-based WS detection in auth; split unknown-bid vs mismatch logs
Previous fix used Context.WebSockets.IsWebSocketRequest, but that
requires UseWebSockets() to have already run — and UseBattleNode
(which calls UseWebSockets) is registered AFTER UseAuthentication
in Program.cs, so the WS feature isn't installed when auth runs.
Switch to reading the raw Upgrade header, which works regardless
of middleware order.

Also split the WS handler's "Unknown battle/viewer pair" warning
into two distinct cases so we can tell unknown-BattleId from
viewer-id-mismatch (which lets us see whether the bridge stored
the right viewer or the client is encrypting a different id).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 01:17:42 -04:00
gamer147
1252f7bd35 fix(battle-node): read WS credentials from headers; skip Steam auth on WS upgrades
Two issues caught in the real-client smoke:

1) BestHTTP's SocketOptions.AdditionalQueryParams puts BattleId and
   viewerId on HTTP request HEADERS for WebSocket-only transport
   (NOT on the URL query string as the in-battle/transport.md spec
   says). Real clients therefore send them as headers; our handler
   was reading from query and rejecting every connect with "Unknown
   battle/viewer pair: <bid>/<garbage>". Fix: header-first, query-
   fallback (so the integration test still works against TestServer).

2) The Steam auth handler was running on every WS upgrade and
   throwing NotSupportedException on Request.Body.Seek (Kestrel's
   HttpRequestStream doesn't support Seek, and a WS upgrade is GET
   with Content-Length: 0 anyway). It flooded logs and added no
   value — the battle node has its own per-connection credentials.
   Skip auth when IsWebSocketRequest is true.

Spec correction for in-battle/transport.md to follow.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 01:12:21 -04:00
gamer147
1dd6a70e8d feat(battle-node): WebSocket endpoint at /socket.io/ + DI extension methods 2026-05-31 22:34:54 -04:00