From b29e535ca5372abc5461c434839c37c869f7ea8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 6 Jun 2024 10:42:09 +0200 Subject: [PATCH 1/7] Add results screen for displaying arbitrary daily challenge scores At this point its primary usage is the daily challenge event feed, but the leaderboard will be using this too shortly. Because the playlists results screen that exists in `master` is hard-coupled to showing the *local user's* best result on a given playlist by way of hard-coupling itself to the relevant API request, allowing show of *arbitrary* score by ID requires a whole bunch of subclassery as things stand. Oh well. Class naming is... best effort, due to the above. --- .../TestScenePlaylistsResultsScreen.cs | 2 +- .../Online/Rooms/ShowPlaylistScoreRequest.cs | 23 +++++++ .../DailyChallenge/DailyChallenge.cs | 5 ++ .../DailyChallenge/DailyChallengeEventFeed.cs | 8 ++- .../Multiplayer/MultiplayerResultsScreen.cs | 2 +- ...Screen.cs => PlaylistItemResultsScreen.cs} | 64 +++++++++---------- .../PlaylistItemScoreResultsScreen.cs | 37 +++++++++++ .../PlaylistItemUserResultsScreen.cs | 46 +++++++++++++ .../OnlinePlay/Playlists/PlaylistsPlayer.cs | 2 +- .../Playlists/PlaylistsRoomSubScreen.cs | 2 +- 10 files changed, 152 insertions(+), 39 deletions(-) create mode 100644 osu.Game/Online/Rooms/ShowPlaylistScoreRequest.cs rename osu.Game/Screens/OnlinePlay/Playlists/{PlaylistsResultsScreen.cs => PlaylistItemResultsScreen.cs} (80%) create mode 100644 osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemScoreResultsScreen.cs create mode 100644 osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemUserResultsScreen.cs diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index fca965052f..a52d29a120 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -413,7 +413,7 @@ namespace osu.Game.Tests.Visual.Playlists }; } - private partial class TestResultsScreen : PlaylistsResultsScreen + private partial class TestResultsScreen : PlaylistItemUserResultsScreen { public new LoadingSpinner LeftSpinner => base.LeftSpinner; public new LoadingSpinner CentreSpinner => base.CentreSpinner; diff --git a/osu.Game/Online/Rooms/ShowPlaylistScoreRequest.cs b/osu.Game/Online/Rooms/ShowPlaylistScoreRequest.cs new file mode 100644 index 0000000000..d8f977a1d4 --- /dev/null +++ b/osu.Game/Online/Rooms/ShowPlaylistScoreRequest.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Online.API; + +namespace osu.Game.Online.Rooms +{ + public class ShowPlaylistScoreRequest : APIRequest + { + private readonly long roomId; + private readonly long playlistItemId; + private readonly long scoreId; + + public ShowPlaylistScoreRequest(long roomId, long playlistItemId, long scoreId) + { + this.roomId = roomId; + this.playlistItemId = playlistItemId; + this.scoreId = scoreId; + } + + protected override string Target => $@"rooms/{roomId}/playlist/{playlistItemId}/scores/{scoreId}"; + } +} diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs index b8d0dbbe7d..381c713233 100644 --- a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs +++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs @@ -209,6 +209,11 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge feed = new DailyChallengeEventFeed { RelativeSizeAxes = Axes.Both, + PresentScore = id => + { + if (this.IsCurrentScreen()) + this.Push(new PlaylistItemScoreResultsScreen(room.RoomID.Value!.Value, playlistItem, id)); + } } ], }, diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeEventFeed.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeEventFeed.cs index c38a921e43..e76238abad 100644 --- a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeEventFeed.cs +++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeEventFeed.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; @@ -19,6 +20,8 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge { private DailyChallengeEventFeedFlow flow = null!; + public Action? PresentScore { get; init; } + [BackgroundDependencyLoader] private void load() { @@ -48,6 +51,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge { Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, + PresentScore = PresentScore, }; flow.Add(row); row.Delay(15000).Then().FadeOut(300, Easing.OutQuint).Expire(); @@ -78,6 +82,8 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge { private readonly NewScoreEvent newScore; + public Action? PresentScore { get; init; } + public NewScoreEventRow(NewScoreEvent newScore) { this.newScore = newScore; @@ -115,7 +121,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge text.AddUserLink(newScore.User); text.AddText(" got "); - text.AddLink($"{newScore.TotalScore:N0} points", () => { }); // TODO: present the score here + text.AddLink($"{newScore.TotalScore:N0} points", () => PresentScore?.Invoke(newScore.ScoreID)); if (newScore.NewRank != null) text.AddText($" and achieved rank #{newScore.NewRank.Value:N0}"); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs index 6ed75508dc..c439df82a6 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs @@ -7,7 +7,7 @@ using osu.Game.Screens.OnlinePlay.Playlists; namespace osu.Game.Screens.OnlinePlay.Multiplayer { - public partial class MultiplayerResultsScreen : PlaylistsResultsScreen + public partial class MultiplayerResultsScreen : PlaylistItemUserResultsScreen { public MultiplayerResultsScreen(ScoreInfo score, long roomId, PlaylistItem playlistItem) : base(score, roomId, playlistItem) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs similarity index 80% rename from osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs rename to osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs index fdb83b5ae8..51fd912ccc 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs @@ -17,10 +17,10 @@ using osu.Game.Screens.Ranking; namespace osu.Game.Screens.OnlinePlay.Playlists { - public partial class PlaylistsResultsScreen : ResultsScreen + public abstract partial class PlaylistItemResultsScreen : ResultsScreen { - private readonly long roomId; - private readonly PlaylistItem playlistItem; + protected readonly long RoomId; + protected readonly PlaylistItem PlaylistItem; protected LoadingSpinner LeftSpinner { get; private set; } = null!; protected LoadingSpinner CentreSpinner { get; private set; } = null!; @@ -30,19 +30,19 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private MultiplayerScores? lowerScores; [Resolved] - private IAPIProvider api { get; set; } = null!; + protected IAPIProvider API { get; private set; } = null!; [Resolved] - private ScoreManager scoreManager { get; set; } = null!; + protected ScoreManager ScoreManager { get; private set; } = null!; [Resolved] - private RulesetStore rulesets { get; set; } = null!; + protected RulesetStore Rulesets { get; private set; } = null!; - public PlaylistsResultsScreen(ScoreInfo? score, long roomId, PlaylistItem playlistItem) + protected PlaylistItemResultsScreen(ScoreInfo? score, long roomId, PlaylistItem playlistItem) : base(score) { - this.roomId = roomId; - this.playlistItem = playlistItem; + RoomId = roomId; + PlaylistItem = playlistItem; } [BackgroundDependencyLoader] @@ -74,13 +74,15 @@ namespace osu.Game.Screens.OnlinePlay.Playlists }); } - protected override APIRequest FetchScores(Action> scoresCallback) + protected abstract APIRequest CreateScoreRequest(); + + protected sealed override APIRequest FetchScores(Action> scoresCallback) { // This performs two requests: - // 1. A request to show the user's score (and scores around). + // 1. A request to show the relevant score (and scores around). // 2. If that fails, a request to index the room starting from the highest score. - var userScoreReq = new ShowPlaylistUserScoreRequest(roomId, playlistItem.ID, api.LocalUser.Value.Id); + var userScoreReq = CreateScoreRequest(); userScoreReq.Success += userScore => { @@ -111,11 +113,12 @@ namespace osu.Game.Screens.OnlinePlay.Playlists setPositions(lowerScores, userScore.Position.Value, 1); } - performSuccessCallback(scoresCallback, allScores); + Schedule(() => PerformSuccessCallback(scoresCallback, allScores)); + hideLoadingSpinners(); }; // On failure, fallback to a normal index. - userScoreReq.Failure += _ => api.Queue(createIndexRequest(scoresCallback)); + userScoreReq.Failure += _ => API.Queue(createIndexRequest(scoresCallback)); return userScoreReq; } @@ -147,8 +150,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private APIRequest createIndexRequest(Action> scoresCallback, MultiplayerScores? pivot = null) { var indexReq = pivot != null - ? new IndexPlaylistScoresRequest(roomId, playlistItem.ID, pivot.Cursor, pivot.Params) - : new IndexPlaylistScoresRequest(roomId, playlistItem.ID); + ? new IndexPlaylistScoresRequest(RoomId, PlaylistItem.ID, pivot.Cursor, pivot.Params) + : new IndexPlaylistScoresRequest(RoomId, PlaylistItem.ID); indexReq.Success += r => { @@ -163,7 +166,11 @@ namespace osu.Game.Screens.OnlinePlay.Playlists setPositions(r, pivot, -1); } - performSuccessCallback(scoresCallback, r.Scores, r); + Schedule(() => + { + PerformSuccessCallback(scoresCallback, r.Scores, r); + hideLoadingSpinners(pivot); + }); }; indexReq.Failure += _ => hideLoadingSpinners(pivot); @@ -177,26 +184,15 @@ namespace osu.Game.Screens.OnlinePlay.Playlists /// The callback to invoke with the final s. /// The s that were retrieved from s. /// An optional pivot around which the scores were retrieved. - private void performSuccessCallback(Action> callback, List scores, MultiplayerScores? pivot = null) => Schedule(() => + protected virtual ScoreInfo[] PerformSuccessCallback(Action> callback, List scores, MultiplayerScores? pivot = null) { - var scoreInfos = scores.Select(s => s.CreateScoreInfo(scoreManager, rulesets, playlistItem, Beatmap.Value.BeatmapInfo)).OrderByTotalScore().ToArray(); + var scoreInfos = scores.Select(s => s.CreateScoreInfo(ScoreManager, Rulesets, PlaylistItem, Beatmap.Value.BeatmapInfo)).OrderByTotalScore().ToArray(); - // Select a score if we don't already have one selected. - // Note: This is done before the callback so that the panel list centres on the selected score before panels are added (eliminating initial scroll). - if (SelectedScore.Value == null) - { - Schedule(() => - { - // Prefer selecting the local user's score, or otherwise default to the first visible score. - SelectedScore.Value = scoreInfos.FirstOrDefault(s => s.User.OnlineID == api.LocalUser.Value.Id) ?? scoreInfos.FirstOrDefault(); - }); - } + // Invoke callback to add the scores. + callback.Invoke(scoreInfos); - // Invoke callback to add the scores. Exclude the user's current score which was added previously. - callback.Invoke(scoreInfos.Where(s => s.OnlineID != Score?.OnlineID)); - - hideLoadingSpinners(pivot); - }); + return scoreInfos; + } private void hideLoadingSpinners(MultiplayerScores? pivot = null) { diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemScoreResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemScoreResultsScreen.cs new file mode 100644 index 0000000000..831b6538a7 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemScoreResultsScreen.cs @@ -0,0 +1,37 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Game.Online.API; +using osu.Game.Online.Rooms; +using osu.Game.Scoring; + +namespace osu.Game.Screens.OnlinePlay.Playlists +{ + /// + /// Shows a selected arbitrary score for a playlist item, with scores around included. + /// + public partial class PlaylistItemScoreResultsScreen : PlaylistItemResultsScreen + { + private readonly long scoreId; + + public PlaylistItemScoreResultsScreen(long roomId, PlaylistItem playlistItem, long scoreId) + : base(null, roomId, playlistItem) + { + this.scoreId = scoreId; + } + + protected override APIRequest CreateScoreRequest() => new ShowPlaylistScoreRequest(RoomId, PlaylistItem.ID, scoreId); + + protected override ScoreInfo[] PerformSuccessCallback(Action> callback, List scores, MultiplayerScores? pivot = null) + { + var scoreInfos = base.PerformSuccessCallback(callback, scores, pivot); + + Schedule(() => SelectedScore.Value = scoreInfos.SingleOrDefault(score => score.OnlineID == scoreId)); + + return scoreInfos; + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemUserResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemUserResultsScreen.cs new file mode 100644 index 0000000000..e038cf3288 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemUserResultsScreen.cs @@ -0,0 +1,46 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Game.Online.API; +using osu.Game.Online.Rooms; +using osu.Game.Scoring; + +namespace osu.Game.Screens.OnlinePlay.Playlists +{ + /// + /// Shows the user's best score for a given playlist item, with scores around included. + /// + public partial class PlaylistItemUserResultsScreen : PlaylistItemResultsScreen + { + public PlaylistItemUserResultsScreen(ScoreInfo? score, long roomId, PlaylistItem playlistItem) + : base(score, roomId, playlistItem) + { + } + + protected override APIRequest CreateScoreRequest() => new ShowPlaylistUserScoreRequest(RoomId, PlaylistItem.ID, API.LocalUser.Value.Id); + + protected override ScoreInfo[] PerformSuccessCallback(Action> callback, List scores, MultiplayerScores? pivot = null) + { + var scoreInfos = scores.Select(s => s.CreateScoreInfo(ScoreManager, Rulesets, PlaylistItem, Beatmap.Value.BeatmapInfo)).OrderByTotalScore().ToArray(); + + // Select a score if we don't already have one selected. + // Note: This is done before the callback so that the panel list centres on the selected score before panels are added (eliminating initial scroll). + if (SelectedScore.Value == null) + { + Schedule(() => + { + // Prefer selecting the local user's score, or otherwise default to the first visible score. + SelectedScore.Value = scoreInfos.FirstOrDefault(s => s.User.OnlineID == API.LocalUser.Value.Id) ?? scoreInfos.FirstOrDefault(); + }); + } + + // Invoke callback to add the scores. Exclude the user's current score which was added previously. + callback.Invoke(scoreInfos.Where(s => s.OnlineID != Score?.OnlineID)); + + return scoreInfos; + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs index 48f63731e1..4a2d8f8f6b 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs @@ -58,7 +58,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists protected override ResultsScreen CreateResults(ScoreInfo score) { Debug.Assert(Room.RoomID.Value != null); - return new PlaylistsResultsScreen(score, Room.RoomID.Value.Value, PlaylistItem) + return new PlaylistItemUserResultsScreen(score, Room.RoomID.Value.Value, PlaylistItem) { AllowRetry = true, ShowUserStatistics = true, diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 3fb9de428a..3126bbf2eb 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -114,7 +114,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists RequestResults = item => { Debug.Assert(RoomId.Value != null); - ParentScreen?.Push(new PlaylistsResultsScreen(null, RoomId.Value.Value, item)); + ParentScreen?.Push(new PlaylistItemUserResultsScreen(null, RoomId.Value.Value, item)); } } }, From 8e8909c999b3a7a3df67f96b338eb7da03f68fb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 2 Jul 2024 09:24:03 +0200 Subject: [PATCH 2/7] Adjust daily challenge screen background colour --- osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs index 381c713233..dedfdecf2e 100644 --- a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs +++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs @@ -9,7 +9,6 @@ using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Extensions; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; @@ -161,7 +160,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"3e3a44") // Temporary. + Colour = colourProvider.Background4, }, new GridContainer { @@ -277,7 +276,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"28242d") // Temporary. + Colour = colourProvider.Background5, }, footerButtons = new FillFlowContainer { From 5fa586848d81a03251798b00fb702ed8cb7f4c68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 6 Jun 2024 11:06:22 +0200 Subject: [PATCH 3/7] Replace old bad daily challenge leaderboard with new implementation - Actually shows scores rather than playlist aggregates (which are useful... in playlists, where there is more than one item) - Actually allows scores to be shown by clicking on them - Doesn't completely break down visually on smaller window sizes The general appearance is not as polished as the old one in details but I wanted something quick that we can get out by next weekend. Also includes the naive method of refetching scores once a new top 50 score is detected. I can add a stagger if required. --- .../TestSceneDailyChallengeLeaderboard.cs | 142 ++++++++++++++ .../SongSelect/TestSceneLeaderboardScoreV2.cs | 82 +++++--- .../DailyChallenge/DailyChallenge.cs | 33 ++-- .../DailyChallengeLeaderboard.cs | 175 ++++++++++++++++++ .../Leaderboards/LeaderboardScoreV2.cs | 43 +++-- .../OnlinePlay/TestRoomRequestsHandler.cs | 48 +++++ 6 files changed, 459 insertions(+), 64 deletions(-) create mode 100644 osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeLeaderboard.cs create mode 100644 osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeLeaderboard.cs diff --git a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeLeaderboard.cs b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeLeaderboard.cs new file mode 100644 index 0000000000..5fff6bb010 --- /dev/null +++ b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeLeaderboard.cs @@ -0,0 +1,142 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Utils; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Rooms; +using osu.Game.Overlays; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; +using osu.Game.Screens.OnlinePlay.DailyChallenge; +using osuTK; + +namespace osu.Game.Tests.Visual.DailyChallenge +{ + public partial class TestSceneDailyChallengeLeaderboard : OsuTestScene + { + private DummyAPIAccess dummyAPI => (DummyAPIAccess)API; + + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Plum); + + [Test] + public void TestBasicBehaviour() + { + DailyChallengeLeaderboard leaderboard = null!; + + AddStep("set up response without user best", () => + { + dummyAPI.HandleRequest = req => + { + if (req is IndexPlaylistScoresRequest indexRequest) + { + indexRequest.TriggerSuccess(createResponse(50, false)); + return true; + } + + return false; + }; + }); + AddStep("create leaderboard", () => Child = leaderboard = new DailyChallengeLeaderboard(new Room { RoomID = { Value = 1 } }, new PlaylistItem(Beatmap.Value.BeatmapInfo)) + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(0.8f), + }); + + AddStep("set up response with user best", () => + { + dummyAPI.HandleRequest = req => + { + if (req is IndexPlaylistScoresRequest indexRequest) + { + indexRequest.TriggerSuccess(createResponse(50, true)); + return true; + } + + return false; + }; + }); + AddStep("force refetch", () => leaderboard.RefetchScores()); + } + + [Test] + public void TestLoadingBehaviour() + { + IndexPlaylistScoresRequest pendingRequest = null!; + DailyChallengeLeaderboard leaderboard = null!; + + AddStep("set up requests handler", () => + { + dummyAPI.HandleRequest = req => + { + if (req is IndexPlaylistScoresRequest indexRequest) + { + pendingRequest = indexRequest; + return true; + } + + return false; + }; + }); + AddStep("create leaderboard", () => Child = leaderboard = new DailyChallengeLeaderboard(new Room { RoomID = { Value = 1 } }, new PlaylistItem(Beatmap.Value.BeatmapInfo)) + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(0.8f), + }); + AddStep("complete load", () => pendingRequest.TriggerSuccess(createResponse(3, true))); + AddStep("force refetch", () => leaderboard.RefetchScores()); + AddStep("complete load", () => pendingRequest.TriggerSuccess(createResponse(4, true))); + } + + private IndexedMultiplayerScores createResponse(int scoreCount, bool returnUserBest) + { + var result = new IndexedMultiplayerScores(); + + for (int i = 0; i < scoreCount; ++i) + { + result.Scores.Add(new MultiplayerScore + { + ID = i, + Accuracy = 1 - (float)i / (2 * scoreCount), + Position = i + 1, + EndedAt = DateTimeOffset.Now, + Passed = true, + Rank = (ScoreRank)RNG.Next((int)ScoreRank.D, (int)ScoreRank.XH), + MaxCombo = 1000 - i, + TotalScore = (long)(1_000_000 * (1 - (float)i / (2 * scoreCount))), + User = new APIUser { Username = $"user {i}" }, + Statistics = new Dictionary() + }); + } + + if (returnUserBest) + { + result.UserScore = new MultiplayerScore + { + ID = 99999, + Accuracy = 0.91, + Position = 4, + EndedAt = DateTimeOffset.Now, + Passed = true, + Rank = ScoreRank.A, + MaxCombo = 100, + TotalScore = 800000, + User = dummyAPI.LocalUser.Value, + Statistics = new Dictionary() + }; + } + + return result; + } + } +} diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboardScoreV2.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboardScoreV2.cs index 0f5eb06df7..33af4907a1 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboardScoreV2.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboardScoreV2.cs @@ -50,35 +50,73 @@ namespace osu.Game.Tests.Visual.SongSelect }); } - [SetUp] - public void Setup() => Schedule(() => + [Test] + public void TestSheared() { - Children = new Drawable[] + AddStep("create content", () => { - fillFlow = new FillFlowContainer + Children = new Drawable[] { - Width = relativeWidth, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(0f, 2f), - Shear = new Vector2(OsuGame.SHEAR, 0) - }, - drawWidthText = new OsuSpriteText(), - }; + fillFlow = new FillFlowContainer + { + Width = relativeWidth, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(0f, 2f), + Shear = new Vector2(OsuGame.SHEAR, 0) + }, + drawWidthText = new OsuSpriteText(), + }; - foreach (var scoreInfo in getTestScores()) + foreach (var scoreInfo in getTestScores()) + { + fillFlow.Add(new LeaderboardScoreV2(scoreInfo) + { + Rank = scoreInfo.Position, + IsPersonalBest = scoreInfo.User.Id == 2, + Shear = Vector2.Zero, + }); + } + + foreach (var score in fillFlow.Children) + score.Show(); + }); + } + + [Test] + public void TestNonSheared() + { + AddStep("create content", () => { - fillFlow.Add(new LeaderboardScoreV2(scoreInfo, scoreInfo.Position, scoreInfo.User.Id == 2) + Children = new Drawable[] { - Shear = Vector2.Zero, - }); - } + fillFlow = new FillFlowContainer + { + Width = relativeWidth, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(0f, 2f), + }, + drawWidthText = new OsuSpriteText(), + }; - foreach (var score in fillFlow.Children) - score.Show(); - }); + foreach (var scoreInfo in getTestScores()) + { + fillFlow.Add(new LeaderboardScoreV2(scoreInfo) + { + Rank = scoreInfo.Position, + IsPersonalBest = scoreInfo.User.Id == 2, + }); + } + + foreach (var score in fillFlow.Children) + score.Show(); + }); + } [SetUpSteps] public void SetUpSteps() diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs index dedfdecf2e..2d58b3b82c 100644 --- a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs +++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs @@ -49,7 +49,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge private readonly Bindable> userMods = new Bindable>(Array.Empty()); private OnlinePlayScreenWaveContainer waves = null!; - private MatchLeaderboard leaderboard = null!; + private DailyChallengeLeaderboard leaderboard = null!; private RoomModSelectOverlay userModsSelectOverlay = null!; private Sample? sampleStart; private IDisposable? userModsSelectOverlayRegistration; @@ -208,33 +208,17 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge feed = new DailyChallengeEventFeed { RelativeSizeAxes = Axes.Both, - PresentScore = id => - { - if (this.IsCurrentScreen()) - this.Push(new PlaylistItemScoreResultsScreen(room.RoomID.Value!.Value, playlistItem, id)); - } + PresentScore = presentScore } ], }, }, null, // Middle column (leaderboard) - new GridContainer + leaderboard = new DailyChallengeLeaderboard(room, playlistItem) { RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] - { - new SectionHeader("Leaderboard") - }, - [leaderboard = new MatchLeaderboard { RelativeSizeAxes = Axes.Both }], - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(), - } + PresentScore = presentScore, }, // Spacer null, @@ -330,6 +314,12 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge metadataClient.MultiplayerRoomScoreSet += onRoomScoreSet; } + private void presentScore(long id) + { + if (this.IsCurrentScreen()) + this.Push(new PlaylistItemScoreResultsScreen(room.RoomID.Value!.Value, playlistItem, id)); + } + private void onRoomScoreSet(MultiplayerRoomScoreSetEvent e) { if (e.RoomID != room.RoomID.Value || e.PlaylistItemID != playlistItem.ID) @@ -351,6 +341,9 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge { breakdown.AddNewScore(ev); feed.AddNewScore(ev); + + if (e.NewRank <= 50) + Schedule(() => leaderboard.RefetchScores()); }); }); } diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeLeaderboard.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeLeaderboard.cs new file mode 100644 index 0000000000..4d4ae755fc --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeLeaderboard.cs @@ -0,0 +1,175 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using System.Threading; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using osu.Game.Online.Rooms; +using osu.Game.Rulesets; +using osu.Game.Scoring; +using osu.Game.Screens.SelectV2.Leaderboards; +using osuTK; + +namespace osu.Game.Screens.OnlinePlay.DailyChallenge +{ + public partial class DailyChallengeLeaderboard : CompositeDrawable + { + public Action? PresentScore { get; init; } + + private readonly Room room; + private readonly PlaylistItem playlistItem; + + private FillFlowContainer scoreFlow = null!; + private Container userBestContainer = null!; + private SectionHeader userBestHeader = null!; + private LoadingLayer loadingLayer = null!; + + private CancellationTokenSource? cancellationTokenSource; + + [Resolved] + private IAPIProvider api { get; set; } = null!; + + [Resolved] + private ScoreManager scoreManager { get; set; } = null!; + + [Resolved] + private RulesetStore rulesets { get; set; } = null!; + + [Resolved] + private IBindable beatmap { get; set; } = null!; + + public DailyChallengeLeaderboard(Room room, PlaylistItem playlistItem) + { + this.room = room; + this.playlistItem = playlistItem; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = + [ + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize) + ], + Content = new[] + { + new Drawable[] { new SectionHeader("Leaderboard") }, + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = scoreFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Right = 20, }, + Direction = FillDirection.Vertical, + Spacing = new Vector2(5), + Scale = new Vector2(0.8f), + Width = 1 / 0.8f, + } + }, + loadingLayer = new LoadingLayer + { + RelativeSizeAxes = Axes.Both, + }, + } + } + }, + new Drawable[] { userBestHeader = new SectionHeader("Personal best") { Alpha = 0, } }, + new Drawable[] + { + userBestContainer = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Right = 20, }, + Scale = new Vector2(0.8f), + Width = 1 / 0.8f, + } + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + RefetchScores(); + } + + public void RefetchScores() + { + var request = new IndexPlaylistScoresRequest(room.RoomID.Value!.Value, playlistItem.ID); + + request.Success += req => + { + var best = req.Scores.Select(s => s.CreateScoreInfo(scoreManager, rulesets, playlistItem, beatmap.Value.BeatmapInfo)).ToArray(); + var userBest = req.UserScore?.CreateScoreInfo(scoreManager, rulesets, playlistItem, beatmap.Value.BeatmapInfo); + + cancellationTokenSource?.Cancel(); + cancellationTokenSource = null; + cancellationTokenSource ??= new CancellationTokenSource(); + + if (best.Length == 0) + { + scoreFlow.Clear(); + loadingLayer.Hide(); + } + else + { + LoadComponentsAsync(best.Select(s => new LeaderboardScoreV2(s, sheared: false) + { + Rank = s.Position, + IsPersonalBest = s.UserID == api.LocalUser.Value.Id, + Action = () => PresentScore?.Invoke(s.OnlineID), + }), loaded => + { + scoreFlow.Clear(); + scoreFlow.AddRange(loaded); + scoreFlow.FadeTo(1, 400, Easing.OutQuint); + loadingLayer.Hide(); + }, cancellationTokenSource.Token); + } + + userBestContainer.Clear(); + + if (userBest != null) + { + userBestContainer.Add(new LeaderboardScoreV2(userBest, sheared: false) + { + Rank = userBest.Position, + IsPersonalBest = true, + Action = () => PresentScore?.Invoke(userBest.OnlineID), + }); + } + + userBestHeader.FadeTo(userBest == null ? 0 : 1); + }; + + loadingLayer.Show(); + scoreFlow.FadeTo(0.5f, 400, Easing.OutQuint); + api.Queue(request); + } + } +} diff --git a/osu.Game/Screens/SelectV2/Leaderboards/LeaderboardScoreV2.cs b/osu.Game/Screens/SelectV2/Leaderboards/LeaderboardScoreV2.cs index 804a9d24b7..700f889d7f 100644 --- a/osu.Game/Screens/SelectV2/Leaderboards/LeaderboardScoreV2.cs +++ b/osu.Game/Screens/SelectV2/Leaderboards/LeaderboardScoreV2.cs @@ -43,6 +43,9 @@ namespace osu.Game.Screens.SelectV2.Leaderboards { public partial class LeaderboardScoreV2 : OsuClickableContainer, IHasContextMenu, IHasCustomTooltip { + public int? Rank { get; init; } + public bool IsPersonalBest { get; init; } + private const float expanded_right_content_width = 210; private const float grade_width = 40; private const float username_min_width = 125; @@ -52,15 +55,12 @@ namespace osu.Game.Screens.SelectV2.Leaderboards private const float rank_label_visibility_width_cutoff = rank_label_width + height + username_min_width + statistics_regular_min_width + expanded_right_content_width; private readonly ScoreInfo score; + private readonly bool sheared; private const int height = 60; private const int corner_radius = 10; private const int transition_duration = 200; - private readonly int? rank; - - private readonly bool isPersonalBest; - private Colour4 foregroundColour; private Colour4 backgroundColour; private ColourInfo totalScoreBackgroundGradient; @@ -104,13 +104,12 @@ namespace osu.Game.Screens.SelectV2.Leaderboards public ITooltip GetCustomTooltip() => new LeaderboardScoreTooltip(); public virtual ScoreInfo TooltipContent => score; - public LeaderboardScoreV2(ScoreInfo score, int? rank, bool isPersonalBest = false) + public LeaderboardScoreV2(ScoreInfo score, bool sheared = true) { this.score = score; - this.rank = rank; - this.isPersonalBest = isPersonalBest; + this.sheared = sheared; - Shear = new Vector2(OsuGame.SHEAR, 0); + Shear = new Vector2(sheared ? OsuGame.SHEAR : 0, 0); RelativeSizeAxes = Axes.X; Height = height; } @@ -120,8 +119,8 @@ namespace osu.Game.Screens.SelectV2.Leaderboards { var user = score.User; - foregroundColour = isPersonalBest ? colourProvider.Background1 : colourProvider.Background5; - backgroundColour = isPersonalBest ? colourProvider.Background2 : colourProvider.Background4; + foregroundColour = IsPersonalBest ? colourProvider.Background1 : colourProvider.Background5; + backgroundColour = IsPersonalBest ? colourProvider.Background2 : colourProvider.Background4; totalScoreBackgroundGradient = ColourInfo.GradientHorizontal(backgroundColour.Opacity(0), backgroundColour); statisticsLabels = GetStatistics(score).Select(s => new ScoreComponentLabel(s, score) @@ -159,7 +158,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards { AutoSizeAxes = Axes.X, RelativeSizeAxes = Axes.Y, - Child = rankLabel = new RankLabel(rank) + Child = rankLabel = new RankLabel(Rank, sheared) { Width = rank_label_width, RelativeSizeAxes = Axes.Y, @@ -243,7 +242,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards { RelativeSizeAxes = Axes.Both, User = score.User, - Shear = new Vector2(-OsuGame.SHEAR, 0), + Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0), Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Colour = ColourInfo.GradientHorizontal(Colour4.White.Opacity(0.5f), Colour4.FromHex(@"222A27").Opacity(1)), @@ -274,7 +273,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards Anchor = Anchor.Centre, Origin = Anchor.Centre, Scale = new Vector2(1.1f), - Shear = new Vector2(-OsuGame.SHEAR, 0), + Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0), RelativeSizeAxes = Axes.Both, }) { @@ -292,7 +291,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards RelativeSizeAxes = Axes.Both, Colour = Colour4.Black.Opacity(0.5f), }, - new RankLabel(rank) + new RankLabel(Rank, sheared) { AutoSizeAxes = Axes.Both, Anchor = Anchor.Centre, @@ -314,7 +313,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards { flagBadgeAndDateContainer = new FillFlowContainer { - Shear = new Vector2(-OsuGame.SHEAR, 0), + Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0), Direction = FillDirection.Horizontal, Spacing = new Vector2(5), AutoSizeAxes = Axes.Both, @@ -338,7 +337,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards nameLabel = new TruncatingSpriteText { RelativeSizeAxes = Axes.X, - Shear = new Vector2(-OsuGame.SHEAR, 0), + Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0), Text = user.Username, Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold) } @@ -354,7 +353,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards Name = @"Statistics container", Padding = new MarginPadding { Right = 40 }, Spacing = new Vector2(25, 0), - Shear = new Vector2(-OsuGame.SHEAR, 0), + Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0), Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, AutoSizeAxes = Axes.Both, @@ -412,7 +411,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards }, RankContainer = new Container { - Shear = new Vector2(-OsuGame.SHEAR, 0), + Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0), Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, RelativeSizeAxes = Axes.Y, @@ -470,7 +469,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards Anchor = Anchor.TopRight, Origin = Anchor.TopRight, UseFullGlyphHeight = false, - Shear = new Vector2(-OsuGame.SHEAR, 0), + Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0), Current = scoreManager.GetBindableTotalScoreString(score), Font = OsuFont.GetFont(size: 30, weight: FontWeight.Light), }, @@ -478,7 +477,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - Shear = new Vector2(-OsuGame.SHEAR, 0), + Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0), AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, Spacing = new Vector2(2f, 0f), @@ -656,14 +655,14 @@ namespace osu.Game.Screens.SelectV2.Leaderboards private partial class RankLabel : Container, IHasTooltip { - public RankLabel(int? rank) + public RankLabel(int? rank, bool sheared) { if (rank >= 1000) TooltipText = $"#{rank:N0}"; Child = new OsuSpriteText { - Shear = new Vector2(-OsuGame.SHEAR, 0), + Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0), Anchor = Anchor.Centre, Origin = Anchor.Centre, Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold, italics: true), diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs index ef4539ba56..36e256b920 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs @@ -99,6 +99,54 @@ namespace osu.Game.Tests.Visual.OnlinePlay }); return true; + case IndexPlaylistScoresRequest roomLeaderboardRequest: + roomLeaderboardRequest.TriggerSuccess(new IndexedMultiplayerScores + { + Scores = + { + new MultiplayerScore + { + ID = currentScoreId++, + Accuracy = 1, + Position = 1, + EndedAt = DateTimeOffset.Now, + Passed = true, + Rank = ScoreRank.S, + MaxCombo = 1000, + TotalScore = 1000000, + User = new APIUser { Username = "best user" }, + Statistics = new Dictionary() + }, + new MultiplayerScore + { + ID = currentScoreId++, + Accuracy = 0.7, + Position = 2, + EndedAt = DateTimeOffset.Now, + Passed = true, + Rank = ScoreRank.B, + MaxCombo = 100, + TotalScore = 200000, + User = new APIUser { Username = "worst user" }, + Statistics = new Dictionary() + }, + }, + UserScore = new MultiplayerScore + { + ID = currentScoreId++, + Accuracy = 0.91, + Position = 4, + EndedAt = DateTimeOffset.Now, + Passed = true, + Rank = ScoreRank.A, + MaxCombo = 100, + TotalScore = 800000, + User = localUser, + Statistics = new Dictionary() + }, + }); + return true; + case PartRoomRequest partRoomRequest: partRoomRequest.TriggerSuccess(); return true; From d5158d10356ee9ef11b6eeffeb0b75f5ad3df4ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 8 Jul 2024 13:36:30 +0200 Subject: [PATCH 4/7] Fix incorrect changes around success callback refactor --- .../OnlinePlay/Playlists/PlaylistItemResultsScreen.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs index 51fd912ccc..dc06b88823 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs @@ -113,8 +113,11 @@ namespace osu.Game.Screens.OnlinePlay.Playlists setPositions(lowerScores, userScore.Position.Value, 1); } - Schedule(() => PerformSuccessCallback(scoresCallback, allScores)); - hideLoadingSpinners(); + Schedule(() => + { + PerformSuccessCallback(scoresCallback, allScores); + hideLoadingSpinners(); + }); }; // On failure, fallback to a normal index. @@ -169,7 +172,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists Schedule(() => { PerformSuccessCallback(scoresCallback, r.Scores, r); - hideLoadingSpinners(pivot); + hideLoadingSpinners(r); }); }; From 8c81ba3357dc91e2329d88a93240abfd7efd0c55 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 9 Jul 2024 14:36:24 -0700 Subject: [PATCH 5/7] Fix preview track persisting to play after leaving multi/playlists room --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 4eb092d08b..515d9fc7a5 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -80,6 +80,9 @@ namespace osu.Game.Screens.OnlinePlay.Match [Resolved(canBeNull: true)] protected OnlinePlayScreen ParentScreen { get; private set; } + [Resolved] + private PreviewTrackManager previewTrackManager { get; set; } = null!; + [Cached] private readonly OnlinePlayBeatmapAvailabilityTracker beatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker(); @@ -483,6 +486,8 @@ namespace osu.Game.Screens.OnlinePlay.Match { UserModsSelectOverlay.Hide(); endHandlingTrack(); + + previewTrackManager.StopAnyPlaying(this); } private void endHandlingTrack() From 4e1240c349169b3493d799f85427f65a1c53717b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 10 Jul 2024 13:54:27 +0900 Subject: [PATCH 6/7] Migrate `ShearedOverlayContainer` to NRT --- .../Overlays/Mods/ShearedOverlayContainer.cs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs index c9c3c62404..d3326cb86b 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -1,9 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -30,16 +27,15 @@ namespace osu.Game.Overlays.Mods /// /// The overlay's header. /// - protected ShearedOverlayHeader Header { get; private set; } + protected ShearedOverlayHeader Header { get; private set; } = null!; /// /// The overlay's footer. /// - protected Container Footer { get; private set; } + protected Container Footer { get; private set; } = null!; - [Resolved(canBeNull: true)] - [CanBeNull] - private ScreenFooter footer { get; set; } + [Resolved] + private ScreenFooter? footer { get; set; } // todo: very temporary property that will be removed once ModSelectOverlay and FirstRunSetupOverlay are updated to use new footer. public virtual bool UseNewFooter => false; @@ -48,17 +44,17 @@ namespace osu.Game.Overlays.Mods /// A container containing all content, including the header and footer. /// May be used for overlay-wide animations. /// - protected Container TopLevelContent { get; private set; } + protected Container TopLevelContent { get; private set; } = null!; /// /// A container for content that is to be displayed between the header and footer. /// - protected Container MainAreaContent { get; private set; } + protected Container MainAreaContent { get; private set; } = null!; /// /// A container for content that is to be displayed inside the footer. /// - protected Container FooterContent { get; private set; } + protected Container FooterContent { get; private set; } = null!; protected override bool StartHidden => true; From f2810193588f55ebc91f17ce4e39a68be7ca54fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 10 Jul 2024 13:58:50 +0900 Subject: [PATCH 7/7] Rename method to match provided argument --- osu.Game/Overlays/Mods/ShearedOverlayContainer.cs | 4 ++-- osu.Game/Screens/Footer/ScreenFooter.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs index d3326cb86b..aed9b395f6 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -152,7 +152,7 @@ namespace osu.Game.Overlays.Mods if (UseNewFooter && footer != null) { - footer.SetOverlayContent(this); + footer.SetActiveOverlayContainer(this); if (footer.State.Value == Visibility.Hidden) { @@ -175,7 +175,7 @@ namespace osu.Game.Overlays.Mods if (UseNewFooter && footer != null) { - footer.ClearOverlayContent(); + footer.ClearActiveOverlayContainer(); if (hideFooterOnPopOut) { diff --git a/osu.Game/Screens/Footer/ScreenFooter.cs b/osu.Game/Screens/Footer/ScreenFooter.cs index cef891f8c0..f9a6d54b96 100644 --- a/osu.Game/Screens/Footer/ScreenFooter.cs +++ b/osu.Game/Screens/Footer/ScreenFooter.cs @@ -121,7 +121,7 @@ namespace osu.Game.Screens.Footer temporarilyHiddenButtons.Clear(); overlays.Clear(); - ClearOverlayContent(); + ClearActiveOverlayContainer(); var oldButtons = buttonsFlow.ToArray(); @@ -168,7 +168,7 @@ namespace osu.Game.Screens.Footer private Container? contentContainer; private readonly List temporarilyHiddenButtons = new List(); - public void SetOverlayContent(ShearedOverlayContainer overlay) + public void SetActiveOverlayContainer(ShearedOverlayContainer overlay) { if (contentContainer != null) { @@ -213,7 +213,7 @@ namespace osu.Game.Screens.Footer content.Show(); } - public void ClearOverlayContent() + public void ClearActiveOverlayContainer() { if (contentContainer == null) return;