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:
@@ -174,8 +174,6 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
}
|
||||
}
|
||||
|
||||
private const int DEPTH_RESULT_DIALOG = 100;
|
||||
|
||||
public static string CurrentDeckName = null;
|
||||
|
||||
private static Format EditDeckFormat = Format.Max;
|
||||
@@ -204,20 +202,6 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
|
||||
private DeckGroupListData _deckGroupListData;
|
||||
|
||||
private const string CRAFT_BTN_LEFT_ON = "pilltab_02_left_on";
|
||||
|
||||
private const string CRAFT_BTN_LEFT_OFF = "pilltab_02_left_off";
|
||||
|
||||
private const string CRAFT_BTN_RIGHT_ON = "pilltab_02_right_on";
|
||||
|
||||
private const string CRAFT_BTN_RIGHT_OFF = "pilltab_02_right_off";
|
||||
|
||||
private const string MY_ROTATION_ALL_PACK_SPRITE = "btn_check_";
|
||||
|
||||
private const string BUTTON_SPRITE_FOOTER_OFF = "off";
|
||||
|
||||
private const string BUTTON_SPRITE_FOOTER_ON = "on";
|
||||
|
||||
private readonly Vector3 MY_ROTATION_FORMAT_ICON_POSITION = new Vector3(45f, -47f, 0f);
|
||||
|
||||
private readonly Vector3 MY_ROTATION_CARD_NUMBER_ICON_POSITION = new Vector3(68f, -62f, -1f);
|
||||
@@ -226,12 +210,6 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
|
||||
private readonly Vector3 MY_ROTATION_CARD_NUMBER_MAX_POSITION = new Vector3(58f, 0f, 0f);
|
||||
|
||||
private const int MY_ROTATION_CLASS_NAME_WIDTH = 107;
|
||||
|
||||
private const int MY_ROTATION_CLASS_UNDER_LINE_WIDTH = 184;
|
||||
|
||||
private const float BACK_BUTTON_POS_Y = -40.7f;
|
||||
|
||||
[SerializeField]
|
||||
private DeckBuildShortageCardView _prefabShortageCardView;
|
||||
|
||||
@@ -429,12 +407,8 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCraftMode => _deckCardBundle.IsCraftMode;
|
||||
|
||||
public bool IsBattleRetry { get; set; }
|
||||
|
||||
public bool IsEditState => m_state == _stateEdit;
|
||||
|
||||
public event Action<int> OnChangeDeckCardNum;
|
||||
|
||||
private DeckData GetDeck(Format format, int deckId)
|
||||
@@ -464,81 +438,6 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
UIManager.GetInstance().OverrideSceneParam(UIManager.ViewScene.DeckList, new DeckListUIParam(deck.Format, conventionDeckList?.Conventioninfo));
|
||||
}
|
||||
|
||||
public static void SetDeckCopyParameter(DeckData deck, bool isCreatedByBuilder, bool isCopySubClass, ConventionDeckList conventionDeckList, MyRotationInfo myRotationInfo = null)
|
||||
{
|
||||
CopySrcDeckData = deck;
|
||||
ClassSet = new ClassSet(deck.GetDeckClassID(), deck.GetDeckSubClassID());
|
||||
IsCopySkinAndSleeve = PlayerPrefsWrapper.GetBool(PlayerPrefsWrapper.IS_COPY_SLEEVE_AND_SKIN);
|
||||
IsCopySubClass = isCopySubClass;
|
||||
IsCreatedByBuilder = isCreatedByBuilder;
|
||||
_conventionDeckList = conventionDeckList;
|
||||
CanUseNonPossessionCard = conventionDeckList == null;
|
||||
if (deck.DeckAttributeType == DeckAttributeType.TrialDeck || deck.DeckAttributeType == DeckAttributeType.SampleDeck)
|
||||
{
|
||||
IsCreatedByBuilder = true;
|
||||
}
|
||||
if (myRotationInfo == null)
|
||||
{
|
||||
MyRotationInfo = Data.MyRotationAllInfo.Get(deck.MyRotationId);
|
||||
}
|
||||
else
|
||||
{
|
||||
MyRotationInfo = myRotationInfo;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetCreateAutoParameter(Format format, bool canUseNonPossessionCard)
|
||||
{
|
||||
IsCreateAuto = true;
|
||||
CanUseNonPossessionCard = canUseNonPossessionCard;
|
||||
}
|
||||
|
||||
public static void SetDeckCopyParameterForDeckIntroduction(DeckData emptyDeck, DeckData srcDeck, MyRotationInfo myRotationInfo = null)
|
||||
{
|
||||
CurrentDeckName = null;
|
||||
CurrentDeckId = emptyDeck.GetDeckID();
|
||||
CurrentDeckData = emptyDeck;
|
||||
EditDeckFormat = emptyDeck.Format;
|
||||
ClassSet = new ClassSet(srcDeck.GetDeckClassID(), srcDeck.GetDeckSubClassID());
|
||||
CopySrcDeckData = srcDeck;
|
||||
IsCopySkinAndSleeve = false;
|
||||
IsCopySubClass = FormatBehaviorManager.GetDefaultBehaviour(srcDeck.Format).UseSubClass && PlayerPrefsWrapper.GetBool(PlayerPrefsWrapper.IS_COPY_SUBCLASS_CARDS);
|
||||
IsCreatedByBuilder = true;
|
||||
IsCreateAuto = false;
|
||||
_conventionDeckList = null;
|
||||
CanUseNonPossessionCard = true;
|
||||
if (EditDeckFormat == Format.MyRotation)
|
||||
{
|
||||
if (myRotationInfo == null)
|
||||
{
|
||||
MyRotationInfo = Data.MyRotationAllInfo.Get(srcDeck.MyRotationId);
|
||||
}
|
||||
else
|
||||
{
|
||||
MyRotationInfo = myRotationInfo;
|
||||
}
|
||||
}
|
||||
UIManager.GetInstance().OverrideSceneParam(UIManager.ViewScene.DeckList, new DeckListUIParam(emptyDeck.Format, null));
|
||||
}
|
||||
|
||||
public static void SetBuildDeckEditParameter(DeckData buildDeck, string deckName, DeckData emptyDeck)
|
||||
{
|
||||
CurrentDeckName = deckName;
|
||||
CurrentDeckId = emptyDeck.GetDeckID();
|
||||
CurrentDeckData = emptyDeck;
|
||||
ClassSet = new ClassSet(buildDeck.GetDeckClassID(), buildDeck.GetDeckSubClassID());
|
||||
EditDeckFormat = emptyDeck.Format;
|
||||
CopySrcDeckData = buildDeck;
|
||||
IsCopySkinAndSleeve = false;
|
||||
IsCopySubClass = FormatBehaviorManager.GetDefaultBehaviour(buildDeck.Format).UseSubClass && PlayerPrefsWrapper.GetBool(PlayerPrefsWrapper.IS_COPY_SUBCLASS_CARDS);
|
||||
IsCreatedByBuilder = true;
|
||||
IsCreateAuto = false;
|
||||
_conventionDeckList = null;
|
||||
CanUseNonPossessionCard = true;
|
||||
MyRotationInfo = null;
|
||||
UIManager.GetInstance().OverrideSceneParam(UIManager.ViewScene.DeckList, new DeckListUIParam(emptyDeck.Format, null));
|
||||
}
|
||||
|
||||
public override bool IsEnableSwipeAutoSameBasicCardAdd()
|
||||
{
|
||||
return true;
|
||||
@@ -789,7 +688,7 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
private void SetupDeckViewerAndText()
|
||||
{
|
||||
m_labelDeckName.text = _deckCardBundle.DeckName;
|
||||
m_labelClassName.text = GameMgr.GetIns().GetDataMgr().GetClanNameByKey((int)ClassSet.MainClass);
|
||||
m_labelClassName.text = ((int)ClassSet.MainClass).ToString(); // Pre-Phase-5b: no clan-name lookup
|
||||
_deckViewer.RemoveData();
|
||||
_deckViewer.SetDeckName(_deckCardBundle.DeckName);
|
||||
_deckViewer.SetClassSet(ClassSet);
|
||||
@@ -913,18 +812,6 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
InitUseSubClassDisplay();
|
||||
}
|
||||
|
||||
public void OnBtnPushSelectCardFilter()
|
||||
{
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_COMMON_BUTTON);
|
||||
ShowFilterMenu(_selectCardFilter, Data.SystemText.Get("Card_0126"));
|
||||
}
|
||||
|
||||
public void OnBtnPushOwnFilter()
|
||||
{
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_COMMON_BUTTON);
|
||||
ShowFilterMenu(_pagingFilter, Data.SystemText.Get("Card_0021"));
|
||||
}
|
||||
|
||||
protected override void OnFinishFadeIn()
|
||||
{
|
||||
_fadeInFinish = true;
|
||||
@@ -953,13 +840,6 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool LoadDeckCard(List<int> idList)
|
||||
{
|
||||
base.IsLoading = true;
|
||||
HideDetail();
|
||||
return _deckCardBundle.LoadDeckCard(idList);
|
||||
}
|
||||
|
||||
public void ChangeCraftMode(bool isCraft)
|
||||
{
|
||||
if (!base.IsLoading && base.CurrentState == _stateEdit)
|
||||
@@ -967,7 +847,7 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
base.IsLoading = true;
|
||||
m_craftOnBtn.normalSprite = (isCraft ? "pilltab_02_right_on" : "pilltab_02_right_off");
|
||||
m_craftOffBtn.normalSprite = (isCraft ? "pilltab_02_left_off" : "pilltab_02_left_on");
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_TOGGLE_ON);
|
||||
|
||||
_deckCardBundle.ChangeCraftMode(isCraft);
|
||||
}
|
||||
}
|
||||
@@ -1035,7 +915,7 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
bool flag = false;
|
||||
if (ConventionInfo == null)
|
||||
{
|
||||
deckUpdateTask = GameMgr.GetIns().GetDeckUpdateTask();
|
||||
deckUpdateTask = null; // Pre-Phase-5b: no DeckUpdateTask headless
|
||||
flag = deckUpdateTask.AchievedInfo._rewards.Count > 0;
|
||||
}
|
||||
if (flag)
|
||||
@@ -1090,7 +970,7 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
|
||||
private void ChangeSceneToBattle()
|
||||
{
|
||||
DataMgr dataManager = GameMgr.GetIns().GetDataMgr();
|
||||
DataMgr dataManager = null; // Pre-Phase-5b: headless has no DataMgr
|
||||
DeckInfoTask task = new DeckInfoTask();
|
||||
task.SetParameter(Format.All);
|
||||
UIManager.GetInstance().StartCoroutine(Toolbox.NetworkManager.Connect(task, delegate
|
||||
@@ -1111,7 +991,7 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
|
||||
private string GetSaveAndRetryDialogText()
|
||||
{
|
||||
DataMgr dataMgr = GameMgr.GetIns().GetDataMgr();
|
||||
DataMgr dataMgr = null; // Pre-Phase-5b: DataMgr not reachable in DeckCardEdit UI headless
|
||||
switch (dataMgr.m_BattleType)
|
||||
{
|
||||
case DataMgr.BattleType.FreeBattle:
|
||||
@@ -1141,7 +1021,7 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
|
||||
private string GetRetryDialogText()
|
||||
{
|
||||
DataMgr dataMgr = GameMgr.GetIns().GetDataMgr();
|
||||
DataMgr dataMgr = null; // Pre-Phase-5b: DataMgr not reachable in DeckCardEdit UI headless
|
||||
switch (dataMgr.m_BattleType)
|
||||
{
|
||||
case DataMgr.BattleType.FreeBattle:
|
||||
@@ -1300,34 +1180,9 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
UpdateCraftShortageButton();
|
||||
}
|
||||
|
||||
private void OnChangeManyDeckCards()
|
||||
{
|
||||
_hasChanged = true;
|
||||
m_costCurve.Refresh(_deckCardBundle.SelectionAreaList.IdList.ToArray());
|
||||
DeckCardNumAnim();
|
||||
UpdateCraftShortageButton();
|
||||
}
|
||||
|
||||
public void SetAllDeckCards(List<int> cardIdList, Action onFirstAnimationFinish = null, float cardRotateDelayTimeMax = float.MaxValue)
|
||||
{
|
||||
_deckCardBundle.SetSelectionArea(cardIdList, onFirstAnimationFinish, cardRotateDelayTimeMax);
|
||||
OnChangeManyDeckCards();
|
||||
}
|
||||
|
||||
public void RemoveAllDeckCards()
|
||||
{
|
||||
_deckCardBundle.ClearSelectionArea();
|
||||
OnChangeManyDeckCards();
|
||||
}
|
||||
|
||||
public CardBundle GetDeckCardBundle()
|
||||
{
|
||||
return _deckCardBundle.SelectionAreaList;
|
||||
}
|
||||
|
||||
private void OnClickAutoDeckBtn(GameObject obj)
|
||||
{
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_COMMON_BUTTON);
|
||||
|
||||
if (Data.MaintenanceCodeList.Contains(NetworkDefine.MAINTENANCE_TYPE.AUTO_DECK_CREATE))
|
||||
{
|
||||
DialogBase dialogBase = UIManager.GetInstance().CreateConfirmationDialog(Data.SystemText.Get("Card_0266"));
|
||||
@@ -1379,7 +1234,7 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
private void OnClickSaveBtn(GameObject obj)
|
||||
{
|
||||
SaveDeck();
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_COMMON_BUTTON);
|
||||
|
||||
}
|
||||
|
||||
private DeckData CreateDeckData()
|
||||
@@ -1408,15 +1263,11 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
HideDetail();
|
||||
_deckViewer.gameObject.SetActive(value: true);
|
||||
_deckViewer.SetDeck(CreateDeckData(), _conventionDeckList);
|
||||
if (QRCodeUtility.IsShowQRCode(_deckViewer, _formatBehavior, base.Format))
|
||||
{
|
||||
_deckViewer.SetQRSmallTexture();
|
||||
}
|
||||
else if (_formatBehavior.CanShowQRCode)
|
||||
if (_formatBehavior.CanShowQRCode)
|
||||
{
|
||||
_deckViewer.SetQRCodeButtonToGray();
|
||||
}
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_COMMON_BUTTON);
|
||||
|
||||
}
|
||||
|
||||
private void HideDeckViewer()
|
||||
@@ -1427,7 +1278,7 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
|
||||
private void OnClickCraftShortageCardButton(GameObject obj)
|
||||
{
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_COMMON_BUTTON);
|
||||
|
||||
if (!CanUseNonPossessionCard || base.IsLoading)
|
||||
{
|
||||
return;
|
||||
@@ -1475,7 +1326,7 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
dialogBase.SetSize(DialogBase.Size.XL);
|
||||
dialogBase.SetTitleLabel(Data.SystemText.Get("Dia_DeckEdit_014_Title"));
|
||||
dialogBase.SetPanelDepth(100);
|
||||
dialogBase.OpenSe = Se.TYPE.NONE;
|
||||
dialogBase.OpenSe = 0;
|
||||
DeckBuildShortageCardView _shortageCardView = UnityEngine.Object.Instantiate(_prefabShortageCardView);
|
||||
dialogBase.SetObj(_shortageCardView.gameObject);
|
||||
dialogBase.OnCloseStart = CloseShortageCardViewer;
|
||||
@@ -1519,7 +1370,7 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
dialogBase.SetButtonLayout(DialogBase.ButtonLayout.OkBtn);
|
||||
List<int> addIdList = _shortageIdList.FindAll(delegate(int id)
|
||||
{
|
||||
if (GameMgr.GetIns().GetDataMgr().IsMaintenanceCard(id))
|
||||
if (false /* Pre-Phase-5b: IsMaintenanceCard headless-false */)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1623,7 +1474,7 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
if (_deckViewer.isActiveAndEnabled)
|
||||
{
|
||||
HideDeckViewer();
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_CANCEL);
|
||||
|
||||
return false;
|
||||
}
|
||||
if (_hasChanged && DeckCardNum > 0)
|
||||
@@ -1709,7 +1560,7 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
List<int> list = new List<int>();
|
||||
List<int> source = deckCardList.ConvertAll((int id) => CardMaster.GetInstance(formatBehavior.CardMasterId).GetCardParameterFromId(id).BaseCardId);
|
||||
List<int> needBaseIdList = source.Distinct().ToList();
|
||||
Dictionary<int, int> possessionBaseCardDictionary = GameMgr.GetIns().GetDataMgr().GetPossessionBaseCardDictionary(isIncludingSpotCard: true, formatBehavior.CardMasterId);
|
||||
Dictionary<int, int> possessionBaseCardDictionary = new(); // Pre-Phase-5b: possession lookup not reachable headless
|
||||
int i;
|
||||
for (i = 0; i < needBaseIdList.Count; i++)
|
||||
{
|
||||
@@ -1799,7 +1650,7 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
|
||||
private void OnClickSwapClassButton(GameObject obj)
|
||||
{
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_COMMON_BUTTON);
|
||||
|
||||
DialogBase dialogBase = UIManager.GetInstance().CreateDialogClose();
|
||||
dialogBase.SetTitleLabel(Data.SystemText.Get("Card_0274"));
|
||||
dialogBase.SetText(Data.SystemText.Get("Card_0275"));
|
||||
@@ -1813,7 +1664,7 @@ public class DeckCardEditUI : CardSelectListUIBase
|
||||
|
||||
private void OnClickMyRotationChangeButton(GameObject g)
|
||||
{
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_COMMON_BUTTON);
|
||||
|
||||
MyRotationPeriodSelectDialog.Create(_myRotationInfo, ClassSet.MainClass, delegate(MyRotationInfo selectData)
|
||||
{
|
||||
if (_myRotationInfo.Id != selectData.Id)
|
||||
|
||||
Reference in New Issue
Block a user