BeatSaberUIController.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. using HMUI;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using IPA.Utilities;
  6. using UnityEngine;
  7. using UnityEngine.UI;
  8. using Logger = SongBrowser.Logging.Logger;
  9. namespace SongBrowser.DataAccess
  10. {
  11. public class BeatSaberUIController
  12. {
  13. // Beat Saber UI Elements
  14. public LevelSelectionFlowCoordinator LevelSelectionFlowCoordinator;
  15. public LevelSelectionNavigationController LevelSelectionNavigationController;
  16. public LevelFilteringNavigationController LevelFilteringNavigationController;
  17. public LevelCollectionNavigationController LevelCollectionNavigationController;
  18. public LevelCollectionViewController LevelCollectionViewController;
  19. public LevelCollectionTableView LevelCollectionTableView;
  20. public StandardLevelDetailViewController LevelDetailViewController;
  21. public StandardLevelDetailView StandardLevelDetailView;
  22. public BeatmapDifficultySegmentedControlController LevelDifficultyViewController;
  23. public BeatmapCharacteristicSegmentedControlController BeatmapCharacteristicSelectionViewController;
  24. public AnnotatedBeatmapLevelCollectionsViewController AnnotatedBeatmapLevelCollectionsViewController;
  25. public RectTransform LevelCollectionTableViewTransform;
  26. public Button TableViewPageUpButton;
  27. public Button TableViewPageDownButton;
  28. public RectTransform ActionButtons;
  29. public ScreenSystem ScreenSystem;
  30. public SimpleDialogPromptViewController SimpleDialogPromptViewControllerPrefab;
  31. /// <summary>
  32. /// Internal BeatSaber song model
  33. /// </summary>
  34. public BeatmapLevelsModel BeatmapLevelsModel;
  35. // Plugin Compat checks
  36. private bool _detectedTwitchPluginQueue = false;
  37. private bool _checkedForTwitchPlugin = false;
  38. /// <summary>
  39. /// Constructor. Acquire all necessary BeatSaberUi elements.
  40. /// </summary>
  41. /// <param name="flowCoordinator"></param>
  42. public BeatSaberUIController(LevelSelectionFlowCoordinator flowCoordinator)
  43. {
  44. Logger.Debug("Collecting all BeatSaberUI Elements...");
  45. LevelSelectionFlowCoordinator = flowCoordinator;
  46. // gather flow coordinator elements
  47. LevelSelectionNavigationController = LevelSelectionFlowCoordinator.GetField<LevelSelectionNavigationController, LevelSelectionFlowCoordinator>("levelSelectionNavigationController");
  48. Logger.Debug("Acquired LevelSelectionNavigationController [{0}]", LevelSelectionNavigationController.GetInstanceID());
  49. LevelFilteringNavigationController = LevelSelectionNavigationController.GetField<LevelFilteringNavigationController, LevelSelectionNavigationController>("_levelFilteringNavigationController");
  50. Logger.Debug("Acquired LevelFilteringNavigationController [{0}]", LevelFilteringNavigationController.GetInstanceID());
  51. LevelCollectionNavigationController = LevelSelectionNavigationController.GetField<LevelCollectionNavigationController, LevelSelectionNavigationController>("_levelCollectionNavigationController");
  52. Logger.Debug("Acquired LevelCollectionNavigationController [{0}]", LevelCollectionNavigationController.GetInstanceID());
  53. LevelCollectionViewController = LevelCollectionNavigationController.GetField<LevelCollectionViewController, LevelCollectionNavigationController>("_levelCollectionViewController");
  54. Logger.Debug("Acquired LevelPackLevelsViewController [{0}]", LevelCollectionViewController.GetInstanceID());
  55. LevelDetailViewController = LevelCollectionNavigationController.GetField<StandardLevelDetailViewController, LevelCollectionNavigationController>("_levelDetailViewController");
  56. Logger.Debug("Acquired StandardLevelDetailViewController [{0}]", LevelDetailViewController.GetInstanceID());
  57. LevelCollectionTableView = this.LevelCollectionViewController.GetField<LevelCollectionTableView, LevelCollectionViewController>("_levelCollectionTableView");
  58. Logger.Debug("Acquired LevelPackLevelsTableView [{0}]", LevelCollectionTableView.GetInstanceID());
  59. StandardLevelDetailView = LevelDetailViewController.GetField<StandardLevelDetailView, StandardLevelDetailViewController>("_standardLevelDetailView");
  60. Logger.Debug("Acquired StandardLevelDetailView [{0}]", StandardLevelDetailView.GetInstanceID());
  61. BeatmapCharacteristicSelectionViewController = StandardLevelDetailView.GetField<BeatmapCharacteristicSegmentedControlController, StandardLevelDetailView>("_beatmapCharacteristicSegmentedControlController");
  62. Logger.Debug("Acquired BeatmapCharacteristicSegmentedControlController [{0}]", BeatmapCharacteristicSelectionViewController.GetInstanceID());
  63. LevelDifficultyViewController = StandardLevelDetailView.GetField<BeatmapDifficultySegmentedControlController, StandardLevelDetailView>("_beatmapDifficultySegmentedControlController");
  64. Logger.Debug("Acquired BeatmapDifficultySegmentedControlController [{0}]", LevelDifficultyViewController.GetInstanceID());
  65. LevelCollectionTableViewTransform = LevelCollectionTableView.transform as RectTransform;
  66. Logger.Debug("Acquired TableViewRectTransform from LevelPackLevelsTableView [{0}]", LevelCollectionTableViewTransform.GetInstanceID());
  67. AnnotatedBeatmapLevelCollectionsViewController = LevelFilteringNavigationController.GetField<AnnotatedBeatmapLevelCollectionsViewController, LevelFilteringNavigationController>("_annotatedBeatmapLevelCollectionsViewController");
  68. Logger.Debug("Acquired AnnotatedBeatmapLevelCollectionsViewController from LevelFilteringNavigationController [{0}]", AnnotatedBeatmapLevelCollectionsViewController.GetInstanceID());
  69. TableView tableView = LevelCollectionTableView.GetField<TableView, LevelCollectionTableView>("_tableView");
  70. TableViewPageUpButton = tableView.GetField<Button, TableView>("_pageUpButton");
  71. TableViewPageDownButton = tableView.GetField<Button, TableView>("_pageDownButton");
  72. Logger.Debug("Acquired Page Up and Down buttons...");
  73. ActionButtons = StandardLevelDetailView.GetComponentsInChildren<RectTransform>().First(x => x.name == "ActionButtons");
  74. Logger.Debug("Acquired ActionButtons [{0}]", ActionButtons.GetInstanceID());
  75. ScreenSystem = Resources.FindObjectsOfTypeAll<ScreenSystem>().Last();
  76. Logger.Debug("Acquired ScreenSystem [{0}]", ScreenSystem.GetInstanceID());
  77. SimpleDialogPromptViewControllerPrefab = Resources.FindObjectsOfTypeAll<SimpleDialogPromptViewController>().Last();
  78. Logger.Debug("Acquired SimpleDialogPromptViewControllerPrefab [{0}]", SimpleDialogPromptViewControllerPrefab.GetInstanceID());
  79. BeatmapLevelsModel = Resources.FindObjectsOfTypeAll<BeatmapLevelsModel>().Last();
  80. Logger.Debug("Acquired BeatmapLevelsModel [{0}]", BeatmapLevelsModel);
  81. }
  82. /// <summary>
  83. /// Get the currently selected level pack within the LevelPackLevelViewController hierarchy.
  84. /// </summary>
  85. /// <returns></returns>
  86. private IBeatmapLevelPack GetCurrentSelectedLevelPack()
  87. {
  88. if (LevelCollectionNavigationController == null)
  89. {
  90. return null;
  91. }
  92. var pack = LevelCollectionNavigationController.GetField<IBeatmapLevelPack, LevelCollectionNavigationController>("_levelPack");
  93. return pack;
  94. }
  95. /// <summary>
  96. /// Helper to get either or playlist or
  97. /// </summary>
  98. /// <returns></returns>
  99. public IAnnotatedBeatmapLevelCollection GetCurrentSelectedAnnotatedBeatmapLevelCollection()
  100. {
  101. IAnnotatedBeatmapLevelCollection collection = GetCurrentSelectedLevelPack();
  102. if (collection == null)
  103. {
  104. collection = GetCurrentSelectedPlaylist();
  105. }
  106. return collection;
  107. }
  108. /// <summary>
  109. /// Get the currently selected level collection from playlists.
  110. /// </summary>
  111. /// <returns></returns>
  112. private IPlaylist GetCurrentSelectedPlaylist()
  113. {
  114. if (AnnotatedBeatmapLevelCollectionsViewController == null)
  115. {
  116. return null;
  117. }
  118. IPlaylist playlist = AnnotatedBeatmapLevelCollectionsViewController.selectedAnnotatedBeatmapLevelCollection as IPlaylist;
  119. return playlist;
  120. }
  121. /// <summary>
  122. /// Get level collection by level collection name.
  123. /// </summary>
  124. /// <param name="levelCollectionName"></param>
  125. /// <returns></returns>
  126. public IAnnotatedBeatmapLevelCollection GetLevelCollectionByName(String levelCollectionName)
  127. {
  128. IAnnotatedBeatmapLevelCollection levelCollection = null;
  129. // search level packs
  130. BeatmapLevelPackCollectionSO beatMapLevelPackCollection = Resources.FindObjectsOfTypeAll<BeatmapLevelPackCollectionSO>().Last();
  131. IBeatmapLevelPack[] levelPacks = beatMapLevelPackCollection.GetField<IBeatmapLevelPack[], BeatmapLevelPackCollectionSO>("_allBeatmapLevelPacks");
  132. foreach (IBeatmapLevelPack o in levelPacks)
  133. {
  134. if (String.Equals(o.collectionName, levelCollectionName))
  135. {
  136. levelCollection = o;
  137. break;
  138. }
  139. }
  140. // search playlists
  141. if (levelCollection == null)
  142. {
  143. IReadOnlyList<IAnnotatedBeatmapLevelCollection> _annotatedBeatmapLevelCollections = AnnotatedBeatmapLevelCollectionsViewController.GetField<IReadOnlyList<IAnnotatedBeatmapLevelCollection>, AnnotatedBeatmapLevelCollectionsViewController>("_annotatedBeatmapLevelCollections");
  144. foreach (IAnnotatedBeatmapLevelCollection c in _annotatedBeatmapLevelCollections)
  145. {
  146. if (String.Equals(c.collectionName, levelCollectionName))
  147. {
  148. levelCollection = c;
  149. break;
  150. }
  151. }
  152. }
  153. return levelCollection;
  154. }
  155. /// <summary>
  156. /// Get Current levels from current level collection.
  157. /// </summary>
  158. /// <returns></returns>
  159. public IPreviewBeatmapLevel[] GetCurrentLevelCollectionLevels()
  160. {
  161. var levelCollection = GetCurrentSelectedAnnotatedBeatmapLevelCollection();
  162. if (levelCollection == null)
  163. {
  164. Logger.Debug("Current selected level collection is null for some reason...");
  165. return null;
  166. }
  167. return levelCollection.beatmapLevelCollection.beatmapLevels;
  168. }
  169. public bool SelectLevelCategory(String levelCategoryName)
  170. {
  171. Logger.Trace("SelectLevelCategory({0})", levelCategoryName);
  172. try
  173. {
  174. if (String.IsNullOrEmpty(levelCategoryName))
  175. {
  176. // hack for now, just assume custom levels if a user has an old settings file, corrects itself first time they change level packs.
  177. levelCategoryName = SelectLevelCategoryViewController.LevelCategory.CustomSongs.ToString();
  178. }
  179. SelectLevelCategoryViewController.LevelCategory category;
  180. try
  181. {
  182. category = (SelectLevelCategoryViewController.LevelCategory)Enum.Parse(typeof(SelectLevelCategoryViewController.LevelCategory), levelCategoryName, true);
  183. }
  184. catch (Exception)
  185. {
  186. // invalid input
  187. return false;
  188. }
  189. if (category == LevelFilteringNavigationController.selectedLevelCategory)
  190. {
  191. Logger.Debug($"Level category [{category}] is already selected");
  192. return false;
  193. }
  194. Logger.Info("Selecting level category: {0}", levelCategoryName);
  195. var selectLeveCategoryViewController = LevelFilteringNavigationController.GetComponentInChildren<SelectLevelCategoryViewController>();
  196. var iconSegementController = selectLeveCategoryViewController.GetComponentInChildren<IconSegmentedControl>();
  197. int selectCellNumber = (from x in selectLeveCategoryViewController.GetField<SelectLevelCategoryViewController.LevelCategoryInfo[], SelectLevelCategoryViewController>("_levelCategoryInfos")
  198. select x.levelCategory).ToList().IndexOf(category);
  199. iconSegementController.SelectCellWithNumber(selectCellNumber);
  200. //selectLeveCategoryViewController.LevelFilterCategoryIconSegmentedControlDidSelectCell(iconSegementController, selectCellNumber);
  201. LevelFilteringNavigationController.UpdateSecondChildControllerContent(category);
  202. //AnnotatedBeatmapLevelCollectionsViewController.RefreshAvailability();
  203. Logger.Debug("Done selecting level category.");
  204. return true;
  205. } catch (Exception e)
  206. {
  207. Logger.Exception(e);
  208. }
  209. return false;
  210. }
  211. /// <summary>
  212. /// Select a level collection.
  213. /// </summary>
  214. /// <param name="levelCollectionName"></param>
  215. public void SelectLevelCollection(String levelCollectionName)
  216. {
  217. Logger.Trace("SelectLevelCollection({0})", levelCollectionName);
  218. try
  219. {
  220. IAnnotatedBeatmapLevelCollection collection = GetLevelCollectionByName(levelCollectionName);
  221. if (collection == null)
  222. {
  223. Logger.Debug("Could not locate requested level collection...");
  224. return;
  225. }
  226. Logger.Info("Selecting level collection: {0}", collection.collectionName);
  227. LevelFilteringNavigationController.SelectAnnotatedBeatmapLevelCollection(collection as IBeatmapLevelPack);
  228. Logger.Debug("Done selecting level collection!");
  229. }
  230. catch (Exception e)
  231. {
  232. Logger.Exception(e);
  233. }
  234. }
  235. /// <summary>
  236. /// Scroll TableView to proper row, fire events.
  237. /// </summary>
  238. /// <param name="table"></param>
  239. /// <param name="levelID"></param>
  240. public void SelectAndScrollToLevel(string levelID)
  241. {
  242. Logger.Debug("Scrolling to LevelID: {0}", levelID);
  243. // Check once per load
  244. if (!_checkedForTwitchPlugin)
  245. {
  246. Logger.Info("Checking for BeatSaber Twitch Integration Plugin...");
  247. _detectedTwitchPluginQueue = Resources.FindObjectsOfTypeAll<HMUI.ViewController>().Any(x => x.name == "RequestInfo");
  248. Logger.Info("BeatSaber Twitch Integration plugin detected: " + _detectedTwitchPluginQueue);
  249. _checkedForTwitchPlugin = true;
  250. }
  251. // Skip scrolling to level if twitch plugin has queue active.
  252. if (_detectedTwitchPluginQueue)
  253. {
  254. Logger.Debug("Skipping SelectAndScrollToLevel() because we detected Twitch Integration Plugin has a Queue active...");
  255. return;
  256. }
  257. // try to find the index and scroll to it
  258. int selectedIndex = 0;
  259. List<IPreviewBeatmapLevel> levels = GetCurrentLevelCollectionLevels().ToList();
  260. if (levels.Count <= 0)
  261. {
  262. return;
  263. }
  264. // acquire the index or try the last row
  265. selectedIndex = levels.FindIndex(x => x.levelID == levelID);
  266. if (selectedIndex < 0)
  267. {
  268. // this might look like an off by one error but the _level list we keep is missing the header entry BeatSaber.
  269. // so the last row is +1 the max index, the count.
  270. int maxCount = levels.Count;
  271. int selectedRow = LevelCollectionTableView.GetField<int, LevelCollectionTableView>("_selectedRow");
  272. Logger.Debug("Song is not in the level pack, cannot scroll to it... Using last known row {0}/{1}", selectedRow, maxCount);
  273. selectedIndex = Math.Min(maxCount, selectedRow);
  274. }
  275. else if (LevelCollectionViewController.GetField<bool, LevelCollectionViewController>("_showHeader"))
  276. {
  277. // the header counts as an index, so if the index came from the level array we have to add 1.
  278. selectedIndex += 1;
  279. }
  280. ScrollToLevelByRow(selectedIndex);
  281. }
  282. /// <summary>
  283. /// Scroll to a level by Row
  284. /// </summary>
  285. /// <param name="selectedIndex"></param>
  286. public void ScrollToLevelByRow(int selectedIndex)
  287. {
  288. Logger.Debug("Scrolling level list to idx: {0}", selectedIndex);
  289. TableView tableView = LevelCollectionTableView.GetField<TableView, LevelCollectionTableView>("_tableView");
  290. var selectedRow = LevelCollectionTableView.GetField<int, LevelCollectionTableView>("_selectedRow");
  291. if (selectedRow != selectedIndex && LevelCollectionTableView.isActiveAndEnabled)
  292. {
  293. LevelCollectionTableView.HandleDidSelectRowEvent(tableView, selectedIndex);
  294. }
  295. tableView.ScrollToCellWithIdx(selectedIndex, TableViewScroller.ScrollPositionType.Beginning, true);
  296. tableView.SelectCellWithIdx(selectedIndex);
  297. }
  298. /// <summary>
  299. /// Try to refresh the song list. Broken for now.
  300. /// </summary>
  301. public void RefreshSongList(string currentSelectedLevelId, bool scrollToLevel = true)
  302. {
  303. Logger.Info("Refreshing the song list view.");
  304. try
  305. {
  306. var levels = GetCurrentLevelCollectionLevels();
  307. if (levels == null)
  308. {
  309. Logger.Info("Nothing to refresh yet.");
  310. return;
  311. }
  312. Logger.Debug("Checking if TableView is initialized...");
  313. TableView tableView = LevelCollectionTableView.GetField<TableView, LevelCollectionTableView>("_tableView");
  314. bool tableViewInit = tableView.GetField<bool, TableView>("_isInitialized");
  315. Logger.Debug("Reloading SongList TableView");
  316. tableView.ReloadData();
  317. Logger.Debug("Attempting to scroll to level [{0}]", currentSelectedLevelId);
  318. String selectedLevelID = currentSelectedLevelId;
  319. if (!String.IsNullOrEmpty(currentSelectedLevelId))
  320. {
  321. selectedLevelID = currentSelectedLevelId;
  322. }
  323. else
  324. {
  325. if (levels.Length > 0)
  326. {
  327. Logger.Debug("Currently selected level ID does not exist, picking the first...");
  328. selectedLevelID = levels.FirstOrDefault().levelID;
  329. }
  330. }
  331. if (scrollToLevel)
  332. {
  333. SelectAndScrollToLevel(selectedLevelID);
  334. }
  335. }
  336. catch (Exception e)
  337. {
  338. Logger.Exception("Exception refreshing song list:", e);
  339. }
  340. }
  341. }
  342. }