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

@@ -7,7 +7,6 @@ public class BingoDrawTask : BaseTask
{
public class BingoDrawTaskParam : BaseParam
{
public int draw_count;
}
public class BingoDrawTaskData
@@ -58,14 +57,6 @@ public class BingoDrawTask : BaseTask
base.type = ApiType.Type.BingoDraw;
}
public void SetParameter(int buyNum, int viewerId)
{
BingoDrawTaskParam bingoDrawTaskParam = new BingoDrawTaskParam();
bingoDrawTaskParam.viewer_id = viewerId.ToString();
bingoDrawTaskParam.draw_count = buyNum;
base.Params = bingoDrawTaskParam;
}
protected override int Parse()
{
int num = base.Parse();

View File

@@ -1,224 +0,0 @@
using System;
using System.Collections.Generic;
using LitJson;
namespace Wizard.Bingo;
public class BingoInfoTask : BaseTask
{
public struct SquareData
{
public int SquareId;
public int DisplayId;
public bool IsOpen;
public int TreasureBoxGradeId;
public SquareData(JsonData squareJsonData)
{
SquareId = squareJsonData["square_id"].ToInt();
DisplayId = squareJsonData["display_id"].ToInt();
IsOpen = squareJsonData["is_open"].ToInt() == 1;
TreasureBoxGradeId = squareJsonData["treasure_box_grade_id"].ToInt();
}
}
public struct BingoMissionData
{
public string MissionTitle { get; private set; }
public int MissionCurrent { get; private set; }
public int MissionMax { get; private set; }
public bool IsCleared { get; private set; }
public ReceivedReward Reward { get; private set; }
public float MissionRatio
{
get
{
if (MissionMax == 0)
{
return 0f;
}
return (float)MissionCurrent / (float)MissionMax;
}
}
public BingoMissionData(JsonData json)
{
MissionTitle = json["mission_name"].ToString();
MissionCurrent = json["total_count"].ToInt();
MissionMax = json["require_number"].ToInt();
IsCleared = json["is_achieved"].ToInt() == 1;
ReceivedReward reward = ReceivedReward.CreateFromBingoMissionReward(json["reward_list"][0]);
Reward = reward;
}
}
public struct CurrentLineRewardInfo
{
public string LineNum { get; private set; }
public bool IsCleared { get; private set; }
public ReceivedReward Reward { get; private set; }
public CurrentLineRewardInfo(JsonData json)
{
LineNum = json["line_num"].ToString();
IsCleared = json["is_achieved"].ToInt() == 1;
ReceivedReward reward = ReceivedReward.CreateFromBingoMissionReward(json);
Reward = reward;
}
}
public class BingoInfoData
{
public int CampaignId;
public int SheetNum { get; private set; }
public int BingoTicketNum { get; private set; }
public int MaxSquareNum { get; private set; }
public int MaxSheetNum { get; private set; }
public int BingoDrawCost { get; private set; }
public List<SquareData> SquareDataList { get; private set; }
public Dictionary<int, List<ReceivedReward>> AllLineRewardsListDic { get; private set; }
public DateTime StartTime { get; private set; }
public DateTime EndTime { get; private set; }
public string NotificationId { get; private set; }
public List<BingoMissionData> MissionList { get; private set; }
public List<CurrentLineRewardInfo> CurrentLineRewardList { get; private set; }
public DateTime MissionEndTime { get; private set; }
public bool IsMissionActivePeriod { get; private set; }
public BingoInfoData(int campaignId, int sheetNum, int bingoTicketNum, int maxSquareNum, int maxSheetNum, int bingoDrawCost, DateTime startTime, DateTime endTime, List<SquareData> squareDataList, Dictionary<int, List<ReceivedReward>> allLineRewardsListDic, string notificationId, List<BingoMissionData> missionList, List<CurrentLineRewardInfo> currentLineRewardList, DateTime missionEndTime, bool isMissionActivePeriod)
{
CampaignId = campaignId;
SheetNum = sheetNum;
BingoTicketNum = bingoTicketNum;
MaxSquareNum = maxSquareNum;
MaxSheetNum = maxSheetNum;
BingoDrawCost = bingoDrawCost;
StartTime = startTime;
EndTime = endTime;
SquareDataList = squareDataList;
AllLineRewardsListDic = allLineRewardsListDic;
NotificationId = notificationId;
MissionList = missionList;
CurrentLineRewardList = currentLineRewardList;
MissionEndTime = missionEndTime;
IsMissionActivePeriod = isMissionActivePeriod;
}
}
public class BingoInfoTaskParam : BaseParam
{
public bool is_display_update;
}
public BingoInfoData Result { get; private set; }
public bool CanGiveDailyLoginBonus { get; private set; }
public BingoDrawTask.BingoDrawTaskData BingoLoginDrawResult { get; private set; }
public BingoInfoTask()
{
base.type = ApiType.Type.BingoInfo;
}
public void SetParameter(bool isDisplayUpdate)
{
BingoInfoTaskParam bingoInfoTaskParam = new BingoInfoTaskParam();
bingoInfoTaskParam.is_display_update = isDisplayUpdate;
base.Params = bingoInfoTaskParam;
}
protected override int Parse()
{
int num = base.Parse();
if (num != 1)
{
return num;
}
JsonData jsonData = base.ResponseData["data"];
CanGiveDailyLoginBonus = jsonData.GetValueOrDefault("can_give_login_bonus", defaultValue: false);
if (CanGiveDailyLoginBonus)
{
return num;
}
int campaignId = jsonData["campaign_id"].ToInt();
int sheetNum = jsonData["sheet_num"].ToInt();
int bingoTicketNum = jsonData["bingo_ticket_num"].ToInt();
int maxSquareNum = jsonData["max_square_num"].ToInt();
int maxSheetNum = jsonData["max_sheet_num"].ToInt();
int bingoDrawCost = jsonData["bingo_draw_cost"].ToInt();
string notificationId = jsonData["notice_text_id"].ToString();
DateTime startTime = DateTime.Parse(jsonData["start_time"].ToString());
DateTime endTime = DateTime.Parse(jsonData["end_time"].ToString());
DateTime missionEndTime = DateTime.Parse(jsonData["mission_end_time"].ToString());
bool isMissionActivePeriod = jsonData["is_mission_active_period"].ToBoolean();
JsonData jsonData2 = jsonData["square_list"];
List<SquareData> list = new List<SquareData>();
for (int i = 1; i < jsonData2.Count; i++)
{
SquareData item = new SquareData(jsonData2[i]);
list.Add(item);
}
JsonData jsonData3 = jsonData["all_line_reward_list"];
Dictionary<int, List<ReceivedReward>> dictionary = new Dictionary<int, List<ReceivedReward>>();
for (int j = 0; j < jsonData3.Count; j++)
{
for (int k = 0; k < jsonData3[j].Count; k++)
{
ReceivedReward item2 = ReceivedReward.CreateFromBingoLineReward(jsonData3[j][k]);
if (dictionary.ContainsKey(j))
{
dictionary[j].Add(item2);
continue;
}
dictionary[j] = new List<ReceivedReward>();
dictionary[j].Add(item2);
}
}
JsonData jsonData4 = jsonData["mission_list"];
List<BingoMissionData> list2 = new List<BingoMissionData>();
for (int l = 0; l < jsonData4.Count; l++)
{
BingoMissionData item3 = new BingoMissionData(jsonData4[l]);
list2.Add(item3);
}
JsonData jsonData5 = jsonData["current_line_reward_list"];
List<CurrentLineRewardInfo> list3 = new List<CurrentLineRewardInfo>();
for (int m = 0; m < jsonData5.Count; m++)
{
CurrentLineRewardInfo item4 = new CurrentLineRewardInfo(jsonData5[m]);
list3.Add(item4);
}
if (jsonData.TryGetValue("login_bonus", out var value))
{
BingoLoginDrawResult = BingoDrawTask.ParseBingoDrawResult(value, isBingoLoginBonus: true);
PlayerStaticData.UpdateHaveUserGoodsNumByJsonData(value["reward_list"]);
}
Result = new BingoInfoData(campaignId, sheetNum, bingoTicketNum, maxSquareNum, maxSheetNum, bingoDrawCost, startTime, endTime, list, dictionary, notificationId, list2, list3, missionEndTime, isMissionActivePeriod);
return num;
}
}

View File

@@ -1,55 +0,0 @@
using UnityEngine;
namespace Wizard.Bingo;
public class BingoMissionDialog : MonoBehaviour
{
[SerializeField]
private GameObject _bingoMissionItemPrefab;
[SerializeField]
private UIGrid _grid;
[SerializeField]
private UIScrollView _scrollPanel;
private BingoInfoTask.BingoInfoData _bingoInfoData;
private ResourceHandler _resourceHandler;
public static void Create(GameObject prefab, BingoInfoTask.BingoInfoData bingoData)
{
DialogBase dialogBase = UIManager.GetInstance().CreateDialogClose();
BingoMissionDialog missionDialog = Object.Instantiate(prefab).GetComponent<BingoMissionDialog>();
missionDialog._resourceHandler = missionDialog.gameObject.AddMissingComponent<ResourceHandler>();
dialogBase.SetObj(missionDialog.gameObject);
dialogBase.SetSize(DialogBase.Size.M);
dialogBase.SetTitleLabel(Data.SystemText.Get("Bingo_0005"));
dialogBase.SetButtonLayout(DialogBase.ButtonLayout.CloseBtn);
dialogBase.OnClose = delegate
{
missionDialog._resourceHandler.UnloadAll();
};
missionDialog.Initialize(bingoData);
}
private void Initialize(BingoInfoTask.BingoInfoData bingoInfoData)
{
_bingoInfoData = bingoInfoData;
UpdateMissionView();
}
private void UpdateMissionView()
{
_grid.transform.DestroyChildren();
for (int i = 0; i < _bingoInfoData.MissionList.Count; i++)
{
bool flag = i + 1 == _bingoInfoData.MissionList.Count;
AchievementWindowBase component = NGUITools.AddChild(_grid.gameObject, _bingoMissionItemPrefab).GetComponent<AchievementWindowBase>();
component.gameObject.SetActive(value: true);
component.SetBingoMission(_bingoInfoData.MissionList[i], !flag, _resourceHandler);
}
_grid.Reposition();
_scrollPanel.ResetPosition();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,179 +0,0 @@
using UnityEngine;
namespace Wizard.Bingo;
public class BingoRewardsDialog : MonoBehaviour
{
public enum MissionViewTab
{
First,
Second,
Third,
Fourth,
AfterFifth
}
[SerializeField]
private GameObject _bingoMissionItemPrefab;
[SerializeField]
private UIGrid _grid;
private ResourceHandler _resourceHandler;
[SerializeField]
private UIButton _firstRewardsButton;
[SerializeField]
private UIButton _secondRewardsButton;
[SerializeField]
private UIButton _thirdRewardsButton;
[SerializeField]
private UIButton _fourthRewardsButton;
[SerializeField]
private UIButton _fifthRewardsButton;
[SerializeField]
private UIScrollView _scrollPanel;
private BingoInfoTask.BingoInfoData _bingoInfoData;
private MissionViewTab _currentViewTab;
public static void Create(GameObject prefab, BingoInfoTask.BingoInfoData bingoData)
{
DialogBase dialogBase = UIManager.GetInstance().CreateDialogClose();
BingoRewardsDialog rewardDialog = Object.Instantiate(prefab).GetComponent<BingoRewardsDialog>();
rewardDialog._resourceHandler = rewardDialog.gameObject.AddMissingComponent<ResourceHandler>();
dialogBase.SetObj(rewardDialog.gameObject);
dialogBase.SetSize(DialogBase.Size.M);
dialogBase.SetTitleLabel(Data.SystemText.Get("Bingo_0006"));
dialogBase.SetButtonLayout(DialogBase.ButtonLayout.CloseBtn);
dialogBase.OnClose = delegate
{
rewardDialog._resourceHandler.UnloadAll();
};
rewardDialog.Initialize(bingoData);
}
private void Initialize(BingoInfoTask.BingoInfoData bingoInfoData)
{
_bingoInfoData = bingoInfoData;
MissionViewTab viewTab = (MissionViewTab)Mathf.Clamp(_bingoInfoData.SheetNum - 1, 0, 4);
OnPushTabButton(viewTab, firstCall: true);
_firstRewardsButton.onClick.Clear();
_firstRewardsButton.GetComponentInChildren<UILabel>().text = string.Format(Data.SystemText.Get("Bingo_0012"), GetCurrentTabIndex(MissionViewTab.First));
_firstRewardsButton.onClick.Add(new EventDelegate(delegate
{
OnPushTabButton(MissionViewTab.First);
}));
_secondRewardsButton.onClick.Clear();
_secondRewardsButton.GetComponentInChildren<UILabel>().text = string.Format(Data.SystemText.Get("Bingo_0012"), GetCurrentTabIndex(MissionViewTab.Second));
_secondRewardsButton.onClick.Add(new EventDelegate(delegate
{
OnPushTabButton(MissionViewTab.Second);
}));
_thirdRewardsButton.onClick.Clear();
_thirdRewardsButton.GetComponentInChildren<UILabel>().text = string.Format(Data.SystemText.Get("Bingo_0012"), GetCurrentTabIndex(MissionViewTab.Third));
_thirdRewardsButton.onClick.Add(new EventDelegate(delegate
{
OnPushTabButton(MissionViewTab.Third);
}));
_fourthRewardsButton.onClick.Clear();
_fourthRewardsButton.GetComponentInChildren<UILabel>().text = string.Format(Data.SystemText.Get("Bingo_0012"), GetCurrentTabIndex(MissionViewTab.Fourth));
_fourthRewardsButton.onClick.Add(new EventDelegate(delegate
{
OnPushTabButton(MissionViewTab.Fourth);
}));
_fifthRewardsButton.onClick.Clear();
_fifthRewardsButton.GetComponentInChildren<UILabel>().text = string.Format(Data.SystemText.Get("Bingo_0013"), GetCurrentTabIndex(MissionViewTab.AfterFifth));
_fifthRewardsButton.onClick.Add(new EventDelegate(delegate
{
OnPushTabButton(MissionViewTab.AfterFifth);
}));
}
private int GetCurrentTabIndex(MissionViewTab missionViewTab)
{
return (int)(missionViewTab + 1);
}
private void OnPushTabButton(MissionViewTab viewTab, bool firstCall = false)
{
if (_currentViewTab != viewTab || firstCall)
{
_currentViewTab = viewTab;
UpdateMissionView(viewTab);
if (!firstCall)
{
GameMgr.GetIns().GetSoundMgr().PlaySe(Se.TYPE.SYS_TOGGLE_ON);
}
}
}
private void UpdateMissionView(MissionViewTab viewTab)
{
ChangeTab(viewTab);
UpdateTabSprite(viewTab);
}
public void ChangeTab(MissionViewTab viewTab)
{
_bingoInfoData.AllLineRewardsListDic.TryGetValue((int)viewTab, out var value);
_grid.transform.DestroyChildren();
for (int i = 0; i < value.Count; i++)
{
bool flag = i + 1 == value.Count;
AchievementWindowBase component = NGUITools.AddChild(_grid.gameObject, _bingoMissionItemPrefab).GetComponent<AchievementWindowBase>();
component.gameObject.SetActive(value: true);
component.SetBingoRewardDetails(value[i], !flag, _resourceHandler);
}
_grid.Reposition();
_scrollPanel.ResetPosition();
}
private void UpdateTabSprite(MissionViewTab scene)
{
switch (scene)
{
case MissionViewTab.First:
_firstRewardsButton.normalSprite = _firstRewardsButton.pressedSprite;
_secondRewardsButton.normalSprite = _secondRewardsButton.disabledSprite;
_thirdRewardsButton.normalSprite = _thirdRewardsButton.disabledSprite;
_fourthRewardsButton.normalSprite = _fourthRewardsButton.disabledSprite;
_fifthRewardsButton.normalSprite = _fifthRewardsButton.disabledSprite;
break;
case MissionViewTab.Second:
_firstRewardsButton.normalSprite = _firstRewardsButton.disabledSprite;
_secondRewardsButton.normalSprite = _secondRewardsButton.pressedSprite;
_thirdRewardsButton.normalSprite = _thirdRewardsButton.disabledSprite;
_fourthRewardsButton.normalSprite = _fourthRewardsButton.disabledSprite;
_fifthRewardsButton.normalSprite = _fifthRewardsButton.disabledSprite;
break;
case MissionViewTab.Third:
_firstRewardsButton.normalSprite = _firstRewardsButton.disabledSprite;
_secondRewardsButton.normalSprite = _secondRewardsButton.disabledSprite;
_thirdRewardsButton.normalSprite = _thirdRewardsButton.pressedSprite;
_fourthRewardsButton.normalSprite = _fourthRewardsButton.disabledSprite;
_fifthRewardsButton.normalSprite = _fifthRewardsButton.disabledSprite;
break;
case MissionViewTab.Fourth:
_firstRewardsButton.normalSprite = _firstRewardsButton.disabledSprite;
_secondRewardsButton.normalSprite = _secondRewardsButton.disabledSprite;
_thirdRewardsButton.normalSprite = _thirdRewardsButton.disabledSprite;
_fourthRewardsButton.normalSprite = _fourthRewardsButton.pressedSprite;
_fifthRewardsButton.normalSprite = _fifthRewardsButton.disabledSprite;
break;
case MissionViewTab.AfterFifth:
_firstRewardsButton.normalSprite = _firstRewardsButton.disabledSprite;
_secondRewardsButton.normalSprite = _secondRewardsButton.disabledSprite;
_thirdRewardsButton.normalSprite = _thirdRewardsButton.disabledSprite;
_fourthRewardsButton.normalSprite = _fourthRewardsButton.disabledSprite;
_fifthRewardsButton.normalSprite = _fifthRewardsButton.pressedSprite;
break;
}
}
}