SongBrowserModel.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. using SongBrowserPlugin.DataAccess;
  2. using SongLoaderPlugin;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.IO;
  7. using System.Linq;
  8. using UnityEngine;
  9. namespace SongBrowserPlugin
  10. {
  11. public class SongBrowserModel
  12. {
  13. private Logger _log = new Logger("SongBrowserModel");
  14. private SongBrowserSettings _settings;
  15. private List<StandardLevelSO> _sortedSongs;
  16. private List<StandardLevelSO> _originalSongs;
  17. private Dictionary<String, SongLoaderPlugin.OverrideClasses.CustomLevel> _levelIdToCustomLevel;
  18. private SongLoaderPlugin.OverrideClasses.CustomLevelCollectionSO _gameplayModeCollection;
  19. private Dictionary<String, double> _cachedLastWriteTimes;
  20. public static String LastSelectedLevelId { get; set; }
  21. public SongBrowserSettings Settings
  22. {
  23. get
  24. {
  25. return _settings;
  26. }
  27. }
  28. public List<StandardLevelSO> SortedSongList
  29. {
  30. get
  31. {
  32. return _sortedSongs;
  33. }
  34. }
  35. public Dictionary<String, SongLoaderPlugin.OverrideClasses.CustomLevel> LevelIdToCustomSongInfos
  36. {
  37. get
  38. {
  39. return _levelIdToCustomLevel;
  40. }
  41. }
  42. /// <summary>
  43. /// Constructor.
  44. /// </summary>
  45. public SongBrowserModel()
  46. {
  47. _cachedLastWriteTimes = new Dictionary<String, double>();
  48. }
  49. /// <summary>
  50. /// Init this model.
  51. /// </summary>
  52. /// <param name="songSelectionMasterView"></param>
  53. /// <param name="songListViewController"></param>
  54. public void Init()
  55. {
  56. _settings = SongBrowserSettings.Load();
  57. _log.Info("Settings loaded, sorting mode is: {0}", _settings.sortMode);
  58. }
  59. /// <summary>
  60. /// Get the song cache from the game.
  61. /// TODO: This might not even be necessary anymore. Need to test interactions with BeatSaverDownloader.
  62. /// </summary>
  63. public void UpdateSongLists(GameplayMode gameplayMode)
  64. {
  65. String customSongsPath = Path.Combine(Environment.CurrentDirectory, "CustomSongs");
  66. DateTime currentLastWriteTIme = File.GetLastWriteTimeUtc(customSongsPath);
  67. string[] directories = Directory.GetDirectories(customSongsPath);
  68. // Get LastWriteTimes
  69. var Epoch = new DateTime(1970, 1, 1);
  70. foreach (string dir in directories)
  71. {
  72. // Flip slashes, match SongLoaderPlugin
  73. string slashed_dir = dir.Replace("\\", "/");
  74. //_log.Debug("Fetching LastWriteTime for {0}", slashed_dir);
  75. _cachedLastWriteTimes[slashed_dir] = (File.GetLastWriteTimeUtc(dir) - Epoch).TotalMilliseconds;
  76. }
  77. // Update song Infos
  78. this.UpdateSongInfos(gameplayMode);
  79. this.ProcessSongList();
  80. }
  81. /// <summary>
  82. /// Get the song infos from SongLoaderPluging
  83. /// </summary>
  84. private void UpdateSongInfos(GameplayMode gameplayMode)
  85. {
  86. _log.Trace("UpdateSongInfos for Gameplay Mode {0}", gameplayMode);
  87. SongLoaderPlugin.OverrideClasses.CustomLevelCollectionsForGameplayModes collections = SongLoaderPlugin.SongLoader.Instance.GetPrivateField<SongLoaderPlugin.OverrideClasses.CustomLevelCollectionsForGameplayModes>("_customLevelCollectionsForGameplayModes");
  88. _gameplayModeCollection = collections.GetCollection(gameplayMode) as SongLoaderPlugin.OverrideClasses.CustomLevelCollectionSO;
  89. _originalSongs = collections.GetLevels(gameplayMode).ToList();
  90. _sortedSongs = _originalSongs;
  91. _levelIdToCustomLevel = SongLoader.CustomLevels.ToDictionary(x => x.levelID, x => x);
  92. _log.Debug("Song Browser knows about {0} songs from SongLoader...", _sortedSongs.Count);
  93. }
  94. /// <summary>
  95. /// Sort the song list based on the settings.
  96. /// </summary>
  97. private void ProcessSongList()
  98. {
  99. _log.Trace("ProcessSongList()");
  100. // Weights used for keeping the original songs in order
  101. // Invert the weights from the game so we can order by descending and make LINQ work with us...
  102. /* Level4, Level2, Level9, Level5, Level10, Level6, Level7, Level1, Level3, Level8, */
  103. Dictionary<string, int> weights = new Dictionary<string, int>
  104. {
  105. ["Level4"] = 11,
  106. ["Level2"] = 10,
  107. ["Level9"] = 9,
  108. ["Level5"] = 8,
  109. ["Level10"] = 7,
  110. ["Level6"] = 6,
  111. ["Level7"] = 5,
  112. ["Level1"] = 4,
  113. ["Level3"] = 3,
  114. ["Level8"] = 2,
  115. ["Level11"] = 1
  116. };
  117. // This has come in handy many times for debugging issues with Newest.
  118. /*foreach (StandardLevelSO level in _originalSongs)
  119. {
  120. if (_levelIdToCustomLevel.ContainsKey(level.levelID))
  121. {
  122. _log.Debug("HAS KEY: {0}", level.levelID);
  123. }
  124. else
  125. {
  126. _log.Debug("Missing KEY: {0}", level.levelID);
  127. }
  128. }*/
  129. Stopwatch stopwatch = Stopwatch.StartNew();
  130. switch (_settings.sortMode)
  131. {
  132. case SongSortMode.Favorites:
  133. _log.Info("Sorting song list as favorites");
  134. _sortedSongs = _originalSongs
  135. .AsQueryable()
  136. .OrderBy(x => _settings.favorites.Contains(x.levelID) == false)
  137. .ThenBy(x => x.songName)
  138. .ThenBy(x => x.songAuthorName)
  139. .ToList();
  140. break;
  141. case SongSortMode.Original:
  142. _log.Info("Sorting song list as original");
  143. _sortedSongs = _originalSongs
  144. .AsQueryable()
  145. .OrderByDescending(x => weights.ContainsKey(x.levelID) ? weights[x.levelID] : 0)
  146. .ThenBy(x => x.songName)
  147. .ToList();
  148. break;
  149. case SongSortMode.Newest:
  150. _log.Info("Sorting song list as newest.");
  151. _sortedSongs = _originalSongs
  152. .AsQueryable()
  153. .OrderBy(x => weights.ContainsKey(x.levelID) ? weights[x.levelID] : 0)
  154. .ThenByDescending(x => x.levelID.StartsWith("Level") ? weights[x.levelID] : _cachedLastWriteTimes[_levelIdToCustomLevel[x.levelID].customSongInfo.path])
  155. .ToList();
  156. break;
  157. case SongSortMode.Author:
  158. _log.Info("Sorting song list by author");
  159. _sortedSongs = _originalSongs
  160. .AsQueryable()
  161. .OrderBy(x => x.songAuthorName)
  162. .ThenBy(x => x.songName)
  163. .ToList();
  164. break;
  165. case SongSortMode.Default:
  166. default:
  167. _log.Info("Sorting song list as default (songName)");
  168. _sortedSongs = _originalSongs
  169. .AsQueryable()
  170. .OrderBy(x => x.songName)
  171. .ThenBy(x => x.songAuthorName)
  172. .ToList();
  173. break;
  174. }
  175. stopwatch.Stop();
  176. _log.Info("Sorting songs took {0}ms", stopwatch.ElapsedMilliseconds);
  177. }
  178. }
  179. }