diff --git a/SVSim.BattleNode/Hosting/BattleNodeWebSocketHandler.cs b/SVSim.BattleNode/Hosting/BattleNodeWebSocketHandler.cs index 008e5eb..2c00b0d 100644 --- a/SVSim.BattleNode/Hosting/BattleNodeWebSocketHandler.cs +++ b/SVSim.BattleNode/Hosting/BattleNodeWebSocketHandler.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using SVSim.BattleNode.Sessions; +using SVSim.BattleNode.Sessions.Participants; using SVSim.BattleNode.Wire; namespace SVSim.BattleNode.Hosting; @@ -104,9 +105,21 @@ public sealed class BattleNodeWebSocketHandler var ws = await ctx.WebSockets.AcceptWebSocketAsync(); _store.RemovePending(battleId); - // Phase 1: handler still constructs the old single-WS BattleSession. - // Task 9 switches to BattleSessionV2 + RealParticipant + ScriptedBotParticipant. - var session = new BattleSession(ws, battleId, viewerId, pending.P1.Context, _loggerFactory.CreateLogger()); + + // Phase 1: only Scripted is wired; Pvp + Bot land in subsequent phases. + if (pending.Type != BattleType.Scripted) + { + _log.LogWarning( + "WS upgrade for BattleId={Bid} with unsupported type={Type} (Phase 1 only handles Scripted).", + battleId, pending.Type); + return; + } + + var realParticipant = new RealParticipant(ws, viewerId, pending.P1.Context, + _loggerFactory.CreateLogger()); + var scriptedBot = new ScriptedBotParticipant(); + var session = new BattleSessionV2(battleId, pending.Type, realParticipant, scriptedBot, + _loggerFactory.CreateLogger()); await session.RunAsync(ctx.RequestAborted); } diff --git a/SVSim.BattleNode/Sessions/BattleSessionV2.cs b/SVSim.BattleNode/Sessions/BattleSessionV2.cs index 034ba63..4162695 100644 --- a/SVSim.BattleNode/Sessions/BattleSessionV2.cs +++ b/SVSim.BattleNode/Sessions/BattleSessionV2.cs @@ -42,13 +42,13 @@ public sealed class BattleSessionV2 public async Task RunAsync(CancellationToken cancellation) { - // Run both participants' inbound loops in parallel. First to complete cancels - // the session via the outer cancellation token. - using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellation); - var aTask = A.RunAsync(cts.Token); - var bTask = B.RunAsync(cts.Token); - await Task.WhenAny(aTask, bTask); - cts.Cancel(); + // Run both participants' inbound loops in parallel and wait for them all to + // complete. NoOp/Scripted bots return immediately; Real returns when the WS + // closes. Using WhenAny here would have killed the session as soon as the + // scripted bot's no-op RunAsync resolved. Phase 2's Pvp/Bot cases will need + // disconnect propagation; that's wired in their own task. + var aTask = A.RunAsync(cancellation); + var bTask = B.RunAsync(cancellation); try { await Task.WhenAll(aTask, bTask); } catch { /* swallow cancellation */ } await Task.WhenAll(