From 6e85a6b2dba40203913814c31f599a32d40eb29c Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 3 Jun 2026 18:05:15 -0400 Subject: [PATCH] feat(battle-node): TurnStartHandler emits {spin:0} to opponent in PvP Co-Authored-By: Claude Opus 4.8 --- .../Dispatch/Handlers/TurnStartHandler.cs | 15 ++++++++++++--- .../Sessions/BattleSessionDispatchTests.cs | 4 +++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/SVSim.BattleNode/Sessions/Dispatch/Handlers/TurnStartHandler.cs b/SVSim.BattleNode/Sessions/Dispatch/Handlers/TurnStartHandler.cs index ff7e102..348cdcf 100644 --- a/SVSim.BattleNode/Sessions/Dispatch/Handlers/TurnStartHandler.cs +++ b/SVSim.BattleNode/Sessions/Dispatch/Handlers/TurnStartHandler.cs @@ -1,4 +1,5 @@ using SVSim.BattleNode.Protocol; +using SVSim.BattleNode.Protocol.Bodies; namespace SVSim.BattleNode.Sessions.Dispatch.Handlers; @@ -6,10 +7,18 @@ internal sealed class TurnStartHandler : IFrameHandler { public IReadOnlyList Handle(FrameDispatchContext ctx) { - // Forward the opponent's turn-open to the other side. Union of the two legacy arms: - // BothAfterReady (PvP / scripted real player) OR a scripted-bot emission (test stub path). - if (ctx.BothAfterReady() || ctx.IsScriptedBot(ctx.From)) + // PvP: the active player's TurnStart{orderList} is dropped; the opponent receives {spin} + // (spin=0 for the deterministic-turn slice) and self-generates its turn-open. + if (ctx.Type == BattleType.Pvp && ctx.BothAfterReady()) + { + var frame = ctx.Env with { Body = new OpponentTurnStartBody(Spin: 0) }; + return new[] { new DispatchRoute(ctx.Other, frame, false) }; + } + + // Scripted-bot emission (test stub path): the bot already emits the {spin} shape — forward. + if (ctx.IsScriptedBot(ctx.From)) return new[] { new DispatchRoute(ctx.Other, ctx.Env, false) }; + return Array.Empty(); } } diff --git a/SVSim.UnitTests/BattleNode/Sessions/BattleSessionDispatchTests.cs b/SVSim.UnitTests/BattleNode/Sessions/BattleSessionDispatchTests.cs index 03e07a2..3ffe5d3 100644 --- a/SVSim.UnitTests/BattleNode/Sessions/BattleSessionDispatchTests.cs +++ b/SVSim.UnitTests/BattleNode/Sessions/BattleSessionDispatchTests.cs @@ -384,7 +384,7 @@ public class BattleSessionDispatchTests } [Test] - public void Pvp_TurnStart_from_A_in_BothAfterReady_forwards_to_B() + public void Pvp_TurnStart_from_A_emits_spin0_to_B() { var (s, a, b) = NewPvpSession(); DriveToAfterReady(s, a); @@ -395,6 +395,8 @@ public class BattleSessionDispatchTests Assert.That(routes.Count, Is.EqualTo(1)); Assert.That(routes[0].Target, Is.SameAs(b)); Assert.That(routes[0].Frame.Uri, Is.EqualTo(NetworkBattleUri.TurnStart)); + var body = (SVSim.BattleNode.Protocol.Bodies.OpponentTurnStartBody)routes[0].Frame.Body; + Assert.That(body.Spin, Is.EqualTo(0)); } [Test]