engine cleanup passes 4-7 + multi-instancing ambient rip

Squashes 146 commits from battle-engine-extraction. Net: 2,045 files changed,
+11,896 / -158,687 lines. Ships engine passes 4-7 (dead-code cull, view-layer
stub, receive-path shrink) plus the Phase-5 AsyncLocal ambient deletion that
turns concurrent battles into a type-system property rather than a scope
contract.

## What landed

**Passes 4-7 (chunks 1-34):** Extended the Phase-4 const-false collapse into a
cascading cull across the skill graph, view layer, and receive-path periphery.
Six mode flags (IsWatchBattle/IsReplayBattle/IsAdmin/IsAdminWatch/IsPuzzleQuest/
IsAINetwork) became `const false`, every guarded block deleted. Field*.cs
subclass ctors + BackGroundBase + ObjectChecker culled to no-ops. Mulligan
family reworked to take a mgr param through IMulliganMgr.InitMulligan.
Emotion/Recovery/Resource clusters null-stubbed. Prediction/OperationSimulator/
skill filters converted from static ambient reads to per-mgr reads via
SkillPrm.ownerCard.SelfBattlePlayer.BattleMgr / ins.BattleMgr / this.BattleMgr.

**Phase-5 ambient rip (chunks 35-47):** Deleted BattleAmbient / BattleAmbient-
Context / TestBattleScope in full. Every per-battle mutable slot now lives on
the mgr instance itself:
  mgr.InstanceIsForecast / InstanceIsRandomDraw / InstanceRecoveryInfo /
  InstanceViewerId / InstanceNetworkAgent / GameMgr
BattleManagerBase.GetIns() returns null unconditionally; the residual static
flags + 3 façades (Certification.ViewerId, Data.BattleRecoveryInfo,
ToolboxGame.RealTimeNetworkAgent) are null-tolerant defaults kept for the
handful of engine-internal readers that still reference their types. Zero
BattleAmbient references anywhere in engine + node + tests.

Added pre-seeded GameMgr ctor overload threaded through the mgr chain
(BattleManagerBase → SingleBattleMgr / NetworkBattleManagerBase → NetworkStandard-
BattleMgr → HeadlessBattleMgr / HeadlessNetworkBattleMgr). Fixtures build a
GameMgr, seed it via HeadlessEngineEnv.SeedCharaIds/SeedNetUser, and pass it
to the mgr's ctor — no ambient reach.

Node side (SVSim.BattleNode/SessionBattleEngine): _ctx replaced with a plain
GameMgr field; 34 `using var _ambient = BattleAmbient.Enter(_ctx)` scope wraps
ripped from every accessor and mutator; EngineGlobalInit.WirePerSessionGameMgr
takes GameMgr as a param and runs from SessionBattleEngine.SetupInternal
BEFORE mgr construction.

Test side: TestBattleScope deleted; 18 fixture [SetUp]s migrated to
`HeadlessEngineEnv.EnsureProcessGlobals()`; MultiInstanceEngineTests rewritten
around per-mgr construction (GetIns() → null is the pinned invariant).

## Regression fixes

- **chunk-48** (MulliganCtrl): chunk-35's `= null` stubs on card lookups broke
  the live receive-driven Deal path (BattlePlayerBase.DrawCard NRE'd downstream
  of NetworkPlayerMulliganCtrl.StartMulliganVfx). Restored the three lookups
  via `_battlePlayer.BattleMgr.GetBattleCardIdx`. Engine tests were satisfied
  by the WireMulliganPhase seam; unit tests exposed the live-path gap.

## Ship state

- SVSim.BattleEngine.Tests: 56/56 pass, 2 skip
- SVSim.UnitTests: 1554/1554 pass (was 1523/31-fail before chunk 48)
- Solution build: 0 source warnings (40 pre-existing NU1902 MessagePack CVEs
  in SVSim.EmulatedEntrypoint, unrelated)
