feat(engine-ambient): delete static fallbacks; add MultiInstanceEngineTests

Step 8 (final) of multi-instancing migration. All per-battle statics now
require a BattleAmbient scope — unwrapped writes throw InvalidOperationException
(fail-fast forcing function). MultiInstanceEngineTests proves correctness:
two parallel battles resolve independently, N=4/8/16 stress matches sequential
baseline, GameMgr.GetIns throws without scope.

Migration complete. EngineSessionGate gone. Suite fully green.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-06-07 23:19:37 -04:00
parent 9e93a7b198
commit c789d836f1
15 changed files with 449 additions and 131 deletions

View File

@@ -173,10 +173,16 @@ internal sealed class SessionBattleEngine
SeedDeck(mgr, seatADeck, isPlayer: true);
SeedDeck(mgr, seatBDeck, isPlayer: false);
WireMulliganPhase(mgr); // wire OperateReceive.OnReceiveDeal -> StartDeal (deal seats the hand)
// Publish the mgr on the per-session ambient BEFORE wiring the mulligan phase: that ctor
// chains into MulliganInfoControl.InitMulliganInfo, which reads BattleManagerBase.GetIns()
// (MulliganInfoControl.cs:259). With the fallback gone (Task 8), an unset ambient.Mgr would
// resolve to null and NRE on the very next field read. Set ambient.Mgr here so the wiring
// resolves the per-session mgr cleanly.
_mgr = mgr;
_ctx.Mgr = _mgr;
WireMulliganPhase(mgr); // wire OperateReceive.OnReceiveDeal -> StartDeal (deal seats the hand)
// Use the mgr's OWN receiver — the ctor already wired it to the mgr's OperateReceive +
// NetworkBattleData (NetworkBattleManagerBase.cs:266, non-recovery branch). This is the same
// receiver the engine's RecoveryDataHandler drives when replaying recorded frames.