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

@@ -1,7 +1,6 @@
using System;
using Cute;
using UnityEngine;
using Wizard.Dialog.DataLink;
using Wizard.Dialog.Setting;
namespace Wizard;
@@ -11,58 +10,17 @@ public class AccountBase : MonoBehaviour
public enum DisplayType
{
NONE,
TITLE,
OTHER,
ACCOUNT_LINK,
ID_PASSWORD_INPUT,
PASSWORD_SETTING
}
ACCOUNT_LINK }
public enum TransitionOriginalScreen
{
TITLE,
OTHER
}
[SerializeField]
private GameObject TransferContainer;
[SerializeField]
private GameObject InputContainer;
[SerializeField]
public IdPasswordInput _idPasswordInput;
[SerializeField]
public PasswordSetting _passwordSetting;
[SerializeField]
private GameObject TopContainer;
[SerializeField]
private Transform _topContainerLine;
[SerializeField]
private GameObject TitleContainer;
[SerializeField]
private GameObject OtherContainer;
[SerializeField]
private UIButton _appleButton;
private const int INDEX_TOP_LABEL = 0;
private const int INDEX_MID_LABEL = 1;
private const int INDEX_INPUT_LABEL = 2;
private const int INDEX_BOTTOM_LABEL = 3;
private const int INDEX_BUTTON_LABEL = 4;
private Vector3 AccountPos = new Vector3(0f, 70f, 0f);
[SerializeField]
private UIGrid ButtonGrid;
@@ -72,9 +30,6 @@ public class AccountBase : MonoBehaviour
[SerializeField]
public NguiObjs BtnGoogle;
[SerializeField]
public NguiObjs BtnFaceBook;
[SerializeField]
public NguiObjs ButtonGetCode;
@@ -84,12 +39,6 @@ public class AccountBase : MonoBehaviour
[SerializeField]
public UILabel CommonLabelBtm;
private readonly Vector3 TopContainerPosition = new Vector3(0f, -90f, 0f);
private const int MIN_PASSWORD_LENGTH = 8;
private const int MAX_PASSWORD_LENGTH = 16;
private void Awake()
{
SetLabels();
@@ -106,340 +55,4 @@ public class AccountBase : MonoBehaviour
CommonLabelTop.text = systemText.Get("Account_0095");
CommonLabelBtm.text = systemText.Get("Account_0073");
}
public void SetDisplayType(DisplayType type)
{
switch (type)
{
case DisplayType.TITLE:
TransferContainer.SetActive(value: true);
InputContainer.SetActive(value: false);
_idPasswordInput.gameObject.SetActive(value: false);
_passwordSetting.gameObject.SetActive(value: false);
OtherContainer.SetActive(value: false);
TitleContainer.SetActive(value: false);
UIUtil.SetPositionY(TopContainer.transform, -90f);
_topContainerLine.gameObject.SetActive(value: false);
break;
case DisplayType.OTHER:
TransferContainer.SetActive(value: true);
InputContainer.SetActive(value: false);
_idPasswordInput.gameObject.SetActive(value: false);
_passwordSetting.gameObject.SetActive(value: false);
TitleContainer.SetActive(value: false);
break;
case DisplayType.ACCOUNT_LINK:
TransferContainer.SetActive(value: true);
TransferContainer.transform.localPosition = AccountPos;
InputContainer.SetActive(value: false);
_idPasswordInput.gameObject.SetActive(value: false);
_passwordSetting.gameObject.SetActive(value: false);
TopContainer.SetActive(value: false);
OtherContainer.SetActive(value: false);
if (GetActiveChildCount(ButtonGrid.transform) <= 2)
{
UIUtil.SetPositionY(TitleContainer.transform, 50f);
UIUtil.SetPositionY(ButtonGrid.transform, -96f);
}
else
{
UIUtil.SetPositionY(TitleContainer.transform, 0f);
UIUtil.SetPositionY(ButtonGrid.transform, -60f);
}
break;
case DisplayType.ID_PASSWORD_INPUT:
TransferContainer.SetActive(value: false);
InputContainer.SetActive(value: false);
_idPasswordInput.gameObject.SetActive(value: true);
_passwordSetting.gameObject.SetActive(value: false);
break;
case DisplayType.PASSWORD_SETTING:
TransferContainer.SetActive(value: false);
InputContainer.SetActive(value: false);
_idPasswordInput.gameObject.SetActive(value: false);
_passwordSetting.gameObject.SetActive(value: true);
break;
}
}
public DialogBase Init(DisplayType type, TransitionOriginalScreen from, Action close_callback = null)
{
SystemText text = Data.SystemText;
SetDisplayType(type);
DialogBase dialog = UIManager.GetInstance().CreateDialogClose();
dialog.SetSize(DialogBase.Size.M);
dialog.SetObj(base.gameObject);
if (type != DisplayType.PASSWORD_SETTING)
{
if (type == DisplayType.OTHER)
{
dialog.SetTitleLabel(text.Get("Account_0001"));
}
else
{
dialog.SetTitleLabel(text.Get("Account_0004"));
}
dialog.SetButtonLayout(DialogBase.ButtonLayout.CloseBtn);
}
BtnTransferCode.buttons[0].onClick.Add(new EventDelegate(delegate
{
OnClickIdPasswordInputButton(dialog, from);
}));
BtnGoogle.buttons[0].onClick.Add(new EventDelegate(delegate
{
dialog.CloseWithoutSelect();
UIManager.GetInstance().AccountTransferHelper.PressedTransitongButton = CuteNetworkDefine.ACCOUNT_TYPE.GOOGLE_PLAY;
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_COMMON_BUTTON);
DialogBase dialogBase = UIManager.GetInstance().CreateDialogClose();
dialogBase.SetSize(DialogBase.Size.M);
dialogBase.SetTitleLabel(text.Get("Account_0015"));
dialogBase.SetText(text.Get("Account_0077"));
dialogBase.SetButtonLayout(DialogBase.ButtonLayout.BlueBtn_GrayBtn);
dialogBase.SetButtonText(text.Get("Account_0084"), text.Get("Account_0080"));
EventDelegate method_btn = new EventDelegate(delegate
{
UIManager.GetInstance().AccountTransferHelper.DataTransfer(close_callback);
});
dialogBase.SetButtonDelegate(method_btn);
}));
BtnFaceBook.buttons[0].onClick.Add(new EventDelegate(delegate
{
dialog.CloseWithoutSelect();
UIManager.GetInstance().AccountTransferHelper.PressedTransitongButton = CuteNetworkDefine.ACCOUNT_TYPE.FACEBOOK;
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_COMMON_BUTTON);
DialogBase dialogBase = UIManager.GetInstance().CreateDialogClose();
dialogBase.SetSize(DialogBase.Size.M);
dialogBase.SetTitleLabel(text.Get("Account_0058"));
dialogBase.SetText(text.Get("Account_0079"));
dialogBase.SetButtonLayout(DialogBase.ButtonLayout.BlueBtn_GrayBtn);
dialogBase.SetButtonText(text.Get("Account_0084"), text.Get("Account_0080"));
EventDelegate method_btn = new EventDelegate(delegate
{
UIManager.GetInstance().WebViewHelper.CreateOpenURLWindow(WebViewHelper.UrlType.FACEBOOK);
});
dialogBase.SetButtonDelegate(method_btn);
}));
ButtonGetCode.buttons[0].onClick.Add(new EventDelegate(delegate
{
OnClickPasswordSettingButton(dialog, from);
}));
return dialog;
}
private void OnClickIdPasswordInputButton(DialogBase previousDialog, TransitionOriginalScreen from)
{
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
previousDialog.CloseWithoutSelect();
DialogBase idPasswordInputDialog = UIManager.GetInstance().AccountTransferHelper.CreateAccountTransferDialog(DisplayType.ID_PASSWORD_INPUT, from);
IdPasswordInput idPasswordInputObject = idPasswordInputDialog.InsideObject.GetComponent<AccountBase>()._idPasswordInput;
UIInputWizard idInput = idPasswordInputObject._idInput;
UIInputWizard passwordInput = idPasswordInputObject._passwordInput;
EventDelegate item = new EventDelegate(delegate
{
idPasswordInputDialog.SetButtonDisable(string.IsNullOrEmpty(idInput.value) || string.IsNullOrEmpty(passwordInput.value));
});
UIInputWizard[] array = new UIInputWizard[2] { idInput, passwordInput };
foreach (UIInputWizard input in array)
{
EventDelegate item2 = new EventDelegate(delegate
{
InputDialog.TextInputLimitCheck(input.characterLimit);
});
input.onChange.Add(item);
input.onSubmit.Add(item2);
input.onDeselect.Add(item2);
}
SystemText systemText = Data.SystemText;
idPasswordInputDialog.SetSize(DialogBase.Size.S);
idPasswordInputDialog.SetTitleLabel(systemText.Get("Account_0001"));
idPasswordInputDialog.SetButtonLayout(DialogBase.ButtonLayout.DecisionBtn);
idPasswordInputDialog.SetButtonDisable(isEnableOK: true);
idPasswordInputDialog.isNotCloseWindowButton1 = true;
idPasswordInputDialog.onPushButton1 = delegate
{
string userId = idInput.value;
string passwordHash = Cryptographer.MakeMd5(passwordInput.value);
CheckTimeSlipRotationPeriod(delegate
{
GetGameDataByTransitionCode getGameDataByTransitionCode = new GetGameDataByTransitionCode();
getGameDataByTransitionCode.SetParameter(userId, passwordHash);
UIManager.GetInstance().StartCoroutine(Toolbox.NetworkManager.Connect(getGameDataByTransitionCode, delegate(NetworkTask.ResultCode code)
{
idPasswordInputDialog.Close();
UIManager.GetInstance().AccountTransferHelper.ShowAccountMagritionConfirmByTransferCode(code);
}, null, delegate
{
idPasswordInputObject.ResetInputValue();
}));
});
};
}
private static void CheckTimeSlipRotationPeriod(Action onFinish)
{
if (UIManager.GetInstance().GetCurrentScene() == UIManager.ViewScene.Title)
{
onFinish.Call();
return;
}
CheckTimeSlipRotationPeriodTask task = new CheckTimeSlipRotationPeriodTask();
UIManager.GetInstance().StartCoroutine(Toolbox.NetworkManager.Connect(task, delegate
{
onFinish.Call();
}));
}
private void OnClickPasswordSettingButton(DialogBase previousDialog, TransitionOriginalScreen from)
{
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
previousDialog.CloseWithoutSelect();
SystemText text = Data.SystemText;
DialogBase dialogBase = UIManager.GetInstance().CreateDialogClose();
dialogBase.SetSize(DialogBase.Size.M);
dialogBase.SetTitleLabel(text.Get("Account_0101"));
dialogBase.SetText(text.Get("Account_0102"));
dialogBase.SetButtonLayout(DialogBase.ButtonLayout.BlueBtn_CancelBtn);
dialogBase.SetButtonText(text.Get("Account_0103"));
dialogBase.onPushButton1 = delegate
{
DialogBase passwordSettingDialog = UIManager.GetInstance().AccountTransferHelper.CreateAccountTransferDialog(DisplayType.PASSWORD_SETTING, from);
AccountBase passwordSettingAccountBase = passwordSettingDialog.InsideObject.GetComponent<AccountBase>();
PasswordSetting passwordSettingObject = passwordSettingAccountBase._passwordSetting;
UIInputWizard firstInput = passwordSettingObject._firstInput;
UIInputWizard secondInput = passwordSettingObject._secondInput;
EventDelegate eventDelegate = new EventDelegate(delegate
{
passwordSettingDialog.SetButtonDisable(string.IsNullOrEmpty(firstInput.value) || string.IsNullOrEmpty(secondInput.value) || !passwordSettingObject._acceptToggle.GetValue());
});
UIInputWizard[] array = new UIInputWizard[2] { firstInput, secondInput };
foreach (UIInputWizard input in array)
{
EventDelegate item = new EventDelegate(delegate
{
InputDialog.TextInputLimitCheck(input.characterLimit);
});
input.onChange.Add(eventDelegate);
input.onSubmit.Add(item);
input.onDeselect.Add(item);
}
passwordSettingObject._privacyPolicyButton.onClick.Add(new EventDelegate(delegate
{
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_BTN_DECIDE);
UIManager.GetInstance().WebViewHelper.OpenWebView(WebViewHelper.WebViewType.PRIVACY_POLICY);
}));
ItemToggle acceptToggle = passwordSettingObject._acceptToggle;
acceptToggle.SetValue(value: false);
acceptToggle.AddChangeCallback(eventDelegate);
passwordSettingDialog.SetSize(DialogBase.Size.M);
passwordSettingDialog.SetTitleLabel(text.Get("Account_0101"));
passwordSettingDialog.SetButtonLayout(DialogBase.ButtonLayout.BlueBtn_CancelBtn);
passwordSettingDialog.SetButtonText(text.Get("Account_0103"));
passwordSettingDialog.SetButtonDisable(isEnableOK: true);
passwordSettingDialog.isNotCloseWindowButton1 = true;
passwordSettingDialog.onPushButton1 = delegate
{
if (!CheckDataLinkPassword(firstInput.value, secondInput.value))
{
passwordSettingObject.ResetInputValue();
DialogBase dialogBase2 = UIManager.GetInstance().CreateDialogClose();
dialogBase2.SetPanelDepth(passwordSettingAccountBase.GetComponent<UIPanel>().depth + 5);
dialogBase2.SetTitleLabel(text.Get("Account_0101"));
dialogBase2.SetText(text.Get("Account_0107"));
dialogBase2.SetButtonLayout(DialogBase.ButtonLayout.OkBtn);
}
else
{
passwordSettingDialog.Close();
UpdatePassword(firstInput.value);
}
};
};
}
private static void UpdatePassword(string password)
{
SystemText text = Data.SystemText;
CheckTimeSlipRotationPeriodTask task = new CheckTimeSlipRotationPeriodTask();
UIManager.GetInstance().StartCoroutine(Toolbox.NetworkManager.Connect(task, delegate
{
string parameter = Cryptographer.MakeMd5(password);
PublistTransitionCode publistTransitionCode = new PublistTransitionCode();
publistTransitionCode.SetParameter(parameter);
UIManager.GetInstance().StartCoroutine(Toolbox.NetworkManager.Connect(publistTransitionCode, delegate
{
GameStartCheckTask.IsSetTransitionPassword = true;
GameMgr.GetIns().GetPrefabMgr().Load("UI/layoutParts/Dialog/PasswordConfirm");
DialogBase dialogBase = UIManager.GetInstance().CreateDialogClose();
dialogBase.SetSize(DialogBase.Size.M);
dialogBase.SetButtonLayout(DialogBase.ButtonLayout.OkBtn);
NguiObjs component = NGUITools.AddChild(dialogBase.gameObject, GameMgr.GetIns().GetPrefabMgr().Get("UI/layoutParts/Dialog/PasswordConfirm")).GetComponent<NguiObjs>();
component.labels[0].text = text.Get("Account_0108");
component.labels[1].text = text.Get("Account_0109");
component.labels[2].text = text.Get("Profile_0008") + ": " + VideoHostingUtil.GetUserIDHidden($"{PlayerStaticData.UserViewerID:#,0}".Replace(",", " "));
component.labels[3].text = text.Get("Account_0110");
component.labels[4].text = text.Get("Profile_0009");
component.buttons[0].onClick.Add(new EventDelegate(delegate
{
NativePluginWrapper.SetStringToClipboard(PlayerStaticData.UserViewerID.ToString());
DialogBase dialogBase2 = UIManager.GetInstance().CreateDialogClose();
dialogBase2.SetPanelDepth(100);
dialogBase2.SetSize(DialogBase.Size.S);
dialogBase2.SetText(text.Get("Profile_0015"));
dialogBase2.SetButtonLayout(DialogBase.ButtonLayout.OkBtn);
}));
}));
}));
}
private bool CheckDataLinkPassword(string firstPass, string secondPass)
{
if (firstPass != secondPass)
{
return false;
}
if (firstPass.Length < 8 || 16 < firstPass.Length)
{
return false;
}
int num = 0;
int num2 = 0;
int num3 = 0;
foreach (char c in firstPass)
{
if ('0' <= c && c <= '9')
{
num++;
continue;
}
if ('a' <= c && c <= 'z')
{
num2++;
continue;
}
if ('A' <= c && c <= 'Z')
{
num3++;
continue;
}
return false;
}
if (num <= 0 || num2 <= 0 || num3 <= 0)
{
return false;
}
return true;
}
private int GetActiveChildCount(Transform parent)
{
int num = 0;
for (int i = 0; i < parent.childCount; i++)
{
if (parent.GetChild(i).gameObject.activeSelf)
{
num++;
}
}
return num;
}
}