#nullable enable using System; using System.Runtime.Serialization; using SVSim.BattleEngine.Ambient; namespace SVSim.BattleEngine.Tests; /// Per-test ambient scope. Each test that touches engine statics wraps its body /// in `using var scope = new TestBattleScope();` (or with an explicit Mgr/ViewerId). /// /// The constructor enters a fresh (carrying a brand-new /// so per-test mgr/DataMgr writes never bleed across tests), then /// runs the per-ambient seeders that /// no longer does (chara ids on DataMgr, NetworkUserInfoData). Process-globals /// (card master, LoadDetail, Crossover, Certification.udid) come from /// which runs once per process. /// /// Public surface (vs. internal) so SVSim.UnitTests can reuse it via the same project /// reference in Task 7. public sealed class TestBattleScope : IDisposable { private readonly BattleAmbient.Scope _scope; public BattleAmbientContext Ctx { get; } public TestBattleScope(BattleManagerBase? mgr = null, int viewerId = 1001) { // Make sure process-globals are seeded before we enter; idempotent + cheap after first call. HeadlessEngineEnv.EnsureProcessGlobals(); Ctx = new BattleAmbientContext { Mgr = mgr, GameMgr = new GameMgr(), ViewerId = viewerId, IsForecast = true, IsRandomDraw = true, RecoveryInfo = (Wizard.BattleRecoveryInfo)FormatterServices .GetUninitializedObject(typeof(Wizard.BattleRecoveryInfo)), }; _scope = BattleAmbient.Enter(Ctx); // Per-ambient seeders MUST run AFTER scope entry so GameMgr.GetIns() resolves to this // scope's GameMgr (not a stray one). EnsureProcessGlobals used to do these writes against // the global GameMgr; now they're scoped. HeadlessEngineEnv.SeedCharaIdsOnCurrentAmbient(); HeadlessEngineEnv.SeedNetUserOnCurrentAmbient(); } public void Dispose() => _scope.Dispose(); }