From 2dbfdc3d2c0276e431b4b45bc4dabbb4f480b5b0 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 20 Oct 2025 17:02:35 +0900 Subject: [PATCH 1/2] Preview next song in quick play --- .../Matchmaking/Match/ScreenMatchmaking.cs | 60 ++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/Match/ScreenMatchmaking.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/Match/ScreenMatchmaking.cs index 590d84f310..a40510aab5 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/Match/ScreenMatchmaking.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/Match/ScreenMatchmaking.cs @@ -16,6 +16,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Logging; using osu.Framework.Screens; +using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Graphics.Cursor; @@ -37,7 +38,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match /// /// The main matchmaking screen which houses a custom through the life cycle of a single session. /// - public partial class ScreenMatchmaking : OsuScreen + public partial class ScreenMatchmaking : OsuScreen, IPreviewTrackOwner { /// /// Padding between rows of the content. @@ -74,6 +75,12 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match [Resolved] private AudioManager audio { get; set; } = null!; + [Resolved] + private PreviewTrackManager previewTrackManager { get; set; } = null!; + + [Resolved] + private MusicController music { get; set; } = null!; + private readonly MultiplayerRoom room; private Sample? sampleStart; @@ -308,6 +315,18 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match return false; } + public override void OnEntering(ScreenTransitionEvent e) + { + base.OnEntering(e); + beginHandlingTrack(); + } + + public override void OnSuspending(ScreenTransitionEvent e) + { + onLeaving(); + base.OnSuspending(e); + } + private bool exitConfirmed; public override bool OnExiting(ScreenExitEvent e) @@ -320,6 +339,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match return true; } + onLeaving(); client.LeaveRoom().FireAndForget(); return false; } @@ -342,6 +362,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match public override void OnResuming(ScreenTransitionEvent e) { base.OnResuming(e); + beginHandlingTrack(); if (e.Last is not MultiplayerPlayerLoader playerLoader) return; @@ -355,6 +376,43 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match client.ChangeState(MultiplayerUserState.Idle); } + private void onLeaving() + { + endHandlingTrack(); + } + + /// + /// Handles changes in the track to keep it looping while active. + /// + private void beginHandlingTrack() + { + Beatmap.BindValueChanged(applyLoopingToTrack, true); + } + + /// + /// Stops looping the current track and stops handling further changes to the track. + /// + private void endHandlingTrack() + { + Beatmap.ValueChanged -= applyLoopingToTrack; + Beatmap.Value.Track.Looping = false; + + previewTrackManager.StopAnyPlaying(this); + } + + /// + /// Invoked on changes to the beatmap to loop the track. See: . + /// + /// The beatmap change event. + private void applyLoopingToTrack(ValueChangedEvent beatmap) + { + if (!this.IsCurrentScreen()) + return; + + beatmap.NewValue.PrepareTrackForPreview(true); + music.EnsurePlayingSomething(); + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From 23933452fe8eeb9cbe5f41c4785394f374d03856 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 21 Oct 2025 20:00:20 +0900 Subject: [PATCH 2/2] Preserve last beatmap until selection is made --- .../Matchmaking/Match/ScreenMatchmaking.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/Match/ScreenMatchmaking.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/Match/ScreenMatchmaking.cs index a40510aab5..c16cf5cb99 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/Match/ScreenMatchmaking.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/Match/ScreenMatchmaking.cs @@ -231,19 +231,22 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match // Update global gameplay state to correspond to the new selection. // Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info var localBeatmap = beatmapManager.QueryBeatmap($@"{nameof(BeatmapInfo.OnlineID)} == $0 AND {nameof(BeatmapInfo.MD5Hash)} == {nameof(BeatmapInfo.OnlineMD5Hash)}", item.BeatmapID); - Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); - Ruleset.Value = ruleset; - Mods.Value = item.RequiredMods.Select(m => m.ToMod(rulesetInstance)).ToArray(); - if (Beatmap.Value is DummyWorkingBeatmap) + if (localBeatmap != null) { - if (client.LocalUser!.State == MultiplayerUserState.Ready) - client.ChangeState(MultiplayerUserState.Idle).FireAndForget(); + Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); + Ruleset.Value = ruleset; + Mods.Value = item.RequiredMods.Select(m => m.ToMod(rulesetInstance)).ToArray(); + + // Notify the server that the beatmap has been set and that we are ready to start gameplay. + if (client.LocalUser!.State == MultiplayerUserState.Idle) + client.ChangeState(MultiplayerUserState.Ready).FireAndForget(); } else { - if (client.LocalUser!.State == MultiplayerUserState.Idle) - client.ChangeState(MultiplayerUserState.Ready).FireAndForget(); + // Notify the server that we don't have the beatmap. + if (client.LocalUser!.State == MultiplayerUserState.Ready) + client.ChangeState(MultiplayerUserState.Idle).FireAndForget(); } client.ChangeBeatmapAvailability(beatmapAvailabilityTracker.Availability.Value).FireAndForget();