diff --git a/SVSim.BattleNode/Protocol/Bodies/BattleStartBody.cs b/SVSim.BattleNode/Protocol/Bodies/BattleStartBody.cs new file mode 100644 index 0000000..31a0239 --- /dev/null +++ b/SVSim.BattleNode/Protocol/Bodies/BattleStartBody.cs @@ -0,0 +1,28 @@ +using System.Text.Json.Serialization; + +namespace SVSim.BattleNode.Protocol.Bodies; + +public sealed record BattleStartBody( + [property: JsonPropertyName("turnState")] int TurnState, + [property: JsonPropertyName("battleType")] int BattleType, + [property: JsonPropertyName("selfInfo")] BattleStartSelfInfo SelfInfo, + [property: JsonPropertyName("oppoInfo")] BattleStartOppoInfo OppoInfo, + [property: JsonPropertyName("resultCode")] int ResultCode = 1) : IMsgBody; + +public sealed record BattleStartSelfInfo( + [property: JsonPropertyName("rank")] string Rank, + [property: JsonPropertyName("battlePoint")] string BattlePoint, + [property: JsonPropertyName("classId")] string ClassId, + [property: JsonPropertyName("charaId")] string CharaId, + [property: JsonPropertyName("cardMasterName")] string CardMasterName); + +// Note: BattlePoint is int on the wire here (not string as on self) — matches the +// captured prod frame at data_dumps/captures/battle-traffic_tk2_regular.ndjson. +public sealed record BattleStartOppoInfo( + [property: JsonPropertyName("rank")] string Rank, + [property: JsonPropertyName("isMasterRank")] string IsMasterRank, + [property: JsonPropertyName("battlePoint")] int BattlePoint, + [property: JsonPropertyName("masterPoint")] string MasterPoint, + [property: JsonPropertyName("classId")] string ClassId, + [property: JsonPropertyName("charaId")] string CharaId, + [property: JsonPropertyName("cardMasterName")] string CardMasterName); diff --git a/SVSim.UnitTests/BattleNode/Protocol/Bodies/BattleStartBodyTests.cs b/SVSim.UnitTests/BattleNode/Protocol/Bodies/BattleStartBodyTests.cs new file mode 100644 index 0000000..21b7620 --- /dev/null +++ b/SVSim.UnitTests/BattleNode/Protocol/Bodies/BattleStartBodyTests.cs @@ -0,0 +1,54 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using NUnit.Framework; +using SVSim.BattleNode.Protocol.Bodies; + +namespace SVSim.UnitTests.BattleNode.Protocol.Bodies; + +[TestFixture] +public class BattleStartBodyTests +{ + [Test] + public void Serializes_TopLevelFields_WithCorrectWireKeys() + { + var body = new BattleStartBody( + TurnState: 0, BattleType: 11, + SelfInfo: new BattleStartSelfInfo("10", "6270", "1", "1", "card_master_node_10015"), + OppoInfo: new BattleStartOppoInfo("1", "0", 0, "0", "8", "8", "card_master_node_10015")); + + var node = (JsonObject)JsonSerializer.SerializeToNode(body)!; + + Assert.That(node["turnState"]!.GetValue(), Is.EqualTo(0)); + Assert.That(node["battleType"]!.GetValue(), Is.EqualTo(11)); + Assert.That(node["resultCode"]!.GetValue(), Is.EqualTo(1)); // default + } + + [Test] + public void SelfInfo_BattlePoint_IsString_OnTheWire() + { + var body = new BattleStartBody(0, 11, + new BattleStartSelfInfo("10", "6270", "1", "1", "cm"), + new BattleStartOppoInfo("1", "0", 0, "0", "8", "8", "cm")); + + var node = (JsonObject)JsonSerializer.SerializeToNode(body)!; + var selfInfo = (JsonObject)node["selfInfo"]!; + + // Wire shape: self.battlePoint is a string, oppo.battlePoint is an int. Preserved verbatim. + Assert.That(selfInfo["battlePoint"]!.GetValue(), Is.EqualTo("6270")); + } + + [Test] + public void OppoInfo_BattlePoint_IsInt_OnTheWire() + { + var body = new BattleStartBody(0, 11, + new BattleStartSelfInfo("10", "6270", "1", "1", "cm"), + new BattleStartOppoInfo("1", "0", 0, "0", "8", "8", "cm")); + + var node = (JsonObject)JsonSerializer.SerializeToNode(body)!; + var oppoInfo = (JsonObject)node["oppoInfo"]!; + + Assert.That(oppoInfo["battlePoint"]!.GetValue(), Is.EqualTo(0)); + Assert.That(oppoInfo["isMasterRank"]!.GetValue(), Is.EqualTo("0")); + Assert.That(oppoInfo["masterPoint"]!.GetValue(), Is.EqualTo("0")); + } +}