From 3c8f871b2815ca8f4618dbf2e51870e31324cae8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Dec 2020 17:39:08 +0900 Subject: [PATCH 1/8] Move player constructor configuration to dedicated class; add AllowRestart parameter --- .../TestSceneHoldNoteInput.cs | 6 +- .../TestSceneOutOfOrderHits.cs | 6 +- .../TestSceneLegacyBeatmapSkin.cs | 6 +- .../TestSceneOutOfOrderHits.cs | 6 +- .../TestSceneSliderInput.cs | 6 +- .../Screens/Multi/Play/TimeshiftPlayer.cs | 4 +- .../RealtimeMultiplayer/RealtimePlayer.cs | 7 +- osu.Game/Screens/Play/Player.cs | 140 +++++++++--------- osu.Game/Screens/Play/PlayerConfiguration.cs | 23 +++ osu.Game/Screens/Play/ReplayPlayer.cs | 4 +- osu.Game/Screens/Ranking/ResultsScreen.cs | 15 +- osu.Game/Tests/Visual/TestPlayer.cs | 6 +- 12 files changed, 146 insertions(+), 83 deletions(-) create mode 100644 osu.Game/Screens/Play/PlayerConfiguration.cs diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs index 5cb1519196..596430f9e5 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs @@ -355,7 +355,11 @@ namespace osu.Game.Rulesets.Mania.Tests protected override bool PauseOnFocusLost => false; public ScoreAccessibleReplayPlayer(Score score) - : base(score, false, false) + : base(score, new PlayerConfiguration + { + AllowPause = false, + ShowResults = false, + }) { } } diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs index cecac38f70..18891f8c58 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs @@ -176,7 +176,11 @@ namespace osu.Game.Rulesets.Mania.Tests protected override bool PauseOnFocusLost => false; public ScoreAccessibleReplayPlayer(Score score) - : base(score, false, false) + : base(score, new PlayerConfiguration + { + AllowPause = false, + ShowResults = false, + }) { } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index 3ff37c4147..a768626005 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -74,7 +74,11 @@ namespace osu.Game.Rulesets.Osu.Tests private readonly bool userHasCustomColours; public ExposedPlayer(bool userHasCustomColours) - : base(false, false) + : base(new PlayerConfiguration + { + AllowPause = false, + ShowResults = false, + }) { this.userHasCustomColours = userHasCustomColours; } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs index 32a36ab317..296b421a11 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs @@ -439,7 +439,11 @@ namespace osu.Game.Rulesets.Osu.Tests protected override bool PauseOnFocusLost => false; public ScoreAccessibleReplayPlayer(Score score) - : base(score, false, false) + : base(score, new PlayerConfiguration + { + AllowPause = false, + ShowResults = false, + }) { } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs index 0164fb8bf4..2cc031405e 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs @@ -378,7 +378,11 @@ namespace osu.Game.Rulesets.Osu.Tests protected override bool PauseOnFocusLost => false; public ScoreAccessibleReplayPlayer(Score score) - : base(score, false, false) + : base(score, new PlayerConfiguration + { + AllowPause = false, + ShowResults = false, + }) { } } diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index e8462088f1..f07f1c2fb0 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -37,8 +37,8 @@ namespace osu.Game.Screens.Multi.Play [Resolved] private IBindable ruleset { get; set; } - public TimeshiftPlayer(PlaylistItem playlistItem, bool allowPause = true) - : base(allowPause) + public TimeshiftPlayer(PlaylistItem playlistItem, PlayerConfiguration configuration = null) + : base(configuration) { PlaylistItem = playlistItem; } diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs index c6d44686b5..4b878f43d0 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs @@ -12,6 +12,7 @@ using osu.Game.Online.Multiplayer; using osu.Game.Online.RealtimeMultiplayer; using osu.Game.Scoring; using osu.Game.Screens.Multi.Play; +using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; namespace osu.Game.Screens.Multi.RealtimeMultiplayer @@ -31,7 +32,11 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer private readonly ManualResetEventSlim startedEvent = new ManualResetEventSlim(); public RealtimePlayer(PlaylistItem playlistItem) - : base(playlistItem, false) + : base(playlistItem, new PlayerConfiguration + { + AllowPause = false, + AllowRestart = false, + }) { } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index c539dff5d9..a1c91ab26b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -128,18 +128,14 @@ namespace osu.Game.Screens.Play /// protected virtual bool CheckModsAllowFailure() => Mods.Value.OfType().All(m => m.PerformFail()); - private readonly bool allowPause; - private readonly bool showResults; + public readonly PlayerConfiguration Configuration; /// /// Create a new player instance. /// - /// Whether pausing should be allowed. If not allowed, attempting to pause will quit. - /// Whether results screen should be pushed on completion. - public Player(bool allowPause = true, bool showResults = true) + public Player(PlayerConfiguration configuration = null) { - this.allowPause = allowPause; - this.showResults = showResults; + this.Configuration = configuration ??= new PlayerConfiguration(); } private GameplayBeatmap gameplayBeatmap; @@ -317,59 +313,77 @@ namespace osu.Game.Screens.Play } }; - private Drawable createOverlayComponents(WorkingBeatmap working) => new Container + private Drawable createOverlayComponents(WorkingBeatmap working) { - RelativeSizeAxes = Axes.Both, - Children = new[] + var container = new Container { - DimmableStoryboard.OverlayLayerContainer.CreateProxy(), - BreakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor) + RelativeSizeAxes = Axes.Both, + Children = new[] { - Clock = DrawableRuleset.FrameStableClock, - ProcessCustomClock = false, - Breaks = working.Beatmap.Breaks - }, - // display the cursor above some HUD elements. - DrawableRuleset.Cursor?.CreateProxy() ?? new Container(), - DrawableRuleset.ResumeOverlay?.CreateProxy() ?? new Container(), - HUDOverlay = new HUDOverlay(ScoreProcessor, HealthProcessor, DrawableRuleset, Mods.Value) - { - HoldToQuit = + DimmableStoryboard.OverlayLayerContainer.CreateProxy(), + BreakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor) { - Action = performUserRequestedExit, - IsPaused = { BindTarget = GameplayClockContainer.IsPaused } + Clock = DrawableRuleset.FrameStableClock, + ProcessCustomClock = false, + Breaks = working.Beatmap.Breaks }, - PlayerSettingsOverlay = { PlaybackSettings = { UserPlaybackRate = { BindTarget = GameplayClockContainer.UserPlaybackRate } } }, - KeyCounter = + // display the cursor above some HUD elements. + DrawableRuleset.Cursor?.CreateProxy() ?? new Container(), + DrawableRuleset.ResumeOverlay?.CreateProxy() ?? new Container(), + HUDOverlay = new HUDOverlay(ScoreProcessor, HealthProcessor, DrawableRuleset, Mods.Value) { - AlwaysVisible = { BindTarget = DrawableRuleset.HasReplayLoaded }, - IsCounting = false + HoldToQuit = + { + Action = performUserRequestedExit, + IsPaused = { BindTarget = GameplayClockContainer.IsPaused } + }, + PlayerSettingsOverlay = { PlaybackSettings = { UserPlaybackRate = { BindTarget = GameplayClockContainer.UserPlaybackRate } } }, + KeyCounter = + { + AlwaysVisible = { BindTarget = DrawableRuleset.HasReplayLoaded }, + IsCounting = false + }, + RequestSeek = time => + { + GameplayClockContainer.Seek(time); + GameplayClockContainer.Start(); + }, + Anchor = Anchor.Centre, + Origin = Anchor.Centre }, - RequestSeek = time => + skipOverlay = new SkipOverlay(DrawableRuleset.GameplayStartTime) { - GameplayClockContainer.Seek(time); - GameplayClockContainer.Start(); + RequestSkip = GameplayClockContainer.Skip }, - Anchor = Anchor.Centre, - Origin = Anchor.Centre - }, - skipOverlay = new SkipOverlay(DrawableRuleset.GameplayStartTime) - { - RequestSkip = GameplayClockContainer.Skip - }, - FailOverlay = new FailOverlay - { - OnRetry = Restart, - OnQuit = performUserRequestedExit, - }, - PauseOverlay = new PauseOverlay - { - OnResume = Resume, - Retries = RestartCount, - OnRetry = Restart, - OnQuit = performUserRequestedExit, - }, - new HotkeyRetryOverlay + FailOverlay = new FailOverlay + { + OnRetry = Restart, + OnQuit = performUserRequestedExit, + }, + PauseOverlay = new PauseOverlay + { + OnResume = Resume, + Retries = RestartCount, + OnRetry = Restart, + OnQuit = performUserRequestedExit, + }, + new HotkeyExitOverlay + { + Action = () => + { + if (!this.IsCurrentScreen()) return; + + fadeOut(true); + performImmediateExit(); + }, + }, + failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, }, + } + }; + + if (Configuration.AllowRestart) + { + container.Add(new HotkeyRetryOverlay { Action = () => { @@ -378,20 +392,11 @@ namespace osu.Game.Screens.Play fadeOut(true); Restart(); }, - }, - new HotkeyExitOverlay - { - Action = () => - { - if (!this.IsCurrentScreen()) return; - - fadeOut(true); - performImmediateExit(); - }, - }, - failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, }, + }); } - }; + + return container; + } private void onBreakTimeChanged(ValueChangedEvent isBreakTime) { @@ -490,6 +495,9 @@ namespace osu.Game.Screens.Play /// public void Restart() { + if (!Configuration.AllowRestart) + return; + // at the point of restarting the track should either already be paused or the volume should be zero. // stopping here is to ensure music doesn't become audible after exiting back to PlayerLoader. musicController.Stop(); @@ -529,7 +537,7 @@ namespace osu.Game.Screens.Play ValidForResume = false; - if (!showResults) return; + if (!Configuration.ShowResults) return; scoreSubmissionTask ??= Task.Run(async () => { @@ -628,7 +636,7 @@ namespace osu.Game.Screens.Play private bool canPause => // must pass basic screen conditions (beatmap loaded, instance allows pause) - LoadedBeatmapSuccessfully && allowPause && ValidForResume + LoadedBeatmapSuccessfully && Configuration.AllowPause && ValidForResume // replays cannot be paused and exit immediately && !DrawableRuleset.HasReplayLoaded.Value // cannot pause if we are already in a fail state diff --git a/osu.Game/Screens/Play/PlayerConfiguration.cs b/osu.Game/Screens/Play/PlayerConfiguration.cs new file mode 100644 index 0000000000..475a234679 --- /dev/null +++ b/osu.Game/Screens/Play/PlayerConfiguration.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. + +namespace osu.Game.Screens.Play +{ + public class PlayerConfiguration + { + /// + /// Whether pausing should be allowed. If not allowed, attempting to pause will quit. + /// + public bool AllowPause { get; set; } = true; + + /// + /// Whether results screen should be pushed on completion. + /// + public bool ShowResults { get; set; } = true; + + /// + /// Whether the player should be allowed to trigger a restart. + /// + public bool AllowRestart { get; set; } = true; + } +} diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index a07213cb33..e23cc22929 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -16,8 +16,8 @@ namespace osu.Game.Screens.Play // Disallow replays from failing. (see https://github.com/ppy/osu/issues/6108) protected override bool CheckModsAllowFailure() => false; - public ReplayPlayer(Score score, bool allowPause = true, bool showResults = true) - : base(allowPause, showResults) + public ReplayPlayer(Score score, PlayerConfiguration configuration = null) + : base(configuration) { Score = score; } diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 528a1842af..8a12427798 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -164,15 +164,18 @@ namespace osu.Game.Screens.Ranking { buttons.Add(new RetryButton { Width = 300 }); - AddInternal(new HotkeyRetryOverlay + if (player?.Configuration.AllowRestart == true) { - Action = () => + AddInternal(new HotkeyRetryOverlay { - if (!this.IsCurrentScreen()) return; + Action = () => + { + if (!this.IsCurrentScreen()) return; - player?.Restart(); - }, - }); + player?.Restart(); + }, + }); + } } } diff --git a/osu.Game/Tests/Visual/TestPlayer.cs b/osu.Game/Tests/Visual/TestPlayer.cs index f016d29f38..f47391ce6a 100644 --- a/osu.Game/Tests/Visual/TestPlayer.cs +++ b/osu.Game/Tests/Visual/TestPlayer.cs @@ -37,7 +37,11 @@ namespace osu.Game.Tests.Visual public readonly List Results = new List(); public TestPlayer(bool allowPause = true, bool showResults = true, bool pauseOnFocusLost = false) - : base(allowPause, showResults) + : base(new PlayerConfiguration + { + AllowPause = allowPause, + ShowResults = showResults + }) { PauseOnFocusLost = pauseOnFocusLost; } From f9fd909187c3839101e3c52aa476e2460b3e0778 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Dec 2020 18:07:38 +0900 Subject: [PATCH 2/8] Fix missed inspections --- osu.Game/Screens/Play/Player.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a1c91ab26b..f1fb27e154 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -135,7 +135,7 @@ namespace osu.Game.Screens.Play /// public Player(PlayerConfiguration configuration = null) { - this.Configuration = configuration ??= new PlayerConfiguration(); + Configuration = configuration ?? new PlayerConfiguration(); } private GameplayBeatmap gameplayBeatmap; From 9843da59f4866918391902de30125577b60c1464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 23 Dec 2020 20:29:17 +0100 Subject: [PATCH 3/8] Fix intermittent test fail due to duplicate user `TestSceneRealtimeReadyButton` was manually adding `API.LocalUser`, which wasn't actually needed. The base `RealtimeMultiplayerTestScene` by default creates a new room as `API.LocalUser`, therefore automatically adding that user to the room - and as such there is no need to add them manually unless the `joinRoom` ctor param is specified as `false`. --- .../Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs index b7cd81fb32..e9d3ddb32d 100644 --- a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs +++ b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs @@ -55,8 +55,6 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer } } }; - - Client.AddUser(API.LocalUser.Value); }); [Test] From 47020c888731c2370d6f81fbfe9f11d6e7fcfdc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 23 Dec 2020 21:00:47 +0100 Subject: [PATCH 4/8] Add failing test cases --- .../TestSceneRealtimeMultiplayer.cs | 24 +++++++++++++++++++ .../TestRealtimeMultiplayerClient.cs | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayer.cs b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayer.cs index 80955ca380..5cf80df6aa 100644 --- a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayer.cs +++ b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayer.cs @@ -1,7 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using NUnit.Framework; using osu.Game.Screens.Multi.Components; +using osu.Game.Users; namespace osu.Game.Tests.Visual.RealtimeMultiplayer { @@ -15,6 +17,28 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer AddUntilStep("wait for loaded", () => multi.IsLoaded); } + [Test] + public void TestOneUserJoinedMultipleTimes() + { + var user = new User { Id = 33 }; + + AddRepeatStep("add user multiple times", () => Client.AddUser(user), 3); + + AddAssert("room has 2 users", () => Client.Room?.Users.Count == 2); + } + + [Test] + public void TestOneUserLeftMultipleTimes() + { + var user = new User { Id = 44 }; + + AddStep("add user", () => Client.AddUser(user)); + AddAssert("room has 2 users", () => Client.Room?.Users.Count == 2); + + AddRepeatStep("remove user multiple times", () => Client.RemoveUser(user), 3); + AddAssert("room has 1 user", () => Client.Room?.Users.Count == 1); + } + private class TestRealtimeMultiplayer : Screens.Multi.RealtimeMultiplayer.RealtimeMultiplayer { protected override RoomManager CreateRoomManager() => new TestRealtimeRoomManager(); diff --git a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs b/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs index de52633c88..52047016e2 100644 --- a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer public void RemoveUser(User user) { Debug.Assert(Room != null); - ((IMultiplayerClient)this).UserLeft(Room.Users.Single(u => u.User == user)); + ((IMultiplayerClient)this).UserLeft(new MultiplayerRoomUser(user.Id)); Schedule(() => { From a71496bc4e9f0386d13b295eb92496f33144e5b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 23 Dec 2020 20:34:19 +0100 Subject: [PATCH 5/8] Sanity check received user joined messages While test failures fixed in 9843da5 were a shortcoming of the test, they exposed a potential vulnerable point of the multiplayer client logic. In case of unreliable message delivery it is not unreasonable that duplicate messages might arrive, in which case the same scenario that failed in the tests could crash the game. To ensure that is not the case, explicitly screen each new joined user against the room user list, to ensure that duplicates do not show up. `UserLeft` is already tolerant in that respect (if a user is requested to be removed twice by the server, the second removal just won't do anything). --- .../Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs index 6331d324a6..e8dbeda9cb 100644 --- a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs @@ -226,6 +226,10 @@ namespace osu.Game.Online.RealtimeMultiplayer if (Room == null) return; + // for sanity, ensure that there can be no duplicate users in the room user list. + if (Room.Users.Any(existing => existing.UserID == user.UserID)) + return; + Room.Users.Add(user); RoomChanged?.Invoke(); From 61a5d3ef4ae8d7724eb02686a01a33e85cbc4e0c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Dec 2020 13:32:35 +0900 Subject: [PATCH 6/8] Remove double handling of restart allowance on results screen (already handled locally) --- osu.Game/Screens/Ranking/ResultsScreen.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 8a12427798..528a1842af 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -164,18 +164,15 @@ namespace osu.Game.Screens.Ranking { buttons.Add(new RetryButton { Width = 300 }); - if (player?.Configuration.AllowRestart == true) + AddInternal(new HotkeyRetryOverlay { - AddInternal(new HotkeyRetryOverlay + Action = () => { - Action = () => - { - if (!this.IsCurrentScreen()) return; + if (!this.IsCurrentScreen()) return; - player?.Restart(); - }, - }); - } + player?.Restart(); + }, + }); } } From 1f80f01b53f4861a26ee8564bb7b81684cfad488 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Dec 2020 14:46:52 +0900 Subject: [PATCH 7/8] Add accuracy to frame bundle header --- osu.Game/Online/Spectator/FrameHeader.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Spectator/FrameHeader.cs b/osu.Game/Online/Spectator/FrameHeader.cs index b4988fecf9..135b356eda 100644 --- a/osu.Game/Online/Spectator/FrameHeader.cs +++ b/osu.Game/Online/Spectator/FrameHeader.cs @@ -14,6 +14,11 @@ namespace osu.Game.Online.Spectator [Serializable] public class FrameHeader { + /// + /// The current accuracy of the score. + /// + public double Accuracy { get; set; } + /// /// The current combo of the score. /// @@ -42,16 +47,18 @@ namespace osu.Game.Online.Spectator { Combo = score.Combo; MaxCombo = score.MaxCombo; + Accuracy = score.Accuracy; // copy for safety Statistics = new Dictionary(score.Statistics); } [JsonConstructor] - public FrameHeader(int combo, int maxCombo, Dictionary statistics, DateTimeOffset receivedTime) + public FrameHeader(int combo, int maxCombo, double accuracy, Dictionary statistics, DateTimeOffset receivedTime) { Combo = combo; MaxCombo = maxCombo; + Accuracy = accuracy; Statistics = statistics; ReceivedTime = receivedTime; } From d66e2183185bbedc26b2d7350499e71d3aebc0fb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Dec 2020 14:57:23 +0900 Subject: [PATCH 8/8] Source display accuracy from header and remove from ScoreProcessor function --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 15 +++++---------- .../Play/HUD/MultiplayerGameplayLeaderboard.cs | 4 ++-- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 4b2e2bf715..2024290460 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -193,7 +193,7 @@ namespace osu.Game.Rulesets.Scoring private void updateScore() { if (rollingMaxBaseScore != 0) - Accuracy.Value = baseScore / rollingMaxBaseScore; + Accuracy.Value = calculateAccuracyRatio(baseScore, true); TotalScore.Value = getScore(Mode.Value); } @@ -233,13 +233,13 @@ namespace osu.Game.Rulesets.Scoring } /// - /// Given a minimal set of inputs, return the computed score and accuracy for the tracked beatmap / mods combination, at the current point in time. + /// Given a minimal set of inputs, return the computed score for the tracked beatmap / mods combination, at the current point in time. /// /// The to compute the total score in. /// The maximum combo achievable in the beatmap. /// Statistics to be used for calculating accuracy, bonus score, etc. - /// The computed score and accuracy for provided inputs. - public (double score, double accuracy) GetScoreAndAccuracy(ScoringMode mode, int maxCombo, Dictionary statistics) + /// The computed score for provided inputs. + public double GetImmediateScore(ScoringMode mode, int maxCombo, Dictionary statistics) { // calculate base score from statistics pairs int computedBaseScore = 0; @@ -252,12 +252,7 @@ namespace osu.Game.Rulesets.Scoring computedBaseScore += Judgement.ToNumericResult(pair.Key) * pair.Value; } - double pointInTimeAccuracy = calculateAccuracyRatio(computedBaseScore, true); - double comboRatio = calculateComboRatio(maxCombo); - - double score = GetScore(mode, maxAchievableCombo, calculateAccuracyRatio(computedBaseScore), comboRatio, scoreResultCounts); - - return (score, pointInTimeAccuracy); + return GetScore(mode, maxAchievableCombo, calculateAccuracyRatio(computedBaseScore), calculateComboRatio(maxCombo), scoreResultCounts); } /// diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 12321de442..c10ec9e004 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -122,8 +122,8 @@ namespace osu.Game.Screens.Play.HUD if (LastHeader == null) return; - (score.Value, accuracy.Value) = processor.GetScoreAndAccuracy(mode, LastHeader.MaxCombo, LastHeader.Statistics); - + score.Value = processor.GetImmediateScore(mode, LastHeader.MaxCombo, LastHeader.Statistics); + accuracy.Value = LastHeader.Accuracy; currentCombo.Value = LastHeader.Combo; } }