private IEnumerator PopulateCacheFromFileCoroutine() { var coroutine = BeatmapDetailsCache.GetBeatmapDetailsFromCacheCoroutine(BeatmapDetailsLoader.CachedBeatmapDetailsFilePath); List <BeatmapDetails> loadedCache = null; while (coroutine.MoveNext()) { loadedCache = coroutine.Current; if (loadedCache == null) { yield return(null); } } if (loadedCache != null) { if (loadedCache.Count > 0) { Logger.log.Info($"Retrieved {loadedCache.Count} cached beatmap details from file"); } foreach (var detail in loadedCache) { if (!BeatmapDetailsLoader._cache.ContainsKey(detail.LevelID)) { BeatmapDetailsLoader._cache.Add(detail.LevelID, detail); } } } }
public async Task PopulateCacheFromFile() { var detailsList = await BeatmapDetailsCache.GetBeatmapDetailsFromCacheAsync(CachedBeatmapDetailsFilePath); foreach (var detail in detailsList) { _cache.TryAdd(detail.LevelID, detail); } }
public static IEnumerator <List <BeatmapDetails> > GetBeatmapDetailsFromCacheCoroutine(string path) { if (!File.Exists(path)) { Logger.log.Notice($"Cache file could not be found in the path: '{path}'"); yield break; } string fileContents = null; IEnumerator <string> cacheLoader = UnityMediaLoader.LoadTextCoroutine(path); while (cacheLoader.MoveNext()) { fileContents = cacheLoader.Current; if (fileContents == null) { yield return(null); } } if (string.IsNullOrEmpty(fileContents)) { Logger.log.Warn("Beatmap details cache is empty"); yield break; } BeatmapDetailsCache cache = null; try { cache = JsonConvert.DeserializeObject <BeatmapDetailsCache>(fileContents); } catch (JsonSerializationException) { Logger.log.Warn("Unable to deserialize cache file. Could be an older bersion of the cache file (will be replaced after the in-memory cache is rebuilt)"); yield break; } catch (Exception e) { Logger.log.Warn("Unexpected exception occurred when trying to deserialize beatmap details cache"); Logger.log.Debug(e); yield break; } if (cache.Version < CURRENT_CACHE_VERSION) { Logger.log.Warn("Beatmap details cache is outdated. Forcing the cache to be rebuilt"); } else { Logger.log.Info("Successfully loaded details cache from storage"); yield return(cache.Cache); } }
/// <summary> /// Save the cached BeatmapDetails objects to a JSON file located at CachedBeatmapDetailsFilePath. /// </summary> public void SaveCacheToFile() { List <BeatmapDetails> cache = _cache.Values.ToList(); // remove WIP levels from cache (don't save them, since they're likely to change often and // if we don't delete any entries in the cache, it may cause the cache to balloon in size) var wipLevels = Loader.CustomWIPLevels.Values.Select(x => GetCustomLevelIDWithoutDirectory(x.levelID)); cache = cache.Where(c => !wipLevels.Any(wip => wip == c.LevelID)).ToList(); // remove beatmaps that are not loaded var customLevels = Loader.CustomLevels.Values.Select(x => GetCustomLevelIDWithoutDirectory(x.levelID)); cache = cache.Where(c => customLevels.Any(level => level == c.LevelID)).ToList(); BeatmapDetailsCache.SaveBeatmapDetailsToCache(CachedBeatmapDetailsFilePath, cache); }
/// <summary> /// Save the cached BeatmapDetails objects to a JSON file located at CachedBeatmapDetailsFilePath. /// </summary> public void SaveCacheToFile() { if (PluginConfig.DisableFilters) { return; } List <BeatmapDetails> cache = _cache.Values.ToList(); // remove WIP levels from cache (don't save them, since they're likely to change often and // if we don't delete any entries in the cache, it may cause the cache to balloon in size) var wipLevels = Loader.CustomWIPLevels.Values.Select(x => GetCustomLevelIDWithoutDirectory(x.levelID)); cache = cache.Where(c => !wipLevels.Any(wip => wip == c.LevelID)).ToList(); // remove beatmaps that are not loaded var customLevels = Loader.CustomLevels.Values.Select(x => GetCustomLevelIDWithoutDirectory(x.levelID)); cache = cache.Where(c => customLevels.Any(level => level == c.LevelID)).ToList(); // repeat the above checks for user-added folders foreach (var folder in Loader.SeperateSongFolders) { var levels = folder.Levels.Values.Select(x => GetCustomLevelIDWithoutDirectory(x.levelID)); if (folder.SongFolderEntry.WIP) { cache = cache.Where(c => !levels.Any(wip => wip == c.LevelID)).ToList(); } else { cache = cache.Where(c => levels.Any(level => level == c.LevelID)).ToList(); } } if (cache.Count > 0) { BeatmapDetailsCache.SaveBeatmapDetailsToCache(CachedBeatmapDetailsFilePath, cache); } }
private void CachingThread() { try { var sw = Stopwatch.StartNew(); // load cache from file List <BeatmapDetails> loadedCache = BeatmapDetailsCache.GetBeatmapDetailsFromCache(BeatmapDetailsLoader.CachedBeatmapDetailsFilePath); if (loadedCache != null) { if (loadedCache.Count > 0) { Logger.log.Info($"Retrieved {loadedCache.Count} cached beatmap details from file"); } foreach (var detail in loadedCache) { if (!BeatmapDetailsLoader._cache.ContainsKey(detail.LevelID)) { BeatmapDetailsLoader._cache.Add(detail.LevelID, detail); } } } List <IEnumerator <BeatmapDetails> > taskList = new List <IEnumerator <BeatmapDetails> >(WorkChunkSize); List <IPreviewBeatmapLevel> allCustomLevels = BeatmapDetailsLoader.GetAllCustomLevels(); List <SongDataCoreDataStatus> sdcErrorStatusList = new List <SongDataCoreDataStatus>(allCustomLevels.Count); int index = 0; int errorCount = 0; long elapsed = 0; while (index < allCustomLevels.Count) { if (sw.ElapsedMilliseconds > 30000 + elapsed) { elapsed = sw.ElapsedMilliseconds; Logger.log.Debug($"Caching thread has finished caching {index} beatmaps out of {allCustomLevels.Count} ({elapsed} ms elapsed)"); } _manualResetEvent.WaitOne(); if (_isOperationCancelled) { return; } for (int i = 0; i < WorkChunkSize && index < allCustomLevels.Count; ++index) { IPreviewBeatmapLevel level = allCustomLevels[index]; string levelID = GetSimplifiedLevelID(level); BeatmapDetails beatmapDetails; if (BeatmapDetailsLoader._cache.ContainsKey(levelID) && BeatmapDetailsLoader._cache[levelID].SongDuration > 0.01f) { continue; } SongDataCoreDataStatus status = SongDataCoreTweaks.GetBeatmapDetails(level as CustomPreviewBeatmapLevel, out beatmapDetails); if (status == SongDataCoreDataStatus.Success) { if (beatmapDetails.DifficultyBeatmapSets.Any(set => set.DifficultyBeatmaps.Any(diff => diff.NoteJumpMovementSpeed == 0))) { Logger.log.Debug($"BeatmapDetails object generated for '{beatmapDetails.SongName}' from BeatSaver data has some incomplete fields. " + "Discarding and generating BeatmapDetails object from locally stored information instead"); taskList.Add(BeatmapDetails.CreateBeatmapDetailsFromFilesCoroutine(level as CustomPreviewBeatmapLevel)); ++i; } else { BeatmapDetailsLoader._cache[levelID] = beatmapDetails; } } else { if (SongDataCoreTweaks.IsModAvailable) { sdcErrorStatusList.Add(status); } taskList.Add(BeatmapDetails.CreateBeatmapDetailsFromFilesCoroutine(level as CustomPreviewBeatmapLevel)); ++i; } } while (taskList.Any()) { _manualResetEvent.WaitOne(); if (_isOperationCancelled) { return; } for (int i = 0; i < taskList.Count; ++i) { IEnumerator <BeatmapDetails> loadCoroutine = taskList[i]; if (loadCoroutine.MoveNext()) { BeatmapDetails beatmapDetails = loadCoroutine.Current; if (beatmapDetails != null) { BeatmapDetailsLoader._cache[beatmapDetails.LevelID] = beatmapDetails; taskList.Remove(loadCoroutine); --i; } } else { ++errorCount; taskList.Remove(loadCoroutine); --i; } } } } sw.Stop(); Logger.log.Info($"Finished caching the details of {allCustomLevels.Count} beatmaps (took {sw.ElapsedMilliseconds / 1000f} seconds)"); if (errorCount > 0) { Logger.log.Warn($"Unable to cache the beatmap details for {errorCount} songs"); } if (sdcErrorStatusList.Count > 0) { // NOTE: this will need to be updated if i ever add more error status markers Logger.log.Debug($"Unable to retrieve some data from SongDataCore: (" + $"NoData = {sdcErrorStatusList.Count(x => x == SongDataCoreDataStatus.NoData)}, " + $"InvalidBPM = {sdcErrorStatusList.Count(x => x == SongDataCoreDataStatus.InvalidBPM)}, " + $"InvalidDuration = {sdcErrorStatusList.Count(x => x == SongDataCoreDataStatus.InvalidDuration)}, " + $"InvalidCharacteristicString = {sdcErrorStatusList.Count(x => x == SongDataCoreDataStatus.InvalidCharacteristicString)}, " + $"InvalidDifficultyString = {sdcErrorStatusList.Count(x => x == SongDataCoreDataStatus.InvalidDifficultyString)}, " + $"ExceptionThrown = {sdcErrorStatusList.Count(x => x == SongDataCoreDataStatus.ExceptionThrown)})"); } BeatmapDetailsLoader.instance.SaveCacheToFile(); HMMainThreadDispatcher.instance.Enqueue(delegate() { _thread = null; _manualResetEvent.Dispose(); _manualResetEvent = null; CachingFinished?.Invoke(); }); } catch (Exception e) { Logger.log.Warn("Unexpected exception occurred in caching thread"); Logger.log.Debug(e); } }
public static void SaveBeatmapDetailsToCache(string path, List <BeatmapDetails> beatmapDetailsList) { var cache = new BeatmapDetailsCache(beatmapDetailsList); File.WriteAllText(path, JsonConvert.SerializeObject(cache)); }