refactor(engine-ambient): GameMgr.GetIns throws Require; wrap SessionBattleEngine entry points
Step 5 of multi-instancing migration. GameMgr.GetIns() now resolves through BattleAmbient.Require() (throws when no scope active — fail-fast since engine callers unconditionally dereference). SessionBattleEngine now owns a single BattleAmbientContext, pushed via BattleAmbient.Enter at Setup/Receive/all ~30 read accessors and Debug* seams. EngineGlobalInit.WirePerSessionGameMgr extracted out of the _done-gated block: GameMgr is now per-session (ctx.GameMgr is a fresh `new()` per SessionBattleEngine), so the DataMgr chara ids + NetworkUserInfoData seeding must run every Setup, not process-once. The wiring itself is already idempotent. Without this, second-or- later sessions in a process NRE in NetworkBattleManagerBase.CreateBackgroundId. Expected state: SVSim.BattleEngine.Tests have known-failing tests that don't go through SessionBattleEngine (Task 6 wraps HeadlessFixture). SVSim.UnitTests mostly recover; residual failures (deal-frame Accepted:false in conductor integration tests) are captured in data_dumps/task5-test-output/failing-tests-after-task5-node-postwrap.txt for Task 7. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -60,6 +60,12 @@ internal static class EngineGlobalInit
|
||||
|
||||
public static void EnsureInitialized()
|
||||
{
|
||||
// GameMgr is now per-session (BattleAmbientContext.GameMgr), so its DataMgr/NetworkUserInfoData
|
||||
// wiring must run on EVERY call — once-per-process gating it (under _done) leaves a second-or-
|
||||
// later session with an unwired ctx.GameMgr and NREs in NetworkBattleManagerBase.CreateBackgroundId.
|
||||
// The wiring itself is idempotent (zero-or-null guards), so re-running is safe.
|
||||
WirePerSessionGameMgr();
|
||||
|
||||
if (_done) return;
|
||||
lock (_gate)
|
||||
{
|
||||
@@ -107,29 +113,6 @@ internal static class EngineGlobalInit
|
||||
// the postcondition is all-8-class ClassCharacterList. Idempotent (replaces Data.Master).
|
||||
InstallMaster();
|
||||
|
||||
// --- GameMgr DataMgr leader chara ids --------------------------------------------------
|
||||
// Set the backing fields directly: the public SetPlayerCharaId() also pulls MyRotation/
|
||||
// AvatarBattle info (more null statics) the resolution path doesn't need. Idempotent
|
||||
// (plain assignment); only meaningful when still 0.
|
||||
var dm = GameMgr.GetIns().GetDataMgr();
|
||||
SetFieldIfZeroOrNull(dm, "_playerCharaId", PlayerCharaId);
|
||||
SetFieldIfZeroOrNull(dm, "_enemyCharaId", EnemyCharaId);
|
||||
|
||||
// --- NetworkUserInfoData (background lookup on the network mgr's CreateBackgroundId) ----
|
||||
// NetworkBattleManagerBase.CreateBackgroundId reads
|
||||
// GameMgr.GetIns().GetNetworkUserInfoData().GetFieldId() when the RecoveryManager yields no
|
||||
// bg id. GameMgr leaves _netUser null with no lazy init; seed a no-op instance whose
|
||||
// _selfInfo carries just fieldId=1 (== ForestField, a valid background). Only seed when
|
||||
// absent so a HeadlessEngineEnv-set instance is preserved.
|
||||
if (GameMgr.GetIns().GetNetworkUserInfoData() == null)
|
||||
{
|
||||
var netUser = new NetworkUserInfoData();
|
||||
netUser.SetSelfInfo(
|
||||
new Dictionary<string, object> { ["fieldId"] = 1 },
|
||||
isWatchReplayRecovery: false);
|
||||
GameMgr.GetIns().SetNetworkUserInfoData(netUser);
|
||||
}
|
||||
|
||||
// --- Cute.Certification.udid -----------------------------------------------------------
|
||||
// The emit-path payload builder reads Certification.Udid, whose getter lazily decodes from
|
||||
// Toolbox.SavedataManager (null headless). Seed the private static backing field with a
|
||||
@@ -160,6 +143,36 @@ internal static class EngineGlobalInit
|
||||
}
|
||||
}
|
||||
|
||||
// Per-session GameMgr wiring: under the ambient seam, GameMgr.GetIns() resolves to the SESSION's
|
||||
// BattleAmbientContext.GameMgr — a fresh instance per SessionBattleEngine. So the DataMgr chara ids
|
||||
// and NetworkUserInfoData seeding must run on every Setup, not just process-once. The reflection
|
||||
// helpers below are idempotent (SetFieldIfZeroOrNull is a no-op once set; netUser is null-guarded).
|
||||
private static void WirePerSessionGameMgr()
|
||||
{
|
||||
// --- GameMgr DataMgr leader chara ids --------------------------------------------------
|
||||
// Set the backing fields directly: the public SetPlayerCharaId() also pulls MyRotation/
|
||||
// AvatarBattle info (more null statics) the resolution path doesn't need. Idempotent
|
||||
// (plain assignment); only meaningful when still 0.
|
||||
var dm = GameMgr.GetIns().GetDataMgr();
|
||||
SetFieldIfZeroOrNull(dm, "_playerCharaId", PlayerCharaId);
|
||||
SetFieldIfZeroOrNull(dm, "_enemyCharaId", EnemyCharaId);
|
||||
|
||||
// --- NetworkUserInfoData (background lookup on the network mgr's CreateBackgroundId) ----
|
||||
// NetworkBattleManagerBase.CreateBackgroundId reads
|
||||
// GameMgr.GetIns().GetNetworkUserInfoData().GetFieldId() when the RecoveryManager yields no
|
||||
// bg id. GameMgr leaves _netUser null with no lazy init; seed a no-op instance whose
|
||||
// _selfInfo carries just fieldId=1 (== ForestField, a valid background). Only seed when
|
||||
// absent so a HeadlessEngineEnv-set instance is preserved.
|
||||
if (GameMgr.GetIns().GetNetworkUserInfoData() == null)
|
||||
{
|
||||
var netUser = new NetworkUserInfoData();
|
||||
netUser.SetSelfInfo(
|
||||
new Dictionary<string, object> { ["fieldId"] = 1 },
|
||||
isWatchReplayRecovery: false);
|
||||
GameMgr.GetIns().SetNetworkUserInfoData(netUser);
|
||||
}
|
||||
}
|
||||
|
||||
// --- CardMaster (full load) ----------------------------------------------------------------------
|
||||
|
||||
// Production difference (1): enumerate EVERY card row — no want.Contains(id) filter.
|
||||
|
||||
Reference in New Issue
Block a user