feat(battle-node): derive Matched.seed + Ready.idxChangeSeed from master seed

InitBattle now emits Stable(master) as the shared effect seed and the master-
shuffled deck as selfDeck; Swap emits each recipient's per-side IdxChange seed.
BattleSession exposes + logs the master seed per battle for future replay.
Updated lifecycle/dispatch/integration tests (deck assertions now permutation-
based since selfDeck is shuffled).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-06-04 18:20:51 -04:00
parent 6f7fcfe28e
commit 3f5d97cb2f
8 changed files with 80 additions and 26 deletions

View File

@@ -14,7 +14,7 @@ public class ServerBattleFramesTests
{
var env = ServerBattleFrames.BuildMatched(FixtureCtx(), FakeOpponentCtx(),
selfViewerId: 906243102, oppoViewerId: 847666884,
battleId: "b", seed: BattleFrameDefaults.BattleSeed);
battleId: "b", seed: 17_548_138L, selfDeckOrder: FixtureCtx().SelfDeckCardIds);
Assert.That(env.Uri, Is.EqualTo(NetworkBattleUri.Matched));
var body = (MatchedBody)env.Body;
@@ -26,7 +26,7 @@ public class ServerBattleFramesTests
[Test]
public void BuildMatched_ContainsThirtyCardSelfDeck()
{
var env = ServerBattleFrames.BuildMatched(FixtureCtx(), FakeOpponentCtx(), 1, 2, "b", BattleFrameDefaults.BattleSeed);
var env = ServerBattleFrames.BuildMatched(FixtureCtx(), FakeOpponentCtx(), 1, 2, "b", 17_548_138L, FixtureCtx().SelfDeckCardIds);
var body = (MatchedBody)env.Body;
Assert.That(body.SelfDeck.Count, Is.EqualTo(30));
}
@@ -35,7 +35,7 @@ public class ServerBattleFramesTests
public void BuildMatched_deck_idxs_pair_1to30_with_context_card_ids()
{
var draftedDeck = Enumerable.Range(1, 30).Select(i => 200_000_000L + i).ToList();
var env = ServerBattleFrames.BuildMatched(FixtureCtx(draftedDeck), FakeOpponentCtx(), 1, 2, "b", BattleFrameDefaults.BattleSeed);
var env = ServerBattleFrames.BuildMatched(FixtureCtx(draftedDeck), FakeOpponentCtx(), 1, 2, "b", 17_548_138L, draftedDeck);
var body = (MatchedBody)env.Body;
for (int i = 0; i < 30; i++)
@@ -56,7 +56,7 @@ public class ServerBattleFramesTests
EmblemId = "888", DegreeId = "777", FieldId = 42, IsOfficial = 1,
};
var env = ServerBattleFrames.BuildMatched(ctx, FakeOpponentCtx(), 1, 2, "b", BattleFrameDefaults.BattleSeed);
var env = ServerBattleFrames.BuildMatched(ctx, FakeOpponentCtx(), 1, 2, "b", 17_548_138L, ctx.SelfDeckCardIds);
var body = (MatchedBody)env.Body;
Assert.That(body.SelfInfo.CountryCode, Is.EqualTo("JPN"));
@@ -136,11 +136,11 @@ public class ServerBattleFramesTests
}
[Test]
public void BuildReady_IncludesIdxChangeSeedAndSpin_AndUsesGivenHand()
public void BuildReady_IncludesGivenIdxChangeSeedAndSpin_AndUsesGivenHand()
{
var env = ServerBattleFrames.BuildReady(new long[] { 1, 4, 3 });
var env = ServerBattleFrames.BuildReady(new long[] { 1, 4, 3 }, idxChangeSeed: 555_000);
var body = (ReadyBody)env.Body;
Assert.That(body.IdxChangeSeed, Is.EqualTo(771_335_280));
Assert.That(body.IdxChangeSeed, Is.EqualTo(555_000));
Assert.That(body.Spin, Is.EqualTo(243));
Assert.That(body.Self[1].Idx, Is.EqualTo(4));
}
@@ -148,7 +148,7 @@ public class ServerBattleFramesTests
[Test]
public void BuildReady_two_arg_sets_oppo_to_supplied_hand()
{
var env = ServerBattleFrames.BuildReady(new long[] { 1, 4, 3 }, new long[] { 1, 2, 6 });
var env = ServerBattleFrames.BuildReady(new long[] { 1, 4, 3 }, new long[] { 1, 2, 6 }, idxChangeSeed: 555_000);
var body = (ReadyBody)env.Body;
Assert.That(body.Self.Select(p => p.Idx), Is.EqualTo(new[] { 1, 4, 3 }));
@@ -159,7 +159,7 @@ public class ServerBattleFramesTests
[Test]
public void BuildReady_one_arg_defaults_oppo_to_InitialHand()
{
var env = ServerBattleFrames.BuildReady(new long[] { 1, 4, 3 });
var env = ServerBattleFrames.BuildReady(new long[] { 1, 4, 3 }, idxChangeSeed: 555_000);
var body = (ReadyBody)env.Body;
Assert.That(body.Oppo.Select(p => p.Idx), Is.EqualTo(new[] { 1, 2, 3 }),

View File

@@ -28,7 +28,8 @@ public class TypedBodyWireShapeTests
// with "Value cannot be null. Parameter name: source". The prod wire format
// emits envelope keys (uri first) before body keys; we must too.
var env = ServerBattleFrames.BuildMatched(FixtureCtx(), FakeOpponentCtx(),
selfViewerId: 1, oppoViewerId: 2, battleId: "b", seed: BattleFrameDefaults.BattleSeed);
selfViewerId: 1, oppoViewerId: 2, battleId: "b", seed: 17_548_138L,
selfDeckOrder: FixtureCtx().SelfDeckCardIds);
var json = MsgEnvelope.ToJson(env);
var uriIdx = json.IndexOf("\"uri\":", StringComparison.Ordinal);
@@ -47,7 +48,7 @@ public class TypedBodyWireShapeTests
{
var env = ServerBattleFrames.BuildMatched(FixtureCtx(), FakeOpponentCtx(),
selfViewerId: 906243102, oppoViewerId: 847666884, battleId: "597830888107",
seed: BattleFrameDefaults.BattleSeed);
seed: 17_548_138L, selfDeckOrder: FixtureCtx().SelfDeckCardIds);
var json = MsgEnvelope.ToJson(env);
var node = JsonNode.Parse(json)!.AsObject();
@@ -137,7 +138,7 @@ public class TypedBodyWireShapeTests
[Test]
public void BuildReady_SerializesAllFieldsIncludingSeedAndSpin()
{
var env = ServerBattleFrames.BuildReady(new long[] { 1, 4, 3 });
var env = ServerBattleFrames.BuildReady(new long[] { 1, 4, 3 }, idxChangeSeed: 771_335_280);
var json = MsgEnvelope.ToJson(env);
var node = JsonNode.Parse(json)!.AsObject();