/// <summary> /// Populate word storage with the words in the song name, sub-name, author, and map creator of a level pack. /// </summary> /// <param name="levelPack">The level pack whose words you want to store.</param> public void SetupStorage(IBeatmapLevelPack levelPack) { IsLoading = true; _manualResetEvent = new ManualResetEvent(true); _taskCancelled = false; _task = new HMTask( delegate() { var sw = System.Diagnostics.Stopwatch.StartNew(); Logger.log.Info($"Creating word count storage object for the \"{levelPack.packName}\" level pack (contains {levelPack.beatmapLevelCollection.beatmapLevels.Length} songs)"); if (!SetWordsFromLevelPack(levelPack)) { return; } sw.Stop(); Logger.log.Info($"Finished creating word count storage object for the \"{levelPack.packName}\" level pack (took {sw.ElapsedMilliseconds/1000f} seconds, {_words.Count} unique words processed)"); }, delegate() { _manualResetEvent = null; _task = null; IsLoading = false; }); _task.Run(); }
public void OnApplicationStart() { SceneManager.activeSceneChanged += SceneManagerOnActiveSceneChanged; SceneManager.sceneLoaded += SceneManager_sceneLoaded; BailOutInstalled = PluginManager.Plugins.Any(p => p.Name.Contains("Bail") && p.Name.Contains("Out") && p.Name.Contains("Mode")); if (BailOutInstalled) { Console.WriteLine("[StreamInfo] BailOut plugin found."); } else { Console.WriteLine("[StreamInfo] BailOut plugin not found."); } info = new SongInfo(); if (!Directory.Exists(Path.Combine(Environment.CurrentDirectory, "UserData"))) { Directory.CreateDirectory(Path.Combine(Environment.CurrentDirectory, "UserData")); } if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } foreach (string s in new[] { "SongName", "overlaydata" }) { if (!File.Exists(Path.Combine(dir, s + ".txt"))) { Console.WriteLine("[StreamInfo] " + s + ".txt not found. Creating file..."); if (s == "overlaydata") { File.WriteAllLines(Path.Combine(dir, s + ".txt"), new[] { "567,288", "0,0", "75,198", "307,134", "16,132", "87,19", "170,83", "303,19" }); } else { File.WriteAllText(Path.Combine(dir, s + ".txt"), ""); } } } if (ModPrefs.GetBool("StreamInfo", "OverlayEnabled", true, true)) { Console.WriteLine("[StreamInfo] Launching overlay..."); overlay = new Overlay(); overlay.FormClosed += Overlay_FormClosed; Action overlayjob = delegate { Application.Run(overlay); }; OverlayTask = new HMTask(overlayjob); OverlayTask.Run(); overlay.Refresh(); overlayRefreshRate = ModPrefs.GetInt("StreamInfo", "RefreshRate", 100, true); Console.WriteLine("[StreamInfo] Overlay started."); overlayEnabled = true; } }
private void StartData() { getDataJob = delegate { for (int i = 0; i < 10; i++) { Thread.Sleep(150); setupData = Resources.FindObjectsOfTypeAll <MainGameSceneSetupData>().FirstOrDefault(); if (setupData != null) { break; } } if (setupData == null) { Console.WriteLine("[SongStatus] Couldn't find SetupData object."); return; } if (!File.Exists(templateDir)) { File.WriteAllText(templateDir, defaultTemplate); } string temp = File.ReadAllText(templateDir); var diff = setupData.difficultyLevel; var song = diff.level; string mode = GetGameplayModeName(setupData.gameplayMode); var keywords = temp.Split('{', '}'); temp = ReplaceKeyword("songName", song.songName, keywords, temp); temp = ReplaceKeyword("songSubName", song.songSubName, keywords, temp); temp = ReplaceKeyword("authorName", song.songAuthorName, keywords, temp); temp = ReplaceKeyword("gamemode", mode, keywords, temp); temp = ReplaceKeyword("difficulty", diff.difficulty.Name(), keywords, temp); temp = ReplaceKeyword("isNoFail", setupData.gameplayOptions.noEnergy ? "No Fail" : string.Empty, keywords, temp); temp = ReplaceKeyword("isMirrored", setupData.gameplayOptions.mirror ? "Mirrored" : string.Empty, keywords, temp); temp = ReplaceKeyword("beatsPerMinute", song.beatsPerMinute.ToString(CultureInfo.InvariantCulture), keywords, temp); temp = ReplaceKeyword("notesCount", diff.beatmapData.notesCount.ToString(CultureInfo.InvariantCulture), keywords, temp); temp = ReplaceKeyword("obstaclesCount", diff.beatmapData.obstaclesCount.ToString(CultureInfo.InvariantCulture), keywords, temp); temp = ReplaceKeyword("environmentName", song.environmentSceneInfo.sceneName, keywords, temp); File.WriteAllText(dir, temp); }; getData = new HMTask(getDataJob); getData.Run(); }
/// <summary> /// Loads the beatmap details of a list of IPreviewBeatmapLevels. /// </summary> /// <param name="levels">A list of IPreviewBeatmaps.</param> /// <param name="update">A function that will run every 0.1s that gets the number of beatmaps currently loaded.</param> /// <param name="onFinish">The function that is called when the details of all beatmaps are retrieved.</param> public void LoadBeatmaps(IPreviewBeatmapLevel[] levels, Action <int> update = null, Action <BeatmapDetails[]> onFinish = null) { if (IsLoading) { _loadingTask?.Cancel(); } IsLoading = true; if (IsCaching) { PausePopulatingCache(); } _levels = levels; _update = update; StartCoroutine(UpdateCoroutine()); if (_loadingTokenSource != null) { _loadingTokenSource.Dispose(); } _loadingTokenSource = new CancellationTokenSource(); _loadingTask = new HMTask( delegate() { Logger.log.Debug("Starting to load beatmap details"); GetBeatmapLevelsAsync().GetAwaiter().GetResult(); }, delegate() { _loadingTask = null; _loadingTokenSource.Dispose(); _loadingTokenSource = null; IsLoading = false; if (IsCaching || !SongsAreCached) { StartPopulatingCache(); } onFinish?.Invoke(_loadedLevels.ToArray()); }); _loadingTask.Run(); }
/// <summary> /// Should be run whenever in the menu, to start/resume populating the cache for filter/search. /// </summary> public void StartPopulatingCache(bool force = false) { if (IsLoading || (SongsAreCached && !force && !IsCaching)) { return; } if (!IsCaching) { IsCaching = true; _manualResetEvent = new ManualResetEvent(true); _cachingTokenSource = new CancellationTokenSource(); _cachingTask = new HMTask( delegate() { Logger.log.Info("Starting to cache all custom song details"); CacheAllBeatmapLevelsAsync(); SaveCacheToFile(); Logger.log.Info("Finished caching and storing all custom song details"); }, delegate() { _manualResetEvent = null; _cachingTask = null; SongsAreCached = true; IsCaching = false; }); _cachingTask.Run(); } else { // resume old populate cache job _manualResetEvent.Set(); Logger.log.Info("Resuming beatmap details caching thread"); } }
private void RetrieveCustomLevelVideoData(Loader loader, Dictionary <string, CustomPreviewBeatmapLevel> levels) { Action job = delegate { try { float i = 0; foreach (var level in levels) { i++; var songPath = level.Value.customLevelPath; var results = Directory.GetFiles(songPath, "video.json", SearchOption.AllDirectories); if (results.Length == 0) { continue; } var result = results[0]; try { var i1 = i; HMMainThreadDispatcher.instance.Enqueue(delegate { if (_loadingCancelled) { return; } VideoData video = LoadVideo(result, level.Value); if (video != null) { AddVideo(video); } }); } catch (Exception e) { Plugin.logger.Error("Failed to load song folder: " + result); Plugin.logger.Error(e.ToString()); } } } catch (Exception e) { Plugin.logger.Error("RetrieveCustomLevelVideoData failed:"); Plugin.logger.Error(e.ToString()); } }; Action finish = delegate { AreVideosLoaded = true; AreVideosLoading = false; _loadingTask = null; VideosLoadedEvent?.Invoke(); }; _loadingTask = new HMTask(job, finish); _loadingTask.Run(); }
private void RetrieveOSTVideoData() { BeatmapLevelSO[] levels = Resources.FindObjectsOfTypeAll <BeatmapLevelSO>().Where(x => x.GetType() != typeof(CustomBeatmapLevel)).ToArray(); Action job = delegate { try { float i = 0; foreach (var level in levels) { i++; var videoFileName = level.songName; // strip invlid characters foreach (var c in Path.GetInvalidFileNameChars()) { videoFileName = videoFileName.Replace(c, '-'); } videoFileName = videoFileName.Replace('\\', '-'); videoFileName = videoFileName.Replace('/', '-'); var songPath = Path.Combine(Environment.CurrentDirectory, "CustomSongs", "_OST", videoFileName); if (!Directory.Exists(songPath)) { continue; } var results = Directory.GetFiles(songPath, "video.json", SearchOption.AllDirectories); if (results.Length == 0) { continue; } var result = results[0]; try { var i1 = i; HMMainThreadDispatcher.instance.Enqueue(delegate { if (_loadingCancelled) { return; } VideoData video = LoadVideo(result, level.difficultyBeatmapSets[0].difficultyBeatmaps[0].level); if (video != null) { AddVideo(video); } }); } catch (Exception e) { Plugin.logger.Error("Failed to load song folder: " + result); Plugin.logger.Error(e.ToString()); } } } catch (Exception e) { Plugin.logger.Error("RetrieveOSTVideoData failed:"); Plugin.logger.Error(e.ToString()); } }; Action finish = delegate { AreVideosLoaded = true; AreVideosLoading = false; _loadingTask = null; VideosLoadedEvent?.Invoke(); }; _loadingTask = new HMTask(job, finish); _loadingTask.Run(); }
private void RetrieveCustomLevelVideoData() { _loadingTask = new HMTask(() => { try { float i = 0; foreach (var level in mapLevels) { i++; var songPath = level.Value.customLevelPath; var results = Directory.GetFiles(songPath, "video.json", SearchOption.AllDirectories); if (results.Length == 0) { continue; } var result = results[0]; try { var i1 = i; HMMainThreadDispatcher.instance.Enqueue(delegate { VideoDatas videos; if (_loadingCancelled) { return; } try { videos = LoadVideos(result, level.Value); videos.level = level.Value; foreach (VideoData vid in videos.videos) { vid.level = level.Value; } } catch { videos = new VideoDatas { videos = new List <VideoData> { LoadVideo(result, level.Value) }, level = level.Value }; } if (videos != null && videos.videos.Count != 0) { AddLevelsVideos(videos); } }); } catch (Exception e) { Plugin.Logger.Error("Failed to load song folder: " + result); Plugin.Logger.Error(e.ToString()); } } } catch (Exception e) { Plugin.Logger.Error("RetrieveCustomLevelVideoData failed:"); Plugin.Logger.Error(e.ToString()); } }, () => { AreVideosLoaded = true; AreVideosLoading = false; _loadingTask = null; VideosLoadedEvent?.Invoke(); }); _loadingTask.Run(); }
private void RetrieveOSTVideoData() { BeatmapLevelSO[] levels = Resources.FindObjectsOfTypeAll <BeatmapLevelSO>() .Where(x => x.GetType() != typeof(CustomBeatmapLevel)).ToArray(); Plugin.Logger.Info("Getting OST Video Data"); Action job = delegate { try { float i = 0; foreach (BeatmapLevelSO level in levels) { var soundData = new float[level.beatmapLevelData.audioClip.samples]; level.beatmapLevelData.audioClip.GetData(soundData, level.beatmapLevelData.audioClip.samples); i++; var videoFileName = level.songName; // Plugin.Logger.Info($"Trying for: {videoFileName}"); // strip invlid characters foreach (var c in Path.GetInvalidFileNameChars()) { videoFileName = videoFileName.Replace(c, '-'); } videoFileName = videoFileName.Replace('\\', '-'); videoFileName = videoFileName.Replace('/', '-'); var songPath = Path.Combine(Environment.CurrentDirectory, "Beat Saber_Data", "CustomLevels", "_OST", videoFileName); if (!Directory.Exists(songPath)) { continue; } // Plugin.Logger.Info($"Using name: {videoFileName}"); // Plugin.Logger.Info($"At Path: {songPath}"); // Plugin.Logger.Info($"Exists"); var results = Directory.GetFiles(songPath, "video.json", SearchOption.AllDirectories); if (results.Length == 0) { // Plugin.Logger.Info($"No video.json"); continue; } // Plugin.Logger.Info($"Found video.json"); var result = results[0]; Plugin.Logger.Info(result); try { var i1 = i; HMMainThreadDispatcher.instance.Enqueue(() => { VideoDatas videos; if (_loadingCancelled) { return; } IPreviewBeatmapLevel previewBeatmapLevel = level.difficultyBeatmapSets[0].difficultyBeatmaps[0].level; Plugin.Logger.Info($"Loading: {previewBeatmapLevel.songName}"); try { // Plugin.Logger.Info($"Loading as multiple videos"); videos = LoadVideos(result, previewBeatmapLevel); videos.level = previewBeatmapLevel; } catch { // Plugin.Logger.Info($"Loading as single video"); var video = LoadVideo(result, previewBeatmapLevel); videos = new VideoDatas { videos = new List <VideoData> { video }, level = video.level }; } if (videos.videos.Count != 0) { AddLevelsVideos(videos); foreach (var videoData in videos) { // Plugin.Logger.Info($"Found Video: {videoData.ToString()}"); } } else { // Plugin.Logger.Info($"No Videos"); } }); } catch (Exception e) { Plugin.Logger.Error("Failed to load song folder: " + result); Plugin.Logger.Error(e.ToString()); } } } catch (Exception e) { Plugin.Logger.Error("RetrieveOSTVideoData failed:"); Plugin.Logger.Error(e.ToString()); } }; _loadingTask = new HMTask(job, () => { AreVideosLoaded = true; AreVideosLoading = false; _loadingTask = null; VideosLoadedEvent?.Invoke(); }); _loadingTask.Run(); }
private void RetrieveCustomLevelVideoData(SongLoader songLoader, List <CustomLevel> levels) { Action job = delegate { try { float i = 0; foreach (var level in levels) { i++; var songPath = level.customSongInfo.path; var results = Directory.GetFiles(songPath, "video.json", SearchOption.AllDirectories); if (results.Length == 0) { continue; } var result = results[0]; try { var i1 = i; HMMainThreadDispatcher.instance.Enqueue(delegate { if (_loadingCancelled) { return; } VideoData video = LoadVideo(result, level.difficultyBeatmaps[0].level); if (video != null) { AddVideo(video); } }); } catch (Exception e) { Console.WriteLine("Failed to load song folder: " + result); Console.WriteLine(e.ToString()); } } } catch (Exception e) { Console.WriteLine("RetrieveCustomLevelVideoData failed:"); Console.WriteLine(e.ToString()); } }; Action finish = delegate { AreVideosLoaded = true; AreVideosLoading = false; _loadingTask = null; VideosLoadedEvent?.Invoke(); }; _loadingTask = new HMTask(job, finish); _loadingTask.Run(); }
private void RetrieveAllSongs(bool fullRefresh) { var stopwatch = new Stopwatch(); var levelList = new List <CustomLevel>(); if (fullRefresh) { _customLevelPool.ReturnAll(); _beatmapDataPool.ReturnAll(); CustomLevels.Clear(); } Action job = delegate { try { stopwatch.Start(); var path = Environment.CurrentDirectory; path = path.Replace('\\', '/'); var currentHashes = new List <string>(); var cachedSongs = new string[0]; if (Directory.Exists(path + "/CustomSongs/.cache")) { cachedSongs = Directory.GetDirectories(path + "/CustomSongs/.cache"); } else { Directory.CreateDirectory(path + "/CustomSongs/.cache"); } var songZips = Directory.GetFiles(path + "/CustomSongs") .Where(x => x.ToLower().EndsWith(".zip") || x.ToLower().EndsWith(".beat")).ToArray(); foreach (var songZip in songZips) { //Check cache if zip already is extracted string hash; if (Utils.CreateMD5FromFile(songZip, out hash)) { currentHashes.Add(hash); if (cachedSongs.Any(x => x.Contains(hash))) { continue; } using (var unzip = new Unzip(songZip)) { unzip.ExtractToDirectory(path + "/CustomSongs/.cache/" + hash); } } else { Log("Error reading zip " + songZip, LogSeverity.Warn); } } var songFolders = Directory.GetDirectories(path + "/CustomSongs").ToList(); var songCaches = Directory.GetDirectories(path + "/CustomSongs/.cache"); float i = 0; foreach (var song in songFolders) { i++; var results = Directory.GetFiles(song, "info.json", SearchOption.AllDirectories); if (results.Length == 0) { Log("Custom song folder '" + song + "' is missing info.json files!", LogSeverity.Warn); continue; } foreach (var result in results) { var songPath = Path.GetDirectoryName(result).Replace('\\', '/'); if (!fullRefresh) { if (CustomLevels.Any(x => x.customSongInfo.path == songPath)) { continue; } } var customSongInfo = GetCustomSongInfo(songPath); if (customSongInfo == null) { continue; } var id = customSongInfo.GetIdentifier(); if (CustomLevels.Any(x => x.levelID == id && x.customSongInfo != customSongInfo)) { Log("Duplicate song found at " + customSongInfo.path, LogSeverity.Warn); continue; } var i1 = i; HMMainThreadDispatcher.instance.Enqueue(delegate { if (_loadingCancelled) { return; } LoadSong(customSongInfo, levelList); LoadingProgress = i1 / songFolders.Count; }); } } foreach (var song in songCaches) { var hash = Path.GetFileName(song); if (!currentHashes.Contains(hash)) { //Old cache Directory.Delete(song, true); } } } catch (Exception e) { Log("RetrieveAllSongs failed:", LogSeverity.Error); Log(e.ToString(), LogSeverity.Error); throw; } }; Action finish = delegate { stopwatch.Stop(); Log("Loaded " + levelList.Count + " new songs in " + stopwatch.Elapsed.Seconds + " seconds"); CustomLevels.AddRange(levelList); var orderedList = CustomLevels.OrderBy(x => x.songName); CustomLevels = orderedList.ToList(); foreach (var customLevel in CustomLevels) { if (customLevel.customSongInfo.oneSaber) { _oneSaberLevelCollection.LevelList.Add(customLevel); } else { _standardLevelCollection.LevelList.Add(customLevel); _noArrowsLevelCollection.LevelList.Add(customLevel); _partyLevelCollection.LevelList.Add(customLevel); } } AreSongsLoaded = true; AreSongsLoading = false; LoadingProgress = 1; _loadingTask = null; if (SongsLoadedEvent != null) { SongsLoadedEvent(this, CustomLevels); } }; _loadingTask = new HMTask(job, finish); _loadingTask.Run(); }
private void RetrieveAllSongs(bool fullRefresh) { var stopwatch = new Stopwatch(); var levelList = new List <CustomLevel>(); if (fullRefresh) { _customLevelPool.ReturnAll(); _beatmapDataPool.ReturnAll(); CustomLevels.Clear(); } Action job = delegate { try { stopwatch.Start(); var path = Environment.CurrentDirectory; path = path.Replace('\\', '/'); var currentHashes = new List <string>(); var cachedHashes = new List <string>(); var cachedSongs = new string[0]; if (Directory.Exists(path + "/CustomSongs/.cache")) { cachedSongs = Directory.GetDirectories(path + "/CustomSongs/.cache"); } else { Directory.CreateDirectory(path + "/CustomSongs/.cache"); } var songZips = Directory.GetFiles(path + "/CustomSongs") .Where(x => x.ToLower().EndsWith(".zip") || x.ToLower().EndsWith(".beat") || x.ToLower().EndsWith(".bmap")).ToArray(); foreach (var songZip in songZips) { //Check cache if zip already is extracted string hash; string trimmedZip = songZip; trimmedZip = Utils.TrimEnd(trimmedZip, ".zip"); trimmedZip = Utils.TrimEnd(trimmedZip, ".beat"); trimmedZip = Utils.TrimEnd(trimmedZip, ".bmap"); if (Utils.CreateMD5FromFile(songZip, out hash)) { using (var unzip = new Unzip(songZip)) { try { if (Directory.Exists(trimmedZip)) { Log("Directory for Zip already exists, Extracting Zip to Cache instead."); cachedHashes.Add(hash); if (cachedSongs.Any(x => x.Contains(hash))) { continue; } unzip.ExtractToDirectory(path + "/CustomSongs/.cache/" + hash); } else { unzip.ExtractToDirectory(path + "/CustomSongs/" + trimmedZip.Replace(path + "/CustomSongs\\", "")); //Add hash if successfully extracted currentHashes.Add(hash); } } catch (Exception e) { Log("Error extracting zip " + songZip + "\n" + e, LogSeverity.Warn); } } } else { Log("Error reading zip " + songZip, LogSeverity.Warn); } } var songFolders = Directory.GetDirectories(path + "/CustomSongs").ToList(); var songCaches = Directory.GetDirectories(path + "/CustomSongs/.cache"); foreach (var songZip in songZips) { //Delete zip if successfully extracted string hash; if (Utils.CreateMD5FromFile(songZip, out hash)) { if (currentHashes.Contains(hash)) { Log("Zip Successfully Extracted, deleting zip."); File.SetAttributes(songZip, FileAttributes.Normal); File.Delete(songZip); } } } foreach (var song in songCaches) { var hash = Path.GetFileName(song); if (!cachedHashes.Contains(hash)) { //Old cache Directory.Delete(song, true); } } var loadedIDs = new List <string>(); float i = 0; foreach (var song in songFolders) { i++; var results = Directory.GetFiles(song, "info.json", SearchOption.AllDirectories); if (results.Length == 0) { Log("Custom song folder '" + song + "' is missing info.json files!", LogSeverity.Warn); continue; } foreach (var result in results) { try { var songPath = Path.GetDirectoryName(result).Replace('\\', '/'); if (!fullRefresh) { if (CustomLevels.Any(x => x.customSongInfo.path == songPath)) { continue; } } var customSongInfo = GetCustomSongInfo(songPath); if (customSongInfo == null) { continue; } var id = customSongInfo.GetIdentifier(); if (loadedIDs.Any(x => x == id)) { Log("Duplicate song found at " + customSongInfo.path, LogSeverity.Warn); continue; } loadedIDs.Add(id); if (CustomPlatformsPresent && customSongPlatforms) { if (customSongInfo.customEnvironment != null) { if (findCustomEnvironment(customSongInfo.customEnvironment) == -1) { Console.WriteLine("CustomPlatform not found: " + customSongInfo.customEnvironment); if (customSongInfo.customEnvironmentHash != null) { Console.WriteLine("Downloading with hash: " + customSongInfo.customEnvironmentHash); StartCoroutine(downloadCustomPlatform(customSongInfo.customEnvironmentHash, customSongInfo.customEnvironment)); } } } } var i1 = i; HMMainThreadDispatcher.instance.Enqueue(delegate { if (_loadingCancelled) { return; } var level = LoadSong(customSongInfo); if (level != null) { levelList.Add(level); } LoadingProgress = i1 / songFolders.Count; }); } catch (Exception e) { Log("Failed to load song folder: " + result, LogSeverity.Warn); Log(e.ToString(), LogSeverity.Warn); } } } } catch (Exception e) { Log("RetrieveAllSongs failed:", LogSeverity.Error); Log(e.ToString(), LogSeverity.Error); } }; Action finish = delegate { stopwatch.Stop(); Log("Loaded " + levelList.Count + " new songs in " + stopwatch.Elapsed.Seconds + " seconds"); CustomLevels.AddRange(levelList); var orderedList = CustomLevels.OrderBy(x => x.songName); CustomLevels = orderedList.ToList(); foreach (var customLevel in CustomLevels) { CustomLevelCollectionSO.AddCustomLevel(customLevel); } AreSongsLoaded = true; AreSongsLoading = false; LoadingProgress = 1; _loadingTask = null; if (SongsLoadedEvent != null) { SongsLoadedEvent(this, CustomLevels); } }; _loadingTask = new HMTask(job, finish); _loadingTask.Run(); }
private void SceneManagerOnActiveSceneChanged(Scene arg0, Scene arg1) { if (overlayEnabled) { if (arg1.name == "Menu" && InSong) { Console.WriteLine("[StreamInfo] Exited song scene."); InSong = false; StartTask.Cancel(); ats = null; Console.WriteLine("[StreamInfo] Ready for next song."); } else if (env.Contains(arg1.name)) { StartJob = delegate { Console.WriteLine("[StreamInfo] Entered song scene. Initializing..."); InSong = true; EnergyReached0 = false; int runID = 1 + songCount++; Console.WriteLine("[StreamInfo] Finding controllers and data..."); GameEnergyCounter energy = null; ScoreController score = null; MainGameSceneSetupData setupData = null; while (ats == null || energy == null || score == null || setupData == null) { Thread.Sleep(150); ats = UnityEngine.Resources.FindObjectsOfTypeAll <AudioTimeSyncController>().FirstOrDefault(); energy = UnityEngine.Resources.FindObjectsOfTypeAll <GameEnergyCounter>().FirstOrDefault(); score = UnityEngine.Resources.FindObjectsOfTypeAll <ScoreController>().FirstOrDefault(); setupData = UnityEngine.Resources.FindObjectsOfTypeAll <MainGameSceneSetupData>().FirstOrDefault(); } Console.WriteLine("[StreamInfo] Found controllers and data."); bool noFail = false; if (setupData != null) { Console.WriteLine("[StreamInfo] Getting song name data..."); var level = setupData.difficultyLevel.level; _songName = level.songName; _songSub = level.songSubName; _songAuthor = level.songAuthorName; string songname = "\"" + _songName + "\" by " + _songSub + " - " + _songAuthor; File.WriteAllText(Path.Combine(dir, "SongName.txt"), songname + " "); noFail = setupData.gameplayOptions.noEnergy; } Console.WriteLine("[StreamInfo] Hooking Events..."); if (score != null) { score.comboDidChangeEvent += OnComboChange; score.multiplierDidChangeEvent += OnMultiplierChange; score.noteWasMissedEvent += OnNoteMiss; score.noteWasCutEvent += OnNoteCut; score.scoreDidChangeEvent += OnScoreChange; } if (energy != null) { energy.gameEnergyDidChangeEvent += OnEnergyChange; if (!BailOutInstalled) { energy.gameEnergyDidReach0Event += OnEnergyFail; } } info.SetDefault(); if (noFail) { EnergyReached0 = true; info.energy = -3; } Console.WriteLine("[StreamInfo] Starting update loop..."); while (InSong && overlayEnabled && runID == songCount) { if (ats != null) { string time = Math.Floor(ats.songTime / 60).ToString("N0") + ":" + Math.Floor(ats.songTime % 60).ToString("00"); string totaltime = Math.Floor(ats.songLength / 60).ToString("N0") + ":" + Math.Floor(ats.songLength % 60).ToString("00"); string percent = ((ats.songTime / ats.songLength) * 100).ToString("N0"); overlay.UpdateText(info.GetVal("multiplier"), info.GetVal("score"), ScoreController.MaxScoreForNumberOfNotes(info.notes_total), time + " / " + totaltime + " (" + percent + "%)", info.GetVal("combo"), info.GetVal("notes_hit") + "/" + info.GetVal("notes_total"), info.GetVal("energy")); } Thread.Sleep(overlayRefreshRate); } Console.WriteLine("[SongInfo] Thread completed: " + runID); }; StartTask = new HMTask(StartJob); StartTask.Run(); } } }