SongBrowserModel.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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. String cachedSongsPath = Path.Combine(customSongsPath, ".cache");
  67. DateTime currentLastWriteTIme = File.GetLastWriteTimeUtc(customSongsPath);
  68. IEnumerable<string> directories = Directory.EnumerateDirectories(customSongsPath, "*.*", SearchOption.AllDirectories);
  69. // Get LastWriteTimes
  70. var Epoch = new DateTime(1970, 1, 1);
  71. foreach (string dir in directories)
  72. {
  73. // Flip slashes, match SongLoaderPlugin
  74. string slashed_dir = dir.Replace("\\", "/");
  75. //_log.Debug("Fetching LastWriteTime for {0}", slashed_dir);
  76. _cachedLastWriteTimes[slashed_dir] = (File.GetLastWriteTimeUtc(dir) - Epoch).TotalMilliseconds;
  77. }
  78. // Update song Infos
  79. this.UpdateSongInfos(gameplayMode);
  80. this.ProcessSongList();
  81. }
  82. /// <summary>
  83. /// Get the song infos from SongLoaderPluging
  84. /// </summary>
  85. private void UpdateSongInfos(GameplayMode gameplayMode)
  86. {
  87. _log.Trace("UpdateSongInfos for Gameplay Mode {0}", gameplayMode);
  88. SongLoaderPlugin.OverrideClasses.CustomLevelCollectionsForGameplayModes collections = SongLoaderPlugin.SongLoader.Instance.GetPrivateField<SongLoaderPlugin.OverrideClasses.CustomLevelCollectionsForGameplayModes>("_customLevelCollectionsForGameplayModes");
  89. _gameplayModeCollection = collections.GetCollection(gameplayMode) as SongLoaderPlugin.OverrideClasses.CustomLevelCollectionSO;
  90. _originalSongs = collections.GetLevels(gameplayMode).ToList();
  91. _sortedSongs = _originalSongs;
  92. _levelIdToCustomLevel = SongLoader.CustomLevels.ToDictionary(x => x.levelID, x => x);
  93. _log.Debug("Song Browser knows about {0} songs from SongLoader...", _sortedSongs.Count);
  94. }
  95. /// <summary>
  96. /// Sort the song list based on the settings.
  97. /// </summary>
  98. private void ProcessSongList()
  99. {
  100. _log.Trace("ProcessSongList()");
  101. // Weights used for keeping the original songs in order
  102. // Invert the weights from the game so we can order by descending and make LINQ work with us...
  103. /* Level4, Level2, Level9, Level5, Level10, Level6, Level7, Level1, Level3, Level8, Level11 */
  104. Dictionary<string, int> weights = new Dictionary<string, int>
  105. {
  106. ["Level4"] = 11,
  107. ["Level2"] = 10,
  108. ["Level9"] = 9,
  109. ["Level5"] = 8,
  110. ["Level10"] = 7,
  111. ["Level6"] = 6,
  112. ["Level7"] = 5,
  113. ["Level1"] = 4,
  114. ["Level3"] = 3,
  115. ["Level8"] = 2,
  116. ["Level11"] = 1
  117. };
  118. // This has come in handy many times for debugging issues with Newest.
  119. /*foreach (StandardLevelSO level in _originalSongs)
  120. {
  121. if (_levelIdToCustomLevel.ContainsKey(level.levelID))
  122. {
  123. _log.Debug("HAS KEY {0}: {1}", _levelIdToCustomLevel[level.levelID].customSongInfo.path, level.levelID);
  124. }
  125. else
  126. {
  127. _log.Debug("Missing KEY: {0}", level.levelID);
  128. }
  129. }*/
  130. Stopwatch stopwatch = Stopwatch.StartNew();
  131. switch (_settings.sortMode)
  132. {
  133. case SongSortMode.Favorites:
  134. _log.Info("Sorting song list as favorites");
  135. _sortedSongs = _originalSongs
  136. .AsQueryable()
  137. .OrderBy(x => _settings.favorites.Contains(x.levelID) == false)
  138. .ThenBy(x => x.songName)
  139. .ThenBy(x => x.songAuthorName)
  140. .ToList();
  141. break;
  142. case SongSortMode.Original:
  143. _log.Info("Sorting song list as original");
  144. _sortedSongs = _originalSongs
  145. .AsQueryable()
  146. .OrderByDescending(x => weights.ContainsKey(x.levelID) ? weights[x.levelID] : 0)
  147. .ThenBy(x => x.songName)
  148. .ToList();
  149. break;
  150. case SongSortMode.Newest:
  151. _log.Info("Sorting song list as newest.");
  152. _sortedSongs = _originalSongs
  153. .AsQueryable()
  154. .OrderBy(x => weights.ContainsKey(x.levelID) ? weights[x.levelID] : 0)
  155. .ThenByDescending(x => x.levelID.StartsWith("Level") ? weights[x.levelID] : _cachedLastWriteTimes[_levelIdToCustomLevel[x.levelID].customSongInfo.path])
  156. .ToList();
  157. break;
  158. case SongSortMode.Author:
  159. _log.Info("Sorting song list by author");
  160. _sortedSongs = _originalSongs
  161. .AsQueryable()
  162. .OrderBy(x => x.songAuthorName)
  163. .ThenBy(x => x.songName)
  164. .ToList();
  165. break;
  166. case SongSortMode.Default:
  167. default:
  168. _log.Info("Sorting song list as default (songName)");
  169. _sortedSongs = _originalSongs
  170. .AsQueryable()
  171. .OrderBy(x => x.songName)
  172. .ThenBy(x => x.songAuthorName)
  173. .ToList();
  174. break;
  175. }
  176. stopwatch.Stop();
  177. _log.Info("Sorting songs took {0}ms", stopwatch.ElapsedMilliseconds);
  178. }
  179. }
  180. }