refactor(battle-node): drop BattleType.Scripted and the scripted-only builders
Removes the Scripted enum value, the bot's client-shaped emissions (BuildClient*), the canned opponent turn (BuildOpponent*), and OpponentTurnStartSpin. The shared server-frame builders (Matched/BattleStart/Deal/Swap/Ready + ComputeHandAfterSwap) and OpponentJudgeSpin (Bot mode) stay. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -6,13 +6,11 @@ using SVSim.BattleNode.Protocol.Bodies;
|
||||
namespace SVSim.BattleNode.Lifecycle;
|
||||
|
||||
/// <summary>
|
||||
/// v1 hand-rolled scripted opponent. Static frame builders for the five lifecycle uris
|
||||
/// (Matched / BattleStart / Deal / Swap response / Ready) plus a trivial opponent TurnStart
|
||||
/// the dispatch pushes after the player's TurnEnd. The values are templated from the TK2
|
||||
/// captures at <c>data_dumps/captures/battle-traffic_tk2_regular.ndjson</c> — anything
|
||||
/// hardcoded here came from a real prod frame, with names + provenance in
|
||||
/// <see cref="ScriptedProfiles"/>. The player-half of Matched/BattleStart now reads from
|
||||
/// <see cref="MatchContext"/> instead of <see cref="ScriptedProfiles"/>.
|
||||
/// Server-authored battle frames pushed to the client during match setup and teardown
|
||||
/// (Matched / BattleStart / Deal / Swap response / Ready) plus the post-mulligan hand
|
||||
/// computation. Used by every battle mode's handshake/mulligan dispatch arms. Hardcoded
|
||||
/// values are templated from the TK2 prod captures (battle-traffic_tk2_*.ndjson); see
|
||||
/// <see cref="ScriptedProfiles"/> for provenance.
|
||||
/// </summary>
|
||||
public static class ScriptedLifecycle
|
||||
{
|
||||
@@ -127,66 +125,6 @@ public static class ScriptedLifecycle
|
||||
IdxChangeSeed: ScriptedProfiles.ReadyIdxChangeSeed,
|
||||
Spin: ScriptedProfiles.ReadySpin));
|
||||
|
||||
// --- Client-shaped emissions (legacy scripted-bot scaffolding, pending removal) so the
|
||||
// session brokers the bot through the same handshake arms as a human. Bodies for the parameterless
|
||||
// handshake frames are ignored by the session (it reads from.Context / phase); only
|
||||
// Swap's idxList is consumed (empty = keep the dealt hand).
|
||||
|
||||
public static MsgEnvelope BuildClientInitNetwork() => ClientFrame(NetworkBattleUri.InitNetwork, EmitCategory.General);
|
||||
public static MsgEnvelope BuildClientInitBattle() => ClientFrame(NetworkBattleUri.InitBattle, EmitCategory.General);
|
||||
public static MsgEnvelope BuildClientLoaded() => ClientFrame(NetworkBattleUri.Loaded, EmitCategory.General);
|
||||
|
||||
public static MsgEnvelope BuildClientSwap() =>
|
||||
new(NetworkBattleUri.Swap,
|
||||
ViewerId: FakeOpponentViewerId,
|
||||
Uuid: WireConstants.ServerUuid,
|
||||
Bid: null,
|
||||
Try: 0,
|
||||
Cat: EmitCategory.Battle,
|
||||
PubSeq: null,
|
||||
PlaySeq: null,
|
||||
Body: new RawBody(new Dictionary<string, object?> { ["idxList"] = new List<object?>() }));
|
||||
|
||||
private static MsgEnvelope ClientFrame(NetworkBattleUri uri, EmitCategory cat) =>
|
||||
new(uri,
|
||||
ViewerId: FakeOpponentViewerId,
|
||||
Uuid: WireConstants.ServerUuid,
|
||||
Bid: null,
|
||||
Try: 0,
|
||||
Cat: cat,
|
||||
PubSeq: null,
|
||||
PlaySeq: null,
|
||||
Body: new ResultCodeOnlyBody());
|
||||
|
||||
/// <summary>
|
||||
/// First half of the v1.1 scripted opponent turn cycle: pushed after the player's
|
||||
/// TurnEnd, transitions the client into "Opponent's turn…" state. Paired with
|
||||
/// <see cref="BuildOpponentTurnEnd"/>, which immediately follows and hands control
|
||||
/// back to the player.
|
||||
/// </summary>
|
||||
public static MsgEnvelope BuildOpponentTurnStart() =>
|
||||
EnvelopeForPush(NetworkBattleUri.TurnStart,
|
||||
new OpponentTurnStartBody(Spin: ScriptedProfiles.OpponentTurnStartSpin));
|
||||
|
||||
/// <summary>
|
||||
/// Server-pushed TurnEnd transition that closes the opponent's turn and hands control
|
||||
/// back to the player. Paired with <see cref="BuildOpponentTurnStart"/> in the v1.1 loop.
|
||||
/// Wire shape from prod capture battle-traffic_tk2_regular.ndjson L18:
|
||||
/// <c>{"uri":"TurnEnd","turnState":0,"resultCode":1,"playSeq":N}</c>.
|
||||
/// </summary>
|
||||
public static MsgEnvelope BuildOpponentTurnEnd() =>
|
||||
EnvelopeForPush(NetworkBattleUri.TurnEnd, new TurnEndBody(TurnState: 0));
|
||||
|
||||
/// <summary>
|
||||
/// Server-pushed Judge frame that follows the opponent's TurnEnd and unblocks the
|
||||
/// client's <c>JudgeOperation</c> → <c>ControlTurnStartPlayer</c>, transitioning to the
|
||||
/// player's next turn. Without this frame the client hangs on "Opponent's turn…" —
|
||||
/// see <c>data_dumps/captures/battle-traffic.ndjson</c> line 14 (client emits its own
|
||||
/// Judge then waits forever).
|
||||
/// </summary>
|
||||
public static MsgEnvelope BuildOpponentJudge() =>
|
||||
EnvelopeForPush(NetworkBattleUri.Judge, new JudgeBody(Spin: ScriptedProfiles.OpponentJudgeSpin));
|
||||
|
||||
private static IReadOnlyList<PosIdx> BuildPosIdxList(IReadOnlyList<long> hand)
|
||||
{
|
||||
var list = new List<PosIdx>(hand.Count);
|
||||
|
||||
@@ -24,11 +24,6 @@ internal static class ScriptedProfiles
|
||||
public const int ReadyIdxChangeSeed = 771_335_280;
|
||||
public const int ReadySpin = 243;
|
||||
|
||||
// Generic non-zero spin that lands the client in "Opponent's turn..."
|
||||
// display state. v1 doesn't simulate the opponent — once this lands,
|
||||
// the client sits there indefinitely.
|
||||
public const int OpponentTurnStartSpin = 100;
|
||||
|
||||
/// <summary>
|
||||
/// Server-pushed Judge frame spin value. Prod varies per push (55, 175, 73, ...) — it's
|
||||
/// an animation seed, not a stateful value. Fixed at 100 here for test stability;
|
||||
|
||||
@@ -14,9 +14,4 @@ public enum BattleType
|
||||
/// path; matched only in rank rotation / rank unlimited per prod). Server is
|
||||
/// ack-only. <c>p2</c> must be null.</summary>
|
||||
Bot,
|
||||
|
||||
/// <summary>One real player; server scripts the opponent (today's v1.2
|
||||
/// behaviour, preserved as a solo testing harness). <c>p2</c> currently null;
|
||||
/// future server-driven bot config can ride on <c>p2</c>.</summary>
|
||||
Scripted,
|
||||
}
|
||||
|
||||
@@ -340,7 +340,7 @@ public class BattleSessionDispatchTests
|
||||
[Test]
|
||||
public void Pvp_TurnEndFinal_from_A_forwards_envelope_to_B_and_pushes_paired_BattleFinish()
|
||||
{
|
||||
// Same unified handling as Scripted — A is the winner, B is the loser.
|
||||
// Unified TurnEndFinal handling — A is the winner, B is the loser.
|
||||
var (s, a, b) = NewPvpSession();
|
||||
DriveToAfterReady(s, a);
|
||||
DriveToAfterReady(s, b);
|
||||
@@ -539,7 +539,7 @@ public class BattleSessionDispatchTests
|
||||
[Test]
|
||||
public void Bot_Retire_pushes_paired_BattleFinish_RetireLose_to_player_RetireWin_to_bot()
|
||||
{
|
||||
// Unified Retire/Kill dispatch — same paired push as Scripted and PvP.
|
||||
// Unified Retire/Kill dispatch — same paired push as PvP.
|
||||
// NoOpBotParticipant swallows its push.
|
||||
var (s, a, b) = NewBotSession();
|
||||
s.ComputeFrames(a, NewEnvelope(NetworkBattleUri.InitNetwork));
|
||||
|
||||
Reference in New Issue
Block a user