diff --git a/ScoreSaber/Core/Daemons/UploadDaemon.cs b/ScoreSaber/Core/Daemons/UploadDaemon.cs index 3b812cd..39f3a4b 100644 --- a/ScoreSaber/Core/Daemons/UploadDaemon.cs +++ b/ScoreSaber/Core/Daemons/UploadDaemon.cs @@ -14,6 +14,7 @@ using UnityEngine; using ScoreSaber.Core.Utils; using static ScoreSaber.UI.Leaderboard.ScoreSaberLeaderboardViewController; +using System.Threading; namespace ScoreSaber.Core.Daemons { @@ -29,16 +30,16 @@ internal class UploadDaemon : IDisposable, IUploadDaemon { private readonly LeaderboardService _leaderboardService = null; private readonly PlayerDataModel _playerDataModel = null; - private readonly CustomLevelLoader _customLevelLoader = null; + private readonly MaxScoreCache _maxScoreCache = null; private const string UPLOAD_SECRET = "f0b4a81c9bd3ded1081b365f7628781f"; - public UploadDaemon(PlayerService playerService, LeaderboardService leaderboardService, ReplayService replayService, PlayerDataModel playerDataModel, CustomLevelLoader customLevelLoader) { + public UploadDaemon(PlayerService playerService, LeaderboardService leaderboardService, ReplayService replayService, PlayerDataModel playerDataModel, MaxScoreCache maxScoreCache) { _playerService = playerService; _replayService = replayService; _leaderboardService = leaderboardService; _playerDataModel = playerDataModel; - _customLevelLoader = customLevelLoader; + _maxScoreCache = maxScoreCache; SetupUploader(); Plugin.Log.Debug("Upload service setup!"); @@ -63,13 +64,13 @@ private void SetupUploader() { // Standard uploader public void Three(StandardLevelScenesTransitionSetupDataSO standardLevelScenesTransitionSetupDataSO, LevelCompletionResults levelCompletionResults) { - Five(standardLevelScenesTransitionSetupDataSO.gameMode, standardLevelScenesTransitionSetupDataSO.difficultyBeatmap, levelCompletionResults, standardLevelScenesTransitionSetupDataSO.practiceSettings != null); + Five(standardLevelScenesTransitionSetupDataSO.gameMode, standardLevelScenesTransitionSetupDataSO.beatmapLevel, standardLevelScenesTransitionSetupDataSO.beatmapKey, levelCompletionResults, standardLevelScenesTransitionSetupDataSO.practiceSettings != null); } // Multiplayer uploader public void Four(MultiplayerLevelScenesTransitionSetupDataSO multiplayerLevelScenesTransitionSetupDataSO, MultiplayerResultsData multiplayerResultsData) { - if (multiplayerLevelScenesTransitionSetupDataSO.difficultyBeatmap == null) { + if (multiplayerLevelScenesTransitionSetupDataSO.beatmapLevel == null) { return; } if (multiplayerResultsData.localPlayerResultData.multiplayerLevelCompletionResults.levelCompletionResults == null) { @@ -82,10 +83,10 @@ public void Four(MultiplayerLevelScenesTransitionSetupDataSO multiplayerLevelSce return; } - Five(multiplayerLevelScenesTransitionSetupDataSO.gameMode, multiplayerLevelScenesTransitionSetupDataSO.difficultyBeatmap, multiplayerResultsData.localPlayerResultData.multiplayerLevelCompletionResults.levelCompletionResults, false); + Five(multiplayerLevelScenesTransitionSetupDataSO.gameMode, multiplayerLevelScenesTransitionSetupDataSO.beatmapLevel, multiplayerLevelScenesTransitionSetupDataSO.beatmapKey, multiplayerResultsData.localPlayerResultData.multiplayerLevelCompletionResults.levelCompletionResults, false); } - public void Five(string gameMode, IDifficultyBeatmap difficultyBeatmap, LevelCompletionResults levelCompletionResults, bool practicing) { + public void Five(string gameMode, BeatmapLevel beatmapLevel, BeatmapKey beatmapKey, LevelCompletionResults levelCompletionResults, bool practicing) { try { if (Plugin.ReplayState.IsPlaybackEnabled) { return; } @@ -97,7 +98,7 @@ public void Five(string gameMode, IDifficultyBeatmap difficultyBeatmap, LevelCom } if (gameMode == "Solo" || gameMode == "Multiplayer") { - Plugin.Log.Debug($"Starting upload process for {difficultyBeatmap.level.levelID}:{difficultyBeatmap.level.songName}"); + Plugin.Log.Debug($"Starting upload process for {beatmapKey.levelId}:{beatmapLevel.songName}"); if (practicing) { // If practice write replay at this point _replayService.WriteSerializedReplay().RunTask(); @@ -115,7 +116,7 @@ public void Five(string gameMode, IDifficultyBeatmap difficultyBeatmap, LevelCom _replayService.WriteSerializedReplay().RunTask(); return; } - Six(difficultyBeatmap, levelCompletionResults); + Six(beatmapLevel, beatmapKey, levelCompletionResults); } } catch (Exception ex) { UploadStatusChanged?.Invoke(UploadStatus.Error, "Failed to upload score, error written to log."); @@ -124,13 +125,10 @@ public void Five(string gameMode, IDifficultyBeatmap difficultyBeatmap, LevelCom } //This starts the upload processs - async void Six(IDifficultyBeatmap difficultyBeatmap, LevelCompletionResults levelCompletionResults) { + async void Six(BeatmapLevel beatmapLevel, BeatmapKey beatmapKey, LevelCompletionResults levelCompletionResults) { - if (difficultyBeatmap.level is CustomBeatmapLevel) { - var defaultEnvironment = _customLevelLoader.LoadEnvironmentInfo(null, false); - - var beatmapData = await difficultyBeatmap.GetBeatmapDataAsync(defaultEnvironment, _playerDataModel.playerData.playerSpecificSettings); - int maxScore = ScoreModel.ComputeMaxMultipliedScoreForBeatmap(beatmapData); + if (!beatmapLevel.hasPrecalculatedData) { + int maxScore = await _maxScoreCache.GetMaxScore(beatmapLevel, beatmapKey); if (levelCompletionResults.multipliedScore > maxScore) { UploadStatusChanged?.Invoke(UploadStatus.Error, "Failed to upload (score was impossible)"); @@ -140,7 +138,7 @@ async void Six(IDifficultyBeatmap difficultyBeatmap, LevelCompletionResults leve try { UploadStatusChanged?.Invoke(UploadStatus.Packaging, "Packaging score..."); - ScoreSaberUploadData data = ScoreSaberUploadData.Create(difficultyBeatmap, levelCompletionResults, _playerService.localPlayerInfo, GetVersionHash()); + ScoreSaberUploadData data = ScoreSaberUploadData.Create(beatmapLevel, beatmapKey, levelCompletionResults, _playerService.localPlayerInfo, GetVersionHash()); string scoreData = JsonConvert.SerializeObject(data); // TODO: Simplify now that we're open source @@ -151,7 +149,7 @@ async void Six(IDifficultyBeatmap difficultyBeatmap, LevelCompletionResults leve .ToLower(); string scoreDataHex = BitConverter.ToString(Swap(Encoding.UTF8.GetBytes(scoreData), Encoding.UTF8.GetBytes(key))).Replace("-", ""); - Seven(data, scoreDataHex, difficultyBeatmap, levelCompletionResults).RunTask(); + Seven(data, scoreDataHex, beatmapLevel, beatmapKey, levelCompletionResults).RunTask(); } catch (Exception ex) { UploadStatusChanged?.Invoke(UploadStatus.Error, "Failed to upload score, error written to log."); Plugin.Log.Error($"Failed to upload score: {ex}"); @@ -159,7 +157,7 @@ async void Six(IDifficultyBeatmap difficultyBeatmap, LevelCompletionResults leve } } - public async Task Seven(ScoreSaberUploadData scoreSaberUploadData, string uploadData, IDifficultyBeatmap difficultyBeatmap, LevelCompletionResults results) { + public async Task Seven(ScoreSaberUploadData scoreSaberUploadData, string uploadData, BeatmapLevel beatmapLevel, BeatmapKey beatmapKey, LevelCompletionResults results) { try { UploadStatusChanged?.Invoke(UploadStatus.Packaging, "Packaging replay..."); byte[] serializedReplay = await _replayService.WriteSerializedReplay(); @@ -167,7 +165,7 @@ public async Task Seven(ScoreSaberUploadData scoreSaberUploadData, string upload UploadStatusChanged?.Invoke(UploadStatus.Packaging, "Checking leaderboard ranked status..."); bool ranked = true; - Leaderboard currentLeaderboard = await _leaderboardService.GetCurrentLeaderboard(difficultyBeatmap); + Leaderboard currentLeaderboard = await _leaderboardService.GetCurrentLeaderboard(beatmapKey); if (currentLeaderboard != null) { ranked = currentLeaderboard.leaderboardInfo.ranked; @@ -243,7 +241,7 @@ public async Task Seven(ScoreSaberUploadData scoreSaberUploadData, string upload } if (done && !failed) { - SaveLocalReplay(scoreSaberUploadData, difficultyBeatmap, serializedReplay); + SaveLocalReplay(scoreSaberUploadData, beatmapKey, serializedReplay); Plugin.Log.Info("Score uploaded!"); UploadStatusChanged?.Invoke(UploadStatus.Success, $"Score uploaded!"); } @@ -260,7 +258,7 @@ public async Task Seven(ScoreSaberUploadData scoreSaberUploadData, string upload } } - private void SaveLocalReplay(ScoreSaberUploadData scoreSaberUploadData, IDifficultyBeatmap difficultyBeatmap, byte[] replay) { + private void SaveLocalReplay(ScoreSaberUploadData scoreSaberUploadData, BeatmapKey beatmapKey, byte[] replay) { if (replay == null) { Plugin.Log.Error("Failed to write local replay; replay is null"); @@ -269,7 +267,7 @@ private void SaveLocalReplay(ScoreSaberUploadData scoreSaberUploadData, IDifficu try { if (Plugin.Settings.saveLocalReplays) { - string replayPath = $@"{Settings.replayPath}\{scoreSaberUploadData.playerId}-{scoreSaberUploadData.songName.ReplaceInvalidChars().Truncate(155)}-{difficultyBeatmap.difficulty.SerializedName()}-{difficultyBeatmap.parentDifficultyBeatmapSet.beatmapCharacteristic.serializedName}-{scoreSaberUploadData.leaderboardId}.dat"; + string replayPath = $@"{Settings.replayPath}\{scoreSaberUploadData.playerId}-{scoreSaberUploadData.songName.ReplaceInvalidChars().Truncate(155)}-{beatmapKey.difficulty.SerializedName()}-{beatmapKey.beatmapCharacteristic.serializedName}-{scoreSaberUploadData.leaderboardId}.dat"; File.WriteAllBytes(replayPath, replay); } } catch (Exception ex) { diff --git a/ScoreSaber/Core/Data/Models/LeaderboardUploadData.cs b/ScoreSaber/Core/Data/Models/LeaderboardUploadData.cs index e7c3604..e95842e 100644 --- a/ScoreSaber/Core/Data/Models/LeaderboardUploadData.cs +++ b/ScoreSaber/Core/Data/Models/LeaderboardUploadData.cs @@ -1,7 +1,9 @@ -using Newtonsoft.Json; +using HarmonyLib; +using Newtonsoft.Json; using ScoreSaber.Core.Utils; using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; namespace ScoreSaber.Core.Data.Models { @@ -50,20 +52,20 @@ internal class ScoreSaberUploadData { [JsonProperty("deviceControllerRightIdentifier")] internal string deviceControllerRightIdentifier; - internal static ScoreSaberUploadData Create(IDifficultyBeatmap difficultyBeatmap, LevelCompletionResults results, LocalPlayerInfo playerInfo, string infoHash) { + internal static ScoreSaberUploadData Create(BeatmapLevel beatmapLevel, BeatmapKey beatmapKey, LevelCompletionResults results, LocalPlayerInfo playerInfo, string infoHash) { ScoreSaberUploadData data = new ScoreSaberUploadData(); - string[] levelInfo = difficultyBeatmap.level.levelID.Split('_'); - data.gameMode = $"Solo{difficultyBeatmap.parentDifficultyBeatmapSet.beatmapCharacteristic.serializedName}"; - data.difficulty = BeatmapDifficultyMethods.DefaultRating(difficultyBeatmap.difficulty); + string[] levelInfo = beatmapKey.levelId.Split('_'); + data.gameMode = $"Solo{beatmapKey.beatmapCharacteristic.serializedName}"; + data.difficulty = BeatmapDifficultyMethods.DefaultRating(beatmapKey.difficulty); data.infoHash = infoHash; data.leaderboardId = levelInfo[2]; - data.songName = difficultyBeatmap.level.songName; - data.songSubName = difficultyBeatmap.level.songSubName; - data.songAuthorName = difficultyBeatmap.level.songAuthorName; - data.levelAuthorName = difficultyBeatmap.level.levelAuthorName; - data.bpm = Convert.ToInt32(difficultyBeatmap.level.beatsPerMinute); + data.songName = beatmapLevel.songName; + data.songSubName = beatmapLevel.songSubName; + data.songAuthorName = beatmapLevel.songAuthorName; + data.levelAuthorName = friendlyLevelAuthorName(beatmapLevel.allMappers, beatmapLevel.allLighters); + data.bpm = Convert.ToInt32(beatmapLevel.beatsPerMinute); data.playerName = playerInfo.playerName; data.playerId = playerInfo.playerId; @@ -81,5 +83,16 @@ internal static ScoreSaberUploadData Create(IDifficultyBeatmap difficultyBeatmap data.deviceControllerRightIdentifier = VRDevices.GetDeviceControllerRight(); return data; } + + static string friendlyLevelAuthorName(string[] mappers, string[] lighters) { + List mappersAndLighters = new List(); + mappersAndLighters.AddRange(mappers); + mappersAndLighters.AddRange(lighters); + + if(mappersAndLighters.Count <= 1) { + return mappersAndLighters.FirstOrDefault(); + } + return $"{string.Join(", ", mappersAndLighters.Take(mappersAndLighters.Count - 1))} & {mappersAndLighters.Last()}"; + } } } \ No newline at end of file diff --git a/ScoreSaber/Core/Data/Wrappers/LeaderboardInfoMap.cs b/ScoreSaber/Core/Data/Wrappers/LeaderboardInfoMap.cs index efde1c3..848a672 100644 --- a/ScoreSaber/Core/Data/Wrappers/LeaderboardInfoMap.cs +++ b/ScoreSaber/Core/Data/Wrappers/LeaderboardInfoMap.cs @@ -3,13 +3,15 @@ namespace ScoreSaber.Core.Data.Wrappers { internal class LeaderboardInfoMap { internal LeaderboardInfo leaderboardInfo { get; set; } - internal IDifficultyBeatmap difficultyBeatmap { get; set; } + internal BeatmapLevel beatmapLevel { get; set; } + internal BeatmapKey beatmapKey { get; set; } internal string songHash { get; set; } - internal LeaderboardInfoMap(LeaderboardInfo leaderboardInfo, IDifficultyBeatmap difficultyBeatmap) { - this.difficultyBeatmap = difficultyBeatmap; + internal LeaderboardInfoMap(LeaderboardInfo leaderboardInfo, BeatmapLevel beatmapLevel, BeatmapKey beatmapKey) { + this.beatmapLevel = beatmapLevel; + this.beatmapKey = beatmapKey; this.leaderboardInfo = leaderboardInfo; - this.songHash = difficultyBeatmap.level.levelID.Split('_')[2]; + this.songHash = beatmapKey.levelId.Split('_')[2]; } } } diff --git a/ScoreSaber/Core/Data/Wrappers/LeaderboardMap.cs b/ScoreSaber/Core/Data/Wrappers/LeaderboardMap.cs index a653b2b..b078f87 100644 --- a/ScoreSaber/Core/Data/Wrappers/LeaderboardMap.cs +++ b/ScoreSaber/Core/Data/Wrappers/LeaderboardMap.cs @@ -6,11 +6,11 @@ internal class LeaderboardMap { internal LeaderboardInfoMap leaderboardInfoMap { get; set; } internal ScoreMap[] scores { get; set; } - internal LeaderboardMap(Leaderboard leaderboard, IDifficultyBeatmap difficultyBeatmap, IReadonlyBeatmapData beatmapData) { - this.leaderboardInfoMap = new LeaderboardInfoMap(leaderboard.leaderboardInfo, difficultyBeatmap); + internal LeaderboardMap(Leaderboard leaderboard, BeatmapLevel beatmapLevel, BeatmapKey beatmapKey, int maxMultipliedScore) { + this.leaderboardInfoMap = new LeaderboardInfoMap(leaderboard.leaderboardInfo, beatmapLevel, beatmapKey); this.scores = new ScoreMap[leaderboard.scores.Length]; for (int i = 0; i < leaderboard.scores.Length; i++) { - this.scores[i] = new ScoreMap(leaderboard.scores[i], this.leaderboardInfoMap, beatmapData); + this.scores[i] = new ScoreMap(leaderboard.scores[i], this.leaderboardInfoMap, maxMultipliedScore); } } diff --git a/ScoreSaber/Core/Data/Wrappers/ScoreMap.cs b/ScoreSaber/Core/Data/Wrappers/ScoreMap.cs index 2e26a36..3653f21 100644 --- a/ScoreSaber/Core/Data/Wrappers/ScoreMap.cs +++ b/ScoreSaber/Core/Data/Wrappers/ScoreMap.cs @@ -14,7 +14,7 @@ internal class ScoreMap { internal GameplayModifiers gameplayModifiers { get; set; } internal string formattedPlayerName { get; set; } - internal ScoreMap(Score _score, LeaderboardInfoMap customLeaderboardInfo, IReadonlyBeatmapData beatmapData) { + internal ScoreMap(Score _score, LeaderboardInfoMap customLeaderboardInfo, int maxMultipliedScore) { score = _score; GameplayModifiersMap replayMods = new GameplayModifiersMap(); @@ -26,10 +26,10 @@ internal ScoreMap(Score _score, LeaderboardInfoMap customLeaderboardInfo, IReado } } - double maxScore = ScoreModel.ComputeMaxMultipliedScoreForBeatmap(beatmapData) * replayMods.totalMultiplier; + double maxScore = maxMultipliedScore * replayMods.totalMultiplier; this.parent = customLeaderboardInfo; - this.hasLocalReplay = LeaderboardUtils.LocalReplayExists(customLeaderboardInfo.difficultyBeatmap, this); + this.hasLocalReplay = LeaderboardUtils.LocalReplayExists(customLeaderboardInfo.beatmapLevel, customLeaderboardInfo.beatmapKey, this); this.score.weight = Math.Round(score.weight * 100, 2); this.score.pp = Math.Round(score.pp, 2); this.accuracy = Math.Round((score.modifiedScore / maxScore) * 100, 2); diff --git a/ScoreSaber/Core/MainInstaller.cs b/ScoreSaber/Core/MainInstaller.cs index cd8ed56..a494c2d 100644 --- a/ScoreSaber/Core/MainInstaller.cs +++ b/ScoreSaber/Core/MainInstaller.cs @@ -2,6 +2,7 @@ using ScoreSaber.Core.ReplaySystem; using ScoreSaber.Core.ReplaySystem.UI; using ScoreSaber.Core.Services; +using ScoreSaber.Core.Utils; using ScoreSaber.Patches; using ScoreSaber.UI.Elements.Leaderboard; using ScoreSaber.UI.Elements.Profile; @@ -26,6 +27,8 @@ public override void InstallBindings() { Container.Bind().AsSingle(); Container.Bind().AsSingle(); Container.Bind().AsSingle(); + + Container.Bind().AsSingle(); Container.Bind().FromNewComponentAsViewController().AsSingle(); diff --git a/ScoreSaber/Core/ReplaySystem/Accessors.cs b/ScoreSaber/Core/ReplaySystem/Accessors.cs index 3c95ceb..65724c6 100644 --- a/ScoreSaber/Core/ReplaySystem/Accessors.cs +++ b/ScoreSaber/Core/ReplaySystem/Accessors.cs @@ -66,7 +66,6 @@ internal static class Accessors internal static readonly PropertyAccessor.Setter SetCircleVisibility = PropertyAccessor.GetSetter("showCircle"); internal static readonly FieldAccessor.Accessor NoteMaterialBlocks = FieldAccessor.GetAccessor("_materialPropertyBlockControllers"); - internal static readonly FieldAccessor.Accessor resultsViewControllerDifficultyBeatmap = FieldAccessor.GetAccessor("_difficultyBeatmap"); internal static readonly FieldAccessor.Accessor resultsViewControllerLevelCompletionResults = FieldAccessor.GetAccessor("_levelCompletionResults"); internal static readonly FieldAccessor.Accessor animateParentCanvas = FieldAccessor.GetAccessor("_animateParentCanvas"); diff --git a/ScoreSaber/Core/ReplaySystem/Recorders/MetadataRecorder.cs b/ScoreSaber/Core/ReplaySystem/Recorders/MetadataRecorder.cs index c168fbb..8cdb26a 100644 --- a/ScoreSaber/Core/ReplaySystem/Recorders/MetadataRecorder.cs +++ b/ScoreSaber/Core/ReplaySystem/Recorders/MetadataRecorder.cs @@ -49,9 +49,9 @@ public Metadata Export() { return new Metadata() { Version = "3.0.0", - LevelID = _gameplayCoreSceneSetupData.difficultyBeatmap.level.levelID, - Difficulty = BeatmapDifficultyMethods.DefaultRating(_gameplayCoreSceneSetupData.difficultyBeatmap.difficulty), - Characteristic = _gameplayCoreSceneSetupData.difficultyBeatmap.parentDifficultyBeatmapSet.beatmapCharacteristic.serializedName, + LevelID = _gameplayCoreSceneSetupData.beatmapLevel.levelID, + Difficulty = BeatmapDifficultyMethods.DefaultRating(_gameplayCoreSceneSetupData.beatmapKey.difficulty), + Characteristic = _gameplayCoreSceneSetupData.beatmapKey.beatmapCharacteristic.serializedName, Environment = _gameplayCoreSceneSetupData.environmentInfo.serializedName, Modifiers = GetModifierList(_gameplayCoreSceneSetupData.gameplayModifiers), NoteSpawnOffset = _beatmapObjectSpawnControllerInitData.noteJumpValue, diff --git a/ScoreSaber/Core/ReplaySystem/ReplayLoader.cs b/ScoreSaber/Core/ReplaySystem/ReplayLoader.cs index 919194e..ab49a78 100644 --- a/ScoreSaber/Core/ReplaySystem/ReplayLoader.cs +++ b/ScoreSaber/Core/ReplaySystem/ReplayLoader.cs @@ -4,6 +4,7 @@ using ScoreSaber.Core.Utils; using System; using System.Collections.Generic; +using System.Data; using System.IO; using System.Linq; using System.Reflection; @@ -18,28 +19,32 @@ internal class ReplayLoader { private readonly MenuTransitionsHelper _menuTransitionsHelper; private readonly StandardLevelScenesTransitionSetupDataSO _standardLevelScenesTransitionSetupDataSO; private readonly ReplayFileReader _replayFileReader; - public ReplayLoader(PlayerDataModel playerDataModel, MenuTransitionsHelper menuTransitionsHelper) { + private readonly EnvironmentsListModel _environmentsListModel; + + public ReplayLoader(PlayerDataModel playerDataModel, MenuTransitionsHelper menuTransitionsHelper, EnvironmentsListModel environmentsListModel) { _playerDataModel = playerDataModel; _menuTransitionsHelper = menuTransitionsHelper; _standardLevelScenesTransitionSetupDataSO = Accessors.StandardLevelScenesTransitionSetupData(ref _menuTransitionsHelper); _replayFileReader = new ReplayFileReader(); + _environmentsListModel = environmentsListModel; } - public async Task Load(byte[] replay, IDifficultyBeatmap difficultyBeatmap, GameplayModifiers modifiers, string playerName) { + public async Task Load(byte[] replay, BeatmapLevel beatmapLevel, BeatmapKey beatmapKey, GameplayModifiers modifiers, string playerName) { - Plugin.ReplayState.CurrentLevel = difficultyBeatmap; + Plugin.ReplayState.CurrentBeatmapLevel = beatmapLevel; + Plugin.ReplayState.CurrentBeatmapKey = beatmapKey; Plugin.ReplayState.CurrentModifiers = modifiers; Plugin.ReplayState.CurrentPlayerName = playerName; if (replay[0] == 93 && replay[1] == 0 && replay[2] == 0 && replay[3] == 128) { - await LoadLegacyReplay(replay, difficultyBeatmap, modifiers); + await LoadLegacyReplay(replay, beatmapLevel, beatmapKey, modifiers); } else { ReplayFile replayFile = await LoadReplay(replay); - await StartReplay(replayFile, difficultyBeatmap); + await StartReplay(replayFile, beatmapLevel, beatmapKey); } } - private async Task LoadLegacyReplay(byte[] replay, IDifficultyBeatmap difficultyBeatmap, GameplayModifiers gameplayModifiers) { + private async Task LoadLegacyReplay(byte[] replay, BeatmapLevel beatmapLevel, BeatmapKey beatmapKey, GameplayModifiers gameplayModifiers) { await Task.Run(async () => { byte[] decompressed = SevenZip.Compression.LZMA.SevenZipHelper.Decompress(replay); BinaryFormatter formatter = new BinaryFormatter(); @@ -62,13 +67,25 @@ await Task.Run(async () => { gameplayModifiers = new GameplayModifiers(); } - ColorScheme beatmapOverrideColorScheme = null; - if (difficultyBeatmap.level is CustomBeatmapLevel selectedBeatmapLevel && difficultyBeatmap is CustomDifficultyBeatmap selectedDifficultyBeatmap) { - beatmapOverrideColorScheme = selectedBeatmapLevel.GetBeatmapLevelColorScheme(selectedDifficultyBeatmap.beatmapColorSchemeIdx); - } - - _menuTransitionsHelper.StartStandardLevel("Replay", difficultyBeatmap, difficultyBeatmap.level, playerData.overrideEnvironmentSettings, - playerData.colorSchemesSettings.GetSelectedColorScheme(), beatmapOverrideColorScheme, gameplayModifiers, playerSettings, null, "Exit Replay", false, false, null, ReplayEnd, null); + _menuTransitionsHelper.StartStandardLevel( + gameMode: "Replay", + beatmapKey: beatmapKey, + beatmapLevel: beatmapLevel, + overrideEnvironmentSettings: playerData.overrideEnvironmentSettings, + overrideColorScheme: playerData.colorSchemesSettings.GetSelectedColorScheme(), + beatmapOverrideColorScheme: beatmapLevel.GetColorScheme(beatmapKey.beatmapCharacteristic, beatmapKey.difficulty), + gameplayModifiers: gameplayModifiers, + playerSpecificSettings: playerSettings, + practiceSettings: null, + environmentsListModel: _environmentsListModel, + backButtonText: "Exit Replay", + useTestNoteCutSoundEffects: false, + startPaused: false, + beforeSceneSwitchCallback: null, + afterSceneSwitchCallback: null, + levelFinishedCallback: ReplayEnd, + levelRestartedCallback: null + ); }); } @@ -105,7 +122,7 @@ private async Task LoadReplay(byte[] replay) { } - private async Task StartReplay(ReplayFile replay, IDifficultyBeatmap difficultyBeatmap) { + private async Task StartReplay(ReplayFile replay, BeatmapLevel beatmapLevel, BeatmapKey beatmapKey) { await Task.Run(() => { Plugin.ReplayState.IsLegacyReplay = false; @@ -125,15 +142,25 @@ await Task.Run(() => { _standardLevelScenesTransitionSetupDataSO.didFinishEvent -= UploadDaemonHelper.ThreeInstance; - ColorScheme beatmapOverrideColorScheme = null; - if (difficultyBeatmap.level is CustomBeatmapLevel selectedBeatmapLevel && difficultyBeatmap is CustomDifficultyBeatmap selectedDifficultyBeatmap) { - beatmapOverrideColorScheme = selectedBeatmapLevel.GetBeatmapLevelColorScheme(selectedDifficultyBeatmap.beatmapColorSchemeIdx); - } - - UnityMainThreadTaskScheduler.Factory.StartNew(() => _menuTransitionsHelper.StartStandardLevel("Replay", difficultyBeatmap, difficultyBeatmap.level, - playerData.overrideEnvironmentSettings, playerData.colorSchemesSettings.GetOverrideColorScheme(), beatmapOverrideColorScheme, - LeaderboardUtils.GetModifierFromStrings(replay.metadata.Modifiers.ToArray(), false).gameplayModifiers, - playerSettings, null, "Exit Replay", false, false, null, ReplayEnd,null)); + _menuTransitionsHelper.StartStandardLevel( + gameMode: "Replay", + beatmapKey: beatmapKey, + beatmapLevel: beatmapLevel, + overrideEnvironmentSettings: playerData.overrideEnvironmentSettings, + overrideColorScheme: playerData.colorSchemesSettings.GetSelectedColorScheme(), + beatmapOverrideColorScheme: beatmapLevel.GetColorScheme(beatmapKey.beatmapCharacteristic, beatmapKey.difficulty), + gameplayModifiers: LeaderboardUtils.GetModifierFromStrings(replay.metadata.Modifiers.ToArray(), false).gameplayModifiers, + playerSpecificSettings: playerSettings, + practiceSettings: null, + environmentsListModel: _environmentsListModel, + backButtonText: "Exit Replay", + useTestNoteCutSoundEffects: false, + startPaused: false, + beforeSceneSwitchCallback: null, + afterSceneSwitchCallback: null, + levelFinishedCallback: ReplayEnd, + levelRestartedCallback: null + ); }); } diff --git a/ScoreSaber/Core/ReplaySystem/ReplayState.cs b/ScoreSaber/Core/ReplaySystem/ReplayState.cs index 9c0c0f1..a8217ba 100644 --- a/ScoreSaber/Core/ReplaySystem/ReplayState.cs +++ b/ScoreSaber/Core/ReplaySystem/ReplayState.cs @@ -6,7 +6,8 @@ namespace ScoreSaber.Core.ReplaySystem internal class ReplayState { // State management - internal IDifficultyBeatmap CurrentLevel; + internal BeatmapLevel CurrentBeatmapLevel; + internal BeatmapKey CurrentBeatmapKey; internal GameplayModifiers CurrentModifiers; internal string CurrentPlayerName; diff --git a/ScoreSaber/Core/ReplaySystem/UI/Legacy/GameReplayUI.cs b/ScoreSaber/Core/ReplaySystem/UI/Legacy/GameReplayUI.cs index 1110197..6975eef 100644 --- a/ScoreSaber/Core/ReplaySystem/UI/Legacy/GameReplayUI.cs +++ b/ScoreSaber/Core/ReplaySystem/UI/Legacy/GameReplayUI.cs @@ -21,8 +21,8 @@ public void Start() { private void CreateReplayUI() { string replayText = string.Format("REPLAY MODE - Watching {0} play {1} - {2} ({3})", Plugin.ReplayState.CurrentPlayerName, - Plugin.ReplayState.CurrentLevel.level.songAuthorName, Plugin.ReplayState.CurrentLevel.level.songName, - Enum.GetName(typeof(BeatmapDifficulty), Plugin.ReplayState.CurrentLevel.difficulty).Replace("ExpertPlus", "Expert+")); + Plugin.ReplayState.CurrentBeatmapLevel.songAuthorName, Plugin.ReplayState.CurrentBeatmapLevel.songName, + Enum.GetName(typeof(BeatmapDifficulty), Plugin.ReplayState.CurrentBeatmapKey.difficulty).Replace("ExpertPlus", "Expert+")); float timeScale = 1f; if (!Plugin.ReplayState.IsLegacyReplay) { diff --git a/ScoreSaber/Core/ReplaySystem/UI/ResultsViewReplayButtonController.cs b/ScoreSaber/Core/ReplaySystem/UI/ResultsViewReplayButtonController.cs index dc3e5ec..9210a75 100644 --- a/ScoreSaber/Core/ReplaySystem/UI/ResultsViewReplayButtonController.cs +++ b/ScoreSaber/Core/ReplaySystem/UI/ResultsViewReplayButtonController.cs @@ -16,7 +16,8 @@ internal class ResultsViewReplayButtonController : IInitializable, IDisposable { protected readonly Button watchReplayButton = null; private ResultsViewController _resultsViewController; - private IDifficultyBeatmap _difficultyBeatmap; + private BeatmapLevel _beatmapLevel; + private BeatmapKey _beatmapKey; private LevelCompletionResults _levelCompletionResults; private readonly PlayerService _playerService; private readonly ReplayLoader _replayLoader; @@ -52,8 +53,9 @@ private void ResultsViewController_didActivateEvent(bool firstActivation, bool a watchReplayButton.transform.localPosition = new Vector2(42.5f, 27f); } watchReplayButton.interactable = _serializedReplay != null; - _difficultyBeatmap = Accessors.resultsViewControllerDifficultyBeatmap(ref _resultsViewController); - _levelCompletionResults = Accessors.resultsViewControllerLevelCompletionResults(ref _resultsViewController); + _beatmapLevel = _resultsViewController._beatmapLevel; + _beatmapKey = _resultsViewController._beatmapKey; + _levelCompletionResults = _resultsViewController._levelCompletionResults; WaitForReplay().RunTask(); } @@ -89,7 +91,7 @@ public void Dispose() { [UIAction("replay-click")] protected void ClickedReplayButton() { - _replayLoader.Load(_serializedReplay, _difficultyBeatmap, _levelCompletionResults.gameplayModifiers, _playerService.localPlayerInfo.playerName).RunTask(); + _replayLoader.Load(_serializedReplay, _beatmapLevel, _beatmapKey, _levelCompletionResults.gameplayModifiers, _playerService.localPlayerInfo.playerName).RunTask(); watchReplayButton.interactable = false; } diff --git a/ScoreSaber/Core/Services/LeaderboardService.cs b/ScoreSaber/Core/Services/LeaderboardService.cs index a32bfb5..9d5ff2f 100644 --- a/ScoreSaber/Core/Services/LeaderboardService.cs +++ b/ScoreSaber/Core/Services/LeaderboardService.cs @@ -3,32 +3,33 @@ using ScoreSaber.Core.Data.Wrappers; using ScoreSaber.Core.Data.Models; using System; +using System.Linq; namespace ScoreSaber.Core.Services { internal class LeaderboardService { public LeaderboardMap currentLoadedLeaderboard = null; + public CustomLevelLoader customLevelLoader = null; + public BeatmapDataLoader beatmapDataLoader = null; public LeaderboardService() { Plugin.Log.Debug("LeaderboardService Setup"); } - public async Task GetLeaderboardData(IDifficultyBeatmap difficultyBeatmap, PlatformLeaderboardsModel.ScoresScope scope, int page, PlayerSpecificSettings playerSpecificSettings, bool filterAroundCountry = false) { + public async Task GetLeaderboardData(int maxMultipliedScore, BeatmapLevel beatmapLevel, BeatmapKey beatmapKey, PlatformLeaderboardsModel.ScoresScope scope, int page, PlayerSpecificSettings playerSpecificSettings, bool filterAroundCountry = false) { - string leaderboardUrl = GetLeaderboardUrl(difficultyBeatmap, scope, page, filterAroundCountry); + string leaderboardUrl = GetLeaderboardUrl(beatmapKey, scope, page, filterAroundCountry); string leaderboardRawData = await Plugin.HttpInstance.GetAsync(leaderboardUrl); Leaderboard leaderboardData = JsonConvert.DeserializeObject(leaderboardRawData); - var beatmapData = await difficultyBeatmap.GetBeatmapDataAsync(difficultyBeatmap.GetEnvironmentInfo(), playerSpecificSettings); - - Plugin.Log.Debug($"Current leaderboard set to: {difficultyBeatmap.level.levelID}:{difficultyBeatmap.level.songName}"); - currentLoadedLeaderboard = new LeaderboardMap(leaderboardData, difficultyBeatmap, beatmapData); + Plugin.Log.Debug($"Current leaderboard set to: {beatmapKey.levelId}:{beatmapLevel.songName}"); + currentLoadedLeaderboard = new LeaderboardMap(leaderboardData, beatmapLevel, beatmapKey, maxMultipliedScore); return currentLoadedLeaderboard; } - public async Task GetCurrentLeaderboard(IDifficultyBeatmap difficultyBeatmap) { + public async Task GetCurrentLeaderboard(BeatmapKey beatmapKey) { - string leaderboardUrl = GetLeaderboardUrl(difficultyBeatmap, PlatformLeaderboardsModel.ScoresScope.Global, 1, false); + string leaderboardUrl = GetLeaderboardUrl(beatmapKey, PlatformLeaderboardsModel.ScoresScope.Global, 1, false); int attempts = 0; while (attempts < 4) { @@ -44,12 +45,12 @@ public async Task GetCurrentLeaderboard(IDifficultyBeatmap difficul return null; } - private string GetLeaderboardUrl(IDifficultyBeatmap difficultyBeatmap, PlatformLeaderboardsModel.ScoresScope scope, int page, bool filterAroundCountry) { + private string GetLeaderboardUrl(BeatmapKey beatmapKey, PlatformLeaderboardsModel.ScoresScope scope, int page, bool filterAroundCountry) { string url = "/game/leaderboard"; - string leaderboardId = difficultyBeatmap.level.levelID.Split('_')[2]; - string gameMode = $"Solo{difficultyBeatmap.parentDifficultyBeatmapSet.beatmapCharacteristic.serializedName}"; - string difficulty = BeatmapDifficultyMethods.DefaultRating(difficultyBeatmap.difficulty).ToString(); + string leaderboardId = beatmapKey.levelId.Split('_')[2]; + string gameMode = $"Solo{beatmapKey.beatmapCharacteristic.serializedName}"; + string difficulty = BeatmapDifficultyMethods.DefaultRating(beatmapKey.difficulty).ToString(); bool hasPage = true; diff --git a/ScoreSaber/Core/Services/PlayerService.cs b/ScoreSaber/Core/Services/PlayerService.cs index 9686e35..f6b24cc 100644 --- a/ScoreSaber/Core/Services/PlayerService.cs +++ b/ScoreSaber/Core/Services/PlayerService.cs @@ -138,10 +138,10 @@ public async Task GetPlayerInfo(string playerId, bool full) { return playerStats; } - public async Task GetReplayData(IDifficultyBeatmap level, int leaderboardId, ScoreMap scoreMap) { + public async Task GetReplayData(BeatmapLevel beatmapLevel, BeatmapKey beatmapKey, int leaderboardId, ScoreMap scoreMap) { if (scoreMap.hasLocalReplay) { - string replayPath = GetReplayPath(scoreMap.parent.songHash, level.difficulty.SerializedName(), level.parentDifficultyBeatmapSet.beatmapCharacteristic.serializedName, scoreMap.score.leaderboardPlayerInfo.id, level.level.songName); + string replayPath = GetReplayPath(scoreMap.parent.songHash, beatmapKey.difficulty.SerializedName(), beatmapKey.beatmapCharacteristic.serializedName, scoreMap.score.leaderboardPlayerInfo.id, beatmapLevel.songName); if (replayPath != null) { return File.ReadAllBytes(replayPath); } diff --git a/ScoreSaber/Core/Utils/LeaderboardUtils.cs b/ScoreSaber/Core/Utils/LeaderboardUtils.cs index 5ae2a9c..6c0948c 100644 --- a/ScoreSaber/Core/Utils/LeaderboardUtils.cs +++ b/ScoreSaber/Core/Utils/LeaderboardUtils.cs @@ -9,24 +9,24 @@ namespace ScoreSaber.Core.Utils { internal static class LeaderboardUtils { - internal static bool LocalReplayExists(IDifficultyBeatmap difficultyBeatmap, ScoreMap score) { + internal static bool LocalReplayExists(BeatmapLevel beatmapLevel, BeatmapKey beatmapKey, ScoreMap score) { - if (File.Exists(GetReplayPath(difficultyBeatmap, score))) { + if (File.Exists(GetReplayPath(beatmapLevel, beatmapKey, score))) { return true; } - if (File.Exists(GetLegacyReplayPath(difficultyBeatmap, score))) { + if (File.Exists(GetLegacyReplayPath(beatmapLevel, beatmapKey, score))) { return true; } return false; } - internal static string GetReplayPath(IDifficultyBeatmap difficultyBeatmap, ScoreMap scoreMap) { - return $@"{Settings.replayPath}\{scoreMap.score.leaderboardPlayerInfo.id}-{difficultyBeatmap.level.songName.ReplaceInvalidChars().Truncate(155)}-{difficultyBeatmap.difficulty.SerializedName()}-{difficultyBeatmap.parentDifficultyBeatmapSet.beatmapCharacteristic.serializedName}-{scoreMap.parent.songHash}.dat"; + internal static string GetReplayPath(BeatmapLevel beatmapLevel, BeatmapKey beatmapKey, ScoreMap scoreMap) { + return $@"{Settings.replayPath}\{scoreMap.score.leaderboardPlayerInfo.id}-{beatmapLevel.songName.ReplaceInvalidChars().Truncate(155)}-{beatmapKey.difficulty.SerializedName()}-{beatmapKey.beatmapCharacteristic.serializedName}-{scoreMap.parent.songHash}.dat"; } - internal static string GetLegacyReplayPath(IDifficultyBeatmap difficultyBeatmap, ScoreMap scoreMap) { - return $@"{Settings.replayPath}\{scoreMap.score.leaderboardPlayerInfo.id}-{difficultyBeatmap.level.songName.ReplaceInvalidChars().Truncate(155)}-{scoreMap.parent.songHash}.dat"; + internal static string GetLegacyReplayPath(BeatmapLevel beatmapLevel, BeatmapKey beatmapKey, ScoreMap scoreMap) { + return $@"{Settings.replayPath}\{scoreMap.score.leaderboardPlayerInfo.id}-{beatmapLevel.songName.ReplaceInvalidChars().Truncate(155)}-{scoreMap.parent.songHash}.dat"; } internal static string GetFormattedName(ScoreMap scoreMap) { diff --git a/ScoreSaber/Core/Utils/MaxScoreCache.cs b/ScoreSaber/Core/Utils/MaxScoreCache.cs new file mode 100644 index 0000000..e50afdf --- /dev/null +++ b/ScoreSaber/Core/Utils/MaxScoreCache.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace ScoreSaber.Core.Utils { + internal class MaxScoreCache { + private readonly BeatmapLevelLoader _beatmapLevelLoader; + private readonly BeatmapDataLoader _beatmapDataLoader; + + + private Dictionary cache = new Dictionary(); + + public MaxScoreCache(BeatmapLevelLoader beatmapLevelLoader, BeatmapDataLoader beatmapDataLoader) { + _beatmapLevelLoader = beatmapLevelLoader; + _beatmapDataLoader = beatmapDataLoader; + } + + public async Task GetMaxScore(BeatmapLevel beatmapLevel, BeatmapKey beatmapKey) { + if (cache.ContainsKey(beatmapKey)) { + return cache[beatmapKey]; + } + + var beatmapLevelData = (await _beatmapLevelLoader.LoadBeatmapLevelDataAsync(beatmapLevel, CancellationToken.None)).beatmapLevelData; + var beatmapData = await _beatmapDataLoader.LoadBeatmapDataAsync(beatmapLevelData, beatmapKey, beatmapLevel.beatsPerMinute, false, null, null, null, false); + int maxScore = ScoreModel.ComputeMaxMultipliedScoreForBeatmap(beatmapData); + + cache[beatmapKey] = maxScore; + return maxScore; + } + } +} diff --git a/ScoreSaber/Patches/LeaderboardPatches.cs b/ScoreSaber/Patches/LeaderboardPatches.cs index 3df2644..c86f17b 100644 --- a/ScoreSaber/Patches/LeaderboardPatches.cs +++ b/ScoreSaber/Patches/LeaderboardPatches.cs @@ -2,6 +2,7 @@ using BeatSaberMarkupLanguage.Components; using HMUI; using IPA.Utilities; +using ScoreSaber.Core.Utils; using ScoreSaber.Extensions; using ScoreSaber.UI.Leaderboard; using SiraUtil.Affinity; @@ -9,31 +10,36 @@ using System.Collections; using System.Collections.Generic; using System.Reflection; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; using Zenject; using static HMUI.IconSegmentedControl; +using static PlatformLeaderboardsModel; using static ScoreSaber.Patches.LeaderboardPatches; namespace ScoreSaber.Patches { internal class LeaderboardPatches : IInitializable, IAffinity { private readonly ScoreSaberLeaderboardViewController _scoresaberLeaderboardViewController; + private readonly BeatmapLevelsModel _beatmapLevelsModel; private PlatformLeaderboardViewController _platformLeaderboardViewController; private int _lastScopeIndex = -1; - public LeaderboardPatches(ScoreSaberLeaderboardViewController scoresaberLeaderboardViewController) { + public LeaderboardPatches(ScoreSaberLeaderboardViewController scoresaberLeaderboardViewController, BeatmapLevelsModel beatmapLevelsModel) { _scoresaberLeaderboardViewController = scoresaberLeaderboardViewController; + _beatmapLevelsModel = beatmapLevelsModel; } public void Initialize() { } [AffinityPatch(typeof(PlatformLeaderboardViewController), nameof(PlatformLeaderboardViewController.Refresh))] [AffinityPrefix] - bool PatchPlatformLeaderboardsRefresh(ref IDifficultyBeatmap ____difficultyBeatmap, ref List ____scores, ref bool ____hasScoresData, ref LeaderboardTableView ____leaderboardTableView, ref int[] ____playerScorePos, ref PlatformLeaderboardsModel.ScoresScope ____scoresScope, ref LoadingControl ____loadingControl) { + bool PatchPlatformLeaderboardsRefresh(ref BeatmapKey ____beatmapKey, ref List ____scores, ref bool ____hasScoresData, ref LeaderboardTableView ____leaderboardTableView, ref int[] ____playerScorePos, ref PlatformLeaderboardsModel.ScoresScope ____scoresScope, ref LoadingControl ____loadingControl) { // clean up cell clickers foreach (var holder in _scoresaberLeaderboardViewController._cellClickingHolders) { CellClicker existingCellClicker = holder?.cellClickerImage?.gameObject?.GetComponent(); @@ -42,14 +48,15 @@ bool PatchPlatformLeaderboardsRefresh(ref IDifficultyBeatmap ____difficultyBeatm } } - if (____difficultyBeatmap.level is CustomBeatmapLevel) { + if (____beatmapKey.levelId.StartsWith("custom_level_")) { ____hasScoresData = false; ____scores.Clear(); ____leaderboardTableView.SetScores(____scores, ____playerScorePos[(int)____scoresScope]); ____loadingControl.ShowLoading(); _scoresaberLeaderboardViewController.isOST = false; - _scoresaberLeaderboardViewController.RefreshLeaderboard(____difficultyBeatmap, ____leaderboardTableView, ____scoresScope, ____loadingControl, Guid.NewGuid().ToString()).RunTask(); + BeatmapLevel beatmapLevel = _beatmapLevelsModel.GetBeatmapLevel(____beatmapKey.levelId); + _scoresaberLeaderboardViewController.RefreshLeaderboard(beatmapLevel, ____beatmapKey, ____leaderboardTableView, ____scoresScope, ____loadingControl, Guid.NewGuid().ToString()).RunTask(); return false; } else { _scoresaberLeaderboardViewController.isOST = true; diff --git a/ScoreSaber/ScoreSaber.csproj b/ScoreSaber/ScoreSaber.csproj index d952941..cb5a0a9 100644 --- a/ScoreSaber/ScoreSaber.csproj +++ b/ScoreSaber/ScoreSaber.csproj @@ -46,6 +46,11 @@ False False + + $(BeatSaberDir)\Beat Saber_Data\Managed\BGNetCore.dll + False + False + $(BeatSaberDir)\Plugins\BSML.dll @@ -81,6 +86,10 @@ $(BeatSaberDir)\Beat Saber_Data\Managed\Core.dll False + + $(BeatSaberDir)\Beat Saber_Data\Managed\DataModels.dll + False + $(BeatSaberDir)\Beat Saber_Data\Managed\GameplayCore.dll False diff --git a/ScoreSaber/UI/Leaderboard/ScoreSaberLeaderboardViewController.cs b/ScoreSaber/UI/Leaderboard/ScoreSaberLeaderboardViewController.cs index 9245826..f40091f 100644 --- a/ScoreSaber/UI/Leaderboard/ScoreSaberLeaderboardViewController.cs +++ b/ScoreSaber/UI/Leaderboard/ScoreSaberLeaderboardViewController.cs @@ -69,6 +69,7 @@ internal class ScoreSaberLeaderboardViewController : IInitializable, IDisposable private readonly LeaderboardService _leaderboardService; private readonly PlayerDataModel _playerDataModel; internal readonly PlatformLeaderboardViewController _platformLeaderboardViewController; + private readonly MaxScoreCache _maxScoreCache; // TODO: Put this somewhere nicer? public enum UploadStatus { @@ -92,7 +93,7 @@ public bool isOST { } } - public ScoreSaberLeaderboardViewController(DiContainer container, PanelView panelView, IUploadDaemon uploadDaemon, ReplayLoader replayLoader, PlayerService playerService, LeaderboardService leaderboardService, PlayerDataModel playerDataModel, PlatformLeaderboardViewController platformLeaderboardViewController, StandardLevelDetailViewController standardLevelDetailViewController, List profilePictureView, List cellClickingViews) { + public ScoreSaberLeaderboardViewController(DiContainer container, PanelView panelView, IUploadDaemon uploadDaemon, ReplayLoader replayLoader, PlayerService playerService, LeaderboardService leaderboardService, PlayerDataModel playerDataModel, PlatformLeaderboardViewController platformLeaderboardViewController, List profilePictureView, List cellClickingViews, MaxScoreCache maxScoreCache) { _container = container; _panelView = panelView; @@ -104,6 +105,7 @@ public ScoreSaberLeaderboardViewController(DiContainer container, PanelView pane _ImageHolders = profilePictureView; _cellClickingHolders = cellClickingViews; _platformLeaderboardViewController = platformLeaderboardViewController; + _maxScoreCache = maxScoreCache; _infoButtons = new EntryHolder(); _scoreDetailView = new ScoreDetailView(); @@ -238,7 +240,7 @@ private void uploadDaemon_UploadStatusChanged(UploadStatus status, string status private CancellationTokenSource cancellationToken; - public async Task RefreshLeaderboard(IDifficultyBeatmap difficultyBeatmap, LeaderboardTableView tableView, PlatformLeaderboardsModel.ScoresScope scope, LoadingControl loadingControl, string refreshId) { + public async Task RefreshLeaderboard(BeatmapLevel beatmapLevel, BeatmapKey beatmapKey, LeaderboardTableView tableView, PlatformLeaderboardsModel.ScoresScope scope, LoadingControl loadingControl, string refreshId) { try { @@ -262,8 +264,6 @@ public async Task RefreshLeaderboard(IDifficultyBeatmap difficultyBeatmap, Leade } cancellationToken = new CancellationTokenSource(); - var beatmapData = await difficultyBeatmap.GetBeatmapDataAsync(difficultyBeatmap.level.environmentInfo, _playerDataModel.playerData.playerSpecificSettings); - if (_playerService.loginStatus == PlayerService.LoginStatus.Error) { SetErrorState(tableView, loadingControl, null, null, "ScoreSaber authentication failed, please restart Beat Saber", false); ByeImages(); @@ -279,7 +279,8 @@ public async Task RefreshLeaderboard(IDifficultyBeatmap difficultyBeatmap, Leade if (_currentLeaderboardRefreshId == refreshId) { - LeaderboardMap leaderboardData = await _leaderboardService.GetLeaderboardData(difficultyBeatmap, scope, leaderboardPage, _playerDataModel.playerData.playerSpecificSettings, _filterAroundCountry); + int maxMultipliedScore = await _maxScoreCache.GetMaxScore(beatmapLevel, beatmapKey); + LeaderboardMap leaderboardData = await _leaderboardService.GetLeaderboardData(maxMultipliedScore, beatmapLevel, beatmapKey, scope, leaderboardPage, _playerDataModel.playerData.playerSpecificSettings, _filterAroundCountry); if (_currentLeaderboardRefreshId != refreshId) { return; // we need to check this again, since some time may have passed due to waiting for leaderboard data } @@ -431,9 +432,9 @@ private async Task StartReplay(ScoreMap score) { try { _panelView.SetPromptInfo("Downloading Replay...", true); - byte[] replay = await _playerService.GetReplayData(score.parent.difficultyBeatmap, score.parent.leaderboardInfo.id, score); + byte[] replay = await _playerService.GetReplayData(score.parent.beatmapLevel, score.parent.beatmapKey, score.parent.leaderboardInfo.id, score); _panelView.SetPromptInfo("Replay downloaded! Unpacking...", true); - await _replayLoader.Load(replay, score.parent.difficultyBeatmap, score.gameplayModifiers, score.score.leaderboardPlayerInfo.name); + await _replayLoader.Load(replay, score.parent.beatmapLevel, score.parent.beatmapKey, score.gameplayModifiers, score.score.leaderboardPlayerInfo.name); _panelView.SetPromptSuccess("Replay Started!", false, 1f); } catch (ReplayVersionException ex) { _panelView.SetPromptError("Unsupported replay version", false); diff --git a/ScoreSaber/UI/Multiplayer/ScoreSaberMultiplayerLevelSelectionLeaderboardFlowManager.cs b/ScoreSaber/UI/Multiplayer/ScoreSaberMultiplayerLevelSelectionLeaderboardFlowManager.cs index a70b799..0d4bd8a 100644 --- a/ScoreSaber/UI/Multiplayer/ScoreSaberMultiplayerLevelSelectionLeaderboardFlowManager.cs +++ b/ScoreSaber/UI/Multiplayer/ScoreSaberMultiplayerLevelSelectionLeaderboardFlowManager.cs @@ -32,16 +32,17 @@ public void Initialize() { private void LevelSelectionNavigationController_didChangeLevelDetailContentEvent(LevelSelectionNavigationController controller, StandardLevelDetailViewController.ContentType contentType) { + /* TODO is there any replacement for this? or is this even needed? if (controller.selectedDifficultyBeatmap == null) { HideLeaderboard(); return; - } + }*/ ShowLeaderboard(); } - private void LevelSelectionNavigationController_didChangeDifficultyBeatmapEvent(LevelSelectionNavigationController _, IDifficultyBeatmap beatmap) { + private void LevelSelectionNavigationController_didChangeDifficultyBeatmapEvent(LevelSelectionNavigationController _) { ShowLeaderboard(); } @@ -63,14 +64,7 @@ private void ShowLeaderboard() { if (!InMulti()) return; - var selected = _levelSelectionNavigationController.selectedDifficultyBeatmap; - if (selected == null) { - - HideLeaderboard(); - return; - } - - _platformLeaderboardViewController.SetData(selected); + _platformLeaderboardViewController.SetData(_levelSelectionNavigationController.beatmapKey); var currentFlowCoordinator = _mainFlowCoordinator.YoungestChildFlowCoordinatorOrSelf(); ReflectionUtil.InvokeMethod(currentFlowCoordinator, "SetRightScreenViewController", _platformLeaderboardViewController, ViewController.AnimationType.In); _serverPlayerListViewController.gameObject.SetActive(false); // This is a bandaid fix, first time startup it gets stuck while animating kinda like the issue we had before (TODO: Fix in 2024) diff --git a/ScoreSaber/UI/Multiplayer/ScoreSaberMultiplayerResultsLeaderboardFlowManager.cs b/ScoreSaber/UI/Multiplayer/ScoreSaberMultiplayerResultsLeaderboardFlowManager.cs index cdb456e..c192359 100644 --- a/ScoreSaber/UI/Multiplayer/ScoreSaberMultiplayerResultsLeaderboardFlowManager.cs +++ b/ScoreSaber/UI/Multiplayer/ScoreSaberMultiplayerResultsLeaderboardFlowManager.cs @@ -12,7 +12,7 @@ internal class ScoreSaberMultiplayerResultsLeaderboardFlowManager : IInitializab private readonly MultiplayerResultsViewController _multiplayerResultsViewController; private readonly PlatformLeaderboardViewController _platformLeaderboardViewController; - private IDifficultyBeatmap _lastCompletedBeatmap; + private BeatmapKey _lastCompletedBeatmapKey; public ScoreSaberMultiplayerResultsLeaderboardFlowManager(ILevelFinisher levelFinisher, MainFlowCoordinator mainFlowCoordinator, MultiplayerResultsViewController multiplayerResultsViewController, PlatformLeaderboardViewController platformLeaderboardViewController) { @@ -35,19 +35,20 @@ private void MultiplayerResultsViewController_didActivateEvent(bool firstActivat if (!(currentFlowCoordinator is GameServerLobbyFlowCoordinator)) return; - _platformLeaderboardViewController.SetData(_lastCompletedBeatmap); + _platformLeaderboardViewController.SetData(_lastCompletedBeatmapKey); ReflectionUtil.InvokeMethod(currentFlowCoordinator, "SetRightScreenViewController", _platformLeaderboardViewController, ViewController.AnimationType.In); } private void MultiplayerResultsViewController_didDeactivateEvent(bool removedFromHierarchy, bool screenSystemDisabling) { - if (removedFromHierarchy || screenSystemDisabling) - _lastCompletedBeatmap = null; + if (removedFromHierarchy || screenSystemDisabling) { + // TODO there used to be some setting _lastCompletedLevel to null + } } private void LevelFinisher_MultiplayerLevelDidFinish(MultiplayerLevelScenesTransitionSetupDataSO transitionSetupData, MultiplayerResultsData _) { - _lastCompletedBeatmap = transitionSetupData.difficultyBeatmap; + _lastCompletedBeatmapKey = transitionSetupData.beatmapKey; } public void Dispose() { diff --git a/ScoreSaber/manifest.json b/ScoreSaber/manifest.json index 2517599..55f4cda 100644 --- a/ScoreSaber/manifest.json +++ b/ScoreSaber/manifest.json @@ -2,15 +2,16 @@ "$schema": "https://raw.githubusercontent.com/nike4613/ModSaber-MetadataFileSchema/master/Schema.json", "author": "Umbranox", "description": "Allows you to upload scores to an online leaderboard, earn PP from ranked maps and compare scores with others.", - "gameVersion": "1.34.2", + "gameVersion": "1.35.0", "icon": "ScoreSaber.logo.png", "id": "ScoreSaber", "name": "ScoreSaber", "version": "3.3.3", "dependsOn": { - "BSIPA": "^4.1.6", - "BeatSaberMarkupLanguage": "^1.8.0", - "SiraUtil": "^3.1.0" + "BSIPA": "^4.3.0", + "BeatSaberMarkupLanguage": "^1.9.0", + "SiraUtil": "^3.1.0", + "SongCore": "^3.13.0" }, "links": { "website": "https://scoresaber.com"