diff --git a/source/SongCore/Collections.cs b/source/SongCore/Collections.cs index 6c6f8f1..386cf0d 100644 --- a/source/SongCore/Collections.cs +++ b/source/SongCore/Collections.cs @@ -18,11 +18,12 @@ public static class Collections private static readonly List _capabilities = new List(); private static readonly List _customCharacteristics = new List(); - internal static readonly string DataPath = Path.Combine(UnityGame.UserDataPath, "SongCore", "SongCoreExtraData.dat"); + internal static readonly string DataPath = Path.Combine(UnityGame.UserDataPath, nameof(SongCore), "SongCoreExtraData.dat"); 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 CustomBeatmapLevelPack? WipLevelPack; + internal static BeatmapLevelPack? WipLevelPack; internal static ConcurrentDictionary CustomSongsData = new ConcurrentDictionary(); public static ReadOnlyCollection capabilities => _capabilities.AsReadOnly(); @@ -61,18 +62,18 @@ internal static void AddExtraSongData(string hash, string path, string rawSongDa return null; } - public static ExtraSongData.DifficultyData? RetrieveDifficultyData(IDifficultyBeatmap beatmap) + public static ExtraSongData.DifficultyData? RetrieveDifficultyData(BeatmapLevel beatmapLevel, BeatmapKey beatmapKey) { ExtraSongData? songData = null; - if (beatmap.level is CustomPreviewBeatmapLevel customLevel) + if (!beatmapLevel.hasPrecalculatedData) { - songData = RetrieveExtraSongData(Hashing.GetCustomLevelHash(customLevel)); + songData = RetrieveExtraSongData(Hashing.GetCustomLevelHash(beatmapLevel)); } var diffData = songData?._difficulties.FirstOrDefault(x => - x._difficulty == beatmap.difficulty && (x._beatmapCharacteristicName == beatmap.parentDifficultyBeatmapSet.beatmapCharacteristic.characteristicNameLocalizationKey || - x._beatmapCharacteristicName == beatmap.parentDifficultyBeatmapSet.beatmapCharacteristic.serializedName)); + x._difficulty == beatmapKey.difficulty && (x._beatmapCharacteristicName == beatmapKey.beatmapCharacteristic.characteristicNameLocalizationKey || + x._beatmapCharacteristicName == beatmapKey.beatmapCharacteristic.serializedName)); return diffData; } @@ -123,6 +124,8 @@ public static void RegisterCapability(string capability) newChar._containsRotationEvents = containsRotationEvents; newChar._sortingOrder = sortingOrder; + newChar.name = serializedName + "BeatmapCharacteristic"; + if (_customCharacteristics.All(x => x.serializedName != newChar.serializedName)) { _customCharacteristics.Add(newChar); diff --git a/source/SongCore/Data/SongFolderEntries.cs b/source/SongCore/Data/SongFolderEntries.cs index 1d1782f..f2273da 100644 --- a/source/SongCore/Data/SongFolderEntries.cs +++ b/source/SongCore/Data/SongFolderEntries.cs @@ -1,4 +1,4 @@ -using SongCore.OverrideClasses; +using SongCore.OverrideClasses; using System; using System.Collections.Generic; using System.IO; @@ -34,10 +34,9 @@ public SongFolderEntry(string name, string path, FolderLevelPack pack, string im public class SeperateSongFolder { - public readonly ConcurrentDictionary Levels = new ConcurrentDictionary(); + public readonly ConcurrentDictionary Levels = new ConcurrentDictionary(); public SongFolderEntry SongFolderEntry { get; private set; } - public SongCoreCustomLevelCollection LevelCollection { get; private set; } = null; public SongCoreCustomBeatmapLevelPack LevelPack { get; private set; } = null; public SeperateSongFolder? CacheFolder { get; private set; } @@ -48,7 +47,6 @@ public SeperateSongFolder(SongFolderEntry folderEntry, SeperateSongFolder? cache if (folderEntry.Pack == FolderLevelPack.NewPack) { - LevelCollection = new SongCoreCustomLevelCollection(Levels.Values.ToArray()); var image = UI.BasicUI.FolderIcon!; if (!string.IsNullOrEmpty(folderEntry.ImagePath)) @@ -67,7 +65,7 @@ public SeperateSongFolder(SongFolderEntry folderEntry, SeperateSongFolder? cache } } - LevelPack = new SongCoreCustomBeatmapLevelPack(CustomLevelLoader.kCustomLevelPackPrefixId + folderEntry.Name, folderEntry.Name, image, LevelCollection); + LevelPack = new SongCoreCustomBeatmapLevelPack(CustomLevelLoader.kCustomLevelPackPrefixId + folderEntry.Name, folderEntry.Name, image, Levels.Values.ToArray()); } } @@ -76,9 +74,7 @@ public SeperateSongFolder(SongFolderEntry folderEntry, UnityEngine.Sprite image) SongFolderEntry = folderEntry; if (folderEntry.Pack == FolderLevelPack.NewPack) { - LevelCollection = new SongCoreCustomLevelCollection(Levels.Values.ToArray()); - - LevelPack = new SongCoreCustomBeatmapLevelPack(CustomLevelLoader.kCustomLevelPackPrefixId + folderEntry.Name, folderEntry.Name, image, LevelCollection); + LevelPack = new SongCoreCustomBeatmapLevelPack(CustomLevelLoader.kCustomLevelPackPrefixId + folderEntry.Name, folderEntry.Name, image, Levels.Values.ToArray()); } } diff --git a/source/SongCore/HarmonyPatches/AnnotatedBeatmapLevelCollectionCell_RefreshAvailabilityAsync.cs b/source/SongCore/HarmonyPatches/AnnotatedBeatmapLevelCollectionCell_RefreshAvailabilityAsync.cs index 911ff93..18510e8 100644 --- a/source/SongCore/HarmonyPatches/AnnotatedBeatmapLevelCollectionCell_RefreshAvailabilityAsync.cs +++ b/source/SongCore/HarmonyPatches/AnnotatedBeatmapLevelCollectionCell_RefreshAvailabilityAsync.cs @@ -1,3 +1,4 @@ +using System; using HarmonyLib; /* @@ -12,7 +13,7 @@ internal class AnnotatedBeatmapLevelCollectionCell_RefreshAvailabilityAsync { private static void Postfix(AnnotatedBeatmapLevelCollectionCell __instance) { - if (__instance._annotatedBeatmapLevelCollection is CustomBeatmapLevelPack) + if (__instance._beatmapLevelPack.packID.StartsWith(CustomLevelLoader.kCustomLevelPackPrefixId, StringComparison.Ordinal)) { __instance.SetDownloadIconVisible(false); } diff --git a/source/SongCore/HarmonyPatches/BeatmapVersionDetectionPatch.cs b/source/SongCore/HarmonyPatches/BeatmapVersionDetectionPatch.cs index 6f0b108..ea90475 100644 --- a/source/SongCore/HarmonyPatches/BeatmapVersionDetectionPatch.cs +++ b/source/SongCore/HarmonyPatches/BeatmapVersionDetectionPatch.cs @@ -1,36 +1,19 @@ -using System; -using System.Text.RegularExpressions; using HarmonyLib; using JetBrains.Annotations; namespace SongCore.HarmonyPatches { - // This patch fixes the base game implementation, which fails with maps that have no version declared. - // Without this patch affected maps don't load when selected. It is also 100 times faster on average. - [HarmonyPatch(typeof(BeatmapSaveDataHelpers), nameof(BeatmapSaveDataHelpers.GetVersion))] - [UsedImplicitly] + /// + /// This patch fixes the base game implementation, which fails with maps that have no version declared. + /// Without this patch affected maps don't load when selected. + /// + [HarmonyPatch(typeof(BeatmapSaveDataHelpers.VersionSerializedData), nameof(BeatmapSaveDataHelpers.VersionSerializedData.v), MethodType.Getter)] internal static class BeatmapVersionDetectionPatch { - private static readonly Regex VersionRegex = new Regex( - @"""_?version""\s*:\s*""(?[0-9]+\.[0-9]+\.?[0-9]?)""", - RegexOptions.Compiled | RegexOptions.CultureInvariant - ); - - private static readonly Version FallbackVersion = new Version("2.0.0"); - [UsedImplicitly] - private static bool Prefix(string data, ref Version __result) + private static void Postfix(ref string? __result) { - var truncatedText = data.Substring(0, 50); - var match = VersionRegex.Match(truncatedText); - if (!match.Success) - { - __result = FallbackVersion; - return false; - } - - __result = new Version(match.Groups["version"].Value); - return false; + __result ??= BeatmapSaveDataHelpers.noVersion.ToString(); } } -} \ No newline at end of file +} diff --git a/source/SongCore/HarmonyPatches/CosmeticCharacteristicsPatches.cs b/source/SongCore/HarmonyPatches/CosmeticCharacteristicsPatches.cs index 32ee6b3..86e46d0 100644 --- a/source/SongCore/HarmonyPatches/CosmeticCharacteristicsPatches.cs +++ b/source/SongCore/HarmonyPatches/CosmeticCharacteristicsPatches.cs @@ -22,10 +22,10 @@ private static bool Prefix() if (Plugin.Configuration.DisableRotationSpawnLinesOverride) return true; - var beatmap = BS_Utils.Plugin.LevelData.GameplayCoreSceneSetupData.difficultyBeatmap; - if (beatmap == null) + var sceneSetupData = BS_Utils.Plugin.LevelData.GameplayCoreSceneSetupData; + if (sceneSetupData.beatmapLevel == null) return true; - var beatmapData = Collections.RetrieveDifficultyData(beatmap); + var beatmapData = Collections.RetrieveDifficultyData(sceneSetupData.beatmapLevel, sceneSetupData.beatmapKey); if (beatmapData == null) return true; if (beatmapData._showRotationNoteSpawnLines == null) @@ -49,24 +49,23 @@ private static void Prefix(GameplayCoreInstaller __instance) sceneSetupData = __instance._sceneSetupData; - var diffBeatmapLevel = sceneSetupData.difficultyBeatmap.level; - var level = diffBeatmapLevel is CustomBeatmapLevel ? diffBeatmapLevel as CustomPreviewBeatmapLevel : null; - if (level == null) + var beatmapLevel = sceneSetupData.beatmapLevel; + if (beatmapLevel.hasPrecalculatedData) { diffData = null; return; } - diffData = Collections.RetrieveDifficultyData(sceneSetupData.difficultyBeatmap); + diffData = Collections.RetrieveDifficultyData(beatmapLevel, sceneSetupData.beatmapKey); if (diffData == null) return; if (diffData._oneSaber != null && !Plugin.Configuration.DisableOneSaberOverride) { - numberOfColors = sceneSetupData.difficultyBeatmap.parentDifficultyBeatmapSet.beatmapCharacteristic.numberOfColors; - sceneSetupData.difficultyBeatmap.parentDifficultyBeatmapSet.beatmapCharacteristic._numberOfColors = diffData._oneSaber.Value == true ? 1 : 2; + numberOfColors = sceneSetupData.beatmapKey.beatmapCharacteristic.numberOfColors; + sceneSetupData.beatmapKey.beatmapCharacteristic._numberOfColors = diffData._oneSaber.Value == true ? 1 : 2; } } - private static void Postfix(GameplayCoreInstaller __instance) + private static void Postfix() { if (Plugin.Configuration.DisableOneSaberOverride) return; @@ -75,7 +74,7 @@ private static void Postfix(GameplayCoreInstaller __instance) if (diffData._oneSaber != null && !Plugin.Configuration.DisableOneSaberOverride) { - sceneSetupData.difficultyBeatmap.parentDifficultyBeatmapSet.beatmapCharacteristic._numberOfColors = numberOfColors; + sceneSetupData.beatmapKey.beatmapCharacteristic._numberOfColors = numberOfColors; } } @@ -86,19 +85,13 @@ private static void Postfix(GameplayCoreInstaller __instance) [HarmonyPatch(nameof(BeatmapCharacteristicSegmentedControlController.SetData), MethodType.Normal)] internal class CosmeticCharacteristicsPatch { - private static void Postfix(BeatmapCharacteristicSegmentedControlController __instance, IReadOnlyList difficultyBeatmapSets, BeatmapCharacteristicSO selectedBeatmapCharacteristic) + private static void Postfix(BeatmapCharacteristicSegmentedControlController __instance, BeatmapCharacteristicSO selectedBeatmapCharacteristic) { if (!Plugin.Configuration.DisplayCustomCharacteristics) return; - var diffBeatmapSetsBeatmaps = difficultyBeatmapSets.FirstOrDefault().difficultyBeatmaps; - if (diffBeatmapSetsBeatmaps == null) return; - var diffBeatmapItem = diffBeatmapSetsBeatmaps.FirstOrDefault(); - if (diffBeatmapItem == null) return; - var diffBeatmapLevel = diffBeatmapItem.level; + var level = Object.FindObjectOfType()._beatmapLevel; - var level = diffBeatmapLevel is CustomBeatmapLevel ? diffBeatmapLevel as CustomPreviewBeatmapLevel : null; - - if (level == null) return; + if (level.hasPrecalculatedData) return; var songData = Collections.RetrieveExtraSongData(Hashing.GetCustomLevelHash(level)); if (songData == null) return; @@ -121,9 +114,9 @@ private static void Postfix(BeatmapCharacteristicSegmentedControlController __in if (detail != null) { Sprite sprite = characteristic.icon; - if (detail._characteristicIconFilePath != null) - sprite = Utilities.Utils.LoadSpriteFromFile(Path.Combine(level.customLevelPath, detail._characteristicIconFilePath)) ?? characteristic.icon; - string label = detail._characteristicLabel ?? Polyglot.Localization.Get(characteristic.descriptionLocalizationKey); + 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 diff --git a/source/SongCore/HarmonyPatches/CustomSongColorsPatch.cs b/source/SongCore/HarmonyPatches/CustomSongColorsPatch.cs index ccc27a2..8784f0e 100644 --- a/source/SongCore/HarmonyPatches/CustomSongColorsPatch.cs +++ b/source/SongCore/HarmonyPatches/CustomSongColorsPatch.cs @@ -1,86 +1,121 @@ -using System.Collections.Generic; -using System.Reflection; using HarmonyLib; +using SongCore.Data; using SongCore.Utilities; using Utils = SongCore.Utilities.Utils; namespace SongCore.HarmonyPatches { [HarmonyPatch] - internal class SceneTransitionPatch + internal class CustomSongColorsPatch { - private static IEnumerable TargetMethods() + [HarmonyPatch(typeof(StandardLevelScenesTransitionSetupDataSO), nameof(StandardLevelScenesTransitionSetupDataSO.InitColorInfo))] + internal class StandardLevelScenesTransitionSetupDataPatch { - yield return AccessTools.DeclaredMethod(typeof(StandardLevelScenesTransitionSetupDataSO), nameof(StandardLevelScenesTransitionSetupDataSO.Init)); - yield return AccessTools.DeclaredMethod(typeof(MultiplayerLevelScenesTransitionSetupDataSO), nameof(MultiplayerLevelScenesTransitionSetupDataSO.Init)); + private static void Postfix(StandardLevelScenesTransitionSetupDataSO __instance) + { + // TODO: Remove this when it gets fixed. + var colorScheme = __instance.colorScheme; + if (colorScheme._environmentColor0Boost == default) + { + colorScheme._environmentColor0Boost = colorScheme._environmentColor0; + } + if (colorScheme._environmentColor1Boost == default) + { + colorScheme._environmentColor1Boost = colorScheme._environmentColor1; + } + + if (!Plugin.Configuration.CustomSongNoteColors && !Plugin.Configuration.CustomSongEnvironmentColors && !Plugin.Configuration.CustomSongObstacleColors) + { + return; + } + + var songData = Collections.RetrieveDifficultyData(__instance.beatmapLevel, __instance.beatmapKey); + var overrideColorScheme = GetOverrideColorScheme(songData, colorScheme); + if (overrideColorScheme is null) + { + return; + } + + __instance.usingOverrideColorScheme = true; + __instance.colorScheme = overrideColorScheme; + } } - private static void Prefix(ref IDifficultyBeatmap difficultyBeatmap, ref ColorScheme? overrideColorScheme) + [HarmonyPatch(typeof(MultiplayerLevelScenesTransitionSetupDataSO), nameof(MultiplayerLevelScenesTransitionSetupDataSO.InitColorInfo))] + internal class MultiplayerLevelScenesTransitionSetupDataPatch { - // TODO: Remove this when it gets fixed. - if (overrideColorScheme != null) + private static void Postfix(MultiplayerLevelScenesTransitionSetupDataSO __instance) { - if (overrideColorScheme._environmentColor0Boost == default) + // TODO: Remove this when it gets fixed. + var colorScheme = __instance.colorScheme; + if (colorScheme._environmentColor0Boost == default) { - overrideColorScheme._environmentColor0Boost = overrideColorScheme._environmentColor0; + colorScheme._environmentColor0Boost = colorScheme._environmentColor0; } - if (overrideColorScheme._environmentColor1Boost == default) + if (colorScheme._environmentColor1Boost == default) { - overrideColorScheme._environmentColor1Boost = overrideColorScheme._environmentColor1; + colorScheme._environmentColor1Boost = colorScheme._environmentColor1; } - } - if (difficultyBeatmap == null || !Plugin.Configuration.CustomSongNoteColors && !Plugin.Configuration.CustomSongEnvironmentColors && !Plugin.Configuration.CustomSongObstacleColors) - { - return; - } + if (!Plugin.Configuration.CustomSongNoteColors && !Plugin.Configuration.CustomSongEnvironmentColors && !Plugin.Configuration.CustomSongObstacleColors) + { + return; + } - var songData = Collections.RetrieveDifficultyData(difficultyBeatmap); - if (songData == null) - { - return; + var songData = Collections.RetrieveDifficultyData(__instance.beatmapLevel, __instance.beatmapKey); + var overrideColorScheme = GetOverrideColorScheme(songData, colorScheme); + if (overrideColorScheme is null) + { + return; + } + + __instance.usingOverrideColorScheme = true; + __instance.colorScheme = overrideColorScheme; } + } - if (songData._colorLeft == null && songData._colorRight == null && songData._envColorLeft == null && songData._envColorRight == null && songData._envColorWhite == null && songData._obstacleColor == null && songData._envColorLeftBoost == null && songData._envColorRightBoost == null && songData._envColorWhiteBoost == null) + private static ColorScheme? GetOverrideColorScheme(ExtraSongData.DifficultyData? songData, ColorScheme currentColorScheme) + { + if (songData is null || (songData._colorLeft == null && songData._colorRight == null && songData._envColorLeft == null && songData._envColorRight == null && + songData._envColorWhite == null && songData._obstacleColor == null && songData._envColorLeftBoost == null && songData._envColorRightBoost == null && + songData._envColorWhiteBoost == null)) { - return; + return null; } - var environmentInfoSO = difficultyBeatmap.GetEnvironmentInfo(); - var fallbackScheme = overrideColorScheme ?? new ColorScheme(environmentInfoSO.colorScheme); - 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) - ? fallbackScheme.saberAColor + ? currentColorScheme.saberAColor : Utils.ColorFromMapColor(songData._colorLeft); var saberRight = (songData._colorRight == null || !Plugin.Configuration.CustomSongNoteColors) - ? fallbackScheme.saberBColor + ? currentColorScheme.saberBColor : Utils.ColorFromMapColor(songData._colorRight); var envLeft = (songData._envColorLeft == null || !Plugin.Configuration.CustomSongEnvironmentColors) - ? songData._colorLeft == null ? fallbackScheme.environmentColor0 : Utils.ColorFromMapColor(songData._colorLeft) + ? songData._colorLeft == null ? currentColorScheme.environmentColor0 : Utils.ColorFromMapColor(songData._colorLeft) : Utils.ColorFromMapColor(songData._envColorLeft); var envRight = (songData._envColorRight == null || !Plugin.Configuration.CustomSongEnvironmentColors) - ? songData._colorRight == null ? fallbackScheme.environmentColor1 : Utils.ColorFromMapColor(songData._colorRight) + ? songData._colorRight == null ? currentColorScheme.environmentColor1 : Utils.ColorFromMapColor(songData._colorRight) : Utils.ColorFromMapColor(songData._envColorRight); var envWhite = (songData._envColorWhite == null || !Plugin.Configuration.CustomSongEnvironmentColors) - ? fallbackScheme.environmentColorW + ? currentColorScheme.environmentColorW : Utils.ColorFromMapColor(songData._envColorWhite); var envLeftBoost = (songData._envColorLeftBoost == null || !Plugin.Configuration.CustomSongEnvironmentColors) - ? fallbackScheme.environmentColor0Boost + ? currentColorScheme.environmentColor0Boost : Utils.ColorFromMapColor(songData._envColorLeftBoost); var envRightBoost = (songData._envColorRightBoost == null|| !Plugin.Configuration.CustomSongEnvironmentColors) - ? fallbackScheme.environmentColor1Boost + ? currentColorScheme.environmentColor1Boost : Utils.ColorFromMapColor(songData._envColorRightBoost); var envWhiteBoost = (songData._envColorWhiteBoost == null || !Plugin.Configuration.CustomSongEnvironmentColors) - ? fallbackScheme.environmentColorWBoost + ? currentColorScheme.environmentColorWBoost : Utils.ColorFromMapColor(songData._envColorWhiteBoost); var obstacle = (songData._obstacleColor == null || !Plugin.Configuration.CustomSongObstacleColors) - ? fallbackScheme.obstaclesColor + ? currentColorScheme.obstaclesColor : Utils.ColorFromMapColor(songData._obstacleColor); - overrideColorScheme = new ColorScheme("SongCoreMapColorScheme", "SongCore Map Color Scheme", true, "SongCore Map Color Scheme", false, saberLeft, saberRight, envLeft, + + return new ColorScheme("SongCoreMapColorScheme", "SongCore Map Color Scheme", true, "SongCore Map Color Scheme", false, saberLeft, saberRight, envLeft, envRight, envWhite, true, envLeftBoost, envRightBoost, envWhiteBoost, obstacle); } } diff --git a/source/SongCore/HarmonyPatches/LevelSelectionPatch.cs b/source/SongCore/HarmonyPatches/LevelSelectionPatch.cs index c9a5ced..040acdc 100644 --- a/source/SongCore/HarmonyPatches/LevelSelectionPatch.cs +++ b/source/SongCore/HarmonyPatches/LevelSelectionPatch.cs @@ -1,4 +1,5 @@ using System.Globalization; +using System.Linq; using HarmonyLib; namespace SongCore.HarmonyPatches @@ -7,19 +8,20 @@ namespace SongCore.HarmonyPatches [HarmonyPatch(nameof(LevelListTableCell.SetDataFromLevelAsync), MethodType.Normal)] internal class LevelListTableCellSetDataFromLevel { - private static void Postfix(LevelListTableCell __instance, IPreviewBeatmapLevel level) + private static void Postfix(LevelListTableCell __instance, BeatmapLevel level) { // Rounding BPM display for all maps, including official ones __instance._songBpmText.text = System.Math.Round(level.beatsPerMinute).ToString(CultureInfo.InvariantCulture); - if (!string.IsNullOrWhiteSpace(level.levelAuthorName)) + var authors = level.allMappers.Concat(level.allLighters).Join(); + if (!string.IsNullOrWhiteSpace(authors)) { __instance._songAuthorText.richText = true; //Get PinkCore'd string mapperColor = Plugin.Configuration.GreenMapperColor ? "89ff89" : "ff69b4"; - __instance._songAuthorText.text = $"{level.songAuthorName} [{level.levelAuthorName.Replace(@"<", "<\u200B").Replace(@">", ">\u200B")}]"; + __instance._songAuthorText.text = $"{level.songAuthorName} [{authors.Replace(@"<", "<\u200B").Replace(@">", ">\u200B")}]"; } } } diff --git a/source/SongCore/HarmonyPatches/LoadingPatches.cs b/source/SongCore/HarmonyPatches/LoadingPatches.cs index eec1da3..07ba69c 100644 --- a/source/SongCore/HarmonyPatches/LoadingPatches.cs +++ b/source/SongCore/HarmonyPatches/LoadingPatches.cs @@ -1,25 +1,13 @@ +using System; using HarmonyLib; using System.Linq; using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; using SongCore.Utilities; namespace SongCore.HarmonyPatches { - [HarmonyPatch(typeof(CustomBeatmapLevel))] - [HarmonyPatch(new[] - { - typeof(CustomPreviewBeatmapLevel) - })] - [HarmonyPatch(MethodType.Constructor)] - internal class CustomBeatmapLevelDurationPatch - { - private static void Postfix(CustomBeatmapLevel __instance, CustomPreviewBeatmapLevel customPreviewBeatmapLevel) - { - var thisInstance = (CustomPreviewBeatmapLevel) __instance; - Accessors.SongDurationAccessor(ref thisInstance) = customPreviewBeatmapLevel.songDuration; - } - } - [HarmonyPatch(typeof(BeatmapLevelsModel))] [HarmonyPatch(nameof(BeatmapLevelsModel.ReloadCustomLevelPackCollectionAsync), MethodType.Normal)] internal class StopVanillaLoadingPatch @@ -33,13 +21,13 @@ internal class StopVanillaLoadingPatch2 { private static bool Prefix(LevelFilteringNavigationController __instance) { - if (Loader.CustomBeatmapLevelPackCollectionSO == null) + if (Loader.CustomLevelsRepository == null) { return false; } - __instance._customLevelPacks = Loader.CustomBeatmapLevelPackCollectionSO.beatmapLevelPacks; - IEnumerable? packs = null; + __instance._customLevelPacks = Loader.CustomLevelsRepository.beatmapLevelPacks; + IEnumerable? packs = null; if (__instance._ostBeatmapLevelPacks != null) { packs = __instance._ostBeatmapLevelPacks; @@ -62,4 +50,16 @@ private static bool Prefix(LevelFilteringNavigationController __instance) return false; } } + + // TODO: Fixes a bug in game v1.35.0 where it unloads cached custom levels forever. Remove when fixed. + [HarmonyPatch(typeof(BeatmapLevelLoader), nameof(BeatmapLevelLoader.HandleItemWillBeRemovedFromCache))] + internal class BeatmapLevelLoaderHandleItemWillBeRemovedFromCachePatch + { + private static bool Prefix(BeatmapLevelLoader __instance, string beatmapLevelId) + { + __instance._beatmapLevelDataLoader.TryUnload(beatmapLevelId); + + return false; + } + } } \ No newline at end of file diff --git a/source/SongCore/HarmonyPatches/MainSystemsInitRefreshablePatch.cs b/source/SongCore/HarmonyPatches/MainSystemsInitRefreshablePatch.cs index 9ed3274..c1fec38 100644 --- a/source/SongCore/HarmonyPatches/MainSystemsInitRefreshablePatch.cs +++ b/source/SongCore/HarmonyPatches/MainSystemsInitRefreshablePatch.cs @@ -39,7 +39,7 @@ public void Initialize() Loader.SongsLoadedEvent += Loader_SongsLoadedEvent; } - private void Loader_SongsLoadedEvent(Loader _, ConcurrentDictionary __) + private void Loader_SongsLoadedEvent(Loader _, ConcurrentDictionary __) { didChangeEvent?.Invoke(); } diff --git a/source/SongCore/HarmonyPatches/NegativeNjsPatch.cs b/source/SongCore/HarmonyPatches/NegativeNjsPatch.cs index a413f63..a674eaf 100644 --- a/source/SongCore/HarmonyPatches/NegativeNjsPatch.cs +++ b/source/SongCore/HarmonyPatches/NegativeNjsPatch.cs @@ -1,3 +1,4 @@ +using System.Linq; using HarmonyLib; namespace SongCore.HarmonyPatches @@ -9,7 +10,8 @@ internal class AllowNegativeNjsValuesPatch private static void Prefix(ref float startNoteJumpMovementSpeed) { if (!BS_Utils.Plugin.LevelData.IsSet) return; - var mapNjs = BS_Utils.Plugin.LevelData.GameplayCoreSceneSetupData.difficultyBeatmap.noteJumpMovementSpeed; + 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; } diff --git a/source/SongCore/HarmonyPatches/StandardLevelDetailViewControllerPatch.cs b/source/SongCore/HarmonyPatches/StandardLevelDetailViewControllerPatch.cs index 92d08e6..79b853b 100644 --- a/source/SongCore/HarmonyPatches/StandardLevelDetailViewControllerPatch.cs +++ b/source/SongCore/HarmonyPatches/StandardLevelDetailViewControllerPatch.cs @@ -3,9 +3,9 @@ using System.Linq; using System.Reflection; using System.Reflection.Emit; +using BGLib.Polyglot; using HarmonyLib; using MonoMod.Utils; -using Polyglot; using SongCore.Utilities; namespace SongCore.HarmonyPatches diff --git a/source/SongCore/HarmonyPatches/StandardLevelDetailViewRefreshContent.cs b/source/SongCore/HarmonyPatches/StandardLevelDetailViewRefreshContent.cs index 9540c14..ffa9d30 100644 --- a/source/SongCore/HarmonyPatches/StandardLevelDetailViewRefreshContent.cs +++ b/source/SongCore/HarmonyPatches/StandardLevelDetailViewRefreshContent.cs @@ -8,14 +8,14 @@ namespace SongCore.HarmonyPatches { [HarmonyPatch(typeof(StandardLevelDetailView))] - [HarmonyPatch(nameof(StandardLevelDetailView.RefreshContent), MethodType.Normal)] + [HarmonyPatch(nameof(StandardLevelDetailView.CheckIfBeatmapLevelDataExists), MethodType.Normal)] internal class StandardLevelDetailViewRefreshContent { private static readonly Dictionary LevelLabels = new Dictionary(); public static readonly OverrideLabels currentLabels = new OverrideLabels(); - private static IPreviewBeatmapLevel lastLevel; + private static BeatmapLevel lastLevel; public class OverrideLabels { @@ -47,15 +47,14 @@ internal static void ClearOverrideLabels() private static void Postfix(StandardLevelDetailView __instance) { var firstSelection = false; - var selectedDifficultyBeatmap = __instance.selectedDifficultyBeatmap; + var beatmapLevel = __instance._beatmapLevel; var actionButton = __instance.actionButton; var practiceButton = __instance.practiceButton; - var level = selectedDifficultyBeatmap.level is CustomBeatmapLevel ? selectedDifficultyBeatmap.level as CustomPreviewBeatmapLevel : null; - if (level != lastLevel) + if (beatmapLevel != lastLevel) { firstSelection = true; - lastLevel = level; + lastLevel = beatmapLevel; } actionButton.interactable = true; @@ -63,12 +62,12 @@ private static void Postfix(StandardLevelDetailView __instance) RequirementsUI.instance.ButtonGlowColor = false; RequirementsUI.instance.ButtonInteractable = false; - if (level == null) + if (beatmapLevel.hasPrecalculatedData) { return; } - var songData = Collections.RetrieveExtraSongData(Hashing.GetCustomLevelHash(level)); + var songData = Collections.RetrieveExtraSongData(Hashing.GetCustomLevelHash(beatmapLevel)); if (songData == null) { @@ -78,7 +77,7 @@ private static void Postfix(StandardLevelDetailView __instance) } var wipFolderSong = false; - var diffData = Collections.RetrieveDifficultyData(selectedDifficultyBeatmap); + var diffData = Collections.RetrieveDifficultyData(beatmapLevel, __instance.beatmapKey); if (diffData != null) { @@ -110,7 +109,7 @@ private static void Postfix(StandardLevelDetailView __instance) } } - if (level.levelID.EndsWith(" WIP", StringComparison.Ordinal)) + if (beatmapLevel.levelID.EndsWith(" WIP", StringComparison.Ordinal)) { RequirementsUI.instance.ButtonGlowColor = true; RequirementsUI.instance.ButtonInteractable = true; @@ -132,7 +131,7 @@ private static void Postfix(StandardLevelDetailView __instance) } } - if (selectedDifficultyBeatmap.parentDifficultyBeatmapSet.beatmapCharacteristic.serializedName == "MissingCharacteristic") + if (__instance.beatmapKey.beatmapCharacteristic.serializedName == "MissingCharacteristic") { actionButton.interactable = false; practiceButton.interactable = false; @@ -140,8 +139,8 @@ private static void Postfix(StandardLevelDetailView __instance) RequirementsUI.instance.ButtonInteractable = true; } - RequirementsUI.instance.level = level; - RequirementsUI.instance.difficultyBeatmap = selectedDifficultyBeatmap; + RequirementsUI.instance.beatmapLevel = beatmapLevel; + RequirementsUI.instance.beatmapKey = __instance.beatmapKey; RequirementsUI.instance.songData = songData; RequirementsUI.instance.diffData = diffData; RequirementsUI.instance.wipFolder = wipFolderSong; @@ -154,7 +153,7 @@ private static void Postfix(StandardLevelDetailView __instance) { var difficulty = diffLevel._difficulty; string characteristic = diffLevel._beatmapCharacteristicName; - if (characteristic == selectedDifficultyBeatmap.parentDifficultyBeatmapSet.beatmapCharacteristic.serializedName) + if (characteristic == __instance.beatmapKey.beatmapCharacteristic.serializedName) { currentCharacteristic = characteristic; } @@ -197,8 +196,8 @@ private static void Postfix(StandardLevelDetailView __instance) ClearOverrideLabels(); } - __instance._beatmapDifficultySegmentedControlController.SetData(selectedDifficultyBeatmap.parentDifficultyBeatmapSet.difficultyBeatmaps, - __instance._beatmapDifficultySegmentedControlController.selectedDifficulty); + __instance._beatmapDifficultySegmentedControlController.SetData(beatmapLevel.GetDifficulties(__instance.beatmapKey.beatmapCharacteristic), + __instance._beatmapDifficultySegmentedControlController.selectedDifficulty, __instance._allowedBeatmapDifficultyMask); ClearOverrideLabels(); // TODO: Check if this whole if block is still needed @@ -222,7 +221,7 @@ private static void Postfix(StandardLevelDetailView __instance) { __instance._beatmapCharacteristicSegmentedControlController._segmentedControl .SelectCellWithNumber(index); - __instance._beatmapCharacteristicSegmentedControlController.HandleDifficultySegmentedControlDidSelectCell( + __instance._beatmapCharacteristicSegmentedControlController.HandleBeatmapCharacteristicSegmentedControlDidSelectCell( __instance._beatmapCharacteristicSegmentedControlController._segmentedControl, index); } } diff --git a/source/SongCore/Loader.cs b/source/SongCore/Loader.cs index 3fbd34e..dca795d 100644 --- a/source/SongCore/Loader.cs +++ b/source/SongCore/Loader.cs @@ -31,26 +31,24 @@ public class Loader : MonoBehaviour // Actions for loading and refreshing beatmaps public static event Action? LoadingStartedEvent; - public static event Action>? SongsLoadedEvent; + public static event Action>? SongsLoadedEvent; public static event Action? OnLevelPacksRefreshed; public static event Action? DeletingSong; private static readonly ConcurrentDictionary OfficialSongs = new ConcurrentDictionary(); - private static readonly ConcurrentDictionary CustomLevelsById = new ConcurrentDictionary(); - public static ConcurrentDictionary CustomLevels = new ConcurrentDictionary(); - public static ConcurrentDictionary CustomWIPLevels = new ConcurrentDictionary(); - public static ConcurrentDictionary CachedWIPLevels = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary CustomLevelsById = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary LoadedBeatmapLevelsData = new ConcurrentDictionary(); + public static ConcurrentDictionary CustomLevels = new ConcurrentDictionary(); + public static ConcurrentDictionary CustomWIPLevels = new ConcurrentDictionary(); + public static ConcurrentDictionary CachedWIPLevels = new ConcurrentDictionary(); public static readonly List SeperateSongFolders = new List(); public static Sprite defaultCoverImage; public static Loader Instance; - public static SongCoreCustomLevelCollection? CustomLevelsCollection { get; private set; } - public static SongCoreCustomLevelCollection? WIPLevelsCollection { get; private set; } - public static SongCoreCustomLevelCollection? CachedWIPLevelCollection { get; private set; } public static SongCoreCustomBeatmapLevelPack? CustomLevelsPack { get; private set; } public static SongCoreCustomBeatmapLevelPack? WIPLevelsPack { get; private set; } public static SongCoreCustomBeatmapLevelPack? CachedWIPLevelsPack { get; private set; } - public static SongCoreBeatmapLevelPackCollectionSO? CustomBeatmapLevelPackCollectionSO { get; private set; } + public static SongCoreBeatmapLevelsRepository? CustomLevelsRepository { get; private set; } public static bool AreSongsLoaded { get; private set; } public static bool AreSongsLoading { get; private set; } public static float LoadingProgress { get; private set; } @@ -83,10 +81,10 @@ internal void MenuLoadedFresh() // They'll be destroyed on internal restart. _gameScenesManager = FindObjectOfType(); _levelFilteringNavigationController = FindObjectOfType(true); - BeatmapLevelsModelSO = FindObjectOfType(); + BeatmapLevelsModelSO = _levelFilteringNavigationController._beatmapLevelsModel; CustomLevelLoader = FindObjectOfType(); cachedMediaAsyncLoaderSO = CustomLevelLoader._cachedMediaAsyncLoader; - defaultCoverImage = CustomLevelLoader._defaultPackCover; + defaultCoverImage = FindObjectOfType(true)._defaultCoverSprite; beatmapCharacteristicCollection = CustomLevelLoader._beatmapCharacteristicCollection; BS_Utils.Gameplay.Gamemode.Init(); @@ -133,19 +131,19 @@ private void CancelSongLoading() /// public async void RefreshLevelPacks() { - CustomLevelsCollection?.UpdatePreviewLevels(CustomLevels.Values.OrderBy(l => l.songName).ToArray()); - WIPLevelsCollection?.UpdatePreviewLevels(CustomWIPLevels.Values.OrderBy(l => l.songName).ToArray()); - CachedWIPLevelCollection?.UpdatePreviewLevels(CachedWIPLevels.Values.OrderBy(l => l.songName).ToArray()); + CustomLevelsPack?.UpdateBeatmapLevels(CustomLevels.Values.OrderBy(l => l.songName).ToArray()); + WIPLevelsPack?.UpdateBeatmapLevels(CustomWIPLevels.Values.OrderBy(l => l.songName).ToArray()); + CachedWIPLevelsPack?.UpdateBeatmapLevels(CachedWIPLevels.Values.OrderBy(l => l.songName).ToArray()); - if (CachedWIPLevelsPack != null && CustomBeatmapLevelPackCollectionSO != null) + if (CachedWIPLevelsPack != null && CustomLevelsRepository != null) { if (CachedWIPLevels.Count > 0) { - CustomBeatmapLevelPackCollectionSO.AddLevelPack(CachedWIPLevelsPack); + CustomLevelsRepository.AddLevelPack(CachedWIPLevelsPack); } else if (CachedWIPLevels.Count == 0) { - CustomBeatmapLevelPackCollectionSO.RemoveLevelPack(CachedWIPLevelsPack); + CustomLevelsRepository.RemoveLevelPack(CachedWIPLevelsPack); } } @@ -153,21 +151,21 @@ public async void RefreshLevelPacks() { if (folderEntry.SongFolderEntry.Pack == FolderLevelPack.NewPack) { - folderEntry.LevelCollection.UpdatePreviewLevels(folderEntry.Levels.Values.OrderBy(l => l.songName).ToArray()); - if (CustomBeatmapLevelPackCollectionSO != null && (folderEntry.Levels.Count > 0 || folderEntry is ModSeperateSongFolder { AlwaysShow: true })) + folderEntry.LevelPack.UpdateBeatmapLevels(folderEntry.Levels.Values.OrderBy(l => l.songName).ToArray()); + if (CustomLevelsRepository != null && (folderEntry.Levels.Count > 0 || folderEntry is ModSeperateSongFolder { AlwaysShow: true })) { - CustomBeatmapLevelPackCollectionSO.AddLevelPack(folderEntry.LevelPack); + CustomLevelsRepository.AddLevelPack(folderEntry.LevelPack); } } } - BeatmapLevelsModelSO._customLevelPackCollection = CustomBeatmapLevelPackCollectionSO; - BeatmapLevelsModelSO.UpdateAllLoadedBeatmapLevelPacks(); + BeatmapLevelsModelSO._customLevelsRepository = CustomLevelsRepository; + BeatmapLevelsModelSO._allLoadedBeatmapLevelsRepository = BeatmapLevelsModelSO.CreateAllLoadedBeatmapLevelPacks(); BeatmapLevelsModelSO.UpdateLoadedPreviewLevels(); await UnityMainThreadTaskScheduler.Factory.StartNew(() => { - if (_levelFilteringNavigationController.isActiveAndEnabled) + if (!_loadingCancelled && _levelFilteringNavigationController.isActiveAndEnabled) { _levelFilteringNavigationController.UpdateCustomSongs(); } @@ -217,6 +215,8 @@ private async void RetrieveAllSongs(bool fullRefresh) CustomLevels.Clear(); CustomWIPLevels.Clear(); CachedWIPLevels.Clear(); + LoadedBeatmapLevelsData.Clear(); + BeatmapLevelsModelSO.ClearLoadedBeatmapLevelsCaches(); Collections.LevelHashDictionary.Clear(); Collections.HashLevelDictionary.Clear(); foreach (var folder in SeperateSongFolders) @@ -237,21 +237,21 @@ private async void RetrieveAllSongs(bool fullRefresh) try { - void AddOfficialPackCollection(IBeatmapLevelPackCollection packCollection) + void AddOfficialBeatmapLevelsRepository(BeatmapLevelsRepository levelsRepository) { - foreach (var pack in packCollection.beatmapLevelPacks) + foreach (var pack in levelsRepository.beatmapLevelPacks) { - foreach (var level in pack.beatmapLevelCollection.beatmapLevels) + foreach (var level in pack.beatmapLevels) { - OfficialSongs[level.levelID] = new OfficialSongEntry { LevelPackCollection = packCollection, LevelPack = pack, PreviewBeatmapLevel = level }; + OfficialSongs[level.levelID] = new OfficialSongEntry { LevelsRepository = levelsRepository, LevelPack = pack, BeatmapLevel = level }; } } } OfficialSongs.Clear(); - AddOfficialPackCollection(BeatmapLevelsModelSO.ostAndExtrasPackCollection); - AddOfficialPackCollection(BeatmapLevelsModelSO.dlcBeatmapLevelPackCollection); + AddOfficialBeatmapLevelsRepository(BeatmapLevelsModelSO.ostAndExtrasBeatmapLevelsRepository); + AddOfficialBeatmapLevelsRepository(BeatmapLevelsModelSO.dlcBeatmapLevelsRepository); } catch (Exception ex) { @@ -342,7 +342,7 @@ void AddOfficialPackCollection(IBeatmapLevelPackCollection packCollection) string[] results; try { - results = Directory.GetFiles(folder, "info.dat", SearchOption.TopDirectoryOnly); + results = Directory.GetFiles(folder, CustomLevelPathHelper.kStandardLevelInfoFilename, SearchOption.TopDirectoryOnly); } catch (Exception ex) { @@ -353,7 +353,7 @@ void AddOfficialPackCollection(IBeatmapLevelPackCollection packCollection) if (results.Length == 0) { - Logging.Logger.Notice($"Folder: '{folder}' is missing info.dat files!"); + Logging.Logger.Notice($"Folder: '{folder}' is missing {CustomLevelPathHelper.kStandardLevelInfoFilename} files!"); return; } @@ -369,7 +369,7 @@ void AddOfficialPackCollection(IBeatmapLevelPackCollection packCollection) if (!fullRefresh) { - if (CustomLevels.TryGetValue(songPath, out CustomPreviewBeatmapLevel c)) + if (CustomLevels.TryGetValue(songPath, out BeatmapLevel c)) { if (c != null) { @@ -418,6 +418,14 @@ void AddOfficialPackCollection(IBeatmapLevelPackCollection packCollection) LoadingProgress = (float) Interlocked.Increment(ref processedSongsCount) / songFolders.Count; }); + foreach (var beatmapLevelData in LoadedBeatmapLevelsData) + { + if (!CustomLevelLoader._loadedBeatmapLevelsData.ContainsKey(beatmapLevelData.Key)) + { + CustomLevelLoader._loadedBeatmapLevelsData.Add(beatmapLevelData.Key, beatmapLevelData.Value); + } + } + #endregion #region LoadSeperateFolders @@ -445,7 +453,7 @@ void AddOfficialPackCollection(IBeatmapLevelPackCollection packCollection) string[] results; try { - results = Directory.GetFiles(folder, "info.dat", SearchOption.TopDirectoryOnly); + results = Directory.GetFiles(folder, CustomLevelPathHelper.kStandardLevelInfoFilename, SearchOption.TopDirectoryOnly); } catch (Exception ex) { @@ -456,7 +464,7 @@ void AddOfficialPackCollection(IBeatmapLevelPackCollection packCollection) if (results.Length == 0) { - Logging.Logger.Notice($"Folder: '{folder}' is missing info.dat files!"); + Logging.Logger.Notice($"Folder: '{folder}' is missing {CustomLevelPathHelper.kStandardLevelInfoFilename} files!"); continue; } @@ -571,15 +579,15 @@ void AddOfficialPackCollection(IBeatmapLevelPackCollection packCollection) switch (folderEntry.SongFolderEntry.Pack) { case FolderLevelPack.CustomLevels: - CustomLevels = new ConcurrentDictionary(CustomLevels.Concat(folderEntry.Levels.Where(x => !CustomLevels.ContainsKey(x.Key))) + CustomLevels = new ConcurrentDictionary(CustomLevels.Concat(folderEntry.Levels.Where(x => !CustomLevels.ContainsKey(x.Key))) .ToDictionary(x => x.Key, x => x.Value)); break; case FolderLevelPack.CustomWIPLevels: - CustomWIPLevels = new ConcurrentDictionary(CustomWIPLevels + CustomWIPLevels = new ConcurrentDictionary(CustomWIPLevels .Concat(folderEntry.Levels.Where(x => !CustomWIPLevels.ContainsKey(x.Key))).ToDictionary(x => x.Key, x => x.Value)); break; case FolderLevelPack.CachedWIPLevels: - CachedWIPLevels = new ConcurrentDictionary(CachedWIPLevels + CachedWIPLevels = new ConcurrentDictionary(CachedWIPLevels .Concat(folderEntry.Levels.Where(x => !CachedWIPLevels.ContainsKey(x.Key))).ToDictionary(x => x.Key, x => x.Value)); break; } @@ -588,15 +596,15 @@ void AddOfficialPackCollection(IBeatmapLevelPackCollection packCollection) #endregion //Handle LevelPacks - if (CustomBeatmapLevelPackCollectionSO == null || CustomBeatmapLevelPackCollectionSO.beatmapLevelPacks.Length == 0) + if (CustomLevelsRepository == null || CustomLevelsRepository.beatmapLevelPacks.Count == 0) { - if (CustomBeatmapLevelPackCollectionSO == null) + if (CustomLevelsRepository == null) { - CustomBeatmapLevelPackCollectionSO = await UnityMainThreadTaskScheduler.Factory.StartNew(SongCoreBeatmapLevelPackCollectionSO.CreateNew); + CustomLevelsRepository = await UnityMainThreadTaskScheduler.Factory.StartNew(SongCoreBeatmapLevelsRepository.CreateNew); } else { - CustomBeatmapLevelPackCollectionSO.ClearLevelPacks(); + CustomLevelsRepository.ClearLevelPacks(); } #region CreateLevelPacks @@ -604,20 +612,17 @@ void AddOfficialPackCollection(IBeatmapLevelPackCollection packCollection) // Create level collections and level packs // Add level packs to the custom levels pack collection - CustomLevelsCollection = new SongCoreCustomLevelCollection(CustomLevels.Values.ToArray()); - WIPLevelsCollection = new SongCoreCustomLevelCollection(CustomWIPLevels.Values.ToArray()); - CachedWIPLevelCollection = new SongCoreCustomLevelCollection(CachedWIPLevels.Values.ToArray()); // This creates unity sprites, so it needs to be on the main thread await UnityMainThreadTaskScheduler.Factory.StartNew(() => { - CustomLevelsPack = new SongCoreCustomBeatmapLevelPack(CustomLevelLoader.kCustomLevelPackPrefixId + "CustomLevels", "Custom Levels", defaultCoverImage, CustomLevelsCollection); - WIPLevelsPack = new SongCoreCustomBeatmapLevelPack(CustomLevelLoader.kCustomLevelPackPrefixId + "CustomWIPLevels", "WIP Levels", UI.BasicUI.WIPIcon, WIPLevelsCollection); + CustomLevelsPack = new SongCoreCustomBeatmapLevelPack(CustomLevelLoader.kCustomLevelPackPrefixId + CustomLevelPathHelper.kCustomLevelsDirectoryName, "Custom Levels", defaultCoverImage, CustomLevels.Values.ToArray()); + WIPLevelsPack = new SongCoreCustomBeatmapLevelPack(CustomLevelLoader.kCustomLevelPackPrefixId + "CustomWIPLevels", "WIP Levels", UI.BasicUI.WIPIcon, CustomWIPLevels.Values.ToArray()); CachedWIPLevelsPack = new SongCoreCustomBeatmapLevelPack(CustomLevelLoader.kCustomLevelPackPrefixId + "CachedWIPLevels", "Cached WIP Levels", UI.BasicUI.WIPIcon, - CachedWIPLevelCollection); + CachedWIPLevels.Values.ToArray()); - CustomBeatmapLevelPackCollectionSO.AddLevelPack(CustomLevelsPack); - CustomBeatmapLevelPackCollectionSO.AddLevelPack(WIPLevelsPack); - CustomBeatmapLevelPackCollectionSO.AddLevelPack(CachedWIPLevelsPack); + CustomLevelsRepository.AddLevelPack(CustomLevelsPack); + CustomLevelsRepository.AddLevelPack(WIPLevelsPack); + CustomLevelsRepository.AddLevelPack(CachedWIPLevelsPack); }); #endregion @@ -759,25 +764,13 @@ private void DeleteSingleSong(string folderPath, bool deleteFolder) /// Resulting hash for the beatmap, may contain beatmap folder name or 'WIP' at the end /// Folder entry for beatmap folder /// - public static CustomPreviewBeatmapLevel? LoadSong(StandardLevelInfoSaveData saveData, string songPath, out string hash, SongFolderEntry? folderEntry = null) + public static BeatmapLevel? LoadSong(StandardLevelInfoSaveData saveData, string songPath, out string hash, SongFolderEntry? folderEntry = null) { - var wip = songPath.Contains("CustomWIPLevels"); - if (folderEntry != null) - { - if ((folderEntry.Pack == FolderLevelPack.CustomWIPLevels) || (folderEntry.Pack == FolderLevelPack.CachedWIPLevels)) - { - wip = true; - } - else if (folderEntry.WIP) - { - wip = true; - } - } - - CustomPreviewBeatmapLevel? result; - hash = Hashing.GetCustomLevelHash(saveData, songPath); + var wip = songPath.Contains("CustomWIPLevels") || folderEntry != null && (folderEntry.Pack == FolderLevelPack.CustomWIPLevels || folderEntry.Pack == FolderLevelPack.CachedWIPLevels || folderEntry.WIP); + BeatmapLevel? beatmapLevel; try { + hash = Hashing.GetCustomLevelHash(saveData, songPath); string folderName = new DirectoryInfo(songPath).Name; string levelID = CustomLevelLoader.kCustomLevelPrefixId + hash; // Fixed WIP status for duplicate song hashes @@ -791,90 +784,27 @@ private void DeleteSingleSong(string folderPath, bool deleteFolder) levelID += " WIP"; } - string songName = saveData.songName; - string songSubName = saveData.songSubName; - string songAuthorName = saveData.songAuthorName; - string levelAuthorName = saveData.levelAuthorName; - var beatsPerMinute = saveData.beatsPerMinute; - var songTimeOffset = saveData.songTimeOffset; - var shuffle = saveData.shuffle; - var shufflePeriod = saveData.shufflePeriod; - var previewStartTime = saveData.previewStartTime; - var previewDuration = saveData.previewDuration; - EnvironmentInfoSO environmentSceneInfo = CustomLevelLoader.LoadEnvironmentInfo(saveData.environmentName, false); - EnvironmentInfoSO allDirectionEnvironmentInfo = CustomLevelLoader.LoadEnvironmentInfo(saveData.allDirectionsEnvironmentName, true); - PreviewDifficultyBeatmapSet[] beatmapsets = new PreviewDifficultyBeatmapSet[saveData.difficultyBeatmapSets.Length]; - - for (var i = 0; i < saveData.difficultyBeatmapSets.Length; i++) - { - var difficultyBeatmapSet = saveData.difficultyBeatmapSets[i]; + Collections.LevelPathDictionary.TryAdd(levelID, songPath); - var beatmapCharacteristicBySerializedName = beatmapCharacteristicCollection.GetBeatmapCharacteristicBySerializedName(difficultyBeatmapSet.beatmapCharacteristicName); - var array = new BeatmapDifficulty[difficultyBeatmapSet.difficultyBeatmaps.Length]; - for (var j = 0; j < difficultyBeatmapSet.difficultyBeatmaps.Length; j++) - { - difficultyBeatmapSet.difficultyBeatmaps[j].difficulty.BeatmapDifficultyFromSerializedName(out var beatmapDifficulty); - array[j] = beatmapDifficulty; - } + beatmapLevel = CustomLevelLoader.CreateBeatmapLevelFromV3(songPath, saveData); + Accessors.LevelIDAccessor(ref beatmapLevel) = levelID; - beatmapsets[i] = new PreviewDifficultyBeatmapSet(beatmapCharacteristicBySerializedName, array); - } + LoadedBeatmapLevelsData.TryAdd(beatmapLevel.levelID, CustomLevelLoader.CreateBeatmapLevelDataFromV3(saveData, songPath)); - var overrideColorSchemes = Array.Empty(); - if (saveData.colorSchemes != null) - { - overrideColorSchemes = new ColorScheme[saveData.colorSchemes.Length]; - for (var i = 0; i < saveData.colorSchemes.Length; i++) - { - BeatmapLevelColorSchemeSaveData colorSchemeSaveData = saveData.colorSchemes[i]; - if (colorSchemeSaveData.useOverride) - { - var colorSchemeId = colorSchemeSaveData.colorScheme.colorSchemeId; - var saberAColor = colorSchemeSaveData.colorScheme.saberAColor; - var saberBColor = colorSchemeSaveData.colorScheme.saberBColor; - var environmentColor0 = colorSchemeSaveData.colorScheme.environmentColor0; - var environmentColor1 = colorSchemeSaveData.colorScheme.environmentColor1; - var environmentColor0Boost = colorSchemeSaveData.colorScheme.environmentColor0Boost; - var environmentColor1Boost = colorSchemeSaveData.colorScheme.environmentColor1Boost; - var obstaclesColor = colorSchemeSaveData.colorScheme.obstaclesColor; - overrideColorSchemes[i] = new ColorScheme(colorSchemeId, colorSchemeId, true, colorSchemeId, false, - saberAColor, saberBColor, environmentColor0, environmentColor1, Color.white, true, environmentColor0Boost, environmentColor1Boost, Color.white, obstaclesColor); - } - else - { - overrideColorSchemes[i] = null; - } - } - } - - var environmentInfos = Array.Empty(); - if (saveData.environmentNames != null) - { - environmentInfos = new EnvironmentInfoSO[saveData.environmentNames.Length]; - for (var i = 0; i < saveData.environmentNames.Length; i++) - { - environmentInfos[i] = CustomLevelLoader._environmentSceneInfoCollection.GetEnvironmentInfoBySerializedName(saveData.environmentNames[i]); - } - } - - result = new CustomPreviewBeatmapLevel(defaultCoverImage, saveData, songPath, - cachedMediaAsyncLoaderSO, levelID, songName, songSubName, - songAuthorName, levelAuthorName, beatsPerMinute, songTimeOffset, shuffle, shufflePeriod, previewStartTime, previewDuration, - environmentSceneInfo, allDirectionEnvironmentInfo, environmentInfos, overrideColorSchemes, PlayerSensitivityFlag.Safe, beatmapsets); - - GetSongDuration(result, songPath, Path.Combine(songPath, saveData.songFilename)); + GetSongDuration(saveData, beatmapLevel, songPath, Path.Combine(songPath, saveData.songFilename)); } catch (Exception e) { Logging.Logger.Error($"Failed to Load Song: {songPath}"); Logging.Logger.Error(e); - result = null; + beatmapLevel = null; + hash = null; } - return result; + return beatmapLevel; } - public static CustomPreviewBeatmapLevel? LoadSong(CancellationToken token, StandardLevelInfoSaveData saveData, string songPath, out string hash, SongFolderEntry? folderEntry = null) + public static BeatmapLevel? LoadSong(CancellationToken token, StandardLevelInfoSaveData saveData, string songPath, out string hash, SongFolderEntry? folderEntry = null) { token.ThrowIfCancellationRequested(); return LoadSong(saveData, songPath, out hash, folderEntry); @@ -945,14 +875,14 @@ private static void CacheZIPs(string cachePath, string songFolderPath) /// /// /// - private void LoadCachedZIPs(IEnumerable cacheFolders, bool fullRefresh, ConcurrentDictionary beatmapDictionary, SongFolderEntry? folderEntry = null) + private void LoadCachedZIPs(IEnumerable cacheFolders, bool fullRefresh, ConcurrentDictionary beatmapDictionary, SongFolderEntry? folderEntry = null) { foreach (var cachedFolder in cacheFolders) { string[] results; try { - results = Directory.GetFiles(cachedFolder, "info.dat", SearchOption.AllDirectories); + results = Directory.GetFiles(cachedFolder, CustomLevelPathHelper.kStandardLevelInfoFilename, SearchOption.AllDirectories); } catch (DirectoryNotFoundException) { @@ -962,7 +892,7 @@ private void LoadCachedZIPs(IEnumerable cacheFolders, bool fullRefresh, if (results.Length == 0) { - Logging.Logger.Notice($"Folder: '{cachedFolder}' is missing info.dat files!"); + Logging.Logger.Notice($"Folder: '{cachedFolder}' is missing {CustomLevelPathHelper.kStandardLevelInfoFilename} files!"); continue; } @@ -1021,7 +951,7 @@ private void LoadCachedZIPs(IEnumerable cacheFolders, bool fullRefresh, #region HelperFunctionsLoading - private bool SearchBeatmapInMapPack(ConcurrentDictionary mapPack, string songPath) + private bool SearchBeatmapInMapPack(ConcurrentDictionary mapPack, string songPath) { if (mapPack.TryGetValue(songPath, out var customPreviewBeatmapLevel) && customPreviewBeatmapLevel != null) { @@ -1032,9 +962,9 @@ private bool SearchBeatmapInMapPack(ConcurrentDictionary mapPack, + ConcurrentDictionary mapPack, string songPath, - ConcurrentDictionary separateFolder) + ConcurrentDictionary separateFolder) { if (mapPack.TryGetValue(songPath, out var customPreviewBeatmapLevel) && customPreviewBeatmapLevel != null) { @@ -1047,7 +977,7 @@ private bool AssignBeatmapToSeperateFolder( public SongData? LoadCustomLevelSongData(string customLevelPath) { - var path = Path.Combine(customLevelPath, "Info.dat"); + var path = Path.Combine(customLevelPath, CustomLevelPathHelper.kStandardLevelInfoFilename); if (File.Exists(path)) { var rawSongData = File.ReadAllText(path); @@ -1060,8 +990,9 @@ private bool AssignBeatmapToSeperateFolder( return null; } - private CustomPreviewBeatmapLevel? LoadSongAndAddToDictionaries(CancellationToken token, SongData songData, string songPath, SongFolderEntry? entry = 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) { @@ -1097,24 +1028,24 @@ private bool AssignBeatmapToSeperateFolder( /// /// /// - public static IPreviewBeatmapLevel? GetLevelById(string levelId) + public static BeatmapLevel? GetLevelById(string levelId) { if (string.IsNullOrEmpty(levelId)) { return null; } - IPreviewBeatmapLevel? level = null; + BeatmapLevel? level = null; if (levelId.StartsWith(CustomLevelLoader.kCustomLevelPrefixId, StringComparison.Ordinal)) { - if (CustomLevelsById.TryGetValue(levelId, out CustomPreviewBeatmapLevel customLevel)) + if (CustomLevelsById.TryGetValue(levelId, out BeatmapLevel customLevel)) { level = customLevel; } } else if (OfficialSongs.TryGetValue(levelId, out var song)) { - level = song.PreviewBeatmapLevel; + level = song.BeatmapLevel; } return level; @@ -1125,20 +1056,20 @@ private bool AssignBeatmapToSeperateFolder( /// /// /// - public static CustomPreviewBeatmapLevel? GetLevelByHash(string hash) + public static BeatmapLevel? GetLevelByHash(string hash) { if (string.IsNullOrEmpty(hash)) { return null; } - CustomLevelsById.TryGetValue(CustomLevelLoader.kCustomLevelPrefixId + hash.ToUpperInvariant(), out CustomPreviewBeatmapLevel level); + CustomLevelsById.TryGetValue(CustomLevelLoader.kCustomLevelPrefixId + hash.ToUpperInvariant(), out BeatmapLevel level); return level; } private static readonly BeatmapDataLoader loader = new BeatmapDataLoader(); - private static void GetSongDuration(CustomPreviewBeatmapLevel level, string songPath, string oggFile) + private static void GetSongDuration(StandardLevelInfoSaveData saveData, BeatmapLevel level, string songPath, string oggFile) { try { @@ -1166,8 +1097,9 @@ private static void GetSongDuration(CustomPreviewBeatmapLevel level, string song if (length <= 1) { // janky, but whatever - Logging.Logger.Warn($"Failed to parse song length from Ogg file, Approximating using Map length. Song: {level.customLevelPath}"); - length = GetLengthFromMap(level, songPath); + 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}"); + length = GetLengthFromMap(saveData, level, songPath); } } @@ -1195,11 +1127,11 @@ private static void GetSongDuration(CustomPreviewBeatmapLevel level, string song } } - public static float GetLengthFromMap(CustomPreviewBeatmapLevel level, string songPath) + public static float GetLengthFromMap(StandardLevelInfoSaveData saveData, BeatmapLevel level, string songPath) { - var diff = level.standardLevelInfoSaveData.difficultyBeatmapSets.First().difficultyBeatmaps.Last().beatmapFilename; + var diff = saveData.difficultyBeatmapSets.First().difficultyBeatmaps.Last().beatmapFilename; var saveDataString = File.ReadAllText(Path.Combine(songPath, diff)); - var beatmapSaveData = BeatmapSaveDataVersion3.BeatmapSaveData.DeserializeFromJSONString(saveDataString); + var beatmapSaveData = JsonUtility.FromJson(saveDataString); float highestTime = 0; if (beatmapSaveData.colorNotes.Count > 0) @@ -1211,7 +1143,7 @@ public static float GetLengthFromMap(CustomPreviewBeatmapLevel level, string son highestTime = beatmapSaveData.basicBeatmapEvents.Max(x => x.beat); } - var bpmtimeprocessor = new BeatmapDataLoader.BpmTimeProcessor(level.beatsPerMinute, beatmapSaveData.bpmEvents); + var bpmtimeprocessor = new BpmTimeProcessor(level.beatsPerMinute, beatmapSaveData.bpmEvents); return bpmtimeprocessor.ConvertBeatToTime(highestTime); } @@ -1350,9 +1282,9 @@ public static bool TryGetOfficialLevelById(string levelId, out OfficialSongEntry public struct OfficialSongEntry { - public IBeatmapLevelPackCollection LevelPackCollection; - public IBeatmapLevelPack LevelPack; - public IPreviewBeatmapLevel PreviewBeatmapLevel; + public BeatmapLevelsRepository LevelsRepository; + public BeatmapLevelPack LevelPack; + public BeatmapLevel BeatmapLevel; } } } \ No newline at end of file diff --git a/source/SongCore/OverrideClasses/SongCoreBeatmapLevelPackCollectionSO.cs b/source/SongCore/OverrideClasses/SongCoreBeatmapLevelPackCollectionSO.cs deleted file mode 100644 index 78dce26..0000000 --- a/source/SongCore/OverrideClasses/SongCoreBeatmapLevelPackCollectionSO.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.Collections.Generic; - -namespace SongCore.OverrideClasses -{ - public class SongCoreBeatmapLevelPackCollectionSO : BeatmapLevelPackCollectionSO - { - private readonly List _customBeatmapLevelPacks = new List(); - - public static SongCoreBeatmapLevelPackCollectionSO CreateNew() - { - var newCollection = CreateInstance(); - - newCollection._allBeatmapLevelPacks = new IBeatmapLevelPack[] - { - }; - - - newCollection.UpdateArray(); - return newCollection; - } - - public void AddLevelPack(CustomBeatmapLevelPack pack) - { - if (pack != null && !_customBeatmapLevelPacks.Contains(pack)) - { - _customBeatmapLevelPacks.Add(pack); - UpdateArray(); - } - } - - public void RemoveLevelPack(CustomBeatmapLevelPack pack) - { - if (pack != null && _customBeatmapLevelPacks.Contains(pack)) - { - _customBeatmapLevelPacks.Remove(pack); - UpdateArray(); - } - } - - public void ClearLevelPacks() - { - _customBeatmapLevelPacks.Clear(); - } - - private void UpdateArray() - { - _allBeatmapLevelPacks = _customBeatmapLevelPacks.ToArray(); - } - } -} \ No newline at end of file diff --git a/source/SongCore/OverrideClasses/SongCoreBeatmapLevelsRepository.cs b/source/SongCore/OverrideClasses/SongCoreBeatmapLevelsRepository.cs new file mode 100644 index 0000000..e8ab419 --- /dev/null +++ b/source/SongCore/OverrideClasses/SongCoreBeatmapLevelsRepository.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using IPA.Utilities; + +namespace SongCore.OverrideClasses +{ + public class SongCoreBeatmapLevelsRepository : BeatmapLevelsRepository + { + private static readonly FieldAccessor>.Accessor BeatmapLevelPacksAccessor = + FieldAccessor>.GetAccessor(nameof(_beatmapLevelPacks)); + + private readonly List _customBeatmapLevelPacks = new List(); + + private SongCoreBeatmapLevelsRepository(IReadOnlyList beatmapLevelPacks) : base(beatmapLevelPacks) + { + } + + public static SongCoreBeatmapLevelsRepository CreateNew() + { + return new SongCoreBeatmapLevelsRepository(Array.Empty()); + } + + public void AddLevelPack(BeatmapLevelPack pack) + { + if (pack != null && !_customBeatmapLevelPacks.Contains(pack)) + { + _customBeatmapLevelPacks.Add(pack); + RefreshCollections(); + } + } + + public void RemoveLevelPack(BeatmapLevelPack pack) + { + if (pack != null && _customBeatmapLevelPacks.Contains(pack)) + { + _customBeatmapLevelPacks.Remove(pack); + RefreshCollections(); + } + } + + public void ClearLevelPacks() + { + _customBeatmapLevelPacks.Clear(); + } + + private void RefreshCollections() + { + var that = (BeatmapLevelsRepository)this; + BeatmapLevelPacksAccessor(ref that) = _customBeatmapLevelPacks.ToArray(); + + _idToBeatmapLevelPack.Clear(); + _beatmapLevelIdToBeatmapLevelPackId.Clear(); + _idToBeatmapLevel.Clear(); + + foreach (BeatmapLevelPack beatmapLevelPack in beatmapLevelPacks) + { + _idToBeatmapLevelPack.Add(beatmapLevelPack.packID, beatmapLevelPack); + BeatmapLevel[] beatmapLevels = beatmapLevelPack.beatmapLevels; + foreach (BeatmapLevel beatmapLevel in beatmapLevels) + { + _beatmapLevelIdToBeatmapLevelPackId.Add(beatmapLevel.levelID, beatmapLevelPack.packID); + _idToBeatmapLevel.Add(beatmapLevel.levelID, beatmapLevel); + } + } + } + + } +} \ No newline at end of file diff --git a/source/SongCore/OverrideClasses/SongCoreCustomBeatmapLevelPack.cs b/source/SongCore/OverrideClasses/SongCoreCustomBeatmapLevelPack.cs index ef06b5c..ef3669e 100644 --- a/source/SongCore/OverrideClasses/SongCoreCustomBeatmapLevelPack.cs +++ b/source/SongCore/OverrideClasses/SongCoreCustomBeatmapLevelPack.cs @@ -3,17 +3,17 @@ namespace SongCore.OverrideClasses { - public class SongCoreCustomBeatmapLevelPack : CustomBeatmapLevelPack + public class SongCoreCustomBeatmapLevelPack : BeatmapLevelPack { - public SongCoreCustomBeatmapLevelPack(string packID, string packName, Sprite coverImage, CustomBeatmapLevelCollection customBeatmapLevelCollection, string shortPackName = "") - : base(packID, packName, shortPackName == string.Empty ? packName : shortPackName, Sprite.Create(coverImage.texture, coverImage.rect, coverImage.pivot, coverImage.texture.width), coverImage, customBeatmapLevelCollection) + 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) { } - public void UpdateLevelCollection(CustomBeatmapLevelCollection newLevelCollection) + public void UpdateBeatmapLevels(BeatmapLevel[] beatmapLevels) { - var that = (CustomBeatmapLevelPack) this; - Accessors.BeatmapLevelCollectionAccessor(ref that) = newLevelCollection; + var that = (BeatmapLevelPack)this; + Accessors.BeatmapLevelsAccessor(ref that) = beatmapLevels; } } } \ No newline at end of file diff --git a/source/SongCore/OverrideClasses/SongCoreCustomLevelCollection.cs b/source/SongCore/OverrideClasses/SongCoreCustomLevelCollection.cs deleted file mode 100644 index 54cf667..0000000 --- a/source/SongCore/OverrideClasses/SongCoreCustomLevelCollection.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Generic; -using IPA.Utilities; - -namespace SongCore.OverrideClasses -{ - public class SongCoreCustomLevelCollection : CustomBeatmapLevelCollection - { - private static readonly FieldAccessor>.Accessor CustomPreviewBeatmapLevelsAccessor = - FieldAccessor>.GetAccessor(nameof(_customPreviewBeatmapLevels)); - public SongCoreCustomLevelCollection(CustomPreviewBeatmapLevel[] customPreviewBeatmapLevels) : base(customPreviewBeatmapLevels) - { - } - - public void UpdatePreviewLevels(CustomPreviewBeatmapLevel[] levels) - { - var that = (CustomBeatmapLevelCollection) this; - CustomPreviewBeatmapLevelsAccessor(ref that) = levels; - } - } -} \ No newline at end of file diff --git a/source/SongCore/Plugin.cs b/source/SongCore/Plugin.cs index 492187d..beeef69 100644 --- a/source/SongCore/Plugin.cs +++ b/source/SongCore/Plugin.cs @@ -23,7 +23,7 @@ public class Plugin internal static SConfiguration Configuration { get; private set; } - public static Action? CustomSongPlatformSelectionDidChange; + public static Action? CustomSongPlatformSelectionDidChange; public static string standardCharacteristicName = "Standard"; public static string oneSaberCharacteristicName = "OneSaber"; @@ -88,18 +88,18 @@ private void BSEvents_menuSceneLoadedFresh(ScenesTransitionSetupDataSO data) RequirementsUI.instance.Setup(); } - private void BSEvents_levelSelected(LevelCollectionViewController arg1, IPreviewBeatmapLevel level) + private void BSEvents_levelSelected(LevelCollectionViewController arg1, BeatmapLevel level) { - if (level is CustomPreviewBeatmapLevel customLevel && Collections.RetrieveExtraSongData(Hashing.GetCustomLevelHash(customLevel)) is { } songData) + 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, customLevel); + CustomSongPlatformSelectionDidChange?.Invoke(true, songData._customEnvironmentName, songData._customEnvironmentHash, level); } else { - CustomSongPlatformSelectionDidChange?.Invoke(false, songData._customEnvironmentName, songData._customEnvironmentHash, customLevel); + CustomSongPlatformSelectionDidChange?.Invoke(false, songData._customEnvironmentName, songData._customEnvironmentHash, level); } } } diff --git a/source/SongCore/SongCore.csproj b/source/SongCore/SongCore.csproj index 3aadc8c..b93c62f 100644 --- a/source/SongCore/SongCore.csproj +++ b/source/SongCore/SongCore.csproj @@ -32,6 +32,10 @@ $(BeatSaberDir)\Beat Saber_Data\Managed\BGLib.AppFlow.dll false + + $(BeatSaberDir)\Beat Saber_Data\Managed\BGLib.Polyglot.dll + false + $(BeatSaberDir)\Beat Saber_Data\Managed\BGLib.UnityExtension.dll false @@ -49,6 +53,11 @@ False True + + $(BeatSaberDir)\Beat Saber_Data\Managed\DataModels.dll + false + true + $(BeatSaberDir)\Beat Saber_Data\Managed\HMLib.dll False @@ -67,6 +76,10 @@ False True + + $(BeatSaberDir)\Beat Saber_Data\Managed\MediaLoader.dll + false + $(BeatSaberDir)\Beat Saber_Data\Managed\Menu.ColorSettings.dll false @@ -103,6 +116,10 @@ $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.InputLegacyModule.dll False + + $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.JSONSerializeModule.dll + false + $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.UI.dll False diff --git a/source/SongCore/UI/ProgressBar.cs b/source/SongCore/UI/ProgressBar.cs index c11ecb8..4dc739f 100644 --- a/source/SongCore/UI/ProgressBar.cs +++ b/source/SongCore/UI/ProgressBar.cs @@ -108,7 +108,7 @@ private void SongLoaderOnLoadingStartedEvent(Loader obj) _canvas.enabled = true; } - private void SongLoaderOnSongsLoadedEvent(Loader loader, ConcurrentDictionary customLevels) + private void SongLoaderOnSongsLoadedEvent(Loader loader, ConcurrentDictionary customLevels) { _showingMessage = false; string songOrSongs = customLevels.Count == 1 ? "song" : "songs"; diff --git a/source/SongCore/UI/RequirementsUI.cs b/source/SongCore/UI/RequirementsUI.cs index c77bde2..4bbc438 100644 --- a/source/SongCore/UI/RequirementsUI.cs +++ b/source/SongCore/UI/RequirementsUI.cs @@ -34,10 +34,10 @@ public class RequirementsUI : NotifiableSingleton internal Sprite? StandardIcon; //Currently selected song data - public CustomPreviewBeatmapLevel level; + public BeatmapLevel beatmapLevel; + public BeatmapKey beatmapKey; public Data.ExtraSongData songData; public Data.ExtraSongData.DifficultyData? diffData; - public IDifficultyBeatmap difficultyBeatmap; public bool wipFolder; [UIComponent("list")] @@ -183,7 +183,10 @@ internal void ShowRequirements() { if (!string.IsNullOrWhiteSpace(author._iconPath)) { - author.icon = Utils.LoadSpriteFromFile(Path.Combine(level.customLevelPath, author._iconPath)); + if (Collections.LevelPathDictionary.TryGetValue(beatmapLevel.levelID, out var customLevelPath)) + { + author.icon = Utils.LoadSpriteFromFile(Path.Combine(customLevelPath, author._iconPath)); + } customListTableData.data.Add(new CustomCellInfo(author._name, author._role, author.icon != null ? author.icon : InfoIcon)); } else @@ -218,9 +221,9 @@ internal void ShowRequirements() var environmentInfoName = songData._environmentNames.ElementAtOrDefault(diffData._environmentNameIdx.Value); if (environmentInfoName != null) { - if (environmentInfoName != level.environmentInfo.serializedName) + if (environmentInfoName != beatmapLevel.GetEnvironmentName(beatmapKey.beatmapCharacteristic, beatmapKey.difficulty)) { - environmentName = Loader.CustomLevelLoader.LoadEnvironmentInfo(environmentInfoName, false).environmentName; + environmentName = Loader.CustomLevelLoader._environmentsListModel.GetEnvironmentInfoBySerializedNameSafe(environmentInfoName).environmentName; } } } @@ -261,8 +264,8 @@ internal void ShowRequirements() if (customListTableData.data.Count > 0) { - if (environmentName == null && difficultyBeatmap != null) - environmentName = difficultyBeatmap.GetEnvironmentInfo().environmentName; + if (environmentName == null && beatmapLevel != null) + environmentName = beatmapLevel.GetEnvironmentName(beatmapKey.beatmapCharacteristic, beatmapKey.difficulty); customListTableData.data.Add(new CustomCellInfo("Environment Info", $"This Map uses the Environment: {environmentName}", EnvironmentIcon)); } diff --git a/source/SongCore/Utilities/Accessors.cs b/source/SongCore/Utilities/Accessors.cs index 091b444..9e3a8f8 100644 --- a/source/SongCore/Utilities/Accessors.cs +++ b/source/SongCore/Utilities/Accessors.cs @@ -1,16 +1,19 @@ -using IPA.Utilities; +using IPA.Utilities; namespace SongCore.Utilities { internal static class Accessors { - internal static readonly FieldAccessor.Accessor SongDurationAccessor = - FieldAccessor.GetAccessor(ReflectionUtil.ToCompilerGeneratedBackingField(nameof(CustomPreviewBeatmapLevel.songDuration))); + internal static readonly FieldAccessor.Accessor LevelIDAccessor = + FieldAccessor.GetAccessor(nameof(BeatmapLevel.levelID)); - internal static readonly FieldAccessor.Accessor PreviewDurationAccessor = - FieldAccessor.GetAccessor(ReflectionUtil.ToCompilerGeneratedBackingField(nameof(CustomPreviewBeatmapLevel.previewDuration))); + internal static readonly FieldAccessor.Accessor SongDurationAccessor = + FieldAccessor.GetAccessor(nameof(BeatmapLevel.songDuration)); - internal static readonly FieldAccessor.Accessor BeatmapLevelCollectionAccessor = - FieldAccessor.GetAccessor(ReflectionUtil.ToCompilerGeneratedBackingField(nameof(CustomBeatmapLevelPack.beatmapLevelCollection))); + internal static readonly FieldAccessor.Accessor PreviewDurationAccessor = + FieldAccessor.GetAccessor(nameof(BeatmapLevel.previewDuration)); + + internal static readonly FieldAccessor.Accessor BeatmapLevelsAccessor = + FieldAccessor.GetAccessor(nameof(BeatmapLevelPack.beatmapLevels)); } } \ No newline at end of file diff --git a/source/SongCore/Utilities/Hashing.cs b/source/SongCore/Utilities/Hashing.cs index 8ec2a84..8225fda 100644 --- a/source/SongCore/Utilities/Hashing.cs +++ b/source/SongCore/Utilities/Hashing.cs @@ -1,4 +1,4 @@ -using SongCore.Data; +using SongCore.Data; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -119,9 +119,16 @@ private static bool GetCachedSongData(string customLevelPath, out long directory return false; } - public static string GetCustomLevelHash(CustomPreviewBeatmapLevel level) + // TODO: See if there's a way to get the path and the sava data from the level. + public static string? GetCustomLevelHash(BeatmapLevel level) { - return GetCustomLevelHash(level.customLevelPath, level.standardLevelInfoSaveData.difficultyBeatmapSets); + if (level.hasPrecalculatedData) + { + return null; + } + + var hash = level.levelID.Split('_')[2]; + return hash.Length == 40 ? hash : null; } public static string GetCustomLevelHash(StandardLevelInfoSaveData level, string customLevelPath) @@ -129,11 +136,6 @@ public static string GetCustomLevelHash(StandardLevelInfoSaveData level, string return GetCustomLevelHash(customLevelPath, level.difficultyBeatmapSets); } - public static string GetCustomLevelHash(CustomBeatmapLevel level) - { - return GetCustomLevelHash(level.customLevelPath, level.standardLevelInfoSaveData.difficultyBeatmapSets); - } - private static string GetCustomLevelHash(string levelPath, StandardLevelInfoSaveData.DifficultyBeatmapSet[] beatmapSets) { if (GetCachedSongData(levelPath, out var directoryHash, out var songHash)) @@ -142,7 +144,7 @@ private static string GetCustomLevelHash(string levelPath, StandardLevelInfoSave } var levelFolder = levelPath + Path.DirectorySeparatorChar; - IEnumerable combinedBytes = File.ReadAllBytes(levelFolder + "info.dat"); + IEnumerable combinedBytes = File.ReadAllBytes(levelFolder + CustomLevelPathHelper.kStandardLevelInfoFilename); foreach(var beatmapSet in beatmapSets) { diff --git a/source/SongCore/manifest.json b/source/SongCore/manifest.json index 16b59b0..d3c16f9 100644 --- a/source/SongCore/manifest.json +++ b/source/SongCore/manifest.json @@ -2,7 +2,7 @@ "$schema": "https://raw.githubusercontent.com/bsmg/BSIPA-MetadataFileSchema/master/Schema.json", "author": "Kyle1413", "description": "A plugin for handling custom song additions in Beat Saber.", - "gameVersion": "1.34.2", + "gameVersion": "1.35.0", "id": "SongCore", "name": "SongCore", "version": "3.12.1",