test(battlenode): N1 shadow replay tracks captured battle state (Phase 2 N1)
Full single-client capture replay (cl1 send=player seat, receive=opponent seat, ts-ordered) ingests end-to-end: 33 frames, 0 rejects, 0 invariant violations at turn boundaries (leader life/PP/board/hand). Headless gaps filled per playbook (no Engine/ drift): - IsRecovery=true after construction: the engine's own headless replay mode gates the live view/UI layer off (BattleUIContainer, turn-control UI, VFX waits) while keeping the live NetworkBattleReceiver (ND4) and authoritative state. - Seed ToolboxGame.RealTimeNetworkAgent, BattleUIContainer, _backGround, and per-player NullPlayerEmotion no-ops the receive/turn cycle dereferences. - _IfaceImpl.g.cs (shim, not Engine/): BattleCardView.BattleCardIconAnimations returns a lazy non-null no-op so the opponent card-reveal icon-init (deferred VFX) doesn't NRE. - HeadlessCardMaster.Load made cumulative: it replaced the global CardMaster each call, so a Load(deck) evicted the oracle card set and broke tests run after. Adds board-state accessors (LeaderLife/Pp/HandCount/BoardCount) and CaptureReplay ts ordering. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
@@ -8,7 +9,7 @@ using SVSim.BattleNode.Protocol;
|
||||
|
||||
namespace SVSim.BattleEngine.Tests.SessionEngine
|
||||
{
|
||||
internal sealed record CapturedFrame(string Direction, string Uri, MsgEnvelope Env, string RawBody);
|
||||
internal sealed record CapturedFrame(DateTime Ts, string Direction, string Uri, MsgEnvelope Env, string RawBody);
|
||||
|
||||
/// <summary>Parses a battle_test ndjson capture into MsgEnvelopes the engine can ingest.
|
||||
///
|
||||
@@ -29,6 +30,9 @@ namespace SVSim.BattleEngine.Tests.SessionEngine
|
||||
using var doc = JsonDocument.Parse(line);
|
||||
var root = doc.RootElement;
|
||||
var direction = root.TryGetProperty("direction", out var dEl) ? dEl.GetString() ?? "" : "";
|
||||
var ts = root.TryGetProperty("ts", out var tsEl) && tsEl.ValueKind == JsonValueKind.String
|
||||
? DateTime.Parse(tsEl.GetString()!, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind)
|
||||
: default;
|
||||
if (!root.TryGetProperty("body", out var bodyEl) || bodyEl.ValueKind != JsonValueKind.Object)
|
||||
continue;
|
||||
|
||||
@@ -50,7 +54,7 @@ namespace SVSim.BattleEngine.Tests.SessionEngine
|
||||
MsgEnvelope env;
|
||||
try { env = MsgEnvelope.FromJson(normalized); }
|
||||
catch { continue; } // out-of-model / unparseable line
|
||||
frames.Add(new CapturedFrame(direction, uri, env, normalized));
|
||||
frames.Add(new CapturedFrame(ts, direction, uri, env, normalized));
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user