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

@@ -410,26 +410,14 @@ public class BattleManagerBase
private NetworkTouchControl _networkTouchControl;
private static BattleManagerBase _mainFallback;
private static bool _isRandomDrawFallback = false;
public static bool IsRandomDraw {
get => SVSim.BattleEngine.Ambient.BattleAmbient.Current?.IsRandomDraw ?? _isRandomDrawFallback;
set {
var c = SVSim.BattleEngine.Ambient.BattleAmbient.Current;
if (c != null) c.IsRandomDraw = value;
else _isRandomDrawFallback = value;
}
get => SVSim.BattleEngine.Ambient.BattleAmbient.Require().IsRandomDraw;
set => SVSim.BattleEngine.Ambient.BattleAmbient.Require().IsRandomDraw = value;
}
private static bool _isForecastFallback = false;
public static bool IsForecast {
get => SVSim.BattleEngine.Ambient.BattleAmbient.Current?.IsForecast ?? _isForecastFallback;
set {
var c = SVSim.BattleEngine.Ambient.BattleAmbient.Current;
if (c != null) c.IsForecast = value;
else _isForecastFallback = value;
}
get => SVSim.BattleEngine.Ambient.BattleAmbient.Require().IsForecast;
set => SVSim.BattleEngine.Ambient.BattleAmbient.Require().IsForecast = value;
}
public BattleLifeTimeSharedObject BattleLifeTimeSharedObject;
@@ -724,12 +712,15 @@ public class BattleManagerBase
public static BattleManagerBase GetIns()
{
return SVSim.BattleEngine.Ambient.BattleAmbient.Current?.Mgr ?? _mainFallback;
// Soft read: returns null when no scope is active, preserving engine call sites that pattern
// `GetIns()?.Foo ?? default` (or null-tolerant successors). Inside a scope, returns the
// scope's Mgr (which may itself be null mid-Setup — see SessionBattleEngine.SetupInternal,
// which publishes Mgr before WireMulliganPhase).
return SVSim.BattleEngine.Ambient.BattleAmbient.Current?.Mgr;
}
protected BattleManagerBase(IBattleMgrContentsCreator contentsCreator)
{
_mainFallback = this;
BattleLifeTimeSharedObject = new BattleLifeTimeSharedObject();
PublishedSkillList = new List<SkillBase>();
_contentsCreator = contentsCreator;
@@ -1488,7 +1479,6 @@ public class BattleManagerBase
GameMgr.GetIns().GetPrefabMgr().DisposeAllClonedObject();
GameMgr.GetIns().GetGameObjMgr().DisposeUIGameObj();
GameMgr.GetIns().GetPrefabMgr().AllUnLoad();
_mainFallback = null;
}
private void DisposeBattleGameObj_DestroyImmediate(GameObject obj)