- Sequential PVP smoke: verified live (two back-to-back battles, no regression
  on cleanup/spinup)
- Concurrent PVP smoke: verified live

Adds tools/engine-port/ClosureAnalyzer/ — the Roslyn transitive-type-closure
analyzer needed to make future cascade cleanup safe (per feedback memory
"Engine cleanup needs closure tool" from the 2026-06-28 pass-3 failure).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-07-03 19:18:54 -04:00
parent 5c1db83967
commit 2d9a6eea4b
2045 changed files with 11704 additions and 158495 deletions

View File

@@ -86,11 +86,6 @@ public class SkillCollectionBase : IEnumerable<SkillBase>, IEnumerable
return _skillList.IndexOf(skill);
}
public int FindIndex<T>()
{
return _skillList.FindIndex((SkillBase s) => s is T);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
@@ -330,7 +325,7 @@ public class SkillCollectionBase : IEnumerable<SkillBase>, IEnumerable
{
if (processInfo == null && destroyCard.SelfBattlePlayer.Class.SkillApplyInformation.RepeatSkillTimingList.Any((RepeatSkillInfo s) => s.Timing == "when_destroy" && Skill_repeat_skill.CheckCardType(s.Target, destroyCard)))
{
vfx = new RepeatSkillEffectVfx(destroyCard.SelfBattlePlayer.Class.BattleCardView, "when_destroy", destroyCard.IsPlayer);
vfx = new RepeatSkillEffectVfx(destroyCard.SelfBattlePlayer.BattleMgr, destroyCard.SelfBattlePlayer.Class.BattleCardView, "when_destroy", destroyCard.IsPlayer);
}
AllStopRepeatSkill(destroyCard, destroyCard.SelfBattlePlayer, "when_destroy", processInfo != null, skillProcessor);
}
@@ -676,12 +671,6 @@ public class SkillCollectionBase : IEnumerable<SkillBase>, IEnumerable
return CreateProcessInfo((SkillBase s) => s.OnWhenAddToDeckStart, skillProcessor, playerInfoPair, checkerOption);
}
public SkillProcessor.ProcessInfo CreateWhenEnhance(SkillProcessor skillProcessor, BattlePlayerReadOnlyInfoPair playerInfoPair)
{
SkillConditionCheckerOption checkerOption = new SkillConditionCheckerOption();
return CreateProcessInfo((SkillBase s) => s.OnWhenEnhanceStart, skillProcessor, playerInfoPair, checkerOption);
}
public SkillProcessor.ProcessInfo CreateWhenBurialRiteOther(BattleCardBase burialRiteCard, SkillProcessor skillProcessor, BattlePlayerReadOnlyInfoPair playerInfoPair)
{
SkillConditionCheckerOption skillConditionCheckerOption = new SkillConditionCheckerOption();
@@ -946,14 +935,14 @@ public class SkillCollectionBase : IEnumerable<SkillBase>, IEnumerable
return;
}
List<SkillBase> deckSkills = activeSkills.Where((SkillBase c) => c.ConditionTargetFilter is SkillTargetDeckSelfFilter).ToList();
bool flag = GameMgr.GetIns().IsUseUnapprovedList(playerInfoPair.ReadOnlySelf.IsPlayer);
bool flag = _ownerCard.SelfBattlePlayer.BattleMgr.GameMgr.IsUseUnapprovedList(playerInfoPair.ReadOnlySelf.IsPlayer);
if (deckSkills.Any((SkillBase s) => s is Skill_summon_card || s.PreprocessList.Any((SkillPreprocessBase p) => p is SkillPreprocessTimesPerTurn && (p as SkillPreprocessTimesPerTurn).IsSameBaseCardId)))
{
DistinctDeckActiveSkillCards(flag, activeSkills, deckSkills, skillProcessor, playerInfoPair, checkerOption);
}
else if (flag)
{
List<CardDataModel> receiveCardList = (BattleManagerBase.GetIns() as NetworkBattleManagerBase).networkBattleData.GetReceiveData().GetReceiveCardList();
List<CardDataModel> receiveCardList = (_ownerCard.SelfBattlePlayer.BattleMgr as NetworkBattleManagerBase).networkBattleData.GetReceiveData().GetReceiveCardList();
if (receiveCardList != null && !receiveCardList.Where((CardDataModel c) => c.fromState == NetworkBattleDefine.NetworkCardPlaceState.Deck).Any((CardDataModel i) => i.Index == _ownerCard.Index))
{
activeSkills.RemoveAll((SkillBase s) => deckSkills.Contains(s));
@@ -969,14 +958,14 @@ public class SkillCollectionBase : IEnumerable<SkillBase>, IEnumerable
{
if (isUseUnapprovedList)
{
NetworkBattleManagerBase battleMgr = BattleManagerBase.GetIns() as NetworkBattleManagerBase;
NetworkBattleManagerBase battleMgr = _ownerCard.SelfBattlePlayer.BattleMgr as NetworkBattleManagerBase;
NetworkBattleReceiver.ReceiveData receiveData = battleMgr.networkBattleData.GetReceiveData();
bool isCheckHand = deckSkills.Any((SkillBase s) => s is Skill_draw);
List<int> isOpenIndexList = (from c in receiveData.GetReceiveCardList()
where c.IsOpen
select c.Index).ToList();
List<CardDataModel> list = (from c in receiveData.unapprovedList
where (c.fromState == NetworkBattleDefine.NetworkCardPlaceState.Deck || (c.fromState == NetworkBattleDefine.NetworkCardPlaceState.None && (GameMgr.GetIns().IsWatchBattle || battleMgr.IsRecovery))) && c.ToStateList.Count > 0 && (c.ToStateList[0] == NetworkBattleDefine.NetworkCardPlaceState.Field || (isCheckHand && c.ToStateList[0] == NetworkBattleDefine.NetworkCardPlaceState.Hand && isOpenIndexList.Contains(c.Index)))
where (c.fromState == NetworkBattleDefine.NetworkCardPlaceState.Deck || (c.fromState == NetworkBattleDefine.NetworkCardPlaceState.None && (_ownerCard.SelfBattlePlayer.BattleMgr.GameMgr.IsWatchBattle || battleMgr.IsRecovery))) && c.ToStateList.Count > 0 && (c.ToStateList[0] == NetworkBattleDefine.NetworkCardPlaceState.Field || (isCheckHand && c.ToStateList[0] == NetworkBattleDefine.NetworkCardPlaceState.Hand && isOpenIndexList.Contains(c.Index)))
where c.Index == c.skillCardIndex
select c).ToList();
CardDataModel cardDataModel = list.FirstOrDefault((CardDataModel i) => i.Index == _ownerCard.Index);
@@ -1132,21 +1121,21 @@ public class SkillCollectionBase : IEnumerable<SkillBase>, IEnumerable
ActiveSkillWhenPlayEffectInfo activeSkillWhenPlayEffectInfo = list3.Find((ActiveSkillWhenPlayEffectInfo e) => e.Index == i);
if (activeSkillWhenPlayEffectInfo != null && !_ownerCard.SelfBattlePlayer.BattleMgr.IsBattleEnd)
{
if (!BattleManagerBase.GetIns().IsRecovery)
if (!_ownerCard.SelfBattlePlayer.BattleMgr.IsRecovery)
{
switch (activeSkillWhenPlayEffectInfo.Type)
{
case WhenPlayEffectType.Berserk:
sequentialVfxPlayer.Register(new BerserkSkillActivationVfx(activeSkillWhenPlayEffectInfo.OwnerCard.BattleCardView));
sequentialVfxPlayer.Register(NullVfx.GetInstance());
break;
case WhenPlayEffectType.Awake:
sequentialVfxPlayer.Register(new AwakeSkillActivationVfx(activeSkillWhenPlayEffectInfo.OwnerCard.BattleCardView));
sequentialVfxPlayer.Register(NullVfx.GetInstance());
break;
case WhenPlayEffectType.WhenPlay:
sequentialVfxPlayer.Register(new WhenPlaySkillActivationVfx(activeSkillWhenPlayEffectInfo.OwnerCard.BattleCardView));
sequentialVfxPlayer.Register(NullVfx.GetInstance());
break;
case WhenPlayEffectType.WhenDestroy:
sequentialVfxPlayer.Register(new LoadAndPlayEffectVfx("cmn_card_destroy_3", "se_cmn_card_destroy_3", activeSkillWhenPlayEffectInfo.OwnerCard.BattleCardView.Transform, 0f));
sequentialVfxPlayer.Register(VfxWithLoadingSequential.Create());
break;
}
}
@@ -1154,7 +1143,7 @@ public class SkillCollectionBase : IEnumerable<SkillBase>, IEnumerable
}
if (flag && num2 == 1 && !_ownerCard.SelfBattlePlayer.BattleMgr.IsBattleEnd)
{
sequentialVfxPlayer.Register(new RepeatSkillEffectVfx(skillBase.SkillPrm.ownerCard.SelfBattlePlayer.Class.BattleCardView, "when_destroy", skillBase.SkillPrm.ownerCard.IsPlayer));
sequentialVfxPlayer.Register(new RepeatSkillEffectVfx(skillBase.SkillPrm.ownerCard.SelfBattlePlayer.BattleMgr, skillBase.SkillPrm.ownerCard.SelfBattlePlayer.Class.BattleCardView, "when_destroy", skillBase.SkillPrm.ownerCard.IsPlayer));
}
sequentialVfxPlayer.Register(skillBase.CallStart(skillProcessor, playerInfoPair, flag ? skillConditionCheckerOption2 : checkerOption, callParameter, SkillProcessor.ProcessCallType.Start, list2.Contains(i)));
if (skillBase is Skill_invoke_skill skill_invoke_skill)
@@ -1177,7 +1166,7 @@ public class SkillCollectionBase : IEnumerable<SkillBase>, IEnumerable
SkillBase skillBase2 = skill_invoke_skill.NotInsertSkillList.FirstOrDefault((SkillBase s) => s.IsWhenDestroySkill);
if (skillBase2 != null)
{
sequentialVfxPlayer.Register(new LoadAndPlayEffectVfx("cmn_card_destroy_3", "se_cmn_card_destroy_3", skillBase2.SkillPrm.ownerCard.BattleCardView.Transform, 0f));
sequentialVfxPlayer.Register(VfxWithLoadingSequential.Create());
}
}
list = IfNeededRepeatSkills(skill_invoke_skill.InsertSkillList, skillProcessor);
@@ -1190,7 +1179,7 @@ public class SkillCollectionBase : IEnumerable<SkillBase>, IEnumerable
list = IfNeededRepeatSkills(skill_invoke_skill.NotInsertSkillList, skillProcessor);
if (list != null && !_ownerCard.SelfBattlePlayer.BattleMgr.IsBattleEnd)
{
sequentialVfxPlayer.Register(new RepeatSkillEffectVfx(skillBase.SkillPrm.ownerCard.SelfBattlePlayer.Class.BattleCardView, "when_destroy", skillBase.SkillPrm.ownerCard.IsPlayer));
sequentialVfxPlayer.Register(new RepeatSkillEffectVfx(skillBase.SkillPrm.ownerCard.SelfBattlePlayer.BattleMgr, skillBase.SkillPrm.ownerCard.SelfBattlePlayer.Class.BattleCardView, "when_destroy", skillBase.SkillPrm.ownerCard.IsPlayer));
}
}
list3.AddRange(list4);