diff --git a/SVSim.BattleNode/Hosting/BattleNodeWebSocketHandler.cs b/SVSim.BattleNode/Hosting/BattleNodeWebSocketHandler.cs index b89611c..770dbe2 100644 --- a/SVSim.BattleNode/Hosting/BattleNodeWebSocketHandler.cs +++ b/SVSim.BattleNode/Hosting/BattleNodeWebSocketHandler.cs @@ -193,9 +193,19 @@ public sealed class BattleNodeWebSocketHandler } case BattleType.Bot: - // Phase 3 deliverable. - _log.LogWarning("BattleType.Bot not yet supported (Phase 3); BattleId={Bid}", battleId); - return; + { + // Phase 3: real (Real, NoOp) session. Bot's pending always has P2 == null + // (per IMatchingBridge contract validation), so isP1 must be true here. The + // earlier isP1/isP2 check has already rejected viewer mismatches. + _store.RemovePending(battleId); + var botReal = new RealParticipant(ws, viewerId, pending.P1.Context, + _loggerFactory.CreateLogger()); + var noopBot = new NoOpBotParticipant(); + var botSession = new BattleSession(battleId, BattleType.Bot, botReal, noopBot, + _loggerFactory.CreateLogger()); + await botSession.RunAsync(ctx.RequestAborted); + break; + } default: _log.LogError("Unknown BattleType={Type} for BattleId={Bid}; closing WS", pending.Type, battleId);