1
0
mirror of https://github.com/ppy/osu.git synced 2026-06-03 20:06:30 +08:00

Ensure correct global leaderboard state when presenting scores

With `ReplayPlayer` now consuming the `LeaderboardManager`'s global
state, flows such as presenting a score need to set the global state up
correctly to avoid accidentally showing a leaderboard from a completely
different score.

This also incidentally closes https://github.com/ppy/osu/issues/27609.
This commit is contained in:
Bartłomiej Dach
2025-04-10 10:19:04 +02:00
Unverified
parent 17bcc28429
commit 31b98ac7b9
3 changed files with 30 additions and 10 deletions
@@ -27,7 +27,7 @@ namespace osu.Game.Online.Leaderboards
public IBindable<LeaderboardScores?> Scores => scores;
private readonly Bindable<LeaderboardScores?> scores = new Bindable<LeaderboardScores?>();
private LeaderboardCriteria? criteria;
public LeaderboardCriteria? CurrentCriteria { get; private set; }
private IDisposable? localScoreSubscription;
private TaskCompletionSource<LeaderboardScores?>? localFetchCompletionSource;
@@ -45,10 +45,10 @@ namespace osu.Game.Online.Leaderboards
public Task<LeaderboardScores?> FetchWithCriteriaAsync(LeaderboardCriteria newCriteria)
{
if (criteria?.Equals(newCriteria) == true && lastFetchCompletionSource?.Task.IsFaulted == false)
if (CurrentCriteria?.Equals(newCriteria) == true && lastFetchCompletionSource?.Task.IsFaulted == false)
return lastFetchCompletionSource?.Task ?? Task.FromResult(Scores.Value);
criteria = newCriteria;
CurrentCriteria = newCriteria;
localScoreSubscription?.Dispose();
inFlightOnlineRequest?.Cancel();
lastFetchCompletionSource?.TrySetCanceled();
@@ -110,7 +110,7 @@ namespace osu.Game.Online.Leaderboards
private void localScoresChanged(IRealmCollection<ScoreInfo> sender, ChangeSet? changes)
{
Debug.Assert(criteria != null);
Debug.Assert(CurrentCriteria != null);
// This subscription may fire from changes to linked beatmaps, which we don't care about.
// It's currently not possible for a score to be modified after insertion, so we can safely ignore callbacks with only modifications.
@@ -119,9 +119,9 @@ namespace osu.Game.Online.Leaderboards
var newScores = sender.AsEnumerable();
if (criteria.ExactMods != null)
if (CurrentCriteria.ExactMods != null)
{
if (!criteria.ExactMods.Any())
if (!CurrentCriteria.ExactMods.Any())
{
// we need to filter out all scores that have any mods to get all local nomod scores
newScores = newScores.Where(s => !s.Mods.Any());
@@ -130,7 +130,7 @@ namespace osu.Game.Online.Leaderboards
{
// otherwise find all the scores that have all of the currently selected mods (similar to how web applies mod filters)
// we're creating and using a string HashSet representation of selected mods so that it can be translated into the DB query itself
var selectedMods = criteria.ExactMods.Select(m => m.Acronym).ToHashSet();
var selectedMods = CurrentCriteria.ExactMods.Select(m => m.Acronym).ToHashSet();
newScores = newScores.Where(s => selectedMods.SetEquals(s.Mods.Select(m => m.Acronym)));
}
+20
View File
@@ -47,6 +47,7 @@ using osu.Game.IO;
using osu.Game.Localisation;
using osu.Game.Online;
using osu.Game.Online.Chat;
using osu.Game.Online.Leaderboards;
using osu.Game.Online.Rooms;
using osu.Game.Overlays;
using osu.Game.Overlays.BeatmapListing;
@@ -67,6 +68,7 @@ using osu.Game.Screens.OnlinePlay.Multiplayer;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Seasonal;
using osu.Game.Skinning;
using osu.Game.Updater;
@@ -784,6 +786,24 @@ namespace osu.Game
if (!Beatmap.Value.BeatmapInfo.Equals(databasedBeatmap))
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap);
var currentLeaderboard = LeaderboardManager.CurrentCriteria;
bool leaderboardBeatmapMatches = currentLeaderboard != null && databasedBeatmap.Equals(currentLeaderboard.Beatmap);
bool leaderboardRulesetMatches = currentLeaderboard != null && databasedScore.ScoreInfo.Ruleset.Equals(currentLeaderboard.Ruleset);
if (!leaderboardBeatmapMatches || !leaderboardRulesetMatches)
{
var newLeaderboard = currentLeaderboard != null
? currentLeaderboard with { Beatmap = databasedBeatmap, Ruleset = databasedScore.ScoreInfo.Ruleset }
: new LeaderboardCriteria(databasedBeatmap, databasedScore.ScoreInfo.Ruleset, BeatmapLeaderboardScope.Global, null);
LeaderboardManager.FetchWithCriteriaAsync(newLeaderboard)
.ContinueWith(t =>
{
if (t.Exception != null)
Logger.Log($@"Failed to fetch leaderboards when displaying results: {t.Exception}", LoggingTarget.Network);
});
}
switch (presentType)
{
case ScorePresentType.Gameplay:
+3 -3
View File
@@ -204,7 +204,7 @@ namespace osu.Game
private UserLookupCache userCache;
private BeatmapLookupCache beatmapCache;
private LeaderboardManager leaderboardManager;
protected LeaderboardManager LeaderboardManager { get; private set; }
private RulesetConfigCache rulesetConfigCache;
@@ -367,8 +367,8 @@ namespace osu.Game
dependencies.CacheAs<IBindable<WorkingBeatmap>>(Beatmap);
dependencies.CacheAs(Beatmap);
dependencies.Cache(leaderboardManager = new LeaderboardManager());
base.Content.Add(leaderboardManager);
dependencies.Cache(LeaderboardManager = new LeaderboardManager());
base.Content.Add(LeaderboardManager);
// add api components to hierarchy.
if (API is APIAccess apiAccess)