AssetBundleDebuggerWindow.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. #if UNITY_2019_4_OR_NEWER
  2. using System;
  3. using System.Collections.Generic;
  4. using UnityEditor;
  5. using UnityEngine;
  6. using UnityEngine.UIElements;
  7. using UnityEditor.UIElements;
  8. using UnityEditor.Networking.PlayerConnection;
  9. using UnityEngine.Networking.PlayerConnection;
  10. namespace YooAsset.Editor
  11. {
  12. public class AssetBundleDebuggerWindow : EditorWindow
  13. {
  14. [MenuItem("YooAsset/AssetBundle Debugger", false, 104)]
  15. public static void OpenWindow()
  16. {
  17. AssetBundleDebuggerWindow wnd = GetWindow<AssetBundleDebuggerWindow>("AssetBundle Debugger", true, WindowsDefine.DockedWindowTypes);
  18. wnd.minSize = new Vector2(800, 600);
  19. }
  20. /// <summary>
  21. /// 视图模式
  22. /// </summary>
  23. private enum EViewMode
  24. {
  25. /// <summary>
  26. /// 内存视图
  27. /// </summary>
  28. MemoryView,
  29. /// <summary>
  30. /// 资源对象视图
  31. /// </summary>
  32. AssetView,
  33. /// <summary>
  34. /// 资源包视图
  35. /// </summary>
  36. BundleView,
  37. /// <summary>
  38. /// 异步操作视图
  39. /// </summary>
  40. OperationView,
  41. }
  42. private readonly Dictionary<int, RemotePlayerSession> _playerSessions = new Dictionary<int, RemotePlayerSession>();
  43. private ToolbarButton _playerName;
  44. private ToolbarMenu _viewModeMenu;
  45. private SliderInt _frameSlider;
  46. private DebuggerAssetListViewer _assetListViewer;
  47. private DebuggerBundleListViewer _bundleListViewer;
  48. private DebuggerOperationListViewer _operationListViewer;
  49. private EViewMode _viewMode;
  50. private string _searchKeyWord;
  51. private DebugReport _currentReport;
  52. private RemotePlayerSession _currentPlayerSession;
  53. private double _lastRepaintTime = 0;
  54. private int _nextRepaintIndex = -1;
  55. private int _lastRepaintIndex = 0;
  56. private int _rangeIndex = 0;
  57. public void CreateGUI()
  58. {
  59. try
  60. {
  61. VisualElement root = rootVisualElement;
  62. // 加载布局文件
  63. var visualAsset = UxmlLoader.LoadWindowUXML<AssetBundleDebuggerWindow>();
  64. if (visualAsset == null)
  65. return;
  66. visualAsset.CloneTree(root);
  67. // 采样按钮
  68. var sampleBtn = root.Q<Button>("SampleButton");
  69. sampleBtn.clicked += SampleBtn_onClick;
  70. // 导出按钮
  71. var exportBtn = root.Q<Button>("ExportButton");
  72. exportBtn.clicked += ExportBtn_clicked;
  73. // 用户列表菜单
  74. _playerName = root.Q<ToolbarButton>("PlayerName");
  75. _playerName.text = "Editor player";
  76. // 视口模式菜单
  77. _viewModeMenu = root.Q<ToolbarMenu>("ViewModeMenu");
  78. _viewModeMenu.menu.AppendAction(EViewMode.AssetView.ToString(), OnViewModeMenuChange, OnViewModeMenuStatusUpdate, EViewMode.AssetView);
  79. _viewModeMenu.menu.AppendAction(EViewMode.BundleView.ToString(), OnViewModeMenuChange, OnViewModeMenuStatusUpdate, EViewMode.BundleView);
  80. _viewModeMenu.menu.AppendAction(EViewMode.OperationView.ToString(), OnViewModeMenuChange, OnViewModeMenuStatusUpdate, EViewMode.OperationView);
  81. _viewModeMenu.text = EViewMode.AssetView.ToString();
  82. // 搜索栏
  83. var searchField = root.Q<ToolbarSearchField>("SearchField");
  84. searchField.RegisterValueChangedCallback(OnSearchKeyWordChange);
  85. // 帧数相关
  86. {
  87. _frameSlider = root.Q<SliderInt>("FrameSlider");
  88. _frameSlider.label = "Frame:";
  89. _frameSlider.highValue = 0;
  90. _frameSlider.lowValue = 0;
  91. _frameSlider.value = 0;
  92. _frameSlider.RegisterValueChangedCallback(evt =>
  93. {
  94. OnFrameSliderChange(evt.newValue);
  95. });
  96. var frameLast = root.Q<ToolbarButton>("FrameLast");
  97. frameLast.clicked += OnFrameLast_clicked;
  98. var frameNext = root.Q<ToolbarButton>("FrameNext");
  99. frameNext.clicked += OnFrameNext_clicked;
  100. var frameClear = root.Q<ToolbarButton>("FrameClear");
  101. frameClear.clicked += OnFrameClear_clicked;
  102. var recorderToggle = root.Q<ToggleRecord>("FrameRecord");
  103. recorderToggle.RegisterValueChangedCallback(OnRecordToggleValueChange);
  104. }
  105. // 加载视图
  106. _assetListViewer = new DebuggerAssetListViewer();
  107. _assetListViewer.InitViewer();
  108. // 加载视图
  109. _bundleListViewer = new DebuggerBundleListViewer();
  110. _bundleListViewer.InitViewer();
  111. // 加载视图
  112. _operationListViewer = new DebuggerOperationListViewer();
  113. _operationListViewer.InitViewer();
  114. // 显示视图
  115. _viewMode = EViewMode.AssetView;
  116. _assetListViewer.AttachParent(root);
  117. // 远程调试
  118. EditorConnection.instance.Initialize();
  119. EditorConnection.instance.RegisterConnection(OnHandleConnectionEvent);
  120. EditorConnection.instance.RegisterDisconnection(OnHandleDisconnectionEvent);
  121. EditorConnection.instance.Register(RemoteDebuggerDefine.kMsgPlayerSendToEditor, OnHandlePlayerMessage);
  122. RemoteEditorConnection.Instance.Initialize();
  123. RemoteEditorConnection.Instance.Register(RemoteDebuggerDefine.kMsgPlayerSendToEditor, OnHandlePlayerMessage);
  124. }
  125. catch (Exception e)
  126. {
  127. Debug.LogError(e.ToString());
  128. }
  129. }
  130. public void OnDestroy()
  131. {
  132. // 远程调试
  133. EditorConnection.instance.UnregisterConnection(OnHandleConnectionEvent);
  134. EditorConnection.instance.UnregisterDisconnection(OnHandleDisconnectionEvent);
  135. EditorConnection.instance.Unregister(RemoteDebuggerDefine.kMsgPlayerSendToEditor, OnHandlePlayerMessage);
  136. RemoteEditorConnection.Instance.Unregister(RemoteDebuggerDefine.kMsgPlayerSendToEditor);
  137. _playerSessions.Clear();
  138. }
  139. public void Update()
  140. {
  141. // 每间隔1秒绘制一次页面
  142. if (EditorApplication.timeSinceStartup - _lastRepaintTime > 1f)
  143. {
  144. _lastRepaintTime = EditorApplication.timeSinceStartup;
  145. if (_nextRepaintIndex >= 0)
  146. {
  147. RepaintFrame(_nextRepaintIndex);
  148. _nextRepaintIndex = -1;
  149. }
  150. }
  151. }
  152. private void OnHandleConnectionEvent(int playerId)
  153. {
  154. Debug.Log($"Game player connection : {playerId}");
  155. _playerName.text = $"Connected player : {playerId}";
  156. }
  157. private void OnHandleDisconnectionEvent(int playerId)
  158. {
  159. Debug.Log($"Game player disconnection : {playerId}");
  160. _playerName.text = $"Disconneced player : {playerId}";
  161. }
  162. private void OnHandlePlayerMessage(MessageEventArgs args)
  163. {
  164. int playerId = args.playerId;
  165. var debugReport = DebugReport.Deserialize(args.data);
  166. if (debugReport.DebuggerVersion != RemoteDebuggerDefine.DebuggerVersion)
  167. {
  168. Debug.LogWarning($"Debugger versions are inconsistent : {debugReport.DebuggerVersion} != {RemoteDebuggerDefine.DebuggerVersion}");
  169. return;
  170. }
  171. //Debug.Log($"Handle player {playerId} debug report !");
  172. _currentPlayerSession = GetOrCreatePlayerSession(playerId);
  173. _currentPlayerSession.AddDebugReport(debugReport);
  174. _nextRepaintIndex = _currentPlayerSession.MaxRangeValue;
  175. }
  176. private void OnFrameSliderChange(int sliderValue)
  177. {
  178. if (_currentPlayerSession != null)
  179. {
  180. _rangeIndex = _currentPlayerSession.ClampRangeIndex(sliderValue); ;
  181. RepaintFrame(_rangeIndex);
  182. }
  183. }
  184. private void OnFrameLast_clicked()
  185. {
  186. if (_currentPlayerSession != null)
  187. {
  188. _rangeIndex = _currentPlayerSession.ClampRangeIndex(_rangeIndex - 1);
  189. _frameSlider.value = _rangeIndex;
  190. RepaintFrame(_rangeIndex);
  191. }
  192. }
  193. private void OnFrameNext_clicked()
  194. {
  195. if (_currentPlayerSession != null)
  196. {
  197. _rangeIndex = _currentPlayerSession.ClampRangeIndex(_rangeIndex + 1);
  198. _frameSlider.value = _rangeIndex;
  199. RepaintFrame(_rangeIndex);
  200. }
  201. }
  202. private void OnFrameClear_clicked()
  203. {
  204. _nextRepaintIndex = -1;
  205. _lastRepaintIndex = 0;
  206. _rangeIndex = 0;
  207. _frameSlider.label = $"Frame:";
  208. _frameSlider.value = 0;
  209. _frameSlider.lowValue = 0;
  210. _frameSlider.highValue = 0;
  211. _assetListViewer.ClearView();
  212. _bundleListViewer.ClearView();
  213. _operationListViewer.ClearView();
  214. if (_currentPlayerSession != null)
  215. {
  216. _currentPlayerSession.ClearDebugReport();
  217. }
  218. }
  219. private void OnRecordToggleValueChange(ChangeEvent<bool> evt)
  220. {
  221. // 发送采集数据的命令
  222. RemoteCommand command = new RemoteCommand();
  223. command.CommandType = (int)ERemoteCommand.SampleAuto;
  224. command.CommandParam = evt.newValue ? "open" : "close";
  225. byte[] data = RemoteCommand.Serialize(command);
  226. EditorConnection.instance.Send(RemoteDebuggerDefine.kMsgEditorSendToPlayer, data);
  227. RemoteEditorConnection.Instance.Send(RemoteDebuggerDefine.kMsgEditorSendToPlayer, data);
  228. }
  229. private void SampleBtn_onClick()
  230. {
  231. // 发送采集数据的命令
  232. RemoteCommand command = new RemoteCommand();
  233. command.CommandType = (int)ERemoteCommand.SampleOnce;
  234. command.CommandParam = string.Empty;
  235. byte[] data = RemoteCommand.Serialize(command);
  236. EditorConnection.instance.Send(RemoteDebuggerDefine.kMsgEditorSendToPlayer, data);
  237. RemoteEditorConnection.Instance.Send(RemoteDebuggerDefine.kMsgEditorSendToPlayer, data);
  238. }
  239. private void ExportBtn_clicked()
  240. {
  241. if (_currentReport == null)
  242. {
  243. Debug.LogWarning("Debug report is null.");
  244. return;
  245. }
  246. string resultPath = EditorTools.OpenFolderPanel("Export JSON", "Assets/");
  247. if (resultPath != null)
  248. {
  249. // 注意:排序保证生成配置的稳定性
  250. foreach (var packageData in _currentReport.PackageDatas)
  251. {
  252. packageData.ProviderInfos.Sort();
  253. foreach (var providerInfo in packageData.ProviderInfos)
  254. {
  255. providerInfo.DependBundles.Sort();
  256. }
  257. }
  258. string filePath = $"{resultPath}/{nameof(DebugReport)}_{_currentReport.FrameCount}.json";
  259. string fileContent = JsonUtility.ToJson(_currentReport, true);
  260. FileUtility.WriteAllText(filePath, fileContent);
  261. }
  262. }
  263. private void OnSearchKeyWordChange(ChangeEvent<string> e)
  264. {
  265. _searchKeyWord = e.newValue;
  266. if (_currentReport != null)
  267. {
  268. _assetListViewer.RebuildView(_searchKeyWord);
  269. _bundleListViewer.RebuildView(_searchKeyWord);
  270. _operationListViewer.RebuildView(_searchKeyWord);
  271. }
  272. }
  273. private void OnViewModeMenuChange(DropdownMenuAction action)
  274. {
  275. var viewMode = (EViewMode)action.userData;
  276. if (_viewMode != viewMode)
  277. {
  278. _viewMode = viewMode;
  279. VisualElement root = this.rootVisualElement;
  280. _viewModeMenu.text = viewMode.ToString();
  281. if (viewMode == EViewMode.AssetView)
  282. {
  283. _assetListViewer.AttachParent(root);
  284. _bundleListViewer.DetachParent();
  285. _operationListViewer.DetachParent();
  286. }
  287. else if (viewMode == EViewMode.BundleView)
  288. {
  289. _assetListViewer.DetachParent();
  290. _bundleListViewer.AttachParent(root);
  291. _operationListViewer.DetachParent();
  292. }
  293. else if (viewMode == EViewMode.OperationView)
  294. {
  295. _assetListViewer.DetachParent();
  296. _bundleListViewer.DetachParent();
  297. _operationListViewer.AttachParent(root);
  298. }
  299. else
  300. {
  301. throw new NotImplementedException(viewMode.ToString());
  302. }
  303. // 重新绘制该帧数据
  304. RepaintFrame(_lastRepaintIndex);
  305. }
  306. }
  307. private DropdownMenuAction.Status OnViewModeMenuStatusUpdate(DropdownMenuAction action)
  308. {
  309. var viewMode = (EViewMode)action.userData;
  310. if (_viewMode == viewMode)
  311. return DropdownMenuAction.Status.Checked;
  312. else
  313. return DropdownMenuAction.Status.Normal;
  314. }
  315. private RemotePlayerSession GetOrCreatePlayerSession(int playerId)
  316. {
  317. if (_playerSessions.TryGetValue(playerId, out RemotePlayerSession session))
  318. {
  319. return session;
  320. }
  321. else
  322. {
  323. RemotePlayerSession newSession = new RemotePlayerSession(playerId);
  324. _playerSessions.Add(playerId, newSession);
  325. return newSession;
  326. }
  327. }
  328. private void RepaintFrame(int repaintIndex)
  329. {
  330. if (_currentPlayerSession == null)
  331. {
  332. _assetListViewer.ClearView();
  333. _bundleListViewer.ClearView();
  334. _operationListViewer.ClearView();
  335. return;
  336. }
  337. var debugReport = _currentPlayerSession.GetDebugReport(repaintIndex);
  338. if (debugReport != null)
  339. {
  340. _lastRepaintIndex = repaintIndex;
  341. _currentReport = debugReport;
  342. _frameSlider.label = $"Frame: {debugReport.FrameCount}";
  343. _frameSlider.highValue = _currentPlayerSession.MaxRangeValue;
  344. _frameSlider.value = repaintIndex;
  345. if (_viewMode == EViewMode.AssetView)
  346. {
  347. _assetListViewer.FillViewData(debugReport);
  348. _bundleListViewer.ClearView();
  349. _operationListViewer.ClearView();
  350. }
  351. else if (_viewMode == EViewMode.BundleView)
  352. {
  353. _assetListViewer.ClearView();
  354. _bundleListViewer.FillViewData(debugReport);
  355. _operationListViewer.ClearView();
  356. }
  357. else if (_viewMode == EViewMode.OperationView)
  358. {
  359. _assetListViewer.ClearView();
  360. _bundleListViewer.ClearView();
  361. _operationListViewer.FillViewData(debugReport);
  362. }
  363. else
  364. {
  365. throw new System.NotImplementedException(_viewMode.ToString());
  366. }
  367. }
  368. }
  369. }
  370. }
  371. #endif