BeatSaberUIController.cs 19 KB

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