From 1af462b692e96aa3c2811fd3be2b1307bc8dc158 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Oct 2025 23:07:04 +0900 Subject: [PATCH] Add very simple countdown timer for quick play stages (#35433) --- .../Match/StageDisplay.TimerText.cs | 106 ++++++++++++++++++ .../Matchmaking/Match/StageDisplay.cs | 6 + .../Multiplayer/TestMultiplayerClient.cs | 2 +- 3 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Screens/OnlinePlay/Matchmaking/Match/StageDisplay.TimerText.cs diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/Match/StageDisplay.TimerText.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/Match/StageDisplay.TimerText.cs new file mode 100644 index 0000000000..e2af3ef945 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/Match/StageDisplay.TimerText.cs @@ -0,0 +1,106 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Extensions.ObjectExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Matchmaking; +using osu.Game.Online.Multiplayer; +using osuTK; + +namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match +{ + public partial class StageDisplay + { + public partial class TimerText : CompositeDrawable + { + [Resolved] + private MultiplayerClient client { get; set; } = null!; + + private OsuSpriteText text = null!; + + private DateTimeOffset countdownEndTime; + + public TimerText() + { + AutoSizeAxes = Axes.X; + Height = 18; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = text = new OsuSpriteText + { + Height = 18, + Spacing = new Vector2(-1, 0), + Font = OsuFont.Style.Heading2.With(fixedWidth: true), + AlwaysPresent = true, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + client.CountdownStarted += onCountdownStarted; + client.CountdownStopped += onCountdownStopped; + + if (client.Room != null) + { + foreach (var countdown in client.Room.ActiveCountdowns) + onCountdownStarted(countdown); + } + } + + protected override void Update() + { + base.Update(); + + TimeSpan remaining = countdownEndTime - DateTimeOffset.Now; + + text.Alpha = remaining.TotalSeconds > 0 ? 1f : 0.2f; + + if (remaining.TotalSeconds > 10) + text.Font = text.Font.With(weight: FontWeight.SemiBold); + else + text.Font = text.Font.With(weight: FontWeight.Bold); + + int minutes = (int)Math.Max(0, remaining.TotalMinutes); + int seconds = Math.Max(0, remaining.Seconds); + int ms = Math.Max(0, remaining.Milliseconds); + + text.Text = $"{minutes:00}:{seconds:00}.{ms:000}"; + } + + private void onCountdownStarted(MultiplayerCountdown countdown) => Scheduler.Add(() => + { + if (countdown is MatchmakingStageCountdown) + countdownEndTime = DateTimeOffset.Now + countdown.TimeRemaining; + }); + + private void onCountdownStopped(MultiplayerCountdown countdown) => Scheduler.Add(() => + { + if (countdown is not MatchmakingStageCountdown) + return; + + countdownEndTime = DateTimeOffset.Now; + }); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (client.IsNotNull()) + { + client.CountdownStarted -= onCountdownStarted; + client.CountdownStopped -= onCountdownStopped; + } + } + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/Match/StageDisplay.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/Match/StageDisplay.cs index e428e3b044..b45e8054a0 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/Match/StageDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/Match/StageDisplay.cs @@ -72,6 +72,12 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match Direction = FillDirection.Horizontal, }, }, + new TimerText + { + Y = -38, + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }, new StatusText { Y = 32, diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index bd16c36eec..5b2876a989 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -851,7 +851,7 @@ namespace osu.Game.Tests.Visual.Multiplayer await StartCountdown(new MatchmakingStageCountdown { Stage = stage, - TimeRemaining = TimeSpan.FromSeconds(10) + TimeRemaining = TimeSpan.FromSeconds(stage == MatchmakingStage.UserBeatmapSelect ? 30 : 10) }).ConfigureAwait(false); }