From 1b2c4ce9b1cf6959ea8f83cd392cd5bb8d54f1fd Mon Sep 17 00:00:00 2001 From: Meivyn <793322+Meivyn@users.noreply.github.com> Date: Sun, 10 Mar 2024 13:26:04 -0400 Subject: [PATCH 01/17] First pass at migrating to Zenject, WIP --- source/SongCore/Collections.cs | 11 ++ .../CosmeticCharacteristicsPatches.cs | 83 +++------ .../MainSystemsInitRefreshablePatch.cs | 6 +- .../HarmonyPatches/NegativeNjsPatch.cs | 30 +-- .../StandardLevelDetailViewRefreshContent.cs | 83 +-------- source/SongCore/Installers/AppInstaller.cs | 13 ++ source/SongCore/Installers/GameInstaller.cs | 14 ++ source/SongCore/Installers/MenuInstaller.cs | 16 ++ source/SongCore/Loader.cs | 174 +++++++++++------- source/SongCore/Plugin.cs | 42 +---- source/SongCore/SongCore.csproj | 16 +- source/SongCore/UI/ColorsUI.cs | 25 ++- source/SongCore/UI/RequirementsUI.cs | 134 ++++++++++++-- source/SongCore/Utilities/Hashing.cs | 10 +- source/SongCore/manifest.json | 2 +- 15 files changed, 374 insertions(+), 285 deletions(-) create mode 100644 source/SongCore/Installers/AppInstaller.cs create mode 100644 source/SongCore/Installers/GameInstaller.cs create mode 100644 source/SongCore/Installers/MenuInstaller.cs diff --git a/source/SongCore/Collections.cs b/source/SongCore/Collections.cs index 386cf0d..1a3e18a 100644 --- a/source/SongCore/Collections.cs +++ b/source/SongCore/Collections.cs @@ -22,6 +22,7 @@ public static class Collections internal static readonly ConcurrentDictionary LevelHashDictionary = new ConcurrentDictionary(); internal static readonly ConcurrentDictionary> HashLevelDictionary = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); internal static readonly ConcurrentDictionary LevelPathDictionary = new ConcurrentDictionary(); + internal static readonly ConcurrentDictionary LevelSaveDataDictionary = new ConcurrentDictionary(); internal static BeatmapLevelPack? WipLevelPack; internal static ConcurrentDictionary CustomSongsData = new ConcurrentDictionary(); @@ -44,6 +45,16 @@ public static List levelIDsForHash(string hash) return HashLevelDictionary.TryGetValue(hash, out var songs) ? songs : new List(); } + public static string GetLevelPathByLevelId(string levelID) + { + return LevelPathDictionary.TryGetValue(levelID, out var path) ? path : string.Empty; + } + + public static StandardLevelInfoSaveData? GetStandardLevelInfoSaveDataByLevelId(string levelID) + { + return LevelSaveDataDictionary.TryGetValue(levelID, out var standardLevelInfoSaveData) ? standardLevelInfoSaveData : null; + } + internal static void AddExtraSongData(string hash, string path, string rawSongData) { if (!CustomSongsData.ContainsKey(hash)) diff --git a/source/SongCore/HarmonyPatches/CosmeticCharacteristicsPatches.cs b/source/SongCore/HarmonyPatches/CosmeticCharacteristicsPatches.cs index 86e46d0..dbdab23 100644 --- a/source/SongCore/HarmonyPatches/CosmeticCharacteristicsPatches.cs +++ b/source/SongCore/HarmonyPatches/CosmeticCharacteristicsPatches.cs @@ -3,84 +3,55 @@ using System.Linq; using HarmonyLib; using HMUI; +using IPA.Utilities; +using SiraUtil.Affinity; using SongCore.Data; using SongCore.Utilities; using UnityEngine; namespace SongCore.HarmonyPatches { - internal class CosmeticCharacteristicsPatches + internal class CosmeticCharacteristicsPatches : IAffinity { + private readonly GameplayCoreSceneSetupData _gameplayCoreSceneSetupData; + private readonly BeatmapKey _beatmapKey; + private CosmeticCharacteristicsPatches(GameplayCoreSceneSetupData gameplayCoreSceneSetupData, BeatmapKey beatmapKey) + { + _gameplayCoreSceneSetupData = gameplayCoreSceneSetupData; + _beatmapKey = beatmapKey; + } - [HarmonyPatch(typeof(BeatLineManager))] - [HarmonyPatch(nameof(BeatLineManager.HandleNoteWasSpawned))] - internal class BeatLineManager_HandleNoteWasSpawned + [AffinityPatch(typeof(BeatLineManager), nameof(BeatLineManager.HandleNoteWasSpawned))] + [AffinityPrefix] + private bool ShowOrHideRotationNoteSpawnLines() { - private static bool Prefix() + if (Plugin.Configuration.DisableRotationSpawnLinesOverride) { - if (Plugin.Configuration.DisableRotationSpawnLinesOverride) - return true; - - var sceneSetupData = BS_Utils.Plugin.LevelData.GameplayCoreSceneSetupData; - if (sceneSetupData.beatmapLevel == null) - return true; - var beatmapData = Collections.RetrieveDifficultyData(sceneSetupData.beatmapLevel, sceneSetupData.beatmapKey); - if (beatmapData == null) - return true; - if (beatmapData._showRotationNoteSpawnLines == null) - return true; - return beatmapData._showRotationNoteSpawnLines.Value; + return true; } + + var difficultyData = Collections.RetrieveDifficultyData(_gameplayCoreSceneSetupData.beatmapLevel, _beatmapKey); + return difficultyData?._showRotationNoteSpawnLines == null || difficultyData._showRotationNoteSpawnLines.Value; } - [HarmonyPatch(typeof(GameplayCoreInstaller))] - [HarmonyPatch(nameof(GameplayCoreInstaller.InstallBindings))] - internal class GameplayCoreInstaller_InstallBindingsPatch + [AffinityPatch(typeof(SaberManager.InitData), "ctor", AffinityMethodType.Constructor, null, typeof(bool), typeof(SaberType))] + private void ForceOneSaber(ref SaberManager.InitData __instance) { - private static ExtraSongData.DifficultyData? diffData = null; - private static int numberOfColors = -1; - private static GameplayCoreSceneSetupData sceneSetupData = null; - private static void Prefix(GameplayCoreInstaller __instance) + if (Plugin.Configuration.DisableOneSaberOverride || _gameplayCoreSceneSetupData.beatmapLevel.hasPrecalculatedData) { - if (Plugin.Configuration.DisableOneSaberOverride) - return; - - sceneSetupData = __instance._sceneSetupData; - - var beatmapLevel = sceneSetupData.beatmapLevel; - if (beatmapLevel.hasPrecalculatedData) - { - diffData = null; - return; - } - diffData = Collections.RetrieveDifficultyData(beatmapLevel, sceneSetupData.beatmapKey); - if (diffData == null) - return; - if (diffData._oneSaber != null && !Plugin.Configuration.DisableOneSaberOverride) - { - numberOfColors = sceneSetupData.beatmapKey.beatmapCharacteristic.numberOfColors; - sceneSetupData.beatmapKey.beatmapCharacteristic._numberOfColors = diffData._oneSaber.Value == true ? 1 : 2; - } - + return; } - private static void Postfix() - { - if (Plugin.Configuration.DisableOneSaberOverride) - return; - if (diffData == null) - return; - if (diffData._oneSaber != null && !Plugin.Configuration.DisableOneSaberOverride) - { - sceneSetupData.beatmapKey.beatmapCharacteristic._numberOfColors = numberOfColors; - } + var difficultyData = Collections.RetrieveDifficultyData(_gameplayCoreSceneSetupData.beatmapLevel, _beatmapKey); + if (difficultyData is { _oneSaber: not null }) + { + __instance.SetField(nameof(__instance.oneSaberMode), difficultyData._oneSaber.Value); } - } - + // TODO [HarmonyPatch(typeof(BeatmapCharacteristicSegmentedControlController))] [HarmonyPatch(nameof(BeatmapCharacteristicSegmentedControlController.SetData), MethodType.Normal)] internal class CosmeticCharacteristicsPatch diff --git a/source/SongCore/HarmonyPatches/MainSystemsInitRefreshablePatch.cs b/source/SongCore/HarmonyPatches/MainSystemsInitRefreshablePatch.cs index c1fec38..67df9c9 100644 --- a/source/SongCore/HarmonyPatches/MainSystemsInitRefreshablePatch.cs +++ b/source/SongCore/HarmonyPatches/MainSystemsInitRefreshablePatch.cs @@ -1,17 +1,15 @@ -using HarmonyLib; -using System; +using System; using System.Collections.Concurrent; using Zenject; namespace SongCore.HarmonyPatches { - [HarmonyPatch(typeof(MainSystemInit), nameof(MainSystemInit.InstallBindings))] internal class MainSystemsInitRefreshablePatch { public const string refreshableID = "SongCore.Loader.Refresh"; public const string didLoadEventID = "SongCore.Loader.Loaded"; - private static void Postfix(DiContainer container) + public static void Postfix(DiContainer container) { container.Bind().WithId(refreshableID).To().AsSingle(); container.Bind(typeof(IInitializable), typeof(IDisposable), typeof(SongCoreLoaderDidLoad)).To().AsSingle(); diff --git a/source/SongCore/HarmonyPatches/NegativeNjsPatch.cs b/source/SongCore/HarmonyPatches/NegativeNjsPatch.cs index a674eaf..e7ebf38 100644 --- a/source/SongCore/HarmonyPatches/NegativeNjsPatch.cs +++ b/source/SongCore/HarmonyPatches/NegativeNjsPatch.cs @@ -1,19 +1,27 @@ -using System.Linq; -using HarmonyLib; +using SiraUtil.Affinity; namespace SongCore.HarmonyPatches { - [HarmonyPatch(typeof(BeatmapObjectSpawnMovementData))] - [HarmonyPatch(nameof(BeatmapObjectSpawnMovementData.Init), MethodType.Normal)] - internal class AllowNegativeNjsValuesPatch + internal class AllowNegativeNjsValuesPatch : IAffinity { - private static void Prefix(ref float startNoteJumpMovementSpeed) + private readonly GameplayCoreSceneSetupData _gameplayCoreSceneSetupData; + private readonly BeatmapKey _beatmapKey; + + private AllowNegativeNjsValuesPatch(GameplayCoreSceneSetupData gameplayCoreSceneSetupData, BeatmapKey beatmapKey) + { + _gameplayCoreSceneSetupData = gameplayCoreSceneSetupData; + _beatmapKey = beatmapKey; + } + + [AffinityPatch(typeof(BeatmapObjectSpawnMovementData), nameof(BeatmapObjectSpawnMovementData.Init))] + [AffinityPrefix] + private void ForceNegativeStartNoteJumpMovementSpeed(ref float startNoteJumpMovementSpeed) { - if (!BS_Utils.Plugin.LevelData.IsSet) return; - var sceneSetupData = BS_Utils.Plugin.LevelData.GameplayCoreSceneSetupData; - var mapNjs = sceneSetupData.beatmapLevel.beatmapBasicData.First(p => p.Key == (sceneSetupData.beatmapKey.beatmapCharacteristic, sceneSetupData.beatmapKey.difficulty)).Value.noteJumpMovementSpeed; - if (mapNjs < 0) - startNoteJumpMovementSpeed = mapNjs; + var noteJumpMovementSpeed = _gameplayCoreSceneSetupData.beatmapLevel.beatmapBasicData[(_beatmapKey.beatmapCharacteristic, _beatmapKey.difficulty)].noteJumpMovementSpeed; + if (noteJumpMovementSpeed < 0) + { + startNoteJumpMovementSpeed = noteJumpMovementSpeed; + } } } } \ No newline at end of file diff --git a/source/SongCore/HarmonyPatches/StandardLevelDetailViewRefreshContent.cs b/source/SongCore/HarmonyPatches/StandardLevelDetailViewRefreshContent.cs index ffa9d30..c7dc247 100644 --- a/source/SongCore/HarmonyPatches/StandardLevelDetailViewRefreshContent.cs +++ b/source/SongCore/HarmonyPatches/StandardLevelDetailViewRefreshContent.cs @@ -1,14 +1,12 @@ -using System; using HarmonyLib; -using SongCore.UI; using System.Collections.Generic; -using System.Linq; using SongCore.Utilities; namespace SongCore.HarmonyPatches { + // TODO: Check if this could go somewhere else. [HarmonyPatch(typeof(StandardLevelDetailView))] - [HarmonyPatch(nameof(StandardLevelDetailView.CheckIfBeatmapLevelDataExists), MethodType.Normal)] + [HarmonyPatch(nameof(StandardLevelDetailView.RefreshContent), MethodType.Normal)] internal class StandardLevelDetailViewRefreshContent { private static readonly Dictionary LevelLabels = new Dictionary(); @@ -48,8 +46,6 @@ private static void Postfix(StandardLevelDetailView __instance) { var firstSelection = false; var beatmapLevel = __instance._beatmapLevel; - var actionButton = __instance.actionButton; - var practiceButton = __instance.practiceButton; if (beatmapLevel != lastLevel) { @@ -57,11 +53,6 @@ private static void Postfix(StandardLevelDetailView __instance) lastLevel = beatmapLevel; } - actionButton.interactable = true; - practiceButton.interactable = true; - - RequirementsUI.instance.ButtonGlowColor = false; - RequirementsUI.instance.ButtonInteractable = false; if (beatmapLevel.hasPrecalculatedData) { return; @@ -71,80 +62,10 @@ private static void Postfix(StandardLevelDetailView __instance) if (songData == null) { - RequirementsUI.instance.ButtonGlowColor = false; - RequirementsUI.instance.ButtonInteractable = false; return; } var wipFolderSong = false; - var diffData = Collections.RetrieveDifficultyData(beatmapLevel, __instance.beatmapKey); - - if (diffData != null) - { - //If no additional information is present - if (!diffData.additionalDifficultyData._requirements.Any() && - !diffData.additionalDifficultyData._suggestions.Any() && - !diffData.additionalDifficultyData._warnings.Any() && - !diffData.additionalDifficultyData._information.Any() && - !songData.contributors.Any() && !Utilities.Utils.DiffHasColors(diffData)) - { - RequirementsUI.instance.ButtonGlowColor = false; - RequirementsUI.instance.ButtonInteractable = false; - } - else if (!diffData.additionalDifficultyData._warnings.Any()) - { - RequirementsUI.instance.ButtonGlowColor = true; - RequirementsUI.instance.ButtonInteractable = true; - RequirementsUI.instance.SetRainbowColors(Utilities.Utils.DiffHasColors(diffData)); - } - else if (diffData.additionalDifficultyData._warnings.Any()) - { - RequirementsUI.instance.ButtonGlowColor = true; - RequirementsUI.instance.ButtonInteractable = true; - if (diffData.additionalDifficultyData._warnings.Contains("WIP")) - { - actionButton.interactable = false; - } - RequirementsUI.instance.SetRainbowColors(Utilities.Utils.DiffHasColors(diffData)); - } - } - - if (beatmapLevel.levelID.EndsWith(" WIP", StringComparison.Ordinal)) - { - RequirementsUI.instance.ButtonGlowColor = true; - RequirementsUI.instance.ButtonInteractable = true; - actionButton.interactable = false; - wipFolderSong = true; - } - - if (diffData != null) - { - foreach (var requirement in diffData.additionalDifficultyData._requirements) - { - if (!Collections.capabilities.Contains(requirement)) - { - actionButton.interactable = false; - practiceButton.interactable = false; - RequirementsUI.instance.ButtonGlowColor = true; - RequirementsUI.instance.ButtonInteractable = true; - } - } - } - - if (__instance.beatmapKey.beatmapCharacteristic.serializedName == "MissingCharacteristic") - { - actionButton.interactable = false; - practiceButton.interactable = false; - RequirementsUI.instance.ButtonGlowColor = true; - RequirementsUI.instance.ButtonInteractable = true; - } - - RequirementsUI.instance.beatmapLevel = beatmapLevel; - RequirementsUI.instance.beatmapKey = __instance.beatmapKey; - RequirementsUI.instance.songData = songData; - RequirementsUI.instance.diffData = diffData; - RequirementsUI.instance.wipFolder = wipFolderSong; - //Difficulty Label Handling LevelLabels.Clear(); diff --git a/source/SongCore/Installers/AppInstaller.cs b/source/SongCore/Installers/AppInstaller.cs new file mode 100644 index 0000000..006365a --- /dev/null +++ b/source/SongCore/Installers/AppInstaller.cs @@ -0,0 +1,13 @@ +using SongCore.HarmonyPatches; +using Zenject; + +namespace SongCore.Installers +{ + internal class AppInstaller : Installer + { + public override void InstallBindings() + { + MainSystemsInitRefreshablePatch.Postfix(Container); + } + } +} diff --git a/source/SongCore/Installers/GameInstaller.cs b/source/SongCore/Installers/GameInstaller.cs new file mode 100644 index 0000000..ebedbc6 --- /dev/null +++ b/source/SongCore/Installers/GameInstaller.cs @@ -0,0 +1,14 @@ +using SongCore.HarmonyPatches; +using Zenject; + +namespace SongCore.Installers +{ + internal class GameInstaller : Installer + { + public override void InstallBindings() + { + Container.BindInterfacesTo().AsSingle(); + Container.BindInterfacesTo().AsSingle(); + } + } +} diff --git a/source/SongCore/Installers/MenuInstaller.cs b/source/SongCore/Installers/MenuInstaller.cs new file mode 100644 index 0000000..d47e56d --- /dev/null +++ b/source/SongCore/Installers/MenuInstaller.cs @@ -0,0 +1,16 @@ +using SongCore.UI; +using Zenject; + +namespace SongCore.Installers +{ + internal class MenuInstaller : Installer + { + public override void InstallBindings() + { + Container.BindInterfacesAndSelfTo().AsSingle(); + Container.BindInterfacesAndSelfTo().AsSingle(); + Container.Bind().FromNewComponentOnNewGameObject().AsSingle(); + Container.BindInterfacesAndSelfTo().AsSingle(); + } + } +} diff --git a/source/SongCore/Loader.cs b/source/SongCore/Loader.cs index dca795d..bece643 100644 --- a/source/SongCore/Loader.cs +++ b/source/SongCore/Loader.cs @@ -7,27 +7,58 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using BeatSaberMarkupLanguage.Settings; using IPA.Utilities.Async; +using SiraUtil.Zenject; using SongCore.Data; using SongCore.OverrideClasses; using SongCore.UI; using SongCore.Utilities; using UnityEngine; using UnityEngine.SceneManagement; +using Zenject; namespace SongCore { - public class Loader : MonoBehaviour + public class Loader : IAsyncInitializable, IDisposable, ITickable { - private ProgressBar _progressBar; - private GameScenesManager _gameScenesManager; - private LevelFilteringNavigationController _levelFilteringNavigationController; + private readonly GameScenesManager _gameScenesManager; + private readonly LevelFilteringNavigationController _levelFilteringNavigationController; + private readonly LevelPackDetailViewController _levelPackDetailViewController; + private readonly LevelCollectionViewController _levelCollectionViewController; + private readonly BeatmapLevelsModel _beatmapLevelsModel; + private readonly CustomLevelLoader _customLevelLoader; + private readonly CachedMediaAsyncLoader _cachedMediaAsyncLoader; + private readonly BeatmapCharacteristicCollection _beatmapCharacteristicCollection; + private readonly ProgressBar _progressBar; + private readonly BSMLSettings _bsmlSettings; + private readonly SCSettingsController _settingsController; + private readonly string _customWIPPath; + private readonly string _customLevelsPath; + private Task? _loadingTask; private CancellationTokenSource _loadingTaskCancellationTokenSource = new CancellationTokenSource(); private bool _loadingCancelled; private bool _isInitialized; - private string _customWIPPath; - private string _customLevelsPath; + + private Loader(GameScenesManager gameScenesManager, LevelFilteringNavigationController levelFilteringNavigationController, LevelPackDetailViewController levelPackDetailViewController, LevelCollectionViewController levelCollectionViewController, BeatmapLevelsModel beatmapLevelsModel, CustomLevelLoader customLevelLoader, CachedMediaAsyncLoader cachedMediaAsyncLoader, BeatmapCharacteristicCollection beatmapCharacteristicCollection, ProgressBar progressBar, BSMLSettings bsmlSettings) + + { + _gameScenesManager = gameScenesManager; + _levelFilteringNavigationController = levelFilteringNavigationController; + _levelPackDetailViewController = levelPackDetailViewController; + _levelCollectionViewController = levelCollectionViewController; + _beatmapLevelsModel = beatmapLevelsModel; + _customLevelLoader = customLevelLoader; + _cachedMediaAsyncLoader = cachedMediaAsyncLoader; + _beatmapCharacteristicCollection = beatmapCharacteristicCollection; + _progressBar = progressBar; + _bsmlSettings = bsmlSettings; + _settingsController = new SCSettingsController(); + _customWIPPath = Path.Combine(Application.dataPath, "CustomWIPLevels"); + _customLevelsPath = Path.GetFullPath(CustomLevelPathHelper.customLevelsDirectoryPath); + Instance = this; + } // Actions for loading and refreshing beatmaps public static event Action? LoadingStartedEvent; @@ -57,55 +88,58 @@ public class Loader : MonoBehaviour public static CachedMediaAsyncLoader cachedMediaAsyncLoaderSO { get; private set; } public static BeatmapCharacteristicCollection beatmapCharacteristicCollection { get; private set; } - private void Awake() + public async Task InitializeAsync(CancellationToken cancellationToken) { - _customWIPPath = Path.Combine(Application.dataPath, "CustomWIPLevels"); - _customLevelsPath = Path.GetFullPath(CustomLevelPathHelper.customLevelsDirectoryPath); - _progressBar = ProgressBar.Create(); - Instance = this; - DontDestroyOnLoad(gameObject); - } + Logging.Logger.Notice(nameof(InitializeAsync)); + await UnityAsyncHelper.WaitUntilAsync(_progressBar, () => !_gameScenesManager.isInTransition); - private void Initialize() - { - BS_Utils.Utilities.BSEvents.menuSceneLoaded += MenuLoaded; - _gameScenesManager.transitionDidStartEvent += CancelSongLoading; - Hashing.ReadCachedSongHashes(); - Hashing.ReadCachedAudioData(); - _isInitialized = true; - } - - internal void MenuLoadedFresh() - { // Ensures that the static references are still valid Unity objects. // They'll be destroyed on internal restart. - _gameScenesManager = FindObjectOfType(); - _levelFilteringNavigationController = FindObjectOfType(true); - BeatmapLevelsModelSO = _levelFilteringNavigationController._beatmapLevelsModel; - CustomLevelLoader = FindObjectOfType(); - cachedMediaAsyncLoaderSO = CustomLevelLoader._cachedMediaAsyncLoader; - defaultCoverImage = FindObjectOfType(true)._defaultCoverSprite; - beatmapCharacteristicCollection = CustomLevelLoader._beatmapCharacteristicCollection; - BS_Utils.Gameplay.Gamemode.Init(); - - if (_isInitialized) - { - Instance.RefreshLevelPacks(); - } - else + BeatmapLevelsModelSO = _beatmapLevelsModel; + CustomLevelLoader = _customLevelLoader; + cachedMediaAsyncLoaderSO = _cachedMediaAsyncLoader; + defaultCoverImage = _levelPackDetailViewController._defaultCoverSprite; + beatmapCharacteristicCollection = _beatmapCharacteristicCollection; + + if (Hashing.cachedSongHashData.Count == 0) { - Initialize(); - RefreshSongs(); + Hashing.ReadCachedSongHashes(); + Hashing.ReadCachedAudioData(); } - MenuLoaded(); + RefreshSongs(); + + SceneManager.activeSceneChanged += HandleActiveSceneChanged; + + _gameScenesManager.transitionDidStartEvent += CancelSongLoading; + _levelCollectionViewController.didSelectLevelEvent += HandleDidSelectLevel; + _bsmlSettings.AddSettingsMenu(nameof(SongCore), "SongCore.UI.settings.bsml", _settingsController); + } + + public void Dispose() + { + Logging.Logger.Notice(nameof(Dispose)); + + SceneManager.activeSceneChanged -= HandleActiveSceneChanged; + + _gameScenesManager.transitionDidStartEvent -= CancelSongLoading; + _levelCollectionViewController.didSelectLevelEvent -= HandleDidSelectLevel; + _bsmlSettings.RemoveSettingsMenu(_settingsController); } - private void MenuLoaded() + /// + /// Refresh songs on "R" key, full refresh on "Ctrl"+"R" + /// + public void Tick() { - if (_loadingCancelled) + if (Input.GetKeyDown(KeyCode.R)) { - RefreshSongs(); + RefreshSongs(Input.GetKey(KeyCode.LeftControl)); + } + + if (Input.GetKeyDown(KeyCode.X) && Input.GetKey(KeyCode.LeftControl) && _loadingTask != null) + { + CancelSongLoading(); } } @@ -126,6 +160,30 @@ private void CancelSongLoading() } } + private void HandleActiveSceneChanged(Scene previousScene, Scene nextScene) + { + if (_loadingCancelled && nextScene.name == "MainMenu") + { + RefreshSongs(); + } + } + + private static void HandleDidSelectLevel(LevelCollectionViewController levelCollectionViewController, BeatmapLevel beatmapLevel) + { + if (!beatmapLevel.hasPrecalculatedData && Collections.RetrieveExtraSongData(Hashing.GetCustomLevelHash(beatmapLevel)) is { } songData) + { + if (Plugin.Configuration.CustomSongPlatforms && !string.IsNullOrWhiteSpace(songData._customEnvironmentName)) + { + Logging.Logger.Debug("Custom song with platform selected"); + Plugin.CustomSongPlatformSelectionDidChange?.Invoke(true, songData._customEnvironmentName, songData._customEnvironmentHash, beatmapLevel); + } + else + { + Plugin.CustomSongPlatformSelectionDidChange?.Invoke(false, songData._customEnvironmentName, songData._customEnvironmentHash, beatmapLevel); + } + } + } + /// /// This function will add/remove Level Packs from the Custom Levels tab if applicable /// @@ -176,7 +234,7 @@ await UnityMainThreadTaskScheduler.Factory.StartNew(() => public void RefreshSongs(bool fullRefresh = true) { - if (AreSongsLoading || SceneManager.GetActiveScene().name == BS_Utils.SceneNames.Game) + if (AreSongsLoading || SceneManager.GetActiveScene().name == "GameCore") { return; } @@ -216,7 +274,7 @@ private async void RetrieveAllSongs(bool fullRefresh) CustomWIPLevels.Clear(); CachedWIPLevels.Clear(); LoadedBeatmapLevelsData.Clear(); - BeatmapLevelsModelSO.ClearLoadedBeatmapLevelsCaches(); + _beatmapLevelsModel.ClearLoadedBeatmapLevelsCaches(); Collections.LevelHashDictionary.Clear(); Collections.HashLevelDictionary.Clear(); foreach (var folder in SeperateSongFolders) @@ -420,9 +478,9 @@ void AddOfficialBeatmapLevelsRepository(BeatmapLevelsRepository levelsRepository foreach (var beatmapLevelData in LoadedBeatmapLevelsData) { - if (!CustomLevelLoader._loadedBeatmapLevelsData.ContainsKey(beatmapLevelData.Key)) + if (!_customLevelLoader._loadedBeatmapLevelsData.ContainsKey(beatmapLevelData.Key)) { - CustomLevelLoader._loadedBeatmapLevelsData.Add(beatmapLevelData.Key, beatmapLevelData.Value); + _customLevelLoader._loadedBeatmapLevelsData.Add(beatmapLevelData.Key, beatmapLevelData.Value); } } @@ -784,8 +842,6 @@ private void DeleteSingleSong(string folderPath, bool deleteFolder) levelID += " WIP"; } - Collections.LevelPathDictionary.TryAdd(levelID, songPath); - beatmapLevel = CustomLevelLoader.CreateBeatmapLevelFromV3(songPath, saveData); Accessors.LevelIDAccessor(ref beatmapLevel) = levelID; @@ -810,22 +866,6 @@ private void DeleteSingleSong(string folderPath, bool deleteFolder) return LoadSong(saveData, songPath, out hash, folderEntry); } - /// - /// Refresh songs on "R" key, full refresh on "Ctrl"+"R" - /// - private void Update() - { - if (Input.GetKeyDown(KeyCode.R)) - { - RefreshSongs(Input.GetKey(KeyCode.LeftControl)); - } - - if (Input.GetKeyDown(KeyCode.X) && Input.GetKey(KeyCode.LeftControl) && _loadingTask != null) - { - CancelSongLoading(); - } - } - #region HelperFunctionsZIP /// @@ -1013,6 +1053,8 @@ private bool AssignBeatmapToSeperateFolder( levels = new List { level.levelID }; Collections.HashLevelDictionary.TryAdd(hash, levels); } + Collections.LevelSaveDataDictionary.TryAdd(level.levelID, songData.SaveData); + Collections.LevelPathDictionary.TryAdd(level.levelID, songPath); Collections.AddExtraSongData(hash, songPath, songData.RawSongData); } diff --git a/source/SongCore/Plugin.cs b/source/SongCore/Plugin.cs index beeef69..7b825bf 100644 --- a/source/SongCore/Plugin.cs +++ b/source/SongCore/Plugin.cs @@ -1,4 +1,3 @@ -using BeatSaberMarkupLanguage.Settings; using HarmonyLib; using IPA; using SongCore.UI; @@ -10,7 +9,8 @@ using IPA.Config.Stores; using IPA.Loader; using SongCore.HarmonyPatches; -using UnityEngine; +using SiraUtil.Zenject; +using SongCore.Installers; using IPALogger = IPA.Logging.Logger; namespace SongCore @@ -30,7 +30,7 @@ public class Plugin public static string noArrowsCharacteristicName = "NoArrows"; [Init] - public Plugin(IPALogger pluginLogger, PluginMetadata metadata) + public Plugin(IPALogger pluginLogger, PluginMetadata metadata, Zenjector zenjector) { // Workaround for creating BSIPA config in Userdata subdir Directory.CreateDirectory(Path.Combine(UnityGame.UserDataPath, nameof(SongCore))); @@ -39,19 +39,20 @@ public Plugin(IPALogger pluginLogger, PluginMetadata metadata) Logging.Logger = pluginLogger; _metadata = metadata; _harmony = new Harmony("com.kyle1413.BeatSaber.SongCore"); + + zenjector.UseLogger(pluginLogger); + zenjector.Install(Location.App); + zenjector.Install(Location.Menu); + zenjector.Install(Location.GameCore); } [OnStart] public void OnApplicationStart() { - BSMLSettings.instance.AddSettingsMenu(nameof(SongCore), "SongCore.UI.settings.bsml", new SCSettingsController()); - _harmony.Patch(HarmonyTranspilersFixPatch.TargetMethod(), null, null, new HarmonyMethod(AccessTools.Method(typeof(HarmonyTranspilersFixPatch), nameof(HarmonyTranspilersFixPatch.Transpiler)))); _harmony.PatchAll(_metadata.Assembly); BasicUI.GetIcons(); - BS_Utils.Utilities.BSEvents.levelSelected += BSEvents_levelSelected; - BS_Utils.Utilities.BSEvents.lateMenuSceneLoadedFresh += BSEvents_menuSceneLoadedFresh; if (!File.Exists(Collections.DataPath)) { @@ -77,33 +78,6 @@ public void OnApplicationStart() Loader.SeperateSongFolders.InsertRange(0, Data.SeperateSongFolder.ReadSeperateFoldersFromFile(foldersXmlFilePath)); } - private void BSEvents_menuSceneLoadedFresh(ScenesTransitionSetupDataSO data) - { - if (Loader.Instance == null) - { - new GameObject("SongCore Loader").AddComponent(); - } - - Loader.Instance!.MenuLoadedFresh(); - RequirementsUI.instance.Setup(); - } - - private void BSEvents_levelSelected(LevelCollectionViewController arg1, BeatmapLevel level) - { - if (!level.hasPrecalculatedData && Collections.RetrieveExtraSongData(Hashing.GetCustomLevelHash(level)) is { } songData) - { - if (Configuration.CustomSongPlatforms && !string.IsNullOrWhiteSpace(songData._customEnvironmentName)) - { - Logging.Logger.Debug("Custom song with platform selected"); - CustomSongPlatformSelectionDidChange?.Invoke(true, songData._customEnvironmentName, songData._customEnvironmentHash, level); - } - else - { - CustomSongPlatformSelectionDidChange?.Invoke(false, songData._customEnvironmentName, songData._customEnvironmentHash, level); - } - } - } - [OnExit] public void OnApplicationExit() { diff --git a/source/SongCore/SongCore.csproj b/source/SongCore/SongCore.csproj index b93c62f..f1ceba5 100644 --- a/source/SongCore/SongCore.csproj +++ b/source/SongCore/SongCore.csproj @@ -40,10 +40,6 @@ $(BeatSaberDir)\Beat Saber_Data\Managed\BGLib.UnityExtension.dll false - - $(BeatSaberDir)\Plugins\BS_Utils.dll - False - $(BeatSaberDir)\Plugins\BSML.dll False @@ -58,6 +54,10 @@ false true + + $(BeatSaberDir)\Beat Saber_Data\Managed\GameplayCore.dll + false + $(BeatSaberDir)\Beat Saber_Data\Managed\HMLib.dll False @@ -96,6 +96,10 @@ $(BeatSaberDir)\Beat Saber_Data\Managed\Polyglot.dll false + + $(BeatSaberDir)\Plugins\SiraUtil.dll + false + $(BeatSaberDir)\Beat Saber_Data\Managed\Tweening.dll False @@ -104,6 +108,10 @@ $(BeatSaberDir)\Beat Saber_Data\Managed\Unity.TextMeshPro.dll False + + $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.dll + false + $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.CoreModule.dll False diff --git a/source/SongCore/UI/ColorsUI.cs b/source/SongCore/UI/ColorsUI.cs index 94c622c..1c580dc 100644 --- a/source/SongCore/UI/ColorsUI.cs +++ b/source/SongCore/UI/ColorsUI.cs @@ -1,8 +1,7 @@ -using System.Linq; using System.Reflection; using BeatSaberMarkupLanguage; using BeatSaberMarkupLanguage.Attributes; -using BeatSaberMarkupLanguage.Util; +using BeatSaberMarkupLanguage.Components; using HMUI; using SongCore.Data; using SongCore.Utilities; @@ -10,8 +9,19 @@ namespace SongCore.UI { - public class ColorsUI : NotifiableSingleton + public class ColorsUI : NotifiableBase { + private readonly StandardLevelDetailViewController _standardLevelDetailViewController; + private readonly GameplaySetupViewController _gameplaySetupViewController; + private readonly BSMLParser _bsmlParser; + + private ColorsUI(StandardLevelDetailViewController standardLevelDetailViewController, GameplaySetupViewController gameplaySetupViewController, BSMLParser bsmlParser) + { + _standardLevelDetailViewController = standardLevelDetailViewController; + _gameplaySetupViewController = gameplaySetupViewController; + _bsmlParser = bsmlParser; + } + private ColorSchemeView colorSchemeView; private readonly Color voidColor = new Color(0.5f, 0.5f, 0.5f, 0.25f); @@ -56,9 +66,8 @@ private void Parse() { if (!modal) { - StandardLevelDetailViewController standardLevel = Object.FindObjectOfType(); - BSMLParser.instance.Parse(BeatSaberMarkupLanguage.Utilities.GetResourceContent(Assembly.GetExecutingAssembly(), "SongCore.UI.colors.bsml"), - standardLevel.transform.Find("LevelDetail").gameObject, this); + _bsmlParser.Parse(BeatSaberMarkupLanguage.Utilities.GetResourceContent(Assembly.GetExecutingAssembly(), "SongCore.UI.colors.bsml"), + _standardLevelDetailViewController._standardLevelDetailView.gameObject, this); } modal.transform.localPosition = modalPosition; } @@ -66,7 +75,7 @@ private void Parse() [UIAction("#post-parse")] private void PostParse() { - ColorSchemeView colorSchemeViewPrefab = Object.Instantiate(Object.FindObjectOfType(true), selectedColorTransform); + ColorSchemeView colorSchemeViewPrefab = Object.Instantiate(_gameplaySetupViewController._colorsOverrideSettingsPanelController._colorSchemeDropDown._colorSchemeView, selectedColorTransform); colorSchemeView = IPA.Utilities.ReflectionUtil.CopyComponent(colorSchemeViewPrefab, colorSchemeViewPrefab.gameObject); Object.DestroyImmediate(colorSchemeViewPrefab); modalPosition = modal.transform.localPosition; @@ -75,7 +84,7 @@ private void PostParse() private void Dismiss() { - modal.Hide(false, () => RequirementsUI.instance.ShowRequirements()); + modal.Hide(true); } private void SetColors(ExtraSongData.DifficultyData songData) diff --git a/source/SongCore/UI/RequirementsUI.cs b/source/SongCore/UI/RequirementsUI.cs index 4bbc438..de4b2a1 100644 --- a/source/SongCore/UI/RequirementsUI.cs +++ b/source/SongCore/UI/RequirementsUI.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using BeatSaberMarkupLanguage; using BeatSaberMarkupLanguage.Attributes; @@ -5,19 +6,34 @@ using SongCore.Utilities; using System.Linq; using System.Reflection; -using BeatSaberMarkupLanguage.Util; using UnityEngine; using static BeatSaberMarkupLanguage.Components.CustomListTableData; using HMUI; +using SiraUtil.Affinity; using Tweening; +using Zenject; namespace SongCore.UI { - public class RequirementsUI : NotifiableSingleton + public class RequirementsUI : NotifiableBase, IInitializable, IAffinity { + private readonly StandardLevelDetailViewController _standardLevelDetailViewController; + private readonly TimeTweeningManager _tweeningManager; + private readonly BSMLParser _bsmlParser; + private readonly ColorsUI _colorsUI; + + private RequirementsUI(StandardLevelDetailViewController standardLevelDetailViewController, TimeTweeningManager tweeningManager, BSMLParser bsmlParser, ColorsUI colorsUI) + { + _standardLevelDetailViewController = standardLevelDetailViewController; + _tweeningManager = tweeningManager; + _bsmlParser = bsmlParser; + _colorsUI = colorsUI; + instance = this; + } + + public static RequirementsUI instance { get; set; } + private const string BUTTON_BSML = ""; - private StandardLevelDetailViewController standardLevel; - private TweeningManager tweenyManager; private ImageView buttonBG; private Color originalColor0; private Color originalColor1; @@ -80,15 +96,13 @@ public bool ButtonInteractable [UIComponent("root")] protected readonly RectTransform _root = null!; - internal void Setup() + public void Initialize() { GetIcons(); - standardLevel = Object.FindObjectOfType(true); - tweenyManager = Object.FindObjectOfType(); - BSMLParser.instance.Parse(BUTTON_BSML, standardLevel.transform.Find("LevelDetail").gameObject, this); + _bsmlParser.Parse(BUTTON_BSML, _standardLevelDetailViewController._standardLevelDetailView.gameObject, this); infoButtonTransform.localScale *= 0.7f; //no scale property in bsml as of now so manually scaling it - (standardLevel.transform.Find("LevelDetail").Find("FavoriteToggle")?.transform as RectTransform)!.anchoredPosition = new Vector2(3, -2); + ((RectTransform)_standardLevelDetailViewController._standardLevelDetailView._favoriteToggle.transform).anchoredPosition = new Vector2(3, -2); buttonBG = infoButtonTransform.Find("BG").GetComponent(); originalColor0 = buttonBG.color0; originalColor1 = buttonBG.color1; @@ -152,9 +166,8 @@ internal void ShowRequirements() { if (modal == null) { - BSMLParser.instance.Parse(BeatSaberMarkupLanguage.Utilities.GetResourceContent(Assembly.GetExecutingAssembly(), "SongCore.UI.requirements.bsml"), _root.gameObject, this); + _bsmlParser.Parse(BeatSaberMarkupLanguage.Utilities.GetResourceContent(Assembly.GetExecutingAssembly(), "SongCore.UI.requirements.bsml"), _root.gameObject, this); modalPosition = modal!.transform.localPosition; - } modal.transform.localPosition = modalPosition; modal.Show(true); @@ -264,7 +277,7 @@ internal void ShowRequirements() if (customListTableData.data.Count > 0) { - if (environmentName == null && beatmapLevel != null) + if (environmentName == null && beatmapLevel != null) // TODO: Can this actually be null? environmentName = beatmapLevel.GetEnvironmentName(beatmapKey.beatmapCharacteristic, beatmapKey.difficulty); customListTableData.data.Add(new CustomCellInfo("Environment Info", $"This Map uses the Environment: {environmentName}", EnvironmentIcon)); @@ -284,7 +297,7 @@ private void Select(TableView _, int index) var iconSelected = customListTableData.data[index].icon; if (iconSelected == ColorsIcon) { - modal.Hide(false, () => ColorsUI.instance.ShowColors(diffData)); + modal.Hide(false, () => _colorsUI.ShowColors(diffData)); } else if (iconSelected == StandardIcon || iconSelected == OneSaberIcon) { @@ -296,7 +309,7 @@ private void Select(TableView _, int index) internal void SetRainbowColors(bool shouldSet, bool firstPulse = true) { - tweenyManager.KillAllTweens(buttonBG); + _tweeningManager.KillAllTweens(buttonBG); if (shouldSet) { FloatTween tween = new FloatTween(firstPulse ? 0 : 1, firstPulse ? 1 : 0, val => @@ -305,7 +318,7 @@ internal void SetRainbowColors(bool shouldSet, bool firstPulse = true) buttonBG.color1 = new Color(0, 1 - val, val); buttonBG.SetAllDirty(); }, 5f, EaseType.InOutSine); - tweenyManager.AddTween(tween, buttonBG); + _tweeningManager.AddTween(tween, buttonBG); tween.onCompleted = delegate () { SetRainbowColors(true, !firstPulse); }; } @@ -315,5 +328,96 @@ internal void SetRainbowColors(bool shouldSet, bool firstPulse = true) buttonBG.color1 = originalColor1; } } + + [AffinityPatch(typeof(StandardLevelDetailView), nameof(StandardLevelDetailView.CheckIfBeatmapLevelDataExists))] + private void EnableOrDisableSongButtons() + { + ButtonGlowColor = false; + ButtonInteractable = false; + if (_standardLevelDetailViewController._beatmapLevel.hasPrecalculatedData) + { + return; + } + + var songData = Collections.RetrieveExtraSongData(Hashing.GetCustomLevelHash(_standardLevelDetailViewController._beatmapLevel)); + + if (songData == null) + { + ButtonGlowColor = false; + ButtonInteractable = false; + return; + } + + var wipFolderSong = false; + var diffData = Collections.RetrieveDifficultyData(_standardLevelDetailViewController._beatmapLevel, _standardLevelDetailViewController.beatmapKey); + var actionButton = _standardLevelDetailViewController._standardLevelDetailView.actionButton; + var practiceButton = _standardLevelDetailViewController._standardLevelDetailView.practiceButton; + + if (diffData != null) + { + //If no additional information is present + if (!diffData.additionalDifficultyData._requirements.Any() && + !diffData.additionalDifficultyData._suggestions.Any() && + !diffData.additionalDifficultyData._warnings.Any() && + !diffData.additionalDifficultyData._information.Any() && + !songData.contributors.Any() && !Utils.DiffHasColors(diffData)) + { + ButtonGlowColor = false; + ButtonInteractable = false; + } + else if (!diffData.additionalDifficultyData._warnings.Any()) + { + ButtonGlowColor = true; + ButtonInteractable = true; + SetRainbowColors(Utils.DiffHasColors(diffData)); + } + else if (diffData.additionalDifficultyData._warnings.Any()) + { + ButtonGlowColor = true; + ButtonInteractable = true; + if (diffData.additionalDifficultyData._warnings.Contains("WIP")) + { + actionButton.interactable = false; + } + SetRainbowColors(Utils.DiffHasColors(diffData)); + } + } + + if (_standardLevelDetailViewController._beatmapLevel.levelID.EndsWith(" WIP", StringComparison.Ordinal)) + { + ButtonGlowColor = true; + ButtonInteractable = true; + actionButton.interactable = false; + wipFolderSong = true; + } + + if (diffData != null) + { + foreach (var requirement in diffData.additionalDifficultyData._requirements) + { + if (!Collections.capabilities.Contains(requirement)) + { + actionButton.interactable = false; + practiceButton.interactable = false; + ButtonGlowColor = true; + ButtonInteractable = true; + } + } + } + + if (_standardLevelDetailViewController.beatmapKey.beatmapCharacteristic.serializedName == "MissingCharacteristic") + { + actionButton.interactable = false; + practiceButton.interactable = false; + ButtonGlowColor = true; + ButtonInteractable = true; + } + + beatmapLevel = _standardLevelDetailViewController._beatmapLevel; + beatmapKey = _standardLevelDetailViewController.beatmapKey; + this.songData = songData; + this.diffData = diffData; + wipFolder = wipFolderSong; + } } } \ No newline at end of file diff --git a/source/SongCore/Utilities/Hashing.cs b/source/SongCore/Utilities/Hashing.cs index 8225fda..b07d046 100644 --- a/source/SongCore/Utilities/Hashing.cs +++ b/source/SongCore/Utilities/Hashing.cs @@ -119,16 +119,16 @@ private static bool GetCachedSongData(string customLevelPath, out long directory return false; } - // TODO: See if there's a way to get the path and the sava data from the level. - public static string? GetCustomLevelHash(BeatmapLevel level) + public static string GetCustomLevelHash(BeatmapLevel level) { - if (level.hasPrecalculatedData) + var standardLevelInfoSaveData = Collections.GetStandardLevelInfoSaveDataByLevelId(level.levelID); + if (standardLevelInfoSaveData == null) { return null; } - var hash = level.levelID.Split('_')[2]; - return hash.Length == 40 ? hash : null; + var levelPath = Collections.GetLevelPathByLevelId(level.levelID); + return GetCustomLevelHash(levelPath, standardLevelInfoSaveData.difficultyBeatmapSets); } public static string GetCustomLevelHash(StandardLevelInfoSaveData level, string customLevelPath) diff --git a/source/SongCore/manifest.json b/source/SongCore/manifest.json index d3c16f9..d8b4ae1 100644 --- a/source/SongCore/manifest.json +++ b/source/SongCore/manifest.json @@ -9,7 +9,7 @@ "dependsOn": { "BSIPA": "^4.3.0", "BeatSaberMarkupLanguage": "^1.7.6", - "BS Utils": "^1.12.3" + "SiraUtil": "^3.0.0" }, "links": { "project-source": "https://github.com/Kylemc1413/SongCore" From 9c11b1c5a4edf8edcd5f225139190202ba92bd2e Mon Sep 17 00:00:00 2001 From: Meivyn <793322+Meivyn@users.noreply.github.com> Date: Thu, 14 Mar 2024 16:35:55 -0400 Subject: [PATCH 02/17] Refactor some Affinity patches --- source/SongCore/Collections.cs | 10 +- ...atch.cs => AllowNegativeNjsValuesPatch.cs} | 12 +- .../BeatmapLevelDifficultyDataPatches.cs | 37 ++++++ .../HarmonyPatches/BindBeatmapLevelPatch.cs | 13 ++ .../CosmeticCharacteristicsPatch.cs | 87 ++++++++++++++ .../CosmeticCharacteristicsPatches.cs | 111 ------------------ source/SongCore/Installers/AppInstaller.cs | 1 + source/SongCore/Installers/GameInstaller.cs | 2 +- source/SongCore/Installers/MenuInstaller.cs | 2 + source/SongCore/Plugin.cs | 2 +- source/SongCore/SongCore.csproj | 1 + source/SongCore/Utilities/Accessors.cs | 13 +- source/SongCore/Utilities/Hashing.cs | 13 +- 13 files changed, 171 insertions(+), 133 deletions(-) rename source/SongCore/HarmonyPatches/{NegativeNjsPatch.cs => AllowNegativeNjsValuesPatch.cs} (50%) create mode 100644 source/SongCore/HarmonyPatches/BeatmapLevelDifficultyDataPatches.cs create mode 100644 source/SongCore/HarmonyPatches/BindBeatmapLevelPatch.cs create mode 100644 source/SongCore/HarmonyPatches/CosmeticCharacteristicsPatch.cs delete mode 100644 source/SongCore/HarmonyPatches/CosmeticCharacteristicsPatches.cs diff --git a/source/SongCore/Collections.cs b/source/SongCore/Collections.cs index 1a3e18a..62fb7ad 100644 --- a/source/SongCore/Collections.cs +++ b/source/SongCore/Collections.cs @@ -45,14 +45,16 @@ public static List levelIDsForHash(string hash) return HashLevelDictionary.TryGetValue(hash, out var songs) ? songs : new List(); } - public static string GetLevelPathByLevelId(string levelID) + public static string? GetCustomLevelPath(string levelID) { - return LevelPathDictionary.TryGetValue(levelID, out var path) ? path : string.Empty; + LevelPathDictionary.TryGetValue(levelID, out var path); + return path; } - public static StandardLevelInfoSaveData? GetStandardLevelInfoSaveDataByLevelId(string levelID) + public static StandardLevelInfoSaveData? GetStandardLevelInfoSaveData(string levelID) { - return LevelSaveDataDictionary.TryGetValue(levelID, out var standardLevelInfoSaveData) ? standardLevelInfoSaveData : null; + LevelSaveDataDictionary.TryGetValue(levelID, out var standardLevelInfoSaveData); + return standardLevelInfoSaveData; } internal static void AddExtraSongData(string hash, string path, string rawSongData) diff --git a/source/SongCore/HarmonyPatches/NegativeNjsPatch.cs b/source/SongCore/HarmonyPatches/AllowNegativeNjsValuesPatch.cs similarity index 50% rename from source/SongCore/HarmonyPatches/NegativeNjsPatch.cs rename to source/SongCore/HarmonyPatches/AllowNegativeNjsValuesPatch.cs index e7ebf38..b8eddca 100644 --- a/source/SongCore/HarmonyPatches/NegativeNjsPatch.cs +++ b/source/SongCore/HarmonyPatches/AllowNegativeNjsValuesPatch.cs @@ -4,24 +4,22 @@ namespace SongCore.HarmonyPatches { internal class AllowNegativeNjsValuesPatch : IAffinity { - private readonly GameplayCoreSceneSetupData _gameplayCoreSceneSetupData; - private readonly BeatmapKey _beatmapKey; + private readonly BeatmapBasicData _beatmapBasicData; - private AllowNegativeNjsValuesPatch(GameplayCoreSceneSetupData gameplayCoreSceneSetupData, BeatmapKey beatmapKey) + private AllowNegativeNjsValuesPatch(BeatmapBasicData beatmapBasicData) { - _gameplayCoreSceneSetupData = gameplayCoreSceneSetupData; - _beatmapKey = beatmapKey; + _beatmapBasicData = beatmapBasicData; } [AffinityPatch(typeof(BeatmapObjectSpawnMovementData), nameof(BeatmapObjectSpawnMovementData.Init))] [AffinityPrefix] private void ForceNegativeStartNoteJumpMovementSpeed(ref float startNoteJumpMovementSpeed) { - var noteJumpMovementSpeed = _gameplayCoreSceneSetupData.beatmapLevel.beatmapBasicData[(_beatmapKey.beatmapCharacteristic, _beatmapKey.difficulty)].noteJumpMovementSpeed; + var noteJumpMovementSpeed = _beatmapBasicData.noteJumpMovementSpeed; if (noteJumpMovementSpeed < 0) { startNoteJumpMovementSpeed = noteJumpMovementSpeed; } } } -} \ No newline at end of file +} diff --git a/source/SongCore/HarmonyPatches/BeatmapLevelDifficultyDataPatches.cs b/source/SongCore/HarmonyPatches/BeatmapLevelDifficultyDataPatches.cs new file mode 100644 index 0000000..571496f --- /dev/null +++ b/source/SongCore/HarmonyPatches/BeatmapLevelDifficultyDataPatches.cs @@ -0,0 +1,37 @@ +using SiraUtil.Affinity; +using SongCore.Utilities; + +namespace SongCore.HarmonyPatches +{ + internal class BeatmapLevelDifficultyDataPatches : IAffinity + { + private readonly bool? _showRotationNoteSpawnLines; + private readonly bool? _oneSaber; + + private BeatmapLevelDifficultyDataPatches(BeatmapLevel beatmapLevel, BeatmapKey beatmapKey) + { + var difficultyData = Collections.RetrieveDifficultyData(beatmapLevel, beatmapKey); + if (difficultyData != null) + { + _showRotationNoteSpawnLines = difficultyData._showRotationNoteSpawnLines; + _oneSaber = difficultyData._oneSaber; + } + } + + [AffinityPatch(typeof(BeatLineManager), nameof(BeatLineManager.HandleNoteWasSpawned))] + [AffinityPrefix] + private bool ShowOrHideRotationNoteSpawnLines() + { + return _showRotationNoteSpawnLines ?? true; + } + + [AffinityPatch(typeof(SaberManager.InitData), "ctor", AffinityMethodType.Constructor, null, typeof(bool), typeof(SaberType))] + private void ForceOneSaber(SaberManager.InitData __instance) + { + if (_oneSaber.HasValue) + { + Accessors.OneSaberModeAccessor(ref __instance) = _oneSaber.Value; + } + } + } +} diff --git a/source/SongCore/HarmonyPatches/BindBeatmapLevelPatch.cs b/source/SongCore/HarmonyPatches/BindBeatmapLevelPatch.cs new file mode 100644 index 0000000..d8d7f6c --- /dev/null +++ b/source/SongCore/HarmonyPatches/BindBeatmapLevelPatch.cs @@ -0,0 +1,13 @@ +using HarmonyLib; + +namespace SongCore.HarmonyPatches +{ + [HarmonyPatch(typeof(GameplayCoreInstaller), nameof(GameplayCoreInstaller.InstallBindings))] + internal class BindBeatmapLevelPatch + { + private static void Postfix(GameplayCoreInstaller __instance) + { + __instance.Container.Bind().FromInstance(__instance._sceneSetupData.beatmapLevel).AsSingle(); + } + } +} diff --git a/source/SongCore/HarmonyPatches/CosmeticCharacteristicsPatch.cs b/source/SongCore/HarmonyPatches/CosmeticCharacteristicsPatch.cs new file mode 100644 index 0000000..e0378fd --- /dev/null +++ b/source/SongCore/HarmonyPatches/CosmeticCharacteristicsPatch.cs @@ -0,0 +1,87 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using BGLib.Polyglot; +using HMUI; +using SiraUtil.Affinity; +using SongCore.Utilities; +using UnityEngine; + +namespace SongCore.HarmonyPatches +{ + internal class CosmeticCharacteristicsPatch : IAffinity + { + private readonly StandardLevelDetailViewController _standardLevelDetailViewController; + + private CosmeticCharacteristicsPatch(StandardLevelDetailViewController standardLevelDetailViewController) + { + _standardLevelDetailViewController = standardLevelDetailViewController; + } + + [AffinityPatch(typeof(BeatmapCharacteristicSegmentedControlController), nameof(BeatmapCharacteristicSegmentedControlController.SetData))] + private void SetCosmeticCharacteristic(BeatmapCharacteristicSegmentedControlController __instance, BeatmapCharacteristicSO selectedBeatmapCharacteristic) + { + if (!Plugin.Configuration.DisplayCustomCharacteristics) + { + return; + } + + var beatmapLevel = _standardLevelDetailViewController._beatmapLevel; + if (beatmapLevel.hasPrecalculatedData) + { + return; + } + + var extraSongData = Collections.RetrieveExtraSongData(Hashing.GetCustomLevelHash(beatmapLevel)!); + if (extraSongData?._characteristicDetails == null || extraSongData._characteristicDetails.Length == 0) + { + return; + } + + var segmentedControl = __instance._segmentedControl; + var dataItems = segmentedControl._dataItems; + var newDataItems = new List(); + var i = 0; + var cellIndex = 0; + foreach (var dataItem in dataItems) + { + var beatmapCharacteristic = __instance._beatmapCharacteristics[i]; + var serializedName = beatmapCharacteristic.serializedName; + var characteristicDetails = extraSongData._characteristicDetails.FirstOrDefault(c => c._beatmapCharacteristicName == serializedName); + + if (characteristicDetails != null) + { + Sprite? icon = null; + + var customLevelPath = Collections.GetCustomLevelPath(beatmapLevel.levelID); + if (characteristicDetails._characteristicIconFilePath != null && customLevelPath != null) + { + icon = Utils.LoadSpriteFromFile(Path.Combine(customLevelPath, characteristicDetails._characteristicIconFilePath)); + } + + if (icon == null) + { + icon = beatmapCharacteristic.icon; + } + + var label = characteristicDetails._characteristicLabel ?? Localization.Get(beatmapCharacteristic.descriptionLocalizationKey); + newDataItems.Add(new IconSegmentedControl.DataItem(icon, label)); + } + else + { + newDataItems.Add(dataItem); + } + + if (beatmapCharacteristic == selectedBeatmapCharacteristic) + { + cellIndex = i; + } + + i++; + } + + segmentedControl.SetData(newDataItems.ToArray()); + segmentedControl.SelectCellWithNumber(cellIndex); + } + } +} \ No newline at end of file diff --git a/source/SongCore/HarmonyPatches/CosmeticCharacteristicsPatches.cs b/source/SongCore/HarmonyPatches/CosmeticCharacteristicsPatches.cs deleted file mode 100644 index dbdab23..0000000 --- a/source/SongCore/HarmonyPatches/CosmeticCharacteristicsPatches.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using HarmonyLib; -using HMUI; -using IPA.Utilities; -using SiraUtil.Affinity; -using SongCore.Data; -using SongCore.Utilities; -using UnityEngine; - -namespace SongCore.HarmonyPatches -{ - internal class CosmeticCharacteristicsPatches : IAffinity - { - private readonly GameplayCoreSceneSetupData _gameplayCoreSceneSetupData; - private readonly BeatmapKey _beatmapKey; - - private CosmeticCharacteristicsPatches(GameplayCoreSceneSetupData gameplayCoreSceneSetupData, BeatmapKey beatmapKey) - { - _gameplayCoreSceneSetupData = gameplayCoreSceneSetupData; - _beatmapKey = beatmapKey; - } - - [AffinityPatch(typeof(BeatLineManager), nameof(BeatLineManager.HandleNoteWasSpawned))] - [AffinityPrefix] - private bool ShowOrHideRotationNoteSpawnLines() - { - if (Plugin.Configuration.DisableRotationSpawnLinesOverride) - { - return true; - } - - var difficultyData = Collections.RetrieveDifficultyData(_gameplayCoreSceneSetupData.beatmapLevel, _beatmapKey); - return difficultyData?._showRotationNoteSpawnLines == null || difficultyData._showRotationNoteSpawnLines.Value; - } - - - [AffinityPatch(typeof(SaberManager.InitData), "ctor", AffinityMethodType.Constructor, null, typeof(bool), typeof(SaberType))] - private void ForceOneSaber(ref SaberManager.InitData __instance) - { - if (Plugin.Configuration.DisableOneSaberOverride || _gameplayCoreSceneSetupData.beatmapLevel.hasPrecalculatedData) - { - return; - } - - var difficultyData = Collections.RetrieveDifficultyData(_gameplayCoreSceneSetupData.beatmapLevel, _beatmapKey); - if (difficultyData is { _oneSaber: not null }) - { - __instance.SetField(nameof(__instance.oneSaberMode), difficultyData._oneSaber.Value); - } - } - - // TODO - [HarmonyPatch(typeof(BeatmapCharacteristicSegmentedControlController))] - [HarmonyPatch(nameof(BeatmapCharacteristicSegmentedControlController.SetData), MethodType.Normal)] - internal class CosmeticCharacteristicsPatch - { - private static void Postfix(BeatmapCharacteristicSegmentedControlController __instance, BeatmapCharacteristicSO selectedBeatmapCharacteristic) - { - if (!Plugin.Configuration.DisplayCustomCharacteristics) return; - - var level = Object.FindObjectOfType()._beatmapLevel; - - if (level.hasPrecalculatedData) return; - var songData = Collections.RetrieveExtraSongData(Hashing.GetCustomLevelHash(level)); - - if (songData == null) return; - if (songData._characteristicDetails == null) return; - if (__instance._segmentedControl == null) return; - - if (songData._characteristicDetails.Length > 0) - { - var dataItems = __instance._segmentedControl._dataItems; - List newDataItems = new List(); - - int i = 0; - int cell = 0; - foreach (var item in dataItems) - { - var characteristic = __instance._beatmapCharacteristics[i]; - string serializedName = characteristic.serializedName; - ExtraSongData.CharacteristicDetails? detail = songData._characteristicDetails.Where(x => x._beatmapCharacteristicName == serializedName).FirstOrDefault(); - - if (detail != null) - { - Sprite sprite = characteristic.icon; - if (detail._characteristicIconFilePath != null && Collections.LevelPathDictionary.TryGetValue(level.levelID, out var customLevelPath)) - sprite = Utilities.Utils.LoadSpriteFromFile(Path.Combine(customLevelPath, detail._characteristicIconFilePath)) ?? characteristic.icon; - string label = detail._characteristicLabel ?? BGLib.Polyglot.Localization.Get(characteristic.descriptionLocalizationKey); - newDataItems.Add(new IconSegmentedControl.DataItem(sprite, label)); - } - else - { - newDataItems.Add(item); - } - - if (characteristic == selectedBeatmapCharacteristic) - { - cell = i; - } - i++; - } - __instance._segmentedControl.SetData(newDataItems.ToArray()); - __instance._segmentedControl.SelectCellWithNumber(cell); - } - } - } - - } -} diff --git a/source/SongCore/Installers/AppInstaller.cs b/source/SongCore/Installers/AppInstaller.cs index 006365a..cd19518 100644 --- a/source/SongCore/Installers/AppInstaller.cs +++ b/source/SongCore/Installers/AppInstaller.cs @@ -7,6 +7,7 @@ internal class AppInstaller : Installer { public override void InstallBindings() { + // TODO MainSystemsInitRefreshablePatch.Postfix(Container); } } diff --git a/source/SongCore/Installers/GameInstaller.cs b/source/SongCore/Installers/GameInstaller.cs index ebedbc6..3d169ef 100644 --- a/source/SongCore/Installers/GameInstaller.cs +++ b/source/SongCore/Installers/GameInstaller.cs @@ -7,7 +7,7 @@ internal class GameInstaller : Installer { public override void InstallBindings() { - Container.BindInterfacesTo().AsSingle(); + Container.BindInterfacesTo().AsSingle(); Container.BindInterfacesTo().AsSingle(); } } diff --git a/source/SongCore/Installers/MenuInstaller.cs b/source/SongCore/Installers/MenuInstaller.cs index d47e56d..50e3f69 100644 --- a/source/SongCore/Installers/MenuInstaller.cs +++ b/source/SongCore/Installers/MenuInstaller.cs @@ -1,3 +1,4 @@ +using SongCore.HarmonyPatches; using SongCore.UI; using Zenject; @@ -11,6 +12,7 @@ public override void InstallBindings() Container.BindInterfacesAndSelfTo().AsSingle(); Container.Bind().FromNewComponentOnNewGameObject().AsSingle(); Container.BindInterfacesAndSelfTo().AsSingle(); + Container.BindInterfacesTo().AsSingle(); } } } diff --git a/source/SongCore/Plugin.cs b/source/SongCore/Plugin.cs index 7b825bf..e298a9e 100644 --- a/source/SongCore/Plugin.cs +++ b/source/SongCore/Plugin.cs @@ -43,7 +43,7 @@ public Plugin(IPALogger pluginLogger, PluginMetadata metadata, Zenjector zenject zenjector.UseLogger(pluginLogger); zenjector.Install(Location.App); zenjector.Install(Location.Menu); - zenjector.Install(Location.GameCore); + zenjector.Install(Location.StandardPlayer); } [OnStart] diff --git a/source/SongCore/SongCore.csproj b/source/SongCore/SongCore.csproj index f1ceba5..3ef6e5a 100644 --- a/source/SongCore/SongCore.csproj +++ b/source/SongCore/SongCore.csproj @@ -139,6 +139,7 @@ $(BeatSaberDir)\Beat Saber_Data\Managed\Zenject.dll false + true $(BeatSaberDir)\Beat Saber_Data\Managed\Zenject-usage.dll diff --git a/source/SongCore/Utilities/Accessors.cs b/source/SongCore/Utilities/Accessors.cs index 9e3a8f8..9c43c2a 100644 --- a/source/SongCore/Utilities/Accessors.cs +++ b/source/SongCore/Utilities/Accessors.cs @@ -4,16 +4,19 @@ namespace SongCore.Utilities { internal static class Accessors { - internal static readonly FieldAccessor.Accessor LevelIDAccessor = + public static readonly FieldAccessor.Accessor LevelIDAccessor = FieldAccessor.GetAccessor(nameof(BeatmapLevel.levelID)); - internal static readonly FieldAccessor.Accessor SongDurationAccessor = + public static readonly FieldAccessor.Accessor SongDurationAccessor = FieldAccessor.GetAccessor(nameof(BeatmapLevel.songDuration)); - internal static readonly FieldAccessor.Accessor PreviewDurationAccessor = + public static readonly FieldAccessor.Accessor PreviewDurationAccessor = FieldAccessor.GetAccessor(nameof(BeatmapLevel.previewDuration)); - internal static readonly FieldAccessor.Accessor BeatmapLevelsAccessor = + public static readonly FieldAccessor.Accessor BeatmapLevelsAccessor = FieldAccessor.GetAccessor(nameof(BeatmapLevelPack.beatmapLevels)); + + public static readonly FieldAccessor.Accessor OneSaberModeAccessor = + FieldAccessor.GetAccessor(nameof(SaberManager.InitData.oneSaberMode)); } -} \ No newline at end of file +} diff --git a/source/SongCore/Utilities/Hashing.cs b/source/SongCore/Utilities/Hashing.cs index b07d046..abaafd3 100644 --- a/source/SongCore/Utilities/Hashing.cs +++ b/source/SongCore/Utilities/Hashing.cs @@ -119,16 +119,21 @@ private static bool GetCachedSongData(string customLevelPath, out long directory return false; } - public static string GetCustomLevelHash(BeatmapLevel level) + public static string? GetCustomLevelHash(BeatmapLevel level) { - var standardLevelInfoSaveData = Collections.GetStandardLevelInfoSaveDataByLevelId(level.levelID); + var standardLevelInfoSaveData = Collections.GetStandardLevelInfoSaveData(level.levelID); if (standardLevelInfoSaveData == null) { return null; } - var levelPath = Collections.GetLevelPathByLevelId(level.levelID); - return GetCustomLevelHash(levelPath, standardLevelInfoSaveData.difficultyBeatmapSets); + var customLevelPath = Collections.GetCustomLevelPath(level.levelID); + if (customLevelPath == null) + { + return null; + } + + return GetCustomLevelHash(customLevelPath, standardLevelInfoSaveData.difficultyBeatmapSets); } public static string GetCustomLevelHash(StandardLevelInfoSaveData level, string customLevelPath) From f67c97f290fb27d783ca747b936bf2667dd694f5 Mon Sep 17 00:00:00 2001 From: Meivyn <793322+Meivyn@users.noreply.github.com> Date: Fri, 15 Mar 2024 14:56:25 -0400 Subject: [PATCH 03/17] Fix concurrency issue when loading songs --- source/SongCore/Data/SongData.cs | 12 --- source/SongCore/Loader.cs | 159 ++++++++++++++----------------- 2 files changed, 72 insertions(+), 99 deletions(-) diff --git a/source/SongCore/Data/SongData.cs b/source/SongCore/Data/SongData.cs index ce8fd09..d47ed11 100644 --- a/source/SongCore/Data/SongData.cs +++ b/source/SongCore/Data/SongData.cs @@ -9,18 +9,6 @@ namespace SongCore.Data { - public class SongData - { - public string RawSongData; - public StandardLevelInfoSaveData SaveData; - - public SongData(string rawSongData, StandardLevelInfoSaveData saveData) - { - RawSongData = rawSongData; - SaveData = saveData; - } - } - [Serializable] public class ExtraSongData { diff --git a/source/SongCore/Loader.cs b/source/SongCore/Loader.cs index bece643..eb8c5aa 100644 --- a/source/SongCore/Loader.cs +++ b/source/SongCore/Loader.cs @@ -419,6 +419,11 @@ void AddOfficialBeatmapLevelsRepository(BeatmapLevelsRepository levelsRepository { try { + if (_loadingCancelled) + { + return; + } + var songPath = Path.GetDirectoryName(result)!; if (Directory.GetParent(songPath)?.Name == "Backups") { @@ -438,33 +443,24 @@ void AddOfficialBeatmapLevelsRepository(BeatmapLevelsRepository levelsRepository } var wip = songPath.Contains("CustomWIPLevels"); - var songData = LoadCustomLevelSongData(songPath); - if (songData == null) + var level = LoadCustomLevel(songPath, _loadingTaskCancellationTokenSource.Token); + if (level == null) { - Logging.Logger.Notice($"Folder: '{folder}' contains invalid song data!"); + Logging.Logger.Error($"Failed to load custom level: {folder}"); continue; } - if (_loadingCancelled) + if (!wip) { - return; + CustomLevelsById[level.levelID] = level; + CustomLevels[songPath] = level; } - - var level = LoadSongAndAddToDictionaries(_loadingTaskCancellationTokenSource.Token, songData, songPath); - if (level != null) + else { - if (!wip) - { - CustomLevelsById[level.levelID] = level; - CustomLevels[songPath] = level; - } - else - { - CustomWIPLevels[songPath] = level; - } - - foundSongPaths.TryAdd(songPath, false); + CustomWIPLevels[songPath] = level; } + + foundSongPaths.TryAdd(songPath, false); } catch (Exception e) { @@ -501,12 +497,12 @@ void AddOfficialBeatmapLevelsRepository(BeatmapLevelsRepository levelsRepository continue; } - var entryFolders = Directory.GetDirectories(entry.SongFolderEntry.Path).ToList(); + var entryFolders = Directory.GetDirectories(entry.SongFolderEntry.Path); - float i2 = 0; + float count = 0; foreach (var folder in entryFolders) { - i2++; + count++; // Search for an info.dat in the beatmap folder string[] results; try @@ -530,6 +526,11 @@ void AddOfficialBeatmapLevelsRepository(BeatmapLevelsRepository levelsRepository { try { + if (_loadingCancelled) + { + return; + } + // On quick refresh: Check if the beatmap directory is already present in the respective beatmap dictionary // If it is already present on a non full refresh, it will be ignored (changes to the beatmap will not be applied) var songPath = Path.GetDirectoryName(result)!; @@ -563,30 +564,19 @@ void AddOfficialBeatmapLevelsRepository(BeatmapLevelsRepository levelsRepository } } - var songData = LoadCustomLevelSongData(songPath); - if (songData == null) + var level = LoadCustomLevel(songPath, _loadingTaskCancellationTokenSource.Token, entry.SongFolderEntry); + if (level == null) { - Logging.Logger.Notice($"Folder: '{folder}' contains invalid song data!"); - continue; + Logging.Logger.Error($"Failed to load custom level: {folder}"); } - - var count = i2; - - if (_loadingCancelled) - { - return; - } - - var level = LoadSongAndAddToDictionaries(_loadingTaskCancellationTokenSource.Token, songData, songPath, entry.SongFolderEntry); - if (level != null) + else { entry.Levels[songPath] = level; CustomLevelsById[level.levelID] = level; foundSongPaths.TryAdd(songPath, false); } - LoadingProgress = count / entryFolders.Count; - //}); + LoadingProgress = count / entryFolders.Length; } catch (Exception e) { @@ -829,10 +819,11 @@ private void DeleteSingleSong(string folderPath, bool deleteFolder) try { hash = Hashing.GetCustomLevelHash(saveData, songPath); - string folderName = new DirectoryInfo(songPath).Name; + beatmapLevel = CustomLevelLoader.CreateBeatmapLevelFromV3(songPath, saveData); + IBeatmapLevelData beatmapLevelData = CustomLevelLoader.CreateBeatmapLevelDataFromV3(saveData, songPath); string levelID = CustomLevelLoader.kCustomLevelPrefixId + hash; - // Fixed WIP status for duplicate song hashes - if (Collections.LevelHashDictionary.ContainsKey(levelID + (wip ? " WIP" : ""))) + string folderName = new DirectoryInfo(songPath).Name; + while (!Collections.LevelHashDictionary.TryAdd(levelID + (wip ? " WIP" : ""), hash)) { levelID += $"_{folderName}"; } @@ -842,10 +833,19 @@ private void DeleteSingleSong(string folderPath, bool deleteFolder) levelID += " WIP"; } - beatmapLevel = CustomLevelLoader.CreateBeatmapLevelFromV3(songPath, saveData); - Accessors.LevelIDAccessor(ref beatmapLevel) = levelID; + Collections.HashLevelDictionary.AddOrUpdate(hash, new List { levelID }, (_, levels) => + { + lock (levels) + { + levels.Add(levelID); + } + return levels; + }); + Collections.LevelSaveDataDictionary.TryAdd(levelID, saveData); + Collections.LevelPathDictionary.TryAdd(levelID, songPath); - LoadedBeatmapLevelsData.TryAdd(beatmapLevel.levelID, CustomLevelLoader.CreateBeatmapLevelDataFromV3(saveData, songPath)); + Accessors.LevelIDAccessor(ref beatmapLevel) = levelID; + LoadedBeatmapLevelsData.TryAdd(levelID, beatmapLevelData); GetSongDuration(saveData, beatmapLevel, songPath, Path.Combine(songPath, saveData.songFilename)); } @@ -941,7 +941,7 @@ private void LoadCachedZIPs(IEnumerable cacheFolders, bool fullRefresh, try { var songPath = Path.GetDirectoryName(result)!; - if (!fullRefresh && beatmapDictionary != null) + if (!fullRefresh) { if (SearchBeatmapInMapPack(beatmapDictionary, songPath)) { @@ -949,12 +949,6 @@ private void LoadCachedZIPs(IEnumerable cacheFolders, bool fullRefresh, } } - var songData = LoadCustomLevelSongData(songPath); - if (songData == null) - { - continue; - } - UnityMainThreadTaskScheduler.Factory.StartNew(() => { try @@ -964,12 +958,14 @@ private void LoadCachedZIPs(IEnumerable cacheFolders, bool fullRefresh, return; } - var level = LoadSong(songData.SaveData, songPath, out var hash, folderEntry); - if (level != null) + var level = LoadCustomLevel(songPath, _loadingTaskCancellationTokenSource.Token, folderEntry); + if (level == null) { - beatmapDictionary[songPath] = level; - Collections.AddExtraSongData(hash, songPath, songData.RawSongData); + Logging.Logger.Error($"Failed to load custom level: {folderEntry}"); + return; } + + beatmapDictionary[songPath] = level; } catch (Exception ex) { @@ -1015,50 +1011,39 @@ private bool AssignBeatmapToSeperateFolder( return false; } - public SongData? LoadCustomLevelSongData(string customLevelPath) + // TODO: Return beatmap level data? + public BeatmapLevel? LoadCustomLevel(string customLevelPath, CancellationToken cancellationToken, SongFolderEntry? entry = null) { - var path = Path.Combine(customLevelPath, CustomLevelPathHelper.kStandardLevelInfoFilename); - if (File.Exists(path)) - { - var rawSongData = File.ReadAllText(path); - var saveData = StandardLevelInfoSaveData.DeserializeFromJSONString(rawSongData); + cancellationToken.ThrowIfCancellationRequested(); - return saveData == null - ? null - : new SongData(rawSongData, saveData); - } - return null; - } - - private BeatmapLevel? LoadSongAndAddToDictionaries(CancellationToken token, SongData songData, string songPath, SongFolderEntry? entry = null) - { - token.ThrowIfCancellationRequested(); - var level = LoadSong(songData.SaveData, songPath, out string hash, entry); - if (level == null) + var infoFilePath = Path.Combine(customLevelPath, CustomLevelPathHelper.kStandardLevelInfoFilename); + if (!File.Exists(infoFilePath)) { - return level; + return null; } - if (!Collections.LevelHashDictionary.ContainsKey(level.levelID)) + var json = File.ReadAllText(infoFilePath); + if (BeatmapSaveDataHelpers.GetVersion(json) < BeatmapSaveDataHelpers.version4) { - // Add level to LevelHash-Dictionary - Collections.LevelHashDictionary.TryAdd(level.levelID, hash); - // Add hash to HashLevel-Dictionary - if (Collections.HashLevelDictionary.TryGetValue(hash, out var levels)) + var standardLevelInfoSaveData = StandardLevelInfoSaveData.DeserializeFromJSONString(json); + if (standardLevelInfoSaveData == null) { - levels.Add(level.levelID); + return null; } - else + + var beatmapLevel = LoadSong(standardLevelInfoSaveData, customLevelPath, out var hash, entry); + if (beatmapLevel == null) { - levels = new List { level.levelID }; - Collections.HashLevelDictionary.TryAdd(hash, levels); + return null; } - Collections.LevelSaveDataDictionary.TryAdd(level.levelID, songData.SaveData); - Collections.LevelPathDictionary.TryAdd(level.levelID, songPath); - Collections.AddExtraSongData(hash, songPath, songData.RawSongData); + + Collections.AddExtraSongData(hash, customLevelPath, json); + + return beatmapLevel; } - return level; + // TODO: V4 + return null; } #endregion From 9817a43ab391abfba87d6209b5a8aa7b57de15f0 Mon Sep 17 00:00:00 2001 From: Meivyn <793322+Meivyn@users.noreply.github.com> Date: Fri, 15 Mar 2024 15:02:55 -0400 Subject: [PATCH 04/17] Add version check for transpilers fix --- source/SongCore/Plugin.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/SongCore/Plugin.cs b/source/SongCore/Plugin.cs index e298a9e..45f7f56 100644 --- a/source/SongCore/Plugin.cs +++ b/source/SongCore/Plugin.cs @@ -49,7 +49,10 @@ public Plugin(IPALogger pluginLogger, PluginMetadata metadata, Zenjector zenject [OnStart] public void OnApplicationStart() { - _harmony.Patch(HarmonyTranspilersFixPatch.TargetMethod(), null, null, new HarmonyMethod(AccessTools.Method(typeof(HarmonyTranspilersFixPatch), nameof(HarmonyTranspilersFixPatch.Transpiler)))); + if (typeof(Harmony).Assembly.GetName().Version.Minor < 12) + { + _harmony.Patch(HarmonyTranspilersFixPatch.TargetMethod(), null, null, new HarmonyMethod(AccessTools.Method(typeof(HarmonyTranspilersFixPatch), nameof(HarmonyTranspilersFixPatch.Transpiler)))); + } _harmony.PatchAll(_metadata.Assembly); BasicUI.GetIcons(); From 6289bc56c14782994019595e1fcc85195b1fcbd8 Mon Sep 17 00:00:00 2001 From: Meivyn <793322+Meivyn@users.noreply.github.com> Date: Sat, 16 Mar 2024 12:09:51 -0400 Subject: [PATCH 05/17] Fix wrong default pack cover --- source/SongCore/Loader.cs | 2 +- .../SongCore/OverrideClasses/SongCoreCustomBeatmapLevelPack.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/SongCore/Loader.cs b/source/SongCore/Loader.cs index eb8c5aa..2e0dad6 100644 --- a/source/SongCore/Loader.cs +++ b/source/SongCore/Loader.cs @@ -98,7 +98,7 @@ public async Task InitializeAsync(CancellationToken cancellationToken) BeatmapLevelsModelSO = _beatmapLevelsModel; CustomLevelLoader = _customLevelLoader; cachedMediaAsyncLoaderSO = _cachedMediaAsyncLoader; - defaultCoverImage = _levelPackDetailViewController._defaultCoverSprite; + defaultCoverImage = Resources.FindObjectsOfTypeAll().First(s => s.name.Contains("CustomLevelsPack")); beatmapCharacteristicCollection = _beatmapCharacteristicCollection; if (Hashing.cachedSongHashData.Count == 0) diff --git a/source/SongCore/OverrideClasses/SongCoreCustomBeatmapLevelPack.cs b/source/SongCore/OverrideClasses/SongCoreCustomBeatmapLevelPack.cs index ef3669e..4c59584 100644 --- a/source/SongCore/OverrideClasses/SongCoreCustomBeatmapLevelPack.cs +++ b/source/SongCore/OverrideClasses/SongCoreCustomBeatmapLevelPack.cs @@ -6,7 +6,7 @@ namespace SongCore.OverrideClasses public class SongCoreCustomBeatmapLevelPack : BeatmapLevelPack { public SongCoreCustomBeatmapLevelPack(string packID, string packName, Sprite coverImage, BeatmapLevel[] beatmapLevels, string shortPackName = "") - : base(packID, packName, shortPackName == string.Empty ? packName : shortPackName, Sprite.Create(coverImage.texture, coverImage.rect, coverImage.pivot, coverImage.texture.width), coverImage, beatmapLevels, PlayerSensitivityFlag.Safe) + : base(packID, packName, shortPackName == string.Empty ? packName : shortPackName, coverImage == Loader.defaultCoverImage ? coverImage : Sprite.Create(coverImage.texture, coverImage.rect, coverImage.pivot, coverImage.texture.width), coverImage, beatmapLevels, PlayerSensitivityFlag.Safe) { } From 55e05b9ada403dcf1d0a6888068214e346c24d13 Mon Sep 17 00:00:00 2001 From: Meivyn <793322+Meivyn@users.noreply.github.com> Date: Sat, 16 Mar 2024 12:48:43 -0400 Subject: [PATCH 06/17] Uncapitalize logging messages --- source/SongCore/Data/SongFolderEntries.cs | 4 +-- .../HarmonyPatches/CustomSongColorsPatch.cs | 6 ++-- source/SongCore/Loader.cs | 28 +++++++++---------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/source/SongCore/Data/SongFolderEntries.cs b/source/SongCore/Data/SongFolderEntries.cs index f2273da..e7081fc 100644 --- a/source/SongCore/Data/SongFolderEntries.cs +++ b/source/SongCore/Data/SongFolderEntries.cs @@ -61,7 +61,7 @@ public SeperateSongFolder(SongFolderEntry folderEntry, SeperateSongFolder? cache } catch { - Logging.Logger.Info($"Failed to Load Image For Separate Folder \"{folderEntry.Name}\""); + Logging.Logger.Info($"Failed to load image for separate folder \"{folderEntry.Name}\""); } } @@ -142,7 +142,7 @@ public static List ReadSeperateFoldersFromFile(string filePa } catch { - Logging.Logger.Warn("Error Reading folders.xml! Make sure the file is properly formatted."); + Logging.Logger.Warn("Error reading folders.xml! Make sure the file is properly formatted."); } return result; diff --git a/source/SongCore/HarmonyPatches/CustomSongColorsPatch.cs b/source/SongCore/HarmonyPatches/CustomSongColorsPatch.cs index 8784f0e..15ea70b 100644 --- a/source/SongCore/HarmonyPatches/CustomSongColorsPatch.cs +++ b/source/SongCore/HarmonyPatches/CustomSongColorsPatch.cs @@ -83,9 +83,9 @@ private static void Postfix(MultiplayerLevelScenesTransitionSetupDataSO __instan return null; } - if (Plugin.Configuration.CustomSongNoteColors) Logging.Logger.Info("Custom Song Note Colors On"); - if (Plugin.Configuration.CustomSongEnvironmentColors) Logging.Logger.Info("Custom Song Environment Colors On"); - if (Plugin.Configuration.CustomSongObstacleColors) Logging.Logger.Info("Custom Song Obstacle Colors On"); + if (Plugin.Configuration.CustomSongNoteColors) Logging.Logger.Info("Custom song note colors On"); + if (Plugin.Configuration.CustomSongEnvironmentColors) Logging.Logger.Info("Custom song environment colors On"); + if (Plugin.Configuration.CustomSongObstacleColors) Logging.Logger.Info("Custom song obstacle colors On"); var saberLeft = (songData._colorLeft == null || !Plugin.Configuration.CustomSongNoteColors) ? currentColorScheme.saberAColor diff --git a/source/SongCore/Loader.cs b/source/SongCore/Loader.cs index 2e0dad6..30505be 100644 --- a/source/SongCore/Loader.cs +++ b/source/SongCore/Loader.cs @@ -355,7 +355,7 @@ void AddOfficialBeatmapLevelsRepository(BeatmapLevelsRepository levelsRepository } catch (Exception ex) { - Logging.Logger.Error("Failed To Load Cached WIP Levels: "); + Logging.Logger.Error("Failed to load cached WIP levels: "); Logging.Logger.Error(ex); } } @@ -377,7 +377,7 @@ void AddOfficialBeatmapLevelsRepository(BeatmapLevelsRepository levelsRepository } catch (Exception ex) { - Logging.Logger.Error("Failed To Load Cached WIP Levels:"); + Logging.Logger.Error("Failed to load cached WIP levels:"); Logging.Logger.Error(ex); } } @@ -411,7 +411,7 @@ void AddOfficialBeatmapLevelsRepository(BeatmapLevelsRepository levelsRepository if (results.Length == 0) { - Logging.Logger.Notice($"Folder: '{folder}' is missing {CustomLevelPathHelper.kStandardLevelInfoFilename} files!"); + Logging.Logger.Notice($"Folder: '{folder}' is missing {CustomLevelPathHelper.kStandardLevelInfoFilename} file!"); return; } @@ -518,7 +518,7 @@ void AddOfficialBeatmapLevelsRepository(BeatmapLevelsRepository levelsRepository if (results.Length == 0) { - Logging.Logger.Notice($"Folder: '{folder}' is missing {CustomLevelPathHelper.kStandardLevelInfoFilename} files!"); + Logging.Logger.Notice($"Folder: '{folder}' is missing {CustomLevelPathHelper.kStandardLevelInfoFilename} file!"); continue; } @@ -588,7 +588,7 @@ void AddOfficialBeatmapLevelsRepository(BeatmapLevelsRepository levelsRepository } catch (Exception ex) { - Logging.Logger.Error($"Failed to load Separate Folder{SeperateSongFolders[k].SongFolderEntry.Name}"); + Logging.Logger.Error($"Failed to load separate folder{SeperateSongFolders[k].SongFolderEntry.Name}"); Logging.Logger.Error(ex); } } @@ -681,7 +681,7 @@ await UnityMainThreadTaskScheduler.Factory.StartNew(() => } catch (Exception ex) { - Logging.Logger.Error("Failed to Setup LevelPacks:"); + Logging.Logger.Error("Failed to setup LevelPacks:"); Logging.Logger.Error(ex); } @@ -709,7 +709,7 @@ await UnityMainThreadTaskScheduler.Factory.StartNew(() => } catch (Exception ex) { - Logging.Logger.Warn($"Song Loading Task Failed. {ex.Message}"); + Logging.Logger.Warn($"Song loading task failed. {ex.Message}"); return; } @@ -719,7 +719,7 @@ await UnityMainThreadTaskScheduler.Factory.StartNew(() => } else { - Logging.Logger.Warn($"Song Loading Task Cancelled."); + Logging.Logger.Warn($"Song loading task cancelled."); } } @@ -799,7 +799,7 @@ private void DeleteSingleSong(string folderPath, bool deleteFolder) } catch (Exception ex) { - Logging.Logger.Error($"Exception trying to Delete song: {folderPath}"); + Logging.Logger.Error($"Exception trying to delete song: {folderPath}"); Logging.Logger.Error(ex); } } @@ -851,7 +851,7 @@ private void DeleteSingleSong(string folderPath, bool deleteFolder) } catch (Exception e) { - Logging.Logger.Error($"Failed to Load Song: {songPath}"); + Logging.Logger.Error($"Failed to load song: {songPath}"); Logging.Logger.Error(e); beatmapLevel = null; hash = null; @@ -1125,7 +1125,7 @@ private static void GetSongDuration(StandardLevelInfoSaveData saveData, BeatmapL { // janky, but whatever Collections.LevelPathDictionary.TryGetValue(level.levelID, out var customLevelPath); - Logging.Logger.Warn($"Failed to parse song length from Ogg file, Approximating using Map length. Song: {customLevelPath}"); + Logging.Logger.Warn($"Failed to parse song length from audio file, approximating using map length. Song: {customLevelPath}"); length = GetLengthFromMap(saveData, level, songPath); } } @@ -1149,7 +1149,7 @@ private static void GetSongDuration(StandardLevelInfoSaveData saveData, BeatmapL } catch (Exception ex) { - Logging.Logger.Warn("Failed to Parse Song Duration"); + Logging.Logger.Warn("Failed to parse song duration"); Logging.Logger.Warn(ex); } } @@ -1249,7 +1249,7 @@ bool FindBytes(byte[] bytes, int searchLength) } else { - Logging.Logger.Warn($"could not find rate for {oggFile}"); + Logging.Logger.Warn($"Could not find rate for {oggFile}"); return -1; } @@ -1280,7 +1280,7 @@ bool FindBytes(byte[] bytes, int searchLength) if (lastSample == -1) { - Logging.Logger.Warn($"could not find lastSample for {oggFile}"); + Logging.Logger.Warn($"Could not find lastSample for {oggFile}"); return -1; } From e446fa0dde9e7b5aa841ea7f79f964f0cedba3e4 Mon Sep 17 00:00:00 2001 From: Meivyn <793322+Meivyn@users.noreply.github.com> Date: Sat, 16 Mar 2024 14:44:21 -0400 Subject: [PATCH 07/17] Move refreshable patch to installer --- .../MainSystemsInitRefreshablePatch.cs | 51 ------------------- source/SongCore/Installers/AppInstaller.cs | 43 ++++++++++++++-- 2 files changed, 40 insertions(+), 54 deletions(-) delete mode 100644 source/SongCore/HarmonyPatches/MainSystemsInitRefreshablePatch.cs diff --git a/source/SongCore/HarmonyPatches/MainSystemsInitRefreshablePatch.cs b/source/SongCore/HarmonyPatches/MainSystemsInitRefreshablePatch.cs deleted file mode 100644 index 67df9c9..0000000 --- a/source/SongCore/HarmonyPatches/MainSystemsInitRefreshablePatch.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Concurrent; -using Zenject; - -namespace SongCore.HarmonyPatches -{ - internal class MainSystemsInitRefreshablePatch - { - public const string refreshableID = "SongCore.Loader.Refresh"; - public const string didLoadEventID = "SongCore.Loader.Loaded"; - - public static void Postfix(DiContainer container) - { - container.Bind().WithId(refreshableID).To().AsSingle(); - container.Bind(typeof(IInitializable), typeof(IDisposable), typeof(SongCoreLoaderDidLoad)).To().AsSingle(); - IObservableChange loadEvent = container.Resolve(); - container.BindInstance(loadEvent).WithId(didLoadEventID).AsSingle(); - } - - private class SongCoreRefreshable : IRefreshable - { - public void Refresh() - { - if (Loader.AreSongsLoaded) - { - Loader.Instance.RefreshSongs(); - } - } - } - - private class SongCoreLoaderDidLoad : IInitializable, IDisposable, IObservableChange - { - public event Action didChangeEvent; - - public void Initialize() - { - Loader.SongsLoadedEvent += Loader_SongsLoadedEvent; - } - - private void Loader_SongsLoadedEvent(Loader _, ConcurrentDictionary __) - { - didChangeEvent?.Invoke(); - } - - public void Dispose() - { - Loader.SongsLoadedEvent -= Loader_SongsLoadedEvent; - } - } - } -} \ No newline at end of file diff --git a/source/SongCore/Installers/AppInstaller.cs b/source/SongCore/Installers/AppInstaller.cs index cd19518..f6d6f99 100644 --- a/source/SongCore/Installers/AppInstaller.cs +++ b/source/SongCore/Installers/AppInstaller.cs @@ -1,14 +1,51 @@ -using SongCore.HarmonyPatches; +using System; +using System.Collections.Concurrent; using Zenject; namespace SongCore.Installers { internal class AppInstaller : Installer { + private const string refreshableID = "SongCore.Loader.Refresh"; + private const string didLoadEventID = "SongCore.Loader.Loaded"; + public override void InstallBindings() { - // TODO - MainSystemsInitRefreshablePatch.Postfix(Container); + Container.Bind().WithId(refreshableID).To().AsSingle(); + Container.Bind(typeof(IInitializable), typeof(IDisposable), typeof(SongCoreLoaderDidLoad)).To().AsSingle(); + var loadEvent = Container.Resolve() as IObservableChange; + Container.BindInstance(loadEvent).WithId(didLoadEventID).AsSingle(); + } + + private class SongCoreRefreshable : IRefreshable + { + public void Refresh() + { + if (Loader.AreSongsLoaded) + { + Loader.Instance.RefreshSongs(); + } + } + } + + private class SongCoreLoaderDidLoad : IInitializable, IDisposable, IObservableChange + { + public event Action? didChangeEvent; + + public void Initialize() + { + Loader.SongsLoadedEvent += Loader_SongsLoadedEvent; + } + + private void Loader_SongsLoadedEvent(Loader _, ConcurrentDictionary __) + { + didChangeEvent?.Invoke(); + } + + public void Dispose() + { + Loader.SongsLoadedEvent -= Loader_SongsLoadedEvent; + } } } } From 8578dfe5e835eda9797c3287ce9900e75c9c5c61 Mon Sep 17 00:00:00 2001 From: Meivyn <793322+Meivyn@users.noreply.github.com> Date: Sat, 16 Mar 2024 16:54:28 -0400 Subject: [PATCH 08/17] Proper songs refresh/cancel behavior --- source/SongCore/Loader.cs | 97 +++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 54 deletions(-) diff --git a/source/SongCore/Loader.cs b/source/SongCore/Loader.cs index 30505be..9202780 100644 --- a/source/SongCore/Loader.cs +++ b/source/SongCore/Loader.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using BeatSaberMarkupLanguage.Settings; using IPA.Utilities.Async; -using SiraUtil.Zenject; using SongCore.Data; using SongCore.OverrideClasses; using SongCore.UI; @@ -20,11 +19,10 @@ namespace SongCore { - public class Loader : IAsyncInitializable, IDisposable, ITickable + public class Loader : IInitializable, IDisposable, ITickable { private readonly GameScenesManager _gameScenesManager; private readonly LevelFilteringNavigationController _levelFilteringNavigationController; - private readonly LevelPackDetailViewController _levelPackDetailViewController; private readonly LevelCollectionViewController _levelCollectionViewController; private readonly BeatmapLevelsModel _beatmapLevelsModel; private readonly CustomLevelLoader _customLevelLoader; @@ -38,15 +36,12 @@ public class Loader : IAsyncInitializable, IDisposable, ITickable private Task? _loadingTask; private CancellationTokenSource _loadingTaskCancellationTokenSource = new CancellationTokenSource(); - private bool _loadingCancelled; - private bool _isInitialized; - private Loader(GameScenesManager gameScenesManager, LevelFilteringNavigationController levelFilteringNavigationController, LevelPackDetailViewController levelPackDetailViewController, LevelCollectionViewController levelCollectionViewController, BeatmapLevelsModel beatmapLevelsModel, CustomLevelLoader customLevelLoader, CachedMediaAsyncLoader cachedMediaAsyncLoader, BeatmapCharacteristicCollection beatmapCharacteristicCollection, ProgressBar progressBar, BSMLSettings bsmlSettings) + private Loader(GameScenesManager gameScenesManager, LevelFilteringNavigationController levelFilteringNavigationController, LevelCollectionViewController levelCollectionViewController, BeatmapLevelsModel beatmapLevelsModel, CustomLevelLoader customLevelLoader, CachedMediaAsyncLoader cachedMediaAsyncLoader, BeatmapCharacteristicCollection beatmapCharacteristicCollection, ProgressBar progressBar, BSMLSettings bsmlSettings) { _gameScenesManager = gameScenesManager; _levelFilteringNavigationController = levelFilteringNavigationController; - _levelPackDetailViewController = levelPackDetailViewController; _levelCollectionViewController = levelCollectionViewController; _beatmapLevelsModel = beatmapLevelsModel; _customLevelLoader = customLevelLoader; @@ -88,10 +83,14 @@ private Loader(GameScenesManager gameScenesManager, LevelFilteringNavigationCont public static CachedMediaAsyncLoader cachedMediaAsyncLoaderSO { get; private set; } public static BeatmapCharacteristicCollection beatmapCharacteristicCollection { get; private set; } - public async Task InitializeAsync(CancellationToken cancellationToken) + public void Initialize() { - Logging.Logger.Notice(nameof(InitializeAsync)); - await UnityAsyncHelper.WaitUntilAsync(_progressBar, () => !_gameScenesManager.isInTransition); + _gameScenesManager.transitionDidFinishEvent += MenuLoaded; + } + + private void MenuLoaded(ScenesTransitionSetupDataSO scenesTransitionSetupData, DiContainer container) + { + _gameScenesManager.transitionDidFinishEvent -= MenuLoaded; // Ensures that the static references are still valid Unity objects. // They'll be destroyed on internal restart. @@ -105,9 +104,13 @@ public async Task InitializeAsync(CancellationToken cancellationToken) { Hashing.ReadCachedSongHashes(); Hashing.ReadCachedAudioData(); + RefreshSongs(); + } + else + { + RefreshLevelPacks(); + RefreshLoadedBeatmapLevelsData(); } - - RefreshSongs(); SceneManager.activeSceneChanged += HandleActiveSceneChanged; @@ -153,7 +156,6 @@ private void CancelSongLoading() if (AreSongsLoading) { _loadingTaskCancellationTokenSource.Cancel(); - _loadingCancelled = true; AreSongsLoading = false; LoadingProgress = 0; _progressBar.ShowMessage("Loading cancelled\nPress Ctrl+R to refresh"); @@ -162,7 +164,7 @@ private void CancelSongLoading() private void HandleActiveSceneChanged(Scene previousScene, Scene nextScene) { - if (_loadingCancelled && nextScene.name == "MainMenu") + if (_loadingTaskCancellationTokenSource.IsCancellationRequested && nextScene.name == "MainMenu") { RefreshSongs(); } @@ -217,13 +219,13 @@ public async void RefreshLevelPacks() } } - BeatmapLevelsModelSO._customLevelsRepository = CustomLevelsRepository; - BeatmapLevelsModelSO._allLoadedBeatmapLevelsRepository = BeatmapLevelsModelSO.CreateAllLoadedBeatmapLevelPacks(); - BeatmapLevelsModelSO.UpdateLoadedPreviewLevels(); + _beatmapLevelsModel._customLevelsRepository = CustomLevelsRepository; + _beatmapLevelsModel._allLoadedBeatmapLevelsRepository = _beatmapLevelsModel.CreateAllLoadedBeatmapLevelPacks(); + _beatmapLevelsModel.UpdateLoadedPreviewLevels(); await UnityMainThreadTaskScheduler.Factory.StartNew(() => { - if (!_loadingCancelled && _levelFilteringNavigationController.isActiveAndEnabled) + if (!_loadingTaskCancellationTokenSource.IsCancellationRequested && _levelFilteringNavigationController.isActiveAndEnabled) { _levelFilteringNavigationController.UpdateCustomSongs(); } @@ -243,7 +245,6 @@ public void RefreshSongs(bool fullRefresh = true) AreSongsLoaded = false; AreSongsLoading = true; LoadingProgress = 0; - _loadingCancelled = false; _loadingTaskCancellationTokenSource = new CancellationTokenSource(); if (LoadingStartedEvent != null) { @@ -308,8 +309,8 @@ void AddOfficialBeatmapLevelsRepository(BeatmapLevelsRepository levelsRepository OfficialSongs.Clear(); - AddOfficialBeatmapLevelsRepository(BeatmapLevelsModelSO.ostAndExtrasBeatmapLevelsRepository); - AddOfficialBeatmapLevelsRepository(BeatmapLevelsModelSO.dlcBeatmapLevelsRepository); + AddOfficialBeatmapLevelsRepository(_beatmapLevelsModel.ostAndExtrasBeatmapLevelsRepository); + AddOfficialBeatmapLevelsRepository(_beatmapLevelsModel.dlcBeatmapLevelsRepository); } catch (Exception ex) { @@ -391,11 +392,11 @@ void AddOfficialBeatmapLevelsRepository(BeatmapLevelsRepository levelsRepository #region LoadCustomLevels // Get Levels from CustomLevels and CustomWIPLevels folders - var songFolders = Directory.GetDirectories(customLevelsPath).ToList().Concat(Directory.GetDirectories(customWipLevelsPath)).ToList(); + var songFolders = Directory.GetDirectories(customLevelsPath).Concat(Directory.GetDirectories(customWipLevelsPath)).ToArray(); var loadedData = new ConcurrentBag(); var processedSongsCount = 0; - Parallel.ForEach(songFolders, new ParallelOptions { MaxDegreeOfParallelism = Math.Max(1, (Environment.ProcessorCount / 2) - 1) }, (folder) => + Parallel.ForEach(songFolders, new ParallelOptions { MaxDegreeOfParallelism = Math.Max(1, (Environment.ProcessorCount / 2) - 1), CancellationToken = _loadingTaskCancellationTokenSource.Token }, (folder) => { string[] results; try @@ -419,11 +420,6 @@ void AddOfficialBeatmapLevelsRepository(BeatmapLevelsRepository levelsRepository { try { - if (_loadingCancelled) - { - return; - } - var songPath = Path.GetDirectoryName(result)!; if (Directory.GetParent(songPath)?.Name == "Backups") { @@ -443,7 +439,7 @@ void AddOfficialBeatmapLevelsRepository(BeatmapLevelsRepository levelsRepository } var wip = songPath.Contains("CustomWIPLevels"); - var level = LoadCustomLevel(songPath, _loadingTaskCancellationTokenSource.Token); + var level = LoadCustomLevel(songPath); if (level == null) { Logging.Logger.Error($"Failed to load custom level: {folder}"); @@ -469,16 +465,10 @@ void AddOfficialBeatmapLevelsRepository(BeatmapLevelsRepository levelsRepository } } - LoadingProgress = (float) Interlocked.Increment(ref processedSongsCount) / songFolders.Count; + LoadingProgress = (float) Interlocked.Increment(ref processedSongsCount) / songFolders.Length; }); - foreach (var beatmapLevelData in LoadedBeatmapLevelsData) - { - if (!_customLevelLoader._loadedBeatmapLevelsData.ContainsKey(beatmapLevelData.Key)) - { - _customLevelLoader._loadedBeatmapLevelsData.Add(beatmapLevelData.Key, beatmapLevelData.Value); - } - } + RefreshLoadedBeatmapLevelsData(); #endregion @@ -526,11 +516,6 @@ void AddOfficialBeatmapLevelsRepository(BeatmapLevelsRepository levelsRepository { try { - if (_loadingCancelled) - { - return; - } - // On quick refresh: Check if the beatmap directory is already present in the respective beatmap dictionary // If it is already present on a non full refresh, it will be ignored (changes to the beatmap will not be applied) var songPath = Path.GetDirectoryName(result)!; @@ -564,7 +549,7 @@ void AddOfficialBeatmapLevelsRepository(BeatmapLevelsRepository levelsRepository } } - var level = LoadCustomLevel(songPath, _loadingTaskCancellationTokenSource.Token, entry.SongFolderEntry); + var level = LoadCustomLevel(songPath, entry.SongFolderEntry); if (level == null) { Logging.Logger.Error($"Failed to load custom level: {folder}"); @@ -597,7 +582,7 @@ void AddOfficialBeatmapLevelsRepository(BeatmapLevelsRepository levelsRepository _loadingTaskCancellationTokenSource.Token.ThrowIfCancellationRequested(); } - catch (Exception e) when (!(e is OperationCanceledException)) + catch (Exception e) when (e is not OperationCanceledException) { Logging.Logger.Error("RetrieveAllSongs failed:"); Logging.Logger.Error(e); @@ -723,6 +708,17 @@ await UnityMainThreadTaskScheduler.Factory.StartNew(() => } } + private void RefreshLoadedBeatmapLevelsData() + { + foreach (var beatmapLevelData in LoadedBeatmapLevelsData) + { + if (!_customLevelLoader._loadedBeatmapLevelsData.ContainsKey(beatmapLevelData.Key)) + { + _customLevelLoader._loadedBeatmapLevelsData.Add(beatmapLevelData.Key, beatmapLevelData.Value); + } + } + } + /// /// Delete a beatmap (is only used by other mods) /// @@ -953,12 +949,7 @@ private void LoadCachedZIPs(IEnumerable cacheFolders, bool fullRefresh, { try { - if (_loadingCancelled) - { - return; - } - - var level = LoadCustomLevel(songPath, _loadingTaskCancellationTokenSource.Token, folderEntry); + var level = LoadCustomLevel(songPath, folderEntry); if (level == null) { Logging.Logger.Error($"Failed to load custom level: {folderEntry}"); @@ -972,7 +963,7 @@ private void LoadCachedZIPs(IEnumerable cacheFolders, bool fullRefresh, Logging.Logger.Notice($"Failed to load song from {cachedFolder}:"); Logging.Logger.Notice(ex); } - }); + }, _loadingTaskCancellationTokenSource.Token); } catch (Exception ex) { @@ -1012,10 +1003,8 @@ private bool AssignBeatmapToSeperateFolder( } // TODO: Return beatmap level data? - public BeatmapLevel? LoadCustomLevel(string customLevelPath, CancellationToken cancellationToken, SongFolderEntry? entry = null) + public BeatmapLevel? LoadCustomLevel(string customLevelPath, SongFolderEntry? entry = null) { - cancellationToken.ThrowIfCancellationRequested(); - var infoFilePath = Path.Combine(customLevelPath, CustomLevelPathHelper.kStandardLevelInfoFilename); if (!File.Exists(infoFilePath)) { From e384a3d86f37cfaf781e79a1c335ec5a93515318 Mon Sep 17 00:00:00 2001 From: Meivyn <793322+Meivyn@users.noreply.github.com> Date: Sat, 16 Mar 2024 19:50:55 -0400 Subject: [PATCH 09/17] Remove unused author name from `ProgressBar` --- source/SongCore/UI/ProgressBar.cs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/source/SongCore/UI/ProgressBar.cs b/source/SongCore/UI/ProgressBar.cs index 4dc739f..7e42df0 100644 --- a/source/SongCore/UI/ProgressBar.cs +++ b/source/SongCore/UI/ProgressBar.cs @@ -12,11 +12,10 @@ namespace SongCore.UI public class ProgressBar : MonoBehaviour { private Canvas? _canvas; - private TMP_Text? _authorNameText; private TMP_Text? _pluginNameText; private TMP_Text? _headerText; private Image? _loadingBackg; - internal Image? _loadingBar; + private Image? _loadingBar; private static bool _jokeTime = false; private static readonly Vector3 Position = new Vector3(0, 2.5f, 2.5f); @@ -25,10 +24,6 @@ public class ProgressBar : MonoBehaviour private static readonly Vector2 CanvasSize = new Vector2(100, 50); - private const string AuthorNameText = ""; - private const float AuthorNameFontSize = 7f; - private static readonly Vector2 AuthorNamePosition = new Vector2(10, 31); - private const string PluginNameText = "SongCore Loader"; private const float PluginNameFontSize = 9f; private static readonly Vector2 PluginNamePosition = new Vector2(10, 23); @@ -141,14 +136,6 @@ private void Awake() var rectTransform = _canvas.transform as RectTransform; rectTransform.sizeDelta = CanvasSize; - _authorNameText = BeatSaberMarkupLanguage.BeatSaberUI.CreateText(_canvas.transform as RectTransform, AuthorNameText, AuthorNamePosition); - rectTransform = _authorNameText.transform as RectTransform; - rectTransform.SetParent(_canvas.transform, false); - rectTransform.anchoredPosition = AuthorNamePosition; - rectTransform.sizeDelta = HeaderSize; - _authorNameText.text = AuthorNameText; - _authorNameText.fontSize = AuthorNameFontSize; - var pluginText = _jokeTime ? "SongCore Cleaner" : PluginNameText; _pluginNameText = BeatSaberMarkupLanguage.BeatSaberUI.CreateText(_canvas.transform as RectTransform, pluginText, PluginNamePosition); rectTransform = _pluginNameText.transform as RectTransform; From 5d9a8a8ce89cf71fd306357db21464c2262e8cad Mon Sep 17 00:00:00 2001 From: Meivyn <793322+Meivyn@users.noreply.github.com> Date: Sat, 16 Mar 2024 23:56:34 -0400 Subject: [PATCH 10/17] Fix typos (minor breaking changes) --- source/SongCore/Collections.cs | 11 ++-- source/SongCore/Data/SongFolderEntries.cs | 28 ++++----- source/SongCore/Data/folders.xml | 6 +- source/SongCore/Loader.cs | 69 +++++++++++------------ source/SongCore/Plugin.cs | 2 +- source/SongCore/UI/ProgressBar.cs | 6 +- 6 files changed, 59 insertions(+), 63 deletions(-) diff --git a/source/SongCore/Collections.cs b/source/SongCore/Collections.cs index 62fb7ad..887a649 100644 --- a/source/SongCore/Collections.cs +++ b/source/SongCore/Collections.cs @@ -148,7 +148,7 @@ public static void RegisterCapability(string capability) return null; } - public static SeperateSongFolder AddSeperateSongFolder(string name, string folderPath, FolderLevelPack pack, Sprite? image = null, bool wip = false, bool cachezips = false) + public static SeparateSongFolder AddSeparateSongFolder(string name, string folderPath, FolderLevelPack pack, Sprite? image = null, bool wip = false, bool cachezips = false) { UI.BasicUI.GetIcons(); if (!Directory.Exists(folderPath)) @@ -165,14 +165,13 @@ public static SeperateSongFolder AddSeperateSongFolder(string name, string folde } var entry = new SongFolderEntry(name, folderPath, pack, "", wip, cachezips); - var seperateSongFolder = new ModSeperateSongFolder(entry, image == null ? UI.BasicUI.FolderIcon! : image); + var separateSongFolder = new ModSeparateSongFolder(entry, image == null ? UI.BasicUI.FolderIcon! : image); - Loader.SeperateSongFolders.Add(seperateSongFolder); - return seperateSongFolder; + Loader.SeparateSongFolders.Add(separateSongFolder); + return separateSongFolder; } - - public static void DeregisterizeCapability(string capability) + public static void DeregisterCapability(string capability) { _capabilities.Remove(capability); } diff --git a/source/SongCore/Data/SongFolderEntries.cs b/source/SongCore/Data/SongFolderEntries.cs index e7081fc..ea205f9 100644 --- a/source/SongCore/Data/SongFolderEntries.cs +++ b/source/SongCore/Data/SongFolderEntries.cs @@ -32,15 +32,15 @@ public SongFolderEntry(string name, string path, FolderLevelPack pack, string im } } - public class SeperateSongFolder + public class SeparateSongFolder { public readonly ConcurrentDictionary Levels = new ConcurrentDictionary(); public SongFolderEntry SongFolderEntry { get; private set; } public SongCoreCustomBeatmapLevelPack LevelPack { get; private set; } = null; - public SeperateSongFolder? CacheFolder { get; private set; } + public SeparateSongFolder? CacheFolder { get; private set; } - public SeperateSongFolder(SongFolderEntry folderEntry, SeperateSongFolder? cacheFolder = null) + public SeparateSongFolder(SongFolderEntry folderEntry, SeparateSongFolder? cacheFolder = null) { SongFolderEntry = folderEntry; CacheFolder = cacheFolder; @@ -69,7 +69,7 @@ public SeperateSongFolder(SongFolderEntry folderEntry, SeperateSongFolder? cache } } - public SeperateSongFolder(SongFolderEntry folderEntry, UnityEngine.Sprite image) + public SeparateSongFolder(SongFolderEntry folderEntry, UnityEngine.Sprite image) { SongFolderEntry = folderEntry; if (folderEntry.Pack == FolderLevelPack.NewPack) @@ -78,9 +78,9 @@ public SeperateSongFolder(SongFolderEntry folderEntry, UnityEngine.Sprite image) } } - public static List ReadSeperateFoldersFromFile(string filePath) + public static List ReadSeparateFoldersFromFile(string filePath) { - var result = new List(); + var result = new List(); try { XDocument file = XDocument.Load(filePath); @@ -123,20 +123,20 @@ public static List ReadSeperateFoldersFromFile(string filePa // Console.WriteLine(" " + entry.Pack); // Console.WriteLine(" " + entry.WIP); - SeperateSongFolder? cachedSeperate = null; + SeparateSongFolder? cachedSeparate = null; if (zipCaching) { var cachePack = (FolderLevelPack) pack == FolderLevelPack.CustomWIPLevels ? FolderLevelPack.CachedWIPLevels : FolderLevelPack.NewPack; SongFolderEntry cachedSongFolderEntry = new SongFolderEntry($"Cached {name}", Path.Combine(path, "Cache"), cachePack, imagePath, isWIP, false); - cachedSeperate = new SeperateSongFolder(cachedSongFolderEntry); + cachedSeparate = new SeparateSongFolder(cachedSongFolderEntry); } - var seperate = new SeperateSongFolder(entry, cachedSeperate); + var seperate = new SeparateSongFolder(entry, cachedSeparate); result.Add(seperate); - if (cachedSeperate != null) + if (cachedSeparate != null) { - result.Add(cachedSeperate); + result.Add(cachedSeparate); } } } @@ -149,15 +149,15 @@ public static List ReadSeperateFoldersFromFile(string filePa } } - public class ModSeperateSongFolder : SeperateSongFolder + public class ModSeparateSongFolder : SeparateSongFolder { public bool AlwaysShow { get; set; } = true; - public ModSeperateSongFolder(SongFolderEntry folderEntry) : base(folderEntry) + public ModSeparateSongFolder(SongFolderEntry folderEntry) : base(folderEntry) { } - public ModSeperateSongFolder(SongFolderEntry folderEntry, UnityEngine.Sprite image) : base(folderEntry, image) + public ModSeparateSongFolder(SongFolderEntry folderEntry, UnityEngine.Sprite image) : base(folderEntry, image) { } } diff --git a/source/SongCore/Data/folders.xml b/source/SongCore/Data/folders.xml index b5d1b2f..4953d25 100644 --- a/source/SongCore/Data/folders.xml +++ b/source/SongCore/Data/folders.xml @@ -1,4 +1,4 @@ -