feat(battle-node): PvP gameplay-frame forwarding arms
TurnStart / PlayActions / Echo / TurnEndActions / JudgeResult from a real participant in Pvp mode forward to the other participant once BothAfterReady. Scripted's bot-burst case arms (gated on FakeOpponentViewerId) precede the PvP forwarder so they're unaffected. The bot-emission TurnStart/TurnEnd/Judge guard was tightened from `ReferenceEquals(from, A or B)` (always true) to call IsRealForwardableFromScripted directly in the `when` clause. The prior shape used `goto default` to drop non-bot senders, which would have short-circuited the new PvP forwarder for TurnStart in PvP mode.
This commit is contained in:
@@ -149,12 +149,26 @@ public sealed class BattleSession
|
||||
// TurnEnd arm above (gated on session-level Phase) also caught the bot's TurnEnd.
|
||||
// Post-migration that arm gates on the sender's per-participant Phase, which the
|
||||
// bot doesn't have, so the bot's TurnEnd now lands here.
|
||||
case NetworkBattleUri.TurnStart when ReferenceEquals(from, B) || ReferenceEquals(from, A):
|
||||
case NetworkBattleUri.TurnEnd when ReferenceEquals(from, B) || ReferenceEquals(from, A):
|
||||
case NetworkBattleUri.Judge when ReferenceEquals(from, B) || ReferenceEquals(from, A):
|
||||
// The `IsRealForwardableFromScripted` guard ensures this arm matches ONLY the
|
||||
// scripted bot's emissions (sender ViewerId == FakeOpponentViewerId) — without
|
||||
// it, a TurnStart/TurnEnd/Judge from a real participant in PvP mode would match
|
||||
// here and `goto default` would skip the PvP forwarder arm below.
|
||||
case NetworkBattleUri.TurnStart when IsRealForwardableFromScripted(from, env):
|
||||
case NetworkBattleUri.TurnEnd when IsRealForwardableFromScripted(from, env):
|
||||
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.
|
||||
if (!IsRealForwardableFromScripted(from, env)) goto default;
|
||||
result.Add((other, env, false));
|
||||
break;
|
||||
|
||||
// --- PvP gameplay forwarding (post-AfterReady).
|
||||
// Order matters: this MUST come after the FakeOpponentViewerId arms so
|
||||
// Scripted bot emissions don't fall into the PvP forwarder.
|
||||
case NetworkBattleUri.TurnStart when Type == BattleType.Pvp && BothAfterReady():
|
||||
case NetworkBattleUri.PlayActions when Type == BattleType.Pvp && BothAfterReady():
|
||||
case NetworkBattleUri.Echo when Type == BattleType.Pvp && BothAfterReady():
|
||||
case NetworkBattleUri.TurnEndActions when Type == BattleType.Pvp && BothAfterReady():
|
||||
case NetworkBattleUri.JudgeResult when Type == BattleType.Pvp && BothAfterReady():
|
||||
result.Add((other, env, false));
|
||||
break;
|
||||
|
||||
@@ -177,6 +191,13 @@ public sealed class BattleSession
|
||||
return from.ViewerId == ScriptedLifecycle.FakeOpponentViewerId;
|
||||
}
|
||||
|
||||
// Phase 2: PvP gameplay-frame forwarding is gated on BOTH sides having completed
|
||||
// the handshake (i.e. reached AfterReady). Until then, an early TurnStart/PlayActions
|
||||
// from one side has no valid recipient.
|
||||
private bool BothAfterReady() =>
|
||||
(A as IHasHandshakePhase)?.Phase == BattleSessionPhase.AfterReady &&
|
||||
(B as IHasHandshakePhase)?.Phase == BattleSessionPhase.AfterReady;
|
||||
|
||||
private MsgEnvelope BuildAck(NetworkBattleUri uri) => new(
|
||||
uri,
|
||||
ViewerId: ScriptedLifecycle.FakeOpponentViewerId,
|
||||
|
||||
Reference in New Issue
Block a user