refactor(battle-node): name ComputeFrames routes as DispatchRoute
This commit is contained in:
@@ -2,6 +2,7 @@ using Microsoft.Extensions.Logging;
|
||||
using SVSim.BattleNode.Lifecycle;
|
||||
using SVSim.BattleNode.Protocol;
|
||||
using SVSim.BattleNode.Protocol.Bodies;
|
||||
using SVSim.BattleNode.Sessions.Dispatch;
|
||||
using SVSim.BattleNode.Sessions.Participants;
|
||||
|
||||
namespace SVSim.BattleNode.Sessions;
|
||||
@@ -134,10 +135,10 @@ public sealed class BattleSession
|
||||
/// <see cref="Phase"/>. Extracted so unit tests can drive the dispatch without
|
||||
/// standing up real participants.
|
||||
/// </summary>
|
||||
internal IReadOnlyList<(IBattleParticipant Target, MsgEnvelope Frame, bool NoStock)> ComputeFrames(
|
||||
internal IReadOnlyList<DispatchRoute> ComputeFrames(
|
||||
IBattleParticipant from, MsgEnvelope env)
|
||||
{
|
||||
var result = new List<(IBattleParticipant, MsgEnvelope, bool)>();
|
||||
var result = new List<DispatchRoute>();
|
||||
var other = ReferenceEquals(from, A) ? B : A;
|
||||
var phaseFrom = from as IHasHandshakePhase;
|
||||
|
||||
@@ -148,7 +149,7 @@ public sealed class BattleSession
|
||||
switch (env.Uri)
|
||||
{
|
||||
case NetworkBattleUri.InitNetwork when phaseFrom?.Phase == BattleSessionPhase.AwaitingInitNetwork:
|
||||
result.Add((from, BuildAck(NetworkBattleUri.InitNetwork), true));
|
||||
result.Add(new DispatchRoute(from, BuildAck(NetworkBattleUri.InitNetwork), true));
|
||||
phaseFrom!.Phase = BattleSessionPhase.AwaitingInitBattle;
|
||||
break;
|
||||
|
||||
@@ -180,7 +181,7 @@ public sealed class BattleSession
|
||||
case NetworkBattleUri.InitBattle
|
||||
when Type == BattleType.Bot && phaseFrom?.Phase == BattleSessionPhase.AwaitingInitBattle:
|
||||
// Ack only — NO Matched push.
|
||||
result.Add((from, BuildAck(NetworkBattleUri.InitBattle), true));
|
||||
result.Add(new DispatchRoute(from, BuildAck(NetworkBattleUri.InitBattle), true));
|
||||
phaseFrom!.Phase = BattleSessionPhase.AwaitingLoaded;
|
||||
break;
|
||||
|
||||
@@ -199,7 +200,7 @@ public sealed class BattleSession
|
||||
// Judge to sender ONLY (not broadcast — there's no real other side).
|
||||
// The client's JudgeOperation → ControlTurnStartPlayer flips back to
|
||||
// the local AI's turn after this Judge arrives.
|
||||
result.Add((from, BuildJudgeBroadcast(), false));
|
||||
result.Add(new DispatchRoute(from, BuildJudgeBroadcast(), false));
|
||||
break;
|
||||
|
||||
case NetworkBattleUri.InitBattle when phaseFrom?.Phase == BattleSessionPhase.AwaitingInitBattle:
|
||||
@@ -207,7 +208,7 @@ public sealed class BattleSession
|
||||
// selfInfo from from.Context and oppoInfo from other.Context (the scripted
|
||||
// bot's Context fixture preserves the prod-captured cosmetics that previously
|
||||
// lived in ScriptedProfiles).
|
||||
result.Add((from, ScriptedLifecycle.BuildMatched(
|
||||
result.Add(new DispatchRoute(from, ScriptedLifecycle.BuildMatched(
|
||||
from.Context, other.Context,
|
||||
from.ViewerId, other.ViewerId,
|
||||
BattleId, ScriptedProfiles.BattleSeed), false));
|
||||
@@ -222,9 +223,9 @@ public sealed class BattleSession
|
||||
// arm (its silent Loaded arm above wins the match). A per-battle coin-flip is a
|
||||
// follow-up (see plan § Out of scope).
|
||||
var turnState = ReferenceEquals(from, A) ? 0 : 1;
|
||||
result.Add((from, ScriptedLifecycle.BuildBattleStart(
|
||||
result.Add(new DispatchRoute(from, ScriptedLifecycle.BuildBattleStart(
|
||||
from.Context, other.Context, from.ViewerId, turnState), false));
|
||||
result.Add((from, ScriptedLifecycle.BuildDeal(), false));
|
||||
result.Add(new DispatchRoute(from, ScriptedLifecycle.BuildDeal(), false));
|
||||
phaseFrom!.Phase = BattleSessionPhase.AwaitingSwap;
|
||||
break;
|
||||
}
|
||||
@@ -233,7 +234,7 @@ public sealed class BattleSession
|
||||
{
|
||||
var hand = ScriptedLifecycle.ComputeHandAfterSwap(ExtractIdxList(env));
|
||||
// SwapResponse is always immediate — it completes the sender's own mulligan UI.
|
||||
result.Add((from, ScriptedLifecycle.BuildSwapResponse(hand), false));
|
||||
result.Add(new DispatchRoute(from, ScriptedLifecycle.BuildSwapResponse(hand), false));
|
||||
_postSwapHands[from] = hand;
|
||||
phaseFrom!.Phase = BattleSessionPhase.AfterReady;
|
||||
|
||||
@@ -251,7 +252,7 @@ public sealed class BattleSession
|
||||
var ready = opponent is IHasHandshakePhase && _postSwapHands.TryGetValue(opponent, out var oppoHand)
|
||||
? ScriptedLifecycle.BuildReady(_postSwapHands[p], oppoHand) // both hands known
|
||||
: ScriptedLifecycle.BuildReady(_postSwapHands[p]); // non-interactive opponent
|
||||
result.Add((p, ready, false));
|
||||
result.Add(new DispatchRoute(p, ready, false));
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -264,14 +265,14 @@ public sealed class BattleSession
|
||||
{
|
||||
var turnEndBroadcast = BuildTurnEndBroadcast();
|
||||
var judgeBroadcast = BuildJudgeBroadcast();
|
||||
result.Add((from, turnEndBroadcast, false));
|
||||
result.Add((other, turnEndBroadcast, false));
|
||||
result.Add((from, judgeBroadcast, false));
|
||||
result.Add((other, judgeBroadcast, false));
|
||||
result.Add(new DispatchRoute(from, turnEndBroadcast, false));
|
||||
result.Add(new DispatchRoute(other, turnEndBroadcast, false));
|
||||
result.Add(new DispatchRoute(from, judgeBroadcast, false));
|
||||
result.Add(new DispatchRoute(other, judgeBroadcast, false));
|
||||
}
|
||||
else if (Type == BattleType.Scripted)
|
||||
{
|
||||
result.Add((other, env, false));
|
||||
result.Add(new DispatchRoute(other, env, false));
|
||||
}
|
||||
// Bot type: no-op (NoOpBot swallows; client handles its own turn end).
|
||||
break;
|
||||
@@ -286,9 +287,9 @@ public sealed class BattleSession
|
||||
// this dispatch arm owns it. NoOpBotParticipant swallows. Phase → Terminal
|
||||
// so the RunAsync cascade doesn't synthesize a follow-up BattleFinish.
|
||||
case NetworkBattleUri.TurnEndFinal when phaseFrom?.Phase == BattleSessionPhase.AfterReady:
|
||||
result.Add((other, env, false));
|
||||
result.Add((from, BuildBattleFinish(BattleResult.LifeWin), true));
|
||||
result.Add((other, BuildBattleFinish(BattleResult.LifeLose), true));
|
||||
result.Add(new DispatchRoute(other, env, false));
|
||||
result.Add(new DispatchRoute(from, BuildBattleFinish(BattleResult.LifeWin), true));
|
||||
result.Add(new DispatchRoute(other, BuildBattleFinish(BattleResult.LifeLose), true));
|
||||
Phase = BattleSessionPhase.Terminal;
|
||||
break;
|
||||
|
||||
@@ -297,8 +298,8 @@ public sealed class BattleSession
|
||||
// proper retire codes. Bots swallow their push (no real-opponent state).
|
||||
case NetworkBattleUri.Retire:
|
||||
case NetworkBattleUri.Kill:
|
||||
result.Add((from, BuildBattleFinish(BattleResult.RetireLose), true));
|
||||
result.Add((other, BuildBattleFinish(BattleResult.RetireWin), true));
|
||||
result.Add(new DispatchRoute(from, BuildBattleFinish(BattleResult.RetireLose), true));
|
||||
result.Add(new DispatchRoute(other, BuildBattleFinish(BattleResult.RetireWin), true));
|
||||
Phase = BattleSessionPhase.Terminal;
|
||||
break;
|
||||
|
||||
@@ -317,7 +318,7 @@ public sealed class BattleSession
|
||||
case NetworkBattleUri.Judge when IsRealForwardableFromScripted(from, env):
|
||||
// Generic forwarder for scripted-bot emissions. The Scripted bot's TurnStart,
|
||||
// TurnEnd, and Judge are intended for the real participant.
|
||||
result.Add((other, env, false));
|
||||
result.Add(new DispatchRoute(other, env, false));
|
||||
break;
|
||||
|
||||
// Gameplay-frame forwarding (post-AfterReady). Unified across types:
|
||||
@@ -331,7 +332,7 @@ public sealed class BattleSession
|
||||
case NetworkBattleUri.Echo when BothAfterReady():
|
||||
case NetworkBattleUri.TurnEndActions when BothAfterReady():
|
||||
case NetworkBattleUri.JudgeResult when BothAfterReady():
|
||||
result.Add((other, env, false));
|
||||
result.Add(new DispatchRoute(other, env, false));
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
8
SVSim.BattleNode/Sessions/Dispatch/DispatchRoute.cs
Normal file
8
SVSim.BattleNode/Sessions/Dispatch/DispatchRoute.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using SVSim.BattleNode.Protocol;
|
||||
|
||||
namespace SVSim.BattleNode.Sessions.Dispatch;
|
||||
|
||||
/// <summary>One routing decision: deliver <paramref name="Frame"/> to <paramref name="Target"/>.
|
||||
/// Named form of the tuple <c>ComputeFrames</c> historically returned. <paramref name="NoStock"/>
|
||||
/// true for control frames (BattleFinish, ack) — bypasses playSeq assignment + archive.</summary>
|
||||
internal readonly record struct DispatchRoute(IBattleParticipant Target, MsgEnvelope Frame, bool NoStock);
|
||||
Reference in New Issue
Block a user