refactor(battlenode): rename mode-id field off BattleType, add BattleModes (§D)
Behavior-preserving; 271 BattleNode/Matching/Services tests green, full solution builds. "BattleType" meant two things: the Sessions.BattleType enum (Pvp/Bot) and an int "mode id" field. Renamed the int field on MatchContext AND the BattleStartBody wire DTO to BattleModeId (wire key stays "battleType" via JsonPropertyName), so BattleType now means only the enum project-wide. New Bridge/BattleModes.cs (TakeTwo = 11) replaces every 11 literal — both prod MatchContextBuilder sites and the test fixtures/assertions. The arbitrary-passthrough 42 and bot 0 stay literal. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
14
SVSim.BattleNode/Bridge/BattleModes.cs
Normal file
14
SVSim.BattleNode/Bridge/BattleModes.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace SVSim.BattleNode.Bridge;
|
||||
|
||||
/// <summary>
|
||||
/// Known values for <see cref="MatchContext.BattleModeId"/> — the prod do_matching battle-mode id,
|
||||
/// forwarded verbatim onto the wire (<c>battleType</c> field on BattleStart). Names the otherwise
|
||||
/// magic <c>11</c>. Distinct from the <see cref="Sessions.BattleType"/> enum (Pvp/Bot), which is the
|
||||
/// session topology, not the game mode.
|
||||
/// </summary>
|
||||
public static class BattleModes
|
||||
{
|
||||
/// <summary>Take Two (TK2) — the two-pick draft mode the v1 captures were taken from. Prod
|
||||
/// rank-battle frames carry the same value (see <c>MatchContextBuilder</c>).</summary>
|
||||
public const int TakeTwo = 11;
|
||||
}
|
||||
@@ -25,5 +25,8 @@ public sealed record MatchContext(
|
||||
int FieldId,
|
||||
int IsOfficial, // 0 or 1
|
||||
|
||||
// Battle-mode hint, currently TK2 == 11. Future modes populate their own value.
|
||||
int BattleType);
|
||||
// Battle-mode hint (the prod do_matching mode id). Named BattleModeId, NOT BattleType, to
|
||||
// avoid colliding with the <see cref="Sessions.BattleType"/> enum (Pvp/Bot) — a different axis.
|
||||
// Known values live in <see cref="BattleModes"/> (currently just TK2 == 11). Future modes add
|
||||
// their own constant.
|
||||
int BattleModeId);
|
||||
|
||||
@@ -55,7 +55,7 @@ public static class ServerBattleFrames
|
||||
EnvelopeForPush(NetworkBattleUri.BattleStart,
|
||||
new BattleStartBody(
|
||||
TurnState: turnState, // First = this side goes first, Second = second. Caller decides.
|
||||
BattleType: selfCtx.BattleType,
|
||||
BattleModeId: selfCtx.BattleModeId,
|
||||
SelfInfo: new BattleStartSelfInfo(
|
||||
Rank: BattleFrameDefaults.PlayerRank,
|
||||
BattlePoint: BattleFrameDefaults.PlayerBattlePoint,
|
||||
|
||||
@@ -5,7 +5,9 @@ namespace SVSim.BattleNode.Protocol.Bodies;
|
||||
public sealed record BattleStartBody(
|
||||
[property: JsonPropertyName("turnState")]
|
||||
[property: JsonConverter(typeof(JsonNumberEnumConverter<TurnState>))] TurnState TurnState,
|
||||
[property: JsonPropertyName("battleType")] int BattleType,
|
||||
// Wire key stays "battleType" (the client's contract); the CLR name is BattleModeId so the
|
||||
// project keeps one meaning of "BattleType" — the Sessions.BattleType enum (Pvp/Bot).
|
||||
[property: JsonPropertyName("battleType")] int BattleModeId,
|
||||
[property: JsonPropertyName("selfInfo")] BattleStartSelfInfo SelfInfo,
|
||||
[property: JsonPropertyName("oppoInfo")] BattleStartOppoInfo OppoInfo,
|
||||
[property: JsonPropertyName("resultCode")] int ResultCode = (int)ReceiveNodeResultCode.Success) : IMsgBody;
|
||||
|
||||
@@ -23,7 +23,7 @@ public sealed class NoOpBotParticipant : IBattleParticipant
|
||||
ClassId: "0", CharaId: "0", CardMasterName: BotCardMasterName,
|
||||
CountryCode: "", UserName: "Bot", SleeveId: "0",
|
||||
EmblemId: "0", DegreeId: "0", FieldId: 0, IsOfficial: 0,
|
||||
BattleType: 0);
|
||||
BattleModeId: 0);
|
||||
|
||||
// Required by IBattleParticipant, but a silent bot never raises it — suppress the
|
||||
// "event is never used" warning rather than keeping a dead null-emitting method.
|
||||
|
||||
@@ -67,7 +67,7 @@ public class MatchContextBuilder : IMatchContextBuilder
|
||||
// Hardcoded v1; needs equipped-MyPageBackground lookup (see spec §Deferred).
|
||||
FieldId: 43,
|
||||
IsOfficial: viewer.Info.IsOfficial ? 1 : 0,
|
||||
BattleType: 11);
|
||||
BattleModeId: BattleModes.TakeTwo);
|
||||
}
|
||||
|
||||
public async Task<MatchContext> BuildForRankBattleAsync(long viewerId, Format format, int deckNo)
|
||||
@@ -114,6 +114,6 @@ public class MatchContextBuilder : IMatchContextBuilder
|
||||
DegreeId: degreeId,
|
||||
FieldId: 43,
|
||||
IsOfficial: viewer.Info.IsOfficial ? 1 : 0,
|
||||
BattleType: 11);
|
||||
BattleModeId: BattleModes.TakeTwo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,5 +96,5 @@ public class MatchingBridgeTests
|
||||
ClassId: "1", CharaId: "1", CardMasterName: "card_master_node_10015",
|
||||
CountryCode: "KOR", UserName: "Player", SleeveId: "3000011",
|
||||
EmblemId: "701441011", DegreeId: "300003", FieldId: 43, IsOfficial: 0,
|
||||
BattleType: 11);
|
||||
BattleModeId: BattleModes.TakeTwo);
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class WaitingRoomTests
|
||||
ClassId: "1", CharaId: "1", CardMasterName: "card_master_node_10015",
|
||||
CountryCode: "KOR", UserName: "Player", SleeveId: "0",
|
||||
EmblemId: "0", DegreeId: "0", FieldId: 0, IsOfficial: 0,
|
||||
BattleType: 11);
|
||||
BattleModeId: BattleModes.TakeTwo);
|
||||
return new RealParticipant(ws, viewerId, ctx, NullLogger<RealParticipant>.Instance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public class BattleNodeFlowTests
|
||||
ClassId: "1", CharaId: "1", CardMasterName: "card_master_node_10015",
|
||||
CountryCode: "KOR", UserName: "Player", SleeveId: "3000011",
|
||||
EmblemId: "701441011", DegreeId: "300003", FieldId: 43, IsOfficial: 0,
|
||||
BattleType: 11);
|
||||
BattleModeId: BattleModes.TakeTwo);
|
||||
|
||||
/// <summary>
|
||||
/// End-to-end: a viewer with a real TK2 run sees their drafted card-ids in the Matched
|
||||
|
||||
@@ -69,22 +69,22 @@ public class ServerBattleFramesTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildBattleStart_HasTurnStateZero_AndUsesContextBattleType()
|
||||
public void BuildBattleStart_HasTurnStateZero_AndUsesContextBattleModeId()
|
||||
{
|
||||
var env = ServerBattleFrames.BuildBattleStart(FixtureCtx(), FakeOpponentCtx(), selfViewerId: 1, turnState: TurnState.First);
|
||||
var body = (BattleStartBody)env.Body;
|
||||
Assert.That(body.TurnState, Is.EqualTo(TurnState.First));
|
||||
Assert.That(body.BattleType, Is.EqualTo(11));
|
||||
Assert.That(body.BattleModeId, Is.EqualTo(BattleModes.TakeTwo));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildBattleStart_class_chara_cardMaster_battleType_flow_from_context()
|
||||
public void BuildBattleStart_class_chara_cardMaster_battleModeId_flow_from_context()
|
||||
{
|
||||
var ctx = FixtureCtx() with
|
||||
{
|
||||
ClassId = "7", CharaId = "5000123",
|
||||
CardMasterName = "card_master_test_v2",
|
||||
BattleType = 42,
|
||||
BattleModeId = 42,
|
||||
};
|
||||
|
||||
var env = ServerBattleFrames.BuildBattleStart(ctx, FakeOpponentCtx(), selfViewerId: 1, turnState: TurnState.First);
|
||||
@@ -93,7 +93,7 @@ public class ServerBattleFramesTests
|
||||
Assert.That(body.SelfInfo.ClassId, Is.EqualTo("7"));
|
||||
Assert.That(body.SelfInfo.CharaId, Is.EqualTo("5000123"));
|
||||
Assert.That(body.SelfInfo.CardMasterName, Is.EqualTo("card_master_test_v2"));
|
||||
Assert.That(body.BattleType, Is.EqualTo(42));
|
||||
Assert.That(body.BattleModeId, Is.EqualTo(42));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -171,7 +171,7 @@ public class ServerBattleFramesTests
|
||||
ClassId: "1", CharaId: "1", CardMasterName: "card_master_node_10015",
|
||||
CountryCode: "KOR", UserName: "Player", SleeveId: "3000011",
|
||||
EmblemId: "701441011", DegreeId: "300003", FieldId: 43, IsOfficial: 0,
|
||||
BattleType: 11);
|
||||
BattleModeId: BattleModes.TakeTwo);
|
||||
|
||||
// A prod-captured opponent MatchContext fixture that the BuildMatched/BuildBattleStart
|
||||
// helpers read from for the oppo half.
|
||||
@@ -180,5 +180,5 @@ public class ServerBattleFramesTests
|
||||
ClassId: "8", CharaId: "8", CardMasterName: "card_master_node_10015",
|
||||
CountryCode: "JPN", UserName: "Opponent", SleeveId: "704141010",
|
||||
EmblemId: "400001100", DegreeId: "120027", FieldId: 5, IsOfficial: 0,
|
||||
BattleType: 0);
|
||||
BattleModeId: 0);
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ public class TypedBodyWireShapeTests
|
||||
ClassId: "1", CharaId: "1", CardMasterName: "card_master_node_10015",
|
||||
CountryCode: "KOR", UserName: "Player", SleeveId: "3000011",
|
||||
EmblemId: "701441011", DegreeId: "300003", FieldId: 43, IsOfficial: 0,
|
||||
BattleType: 11);
|
||||
BattleModeId: BattleModes.TakeTwo);
|
||||
|
||||
// Prod-captured opponent fixture — 30-card deck and the prod-captured opponent
|
||||
// cosmetics (ClassId/CharaId "8") so the wire bytes asserted below (oppoInfo classId/charaId,
|
||||
@@ -169,5 +169,5 @@ public class TypedBodyWireShapeTests
|
||||
ClassId: "8", CharaId: "8", CardMasterName: "card_master_node_10015",
|
||||
CountryCode: "JPN", UserName: "Opponent", SleeveId: "704141010",
|
||||
EmblemId: "400001100", DegreeId: "120027", FieldId: 5, IsOfficial: 0,
|
||||
BattleType: 0);
|
||||
BattleModeId: 0);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using NUnit.Framework;
|
||||
using SVSim.BattleNode.Bridge;
|
||||
using SVSim.BattleNode.Protocol;
|
||||
using SVSim.BattleNode.Protocol.Bodies;
|
||||
|
||||
@@ -13,7 +14,7 @@ public class BattleStartBodyTests
|
||||
public void Serializes_TopLevelFields_WithCorrectWireKeys()
|
||||
{
|
||||
var body = new BattleStartBody(
|
||||
TurnState: TurnState.First, BattleType: 11,
|
||||
TurnState: TurnState.First, BattleModeId: BattleModes.TakeTwo,
|
||||
SelfInfo: new BattleStartSelfInfo("10", "6270", "1", "1", "card_master_node_10015"),
|
||||
OppoInfo: new BattleStartOppoInfo("1", "0", 0, "0", "8", "8", "card_master_node_10015"));
|
||||
|
||||
|
||||
@@ -64,13 +64,13 @@ public class BattleSessionDispatchConcurrencyTests
|
||||
SelfDeckCardIds: Enumerable.Range(1, 30).Select(_ => 100_011_010L).ToList(),
|
||||
ClassId: "3", CharaId: "3", CardMasterName: "card_master_node_10015",
|
||||
CountryCode: "KOR", UserName: "PlayerA", SleeveId: "3000011",
|
||||
EmblemId: "701441011", DegreeId: "300003", FieldId: 43, IsOfficial: 0, BattleType: 11);
|
||||
EmblemId: "701441011", DegreeId: "300003", FieldId: 43, IsOfficial: 0, BattleModeId: BattleModes.TakeTwo);
|
||||
|
||||
private static MatchContext CtxB() => new(
|
||||
SelfDeckCardIds: Enumerable.Range(1, 30).Select(_ => 200_011_010L).ToList(),
|
||||
ClassId: "5", CharaId: "5", CardMasterName: "card_master_node_10015",
|
||||
CountryCode: "JPN", UserName: "PlayerB", SleeveId: "3000022",
|
||||
EmblemId: "701441022", DegreeId: "300004", FieldId: 44, IsOfficial: 0, BattleType: 11);
|
||||
EmblemId: "701441022", DegreeId: "300004", FieldId: 44, IsOfficial: 0, BattleModeId: BattleModes.TakeTwo);
|
||||
|
||||
/// <summary>Tracks the peak number of dispatches in flight at once. Records the count under a
|
||||
/// short lock, then holds (outside the lock) to widen the overlap window so a serialization bug
|
||||
|
||||
@@ -894,7 +894,7 @@ public class BattleSessionDispatchTests
|
||||
SelfDeckCardIds: Array.Empty<long>(),
|
||||
ClassId: "0", CharaId: "0", CardMasterName: "card_master_node_10015",
|
||||
CountryCode: "", UserName: "Bot", SleeveId: "0",
|
||||
EmblemId: "0", DegreeId: "0", FieldId: 0, IsOfficial: 0, BattleType: 0);
|
||||
EmblemId: "0", DegreeId: "0", FieldId: 0, IsOfficial: 0, BattleModeId: 0);
|
||||
|
||||
[Test]
|
||||
public void Bot_InitNetwork_acks_to_sender()
|
||||
@@ -1051,21 +1051,21 @@ public class BattleSessionDispatchTests
|
||||
ClassId: "3", CharaId: "3", CardMasterName: "card_master_node_10015",
|
||||
CountryCode: "KOR", UserName: "PlayerA", SleeveId: "3000011",
|
||||
EmblemId: "701441011", DegreeId: "300003", FieldId: 43, IsOfficial: 0,
|
||||
BattleType: 11);
|
||||
BattleModeId: BattleModes.TakeTwo);
|
||||
|
||||
private static MatchContext PlayerBCtx() => new(
|
||||
SelfDeckCardIds: Enumerable.Range(1, 30).Select(_ => 200_011_010L).ToList(),
|
||||
ClassId: "5", CharaId: "5", CardMasterName: "card_master_node_10015",
|
||||
CountryCode: "JPN", UserName: "PlayerB", SleeveId: "3000022",
|
||||
EmblemId: "701441022", DegreeId: "300004", FieldId: 44, IsOfficial: 0,
|
||||
BattleType: 11);
|
||||
BattleModeId: BattleModes.TakeTwo);
|
||||
|
||||
private static MatchContext FixtureCtx() => new(
|
||||
SelfDeckCardIds: Enumerable.Range(1, 30).Select(_ => 100_011_010L).ToList(),
|
||||
ClassId: "1", CharaId: "1", CardMasterName: "card_master_node_10015",
|
||||
CountryCode: "KOR", UserName: "Player", SleeveId: "3000011",
|
||||
EmblemId: "701441011", DegreeId: "300003", FieldId: 43, IsOfficial: 0,
|
||||
BattleType: 11);
|
||||
BattleModeId: BattleModes.TakeTwo);
|
||||
|
||||
private static MsgEnvelope NewEnvelope(NetworkBattleUri uri) =>
|
||||
new(uri, ViewerId: 1, Uuid: "u", Bid: null, RetryAttempt: 0,
|
||||
|
||||
@@ -24,7 +24,7 @@ public class BattleSessionStateTests
|
||||
private static MatchContext Ctx(params long[] deck) => new(
|
||||
SelfDeckCardIds: deck, ClassId: "1", CharaId: "1", CardMasterName: "cm",
|
||||
CountryCode: "KOR", UserName: "P", SleeveId: "0", EmblemId: "0", DegreeId: "0",
|
||||
FieldId: 0, IsOfficial: 0, BattleType: 11);
|
||||
FieldId: 0, IsOfficial: 0, BattleModeId: BattleModes.TakeTwo);
|
||||
|
||||
[Test]
|
||||
public void GetOrSeedDeckMap_maps_idx_1based_to_the_shuffled_order()
|
||||
|
||||
@@ -54,5 +54,5 @@ public class BattleSessionTerminateCascadeTests
|
||||
SelfDeckCardIds: Array.Empty<long>(),
|
||||
ClassId: "1", CharaId: "1", CardMasterName: "card_master_node_10015",
|
||||
CountryCode: "JP", UserName: "Test", SleeveId: "0",
|
||||
EmblemId: "0", DegreeId: "0", FieldId: 0, IsOfficial: 0, BattleType: 11);
|
||||
EmblemId: "0", DegreeId: "0", FieldId: 0, IsOfficial: 0, BattleModeId: BattleModes.TakeTwo);
|
||||
}
|
||||
|
||||
@@ -48,5 +48,5 @@ public class InMemoryBattleSessionStoreTests
|
||||
ClassId: "1", CharaId: "1", CardMasterName: "card_master_node_10015",
|
||||
CountryCode: "KOR", UserName: "Player", SleeveId: "3000011",
|
||||
EmblemId: "701441011", DegreeId: "300003", FieldId: 43, IsOfficial: 0,
|
||||
BattleType: 11);
|
||||
BattleModeId: BattleModes.TakeTwo);
|
||||
}
|
||||
|
||||
@@ -150,5 +150,5 @@ public class RealParticipantHandEventTests
|
||||
ClassId: "1", CharaId: "1", CardMasterName: "card_master_node_10015",
|
||||
CountryCode: "KOR", UserName: "Player", SleeveId: "3000011",
|
||||
EmblemId: "701441011", DegreeId: "300003", FieldId: 43, IsOfficial: 0,
|
||||
BattleType: 11);
|
||||
BattleModeId: BattleModes.TakeTwo);
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ public class RealParticipantTests
|
||||
ClassId: "1", CharaId: "1", CardMasterName: "card_master_node_10015",
|
||||
CountryCode: "KOR", UserName: "Player", SleeveId: "3000011",
|
||||
EmblemId: "701441011", DegreeId: "300003", FieldId: 43, IsOfficial: 0,
|
||||
BattleType: 11);
|
||||
BattleModeId: BattleModes.TakeTwo);
|
||||
|
||||
private static MsgEnvelope NewEnvelope(NetworkBattleUri uri) =>
|
||||
new(uri, ViewerId: 1, Uuid: "u", Bid: null, RetryAttempt: 0,
|
||||
|
||||
@@ -14,7 +14,7 @@ public class BotRosterTests
|
||||
SelfDeckCardIds: Array.Empty<long>(),
|
||||
ClassId: classId, CharaId: classId, CardMasterName: "card_master_node_10015",
|
||||
CountryCode: "JP", UserName: userName, SleeveId: "0",
|
||||
EmblemId: "0", DegreeId: "0", FieldId: 0, IsOfficial: 0, BattleType: 11);
|
||||
EmblemId: "0", DegreeId: "0", FieldId: 0, IsOfficial: 0, BattleModeId: BattleModes.TakeTwo);
|
||||
|
||||
private static async Task<BotRoster> NewRosterAsync(SVSimTestFactory factory)
|
||||
{
|
||||
|
||||
@@ -48,7 +48,7 @@ public class InProcessPairUpRankFallbackTests
|
||||
SelfDeckCardIds: Array.Empty<long>(), ClassId: "0", CharaId: "0",
|
||||
CardMasterName: "card_master_node_10015",
|
||||
CountryCode: "JP", UserName: $"P{id}", SleeveId: "0",
|
||||
EmblemId: "0", DegreeId: "0", FieldId: 0, IsOfficial: 0, BattleType: 11));
|
||||
EmblemId: "0", DegreeId: "0", FieldId: 0, IsOfficial: 0, BattleModeId: BattleModes.TakeTwo));
|
||||
|
||||
[Test]
|
||||
public async Task TK2_policy_is_PvpOnly_no_fallback_regression()
|
||||
|
||||
@@ -94,5 +94,5 @@ public class InProcessPairUpTests
|
||||
ClassId: "1", CharaId: "1", CardMasterName: "card_master_node_10015",
|
||||
CountryCode: "KOR", UserName: "Player", SleeveId: "3000011",
|
||||
EmblemId: "701441011", DegreeId: "300003", FieldId: 43, IsOfficial: 0,
|
||||
BattleType: 11);
|
||||
BattleModeId: BattleModes.TakeTwo);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ public class MatchingResolverTests
|
||||
SelfDeckCardIds: Array.Empty<long>(), ClassId: "0", CharaId: "0",
|
||||
CardMasterName: "card_master_node_10015",
|
||||
CountryCode: "JP", UserName: $"P{vid}", SleeveId: "0",
|
||||
EmblemId: "0", DegreeId: "0", FieldId: 0, IsOfficial: 0, BattleType: 11));
|
||||
EmblemId: "0", DegreeId: "0", FieldId: 0, IsOfficial: 0, BattleModeId: BattleModes.TakeTwo));
|
||||
|
||||
[Test]
|
||||
public async Task When_neither_flag_set_calls_pairUp_and_parks_returns_3002_with_empty_url()
|
||||
|
||||
@@ -64,7 +64,7 @@ public class MatchContextBuilderTests
|
||||
Assert.That(ctx.EmblemId, Is.EqualTo(emblemId.ToString()));
|
||||
Assert.That(ctx.DegreeId, Is.EqualTo(degreeId.ToString()));
|
||||
Assert.That(ctx.IsOfficial, Is.EqualTo(0));
|
||||
Assert.That(ctx.BattleType, Is.EqualTo(11));
|
||||
Assert.That(ctx.BattleModeId, Is.EqualTo(BattleModes.TakeTwo));
|
||||
// Hardcoded v1 fixtures (see spec §Deferred plumbing)
|
||||
Assert.That(ctx.CardMasterName, Is.EqualTo("card_master_node_10015"));
|
||||
Assert.That(ctx.FieldId, Is.EqualTo(43));
|
||||
@@ -131,7 +131,7 @@ public class MatchContextBuilderTests
|
||||
var ctx = await builder.BuildForRankBattleAsync(viewerId, Format.Rotation, deckNo: 1);
|
||||
|
||||
Assert.That(ctx.UserName, Is.EqualTo("Ranker"));
|
||||
Assert.That(ctx.BattleType, Is.EqualTo(11), "BattleType=11 matches the prod rank-battle wire value (same as TK2).");
|
||||
Assert.That(ctx.BattleModeId, Is.EqualTo(BattleModes.TakeTwo), "rank-battle carries the same mode id as TK2 on the wire.");
|
||||
Assert.That(ctx.ClassId, Is.Not.Null.And.Not.Empty, "ClassId from the selected deck's class.");
|
||||
Assert.That(ctx.CardMasterName, Is.EqualTo("card_master_node_10015"));
|
||||
Assert.That(ctx.FieldId, Is.EqualTo(43));
|
||||
|
||||
Reference in New Issue
Block a user