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:
@@ -45,30 +45,6 @@ public abstract class MyPageBannerBase : MonoBehaviour
|
||||
{
|
||||
return ImageName.GetHashCode() ^ Click.GetHashCode() ^ Status.GetHashCode();
|
||||
}
|
||||
|
||||
public void Parse(JsonData json)
|
||||
{
|
||||
ImageName = json["image_name"].ToString();
|
||||
Click = json["click"].ToString();
|
||||
Status = json["status"].ToString();
|
||||
if (json.Keys.Contains("image_paths") && json["image_paths"] != null && json["image_paths"].Count != 0)
|
||||
{
|
||||
ImagePaths = new List<string>();
|
||||
JsonData jsonData = json["image_paths"];
|
||||
for (int i = 0; i < jsonData.Count; i++)
|
||||
{
|
||||
ImagePaths.Add(jsonData[i].ToString());
|
||||
}
|
||||
}
|
||||
if (json.Keys.Contains("change_time"))
|
||||
{
|
||||
_changeTime = (float)json["change_time"].ToDouble();
|
||||
}
|
||||
if (json.Keys.Contains("has_reward"))
|
||||
{
|
||||
NeedBadge = json["has_reward"].ToInt() == 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
@@ -92,10 +68,6 @@ public abstract class MyPageBannerBase : MonoBehaviour
|
||||
|
||||
private Coroutine _createCoroutine;
|
||||
|
||||
private const int BANNER_IMAGE_DEPTH = 5;
|
||||
|
||||
public bool IsCreateEnd => _isCreateEnd;
|
||||
|
||||
public bool _isFirstTips { private get; set; }
|
||||
|
||||
public bool IsEnableBanner
|
||||
@@ -132,11 +104,6 @@ public abstract class MyPageBannerBase : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_loadAssetList = new List<string>();
|
||||
}
|
||||
|
||||
public void Show()
|
||||
{
|
||||
if (!_isFirstTips)
|
||||
@@ -145,14 +112,6 @@ public abstract class MyPageBannerBase : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
if (!_isFirstTips)
|
||||
{
|
||||
base.gameObject.SetActive(value: false);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetActive(bool inActive)
|
||||
{
|
||||
_isActive = inActive;
|
||||
@@ -195,11 +154,11 @@ public abstract class MyPageBannerBase : MonoBehaviour
|
||||
switch (click)
|
||||
{
|
||||
case "announce":
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
|
||||
|
||||
UIManager.GetInstance().WebViewHelper.OpenAnnounceWebView(status);
|
||||
return;
|
||||
case "colosseum":
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
|
||||
|
||||
OpenColosseumRound();
|
||||
return;
|
||||
case "dialog_text":
|
||||
@@ -212,15 +171,15 @@ public abstract class MyPageBannerBase : MonoBehaviour
|
||||
OnClickBeginnerMission();
|
||||
return;
|
||||
case "webview":
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
|
||||
|
||||
UIManager.GetInstance().WebViewHelper.OpenWebviewWithPageId(status);
|
||||
return;
|
||||
case "webview_limited":
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
|
||||
|
||||
UIManager.GetInstance().WebViewHelper.OpenWebViewDirectly(status, isLimited: true);
|
||||
return;
|
||||
case "battle_pass_buy":
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
|
||||
|
||||
BattlePassPurchaseDialog.Create();
|
||||
return;
|
||||
case "browser":
|
||||
@@ -235,20 +194,20 @@ public abstract class MyPageBannerBase : MonoBehaviour
|
||||
case "mypage_battle":
|
||||
if (MyPageMenu.Instance != null)
|
||||
{
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
|
||||
|
||||
MyPageMenu.Instance.ChangeMenu(2);
|
||||
}
|
||||
return;
|
||||
case "mypage_deck":
|
||||
if (MyPageMenu.Instance != null)
|
||||
{
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
|
||||
|
||||
MyPageMenu.Instance.ChangeMenu(4);
|
||||
MyPageMenu.Instance.GoToCardDeck();
|
||||
}
|
||||
return;
|
||||
case "competition":
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
|
||||
|
||||
OnClickCompetitionBanner();
|
||||
return;
|
||||
case "bingo":
|
||||
@@ -258,11 +217,11 @@ public abstract class MyPageBannerBase : MonoBehaviour
|
||||
OnClickTreasureBoxCpDialog();
|
||||
return;
|
||||
case "red_ether":
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
|
||||
|
||||
UIManager.GetInstance().ChangeViewScene(UIManager.ViewScene.RedEtherCampaignLobby);
|
||||
return;
|
||||
case "ts_rotation_deck":
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
|
||||
|
||||
DeckListUI.ChangeSceneToDeckList(Format.Rotation);
|
||||
return;
|
||||
case "account_transition_with_two":
|
||||
@@ -284,7 +243,7 @@ public abstract class MyPageBannerBase : MonoBehaviour
|
||||
{
|
||||
transitionData.Status = result;
|
||||
}
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
|
||||
|
||||
SceneTransition.ChangeScene(transitionData, null);
|
||||
}
|
||||
}
|
||||
@@ -356,7 +315,7 @@ public abstract class MyPageBannerBase : MonoBehaviour
|
||||
}
|
||||
if (kind == "speed_challenge_clear")
|
||||
{
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
|
||||
|
||||
DialogBase dialogBase = UIManager.GetInstance().CreateDialogClose();
|
||||
dialogBase.SetButtonLayout(DialogBase.ButtonLayout.OkBtn);
|
||||
dialogBase.SetTitleLabel(systemText.Get("SpeedChallenge_0001"));
|
||||
@@ -374,7 +333,7 @@ public abstract class MyPageBannerBase : MonoBehaviour
|
||||
component.SetTexture("banner_000764");
|
||||
return;
|
||||
}
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
|
||||
|
||||
DialogBase dialogBase2 = UIManager.GetInstance().CreateDialogClose();
|
||||
dialogBase2.SetButtonLayout(DialogBase.ButtonLayout.OkBtn);
|
||||
dialogBase2.SetTitleLabel(systemText.Get("SpeedChallenge_0001"));
|
||||
@@ -479,50 +438,47 @@ public abstract class MyPageBannerBase : MonoBehaviour
|
||||
|
||||
private void OnClickDeckIntroduction(string status, Format format)
|
||||
{
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
|
||||
|
||||
int seriesId = int.Parse(status);
|
||||
DeckIntroduction.Create(_deckIntroductionPrefab, MyPageMenu.Instance.HomeMenu.ContentsRoot, seriesId, format);
|
||||
}
|
||||
|
||||
private static void OnClickTreasureBoxCpDialog()
|
||||
{
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_COMMON_BUTTON);
|
||||
|
||||
TreasureBoxCpDialog.CreateDialog();
|
||||
}
|
||||
|
||||
private static void OnClickIncentiveCampaign(int no)
|
||||
{
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
|
||||
LotteryPage.ChangeSceneLotteryPage(no);
|
||||
|
||||
// LotteryPage removed (DEAD-COLD engine cleanup Task 12)
|
||||
}
|
||||
|
||||
private static void OnClickBingoEvent()
|
||||
{
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
|
||||
BingoPage.ChangeSceneBingoPage();
|
||||
|
||||
// BingoPage removed (DEAD-COLD engine cleanup Task 12)
|
||||
}
|
||||
|
||||
private static void OnClickBeginnerMission()
|
||||
{
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
|
||||
|
||||
ChangeScene(UIManager.ViewScene.BeginnerMission);
|
||||
}
|
||||
|
||||
private static void ChangeScene(UIManager.ViewScene scene)
|
||||
{
|
||||
if (UIManager.GetInstance().GetCurrentScene() == UIManager.ViewScene.Battle)
|
||||
{
|
||||
GameMgr.GetIns().GetBattleCtrl().BattleEnd(scene);
|
||||
}
|
||||
else
|
||||
{
|
||||
UIManager.GetInstance().ChangeViewScene(scene);
|
||||
}
|
||||
// Pre-Phase-5b: Battle scene routed through GameMgr.GetBattleCtrl().BattleEnd. BattleControl
|
||||
// was stubbed in chunk 7 — its BattleEnd is a no-op that just invokes the callback.
|
||||
// Fall straight through to the else branch; UIManager scene change is the only real
|
||||
// side effect and it applies in either path.
|
||||
UIManager.GetInstance().ChangeViewScene(scene);
|
||||
}
|
||||
|
||||
private void OnClickDialogInfo(BannerInfo bannerInfo)
|
||||
{
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
|
||||
|
||||
List<string> listBannerName = bannerInfo.ImagePaths;
|
||||
List<string> listImagePaths = new List<string>();
|
||||
for (int i = 0; i < listBannerName.Count; i++)
|
||||
@@ -553,7 +509,7 @@ public abstract class MyPageBannerBase : MonoBehaviour
|
||||
|
||||
private void OnClickDialog(BannerInfo bannerInfo)
|
||||
{
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
|
||||
|
||||
VoteDataTask voteDataTask = new VoteDataTask();
|
||||
int voteId = int.Parse(bannerInfo.Status);
|
||||
voteDataTask.SetParameter(voteId);
|
||||
@@ -568,48 +524,16 @@ public abstract class MyPageBannerBase : MonoBehaviour
|
||||
{
|
||||
dialog.SetText(string.Format(Data.VoteInfo.after_content_text, Data.VoteInfo.vote_name));
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMgr.GetIns().GetPrefabMgr().Load("UI/layoutParts/Dialog/DialogWinnerPost");
|
||||
GameObject gameObject = GameMgr.GetIns().GetPrefabMgr().Get("UI/layoutParts/Dialog/DialogWinnerPost");
|
||||
GameObject gameObject2 = gameObject.GetComponent<NguiObjs>().objs[1];
|
||||
Action OnFinishPost = delegate
|
||||
{
|
||||
dialog.Close();
|
||||
DialogBase dialogBase = UIManager.GetInstance().CreateDialogClose();
|
||||
dialogBase.SetSize(DialogBase.Size.S);
|
||||
dialogBase.SetPanelDepth(0);
|
||||
dialogBase.SetTitleLabel(Data.VoteInfo.title_text);
|
||||
dialogBase.SetButtonLayout(DialogBase.ButtonLayout.OkBtn);
|
||||
dialogBase.SetFadeButtonEnabled(flag: true);
|
||||
dialogBase.SetText(Data.SystemText.Get("Dia_Vote_002"));
|
||||
};
|
||||
gameObject = NGUITools.AddChild(dialog.gameObject, gameObject);
|
||||
NguiObjs component = gameObject.GetComponent<NguiObjs>();
|
||||
GameObject gameObject3 = component.objs[0];
|
||||
component.labels[0].text = Data.VoteInfo.before_content_text;
|
||||
foreach (KeyValuePair<int, string> item in Data.VoteInfo.vote_target_list)
|
||||
{
|
||||
GameObject postButton = UnityEngine.Object.Instantiate(gameObject2);
|
||||
NguiObjs component2 = postButton.GetComponent<NguiObjs>();
|
||||
postButton.transform.parent = gameObject3.transform;
|
||||
postButton.transform.localScale = gameObject2.transform.localScale;
|
||||
postButton.name = item.Key.ToString();
|
||||
component2.labels[0].text = item.Value;
|
||||
postButton.gameObject.SetActive(value: true);
|
||||
component2.buttons[0].onClick.Add(new EventDelegate(delegate
|
||||
{
|
||||
OnSelectPost(voteId, postButton, OnFinishPost);
|
||||
}));
|
||||
}
|
||||
gameObject3.GetComponent<UIScrollView>().ResetPosition();
|
||||
}
|
||||
// Pre-Phase-5b: vote-not-yet path built a DialogWinnerPost prefab + attached per-target
|
||||
// buttons via GetPrefabMgr. Purely UI dialog composition, unreachable headless.
|
||||
// Stubbed to a no-op; the enclosing "already voted" branch above handles the display path
|
||||
// that any surviving caller would render.
|
||||
}));
|
||||
}
|
||||
|
||||
private static void OnClickDialogText(string status)
|
||||
{
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
|
||||
|
||||
SystemText systemText = Data.SystemText;
|
||||
DialogBase dialogBase = UIManager.GetInstance().CreateDialogClose();
|
||||
dialogBase.SetButtonLayout(DialogBase.ButtonLayout.OkBtn);
|
||||
@@ -639,13 +563,6 @@ public abstract class MyPageBannerBase : MonoBehaviour
|
||||
dialogBase.SetOnClickUrl();
|
||||
}
|
||||
|
||||
private static void OnClickLegendCrystal(string status)
|
||||
{
|
||||
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
|
||||
SpecialCrystalDialog.SetDefaultOpenPage(status);
|
||||
SpecialCrystalDialog.Create(UIManager.GetInstance().LegendCrystalBuyDialogPrefab, null, null, null);
|
||||
}
|
||||
|
||||
private void OnSelectPost(int voteId, GameObject cardObject, Action callback)
|
||||
{
|
||||
int voteTargetId = int.Parse(cardObject.name);
|
||||
@@ -734,16 +651,4 @@ public abstract class MyPageBannerBase : MonoBehaviour
|
||||
dialogBase.SetText(systemText.Get("BeyondHandover_0011"));
|
||||
dialogBase.SetSize(DialogBase.Size.M);
|
||||
}
|
||||
|
||||
protected void OnDestroy()
|
||||
{
|
||||
if (_loadAssetList != null && _loadAssetList.Count != 0)
|
||||
{
|
||||
Toolbox.ResourcesManager.RemoveAssetGroup(_loadAssetList);
|
||||
}
|
||||
if (_createCoroutine != null)
|
||||
{
|
||||
UIManager.GetInstance().StopCoroutine(_createCoroutine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user