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:
@@ -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)));
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user