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 fca965052f65..a52d29a12074 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -413,7 +413,7 @@ private void addCursor(MultiplayerScores scores) }; } - 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 000000000000..d8f977a1d425 --- /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 b8d0dbbe7d77..381c713233e3 100644 --- a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs +++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs @@ -209,6 +209,11 @@ private void load(AudioManager audio) 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 c38a921e4353..e76238abad3e 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 @@ public partial class DailyChallengeEventFeed : CompositeDrawable { private DailyChallengeEventFeedFlow flow = null!; + public Action? PresentScore { get; init; } + [BackgroundDependencyLoader] private void load() { @@ -48,6 +51,7 @@ public void AddNewScore(NewScoreEvent newScoreEvent) { Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, + PresentScore = PresentScore, }; flow.Add(row); row.Delay(15000).Then().FadeOut(300, Easing.OutQuint).Expire(); @@ -78,6 +82,8 @@ private partial class NewScoreEventRow : CompositeDrawable { private readonly NewScoreEvent newScore; + public Action? PresentScore { get; init; } + public NewScoreEventRow(NewScoreEvent newScore) { this.newScore = newScore; @@ -115,7 +121,7 @@ private void load(OsuColour colours) 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 6ed75508dcf3..c439df82a64c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs @@ -7,7 +7,7 @@ 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 fdb83b5ae81d..51fd912ccca8 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs @@ -17,10 +17,10 @@ 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 @@ public partial class PlaylistsResultsScreen : ResultsScreen 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 @@ private void load() }); } - 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 @@ protected override APIRequest FetchScores(Action> scoresC 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 @@ protected override APIRequest FetchScores(Action> scoresC 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 @@ private APIRequest createIndexRequest(Action> scoresCallb 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 @@ private APIRequest createIndexRequest(Action> scoresCallb /// 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 000000000000..831b6538a730 --- /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 000000000000..e038cf328832 --- /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 48f63731e1a8..4a2d8f8f6b05 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs @@ -58,7 +58,7 @@ public override bool OnExiting(ScreenExitEvent e) 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 3fb9de428a23..3126bbf2ebc5 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -114,7 +114,7 @@ protected override void LoadComplete() 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 381c713233e3..dedfdecf2eee 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.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 @@ private void load(AudioManager audio) new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"3e3a44") // Temporary. + Colour = colourProvider.Background4, }, new GridContainer { @@ -277,7 +276,7 @@ [new MatchChatDisplay(room) { RelativeSizeAxes = Axes.Both }] 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 000000000000..5fff6bb01030 --- /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 0f5eb06df76d..33af4907a136 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboardScoreV2.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboardScoreV2.cs @@ -50,35 +50,73 @@ private void load() }); } - [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()) + { + fillFlow.Add(new LeaderboardScoreV2(scoreInfo) + { + Rank = scoreInfo.Position, + IsPersonalBest = scoreInfo.User.Id == 2, + Shear = Vector2.Zero, + }); + } + + foreach (var score in fillFlow.Children) + score.Show(); + }); + } - foreach (var scoreInfo in getTestScores()) + [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 dedfdecf2eee..2d58b3b82cfb 100644 --- a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs +++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs @@ -49,7 +49,7 @@ public partial class DailyChallenge : OsuScreen 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 @@ private void load(AudioManager audio) 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 @@ [new MatchChatDisplay(room) { RelativeSizeAxes = Axes.Both }] 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 @@ private void onRoomScoreSet(MultiplayerRoomScoreSetEvent e) { 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 000000000000..4d4ae755fc09 --- /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 804a9d24b771..700f889d7f14 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 @@ public partial class LeaderboardScoreV2 : OsuClickableContainer, IHasContextMenu 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 @@ public partial class LeaderboardScoreV2 : OsuClickableContainer, IHasContextMenu 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 @@ private void load() { 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 @@ private void load() { 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 @@ private void updateModDisplay() { 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 @@ private void updateModDisplay() 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 @@ private void updateModDisplay() 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 @@ private void updateModDisplay() { 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 @@ private void updateModDisplay() 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 @@ private void updateModDisplay() 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 @@ private void updateModDisplay() }, 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 @@ private void updateModDisplay() 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 @@ private void updateModDisplay() { 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 @@ private void load(OsuColour colours, OverlayColourProvider colourProvider) 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 ef4539ba5662..36e256b9202f 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs @@ -99,6 +99,54 @@ public bool HandleRequest(APIRequest request, APIUser localUser, BeatmapManager }); 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 51fd912ccca8..dc06b8882340 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs @@ -113,8 +113,11 @@ protected sealed override APIRequest FetchScores(Action> 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 @@ private APIRequest createIndexRequest(Action> scoresCallb 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 4eb092d08b50..515d9fc7a54a 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -80,6 +80,9 @@ public abstract partial class RoomSubScreen : OnlinePlaySubScreen, IPreviewTrack [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 @@ private void onLeaving() { 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 c9c3c6240414..d3326cb86bd4 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 @@ public abstract partial class ShearedOverlayContainer : OsuFocusedOverlayContain /// /// 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 @@ public abstract partial class ShearedOverlayContainer : OsuFocusedOverlayContain /// 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 d3326cb86bd4..aed9b395f668 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -152,7 +152,7 @@ protected override void PopIn() if (UseNewFooter && footer != null) { - footer.SetOverlayContent(this); + footer.SetActiveOverlayContainer(this); if (footer.State.Value == Visibility.Hidden) { @@ -175,7 +175,7 @@ protected override void PopOut() 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 cef891f8c052..f9a6d54b9679 100644 --- a/osu.Game/Screens/Footer/ScreenFooter.cs +++ b/osu.Game/Screens/Footer/ScreenFooter.cs @@ -121,7 +121,7 @@ public void SetButtons(IReadOnlyList buttons) temporarilyHiddenButtons.Clear(); overlays.Clear(); - ClearOverlayContent(); + ClearActiveOverlayContainer(); var oldButtons = buttonsFlow.ToArray(); @@ -168,7 +168,7 @@ public void SetButtons(IReadOnlyList buttons) 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 @@ public void SetOverlayContent(ShearedOverlayContainer overlay) content.Show(); } - public void ClearOverlayContent() + public void ClearActiveOverlayContainer() { if (contentContainer == null) return;