From 9a6f4ef76d052bf11317df92284022cf8f74d42b Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Sun, 19 Jun 2022 23:59:37 +0900 Subject: [PATCH 01/21] Save score button on failed screen --- osu.Game/Screens/Play/FailOverlay.cs | 3 +++ osu.Game/Screens/Play/Player.cs | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/osu.Game/Screens/Play/FailOverlay.cs b/osu.Game/Screens/Play/FailOverlay.cs index 1a31b0f46236..b09a7de6314c 100644 --- a/osu.Game/Screens/Play/FailOverlay.cs +++ b/osu.Game/Screens/Play/FailOverlay.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using osu.Game.Graphics; using osuTK.Graphics; using osu.Framework.Allocation; @@ -11,12 +12,14 @@ namespace osu.Game.Screens.Play { public class FailOverlay : GameplayMenuOverlay { + public Action SaveReplay; public override string Header => "failed"; public override string Description => "you're dead, try again?"; [BackgroundDependencyLoader] private void load(OsuColour colours) { + AddButton("Save replay and Quit", colours.Blue, () => SaveReplay?.Invoke()); AddButton("Retry", colours.YellowDark, () => OnRetry?.Invoke()); AddButton("Quit", new Color4(170, 27, 39, 255), () => OnQuit?.Invoke()); } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 4a8460ff4619..c40451048722 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -266,6 +266,7 @@ private void load(AudioManager audio, OsuConfigManager config, OsuGameBase game, }, FailOverlay = new FailOverlay { + SaveReplay = saveReplay, OnRetry = Restart, OnQuit = () => PerformExit(true), }, @@ -1043,6 +1044,21 @@ public override bool OnExiting(ScreenExitEvent e) return base.OnExiting(e); } + // Don't know if prepareScoreForResults useful + private async void saveReplay() + { + var scoreCopy = Score.DeepClone(); + try + { + await ImportScore(scoreCopy).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.Error(ex, @"Score import failed!"); + } + PerformExit(true); + } + /// /// Creates the player's . /// From 5a5cc523ce78421a32cb18ec84bb9e0c75e12275 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Mon, 20 Jun 2022 23:43:33 +0800 Subject: [PATCH 02/21] Let F to -1 temporary --- osu.Game/Scoring/ScoreRank.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Scoring/ScoreRank.cs b/osu.Game/Scoring/ScoreRank.cs index 9de8561b4a74..dc90e417cdd7 100644 --- a/osu.Game/Scoring/ScoreRank.cs +++ b/osu.Game/Scoring/ScoreRank.cs @@ -11,6 +11,11 @@ namespace osu.Game.Scoring { public enum ScoreRank { + // TODO: reconsider changing later on + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankD))] + [Description(@"F")] + F = -1, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankD))] [Description(@"D")] D, From 43ead5820a9db5c1d2403398509f94d7017c4713 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Tue, 21 Jun 2022 00:54:50 +0900 Subject: [PATCH 03/21] deal with test --- osu.Game/Screens/Play/FailOverlay.cs | 2 +- osu.Game/Screens/Play/Player.cs | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/FailOverlay.cs b/osu.Game/Screens/Play/FailOverlay.cs index b09a7de6314c..1b9d81d33b9a 100644 --- a/osu.Game/Screens/Play/FailOverlay.cs +++ b/osu.Game/Screens/Play/FailOverlay.cs @@ -19,9 +19,9 @@ public class FailOverlay : GameplayMenuOverlay [BackgroundDependencyLoader] private void load(OsuColour colours) { - AddButton("Save replay and Quit", colours.Blue, () => SaveReplay?.Invoke()); AddButton("Retry", colours.YellowDark, () => OnRetry?.Invoke()); AddButton("Quit", new Color4(170, 27, 39, 255), () => OnQuit?.Invoke()); + AddButton("Save replay and Quit", colours.Blue, () => SaveReplay?.Invoke()); } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index c40451048722..d38c10c0711c 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -1044,18 +1044,19 @@ public override bool OnExiting(ScreenExitEvent e) return base.OnExiting(e); } - // Don't know if prepareScoreForResults useful - private async void saveReplay() + private void saveReplay() { var scoreCopy = Score.DeepClone(); + try { - await ImportScore(scoreCopy).ConfigureAwait(false); + ImportScore(scoreCopy).ConfigureAwait(false); } catch (Exception ex) { Logger.Error(ex, @"Score import failed!"); } + PerformExit(true); } From f2eb7e055166bcf08c5cf2cbeeeb280498a2275a Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Tue, 21 Jun 2022 19:06:38 +0800 Subject: [PATCH 04/21] Use better design and fix some problem Let saveReplay async but still void Make failed score's rank = F --- osu.Game/Screens/Play/FailOverlay.cs | 42 ++++++++++++++++- osu.Game/Screens/Play/Player.cs | 13 +++--- .../Screens/Play/SaveFailedScoreButton.cs | 45 +++++++++++++++++++ 3 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 osu.Game/Screens/Play/SaveFailedScoreButton.cs diff --git a/osu.Game/Screens/Play/FailOverlay.cs b/osu.Game/Screens/Play/FailOverlay.cs index 1b9d81d33b9a..881791271b54 100644 --- a/osu.Game/Screens/Play/FailOverlay.cs +++ b/osu.Game/Screens/Play/FailOverlay.cs @@ -5,8 +5,14 @@ using System; using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using osuTK; using osuTK.Graphics; using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Screens.Play { @@ -21,7 +27,41 @@ private void load(OsuColour colours) { AddButton("Retry", colours.YellowDark, () => OnRetry?.Invoke()); AddButton("Quit", new Color4(170, 27, 39, 255), () => OnQuit?.Invoke()); - AddButton("Save replay and Quit", colours.Blue, () => SaveReplay?.Invoke()); + // from #10339 maybe this is a better visual effect + Add(new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Height = TwoLayerButton.SIZE_EXTENDED.Y, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex("#333") + }, + new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Spacing = new Vector2(5), + Padding = new MarginPadding(10), + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new SaveFailedScoreButton() + { + OnSave = SaveReplay, + RelativeSizeAxes = Axes.Y, + Width = 300 + }, + } + } + } + }); } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index d38c10c0711c..8223ed1e3d30 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -266,7 +266,7 @@ private void load(AudioManager audio, OsuConfigManager config, OsuGameBase game, }, FailOverlay = new FailOverlay { - SaveReplay = saveReplay, + SaveReplay = saveFailedReplay, OnRetry = Restart, OnQuit = () => PerformExit(true), }, @@ -1024,8 +1024,7 @@ public override bool OnExiting(ScreenExitEvent e) if (prepareScoreForDisplayTask == null) { Score.ScoreInfo.Passed = false; - // potentially should be ScoreRank.F instead? this is the best alternative for now. - Score.ScoreInfo.Rank = ScoreRank.D; + Score.ScoreInfo.Rank = ScoreRank.F; } // EndPlaying() is typically called from ReplayRecorder.Dispose(). Disposal is currently asynchronous. @@ -1044,20 +1043,20 @@ public override bool OnExiting(ScreenExitEvent e) return base.OnExiting(e); } - private void saveReplay() + private async void saveFailedReplay() { + Score.ScoreInfo.Passed = false; + Score.ScoreInfo.Rank = ScoreRank.F; var scoreCopy = Score.DeepClone(); try { - ImportScore(scoreCopy).ConfigureAwait(false); + await ImportScore(scoreCopy).ConfigureAwait(false); } catch (Exception ex) { Logger.Error(ex, @"Score import failed!"); } - - PerformExit(true); } /// diff --git a/osu.Game/Screens/Play/SaveFailedScoreButton.cs b/osu.Game/Screens/Play/SaveFailedScoreButton.cs new file mode 100644 index 000000000000..1c198421abf2 --- /dev/null +++ b/osu.Game/Screens/Play/SaveFailedScoreButton.cs @@ -0,0 +1,45 @@ +// 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.Bindables; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online; + +namespace osu.Game.Screens.Play +{ + public class SaveFailedScoreButton : DownloadButton + { + public Action? OnSave; + + [BackgroundDependencyLoader] + private void load() + { + State.BindValueChanged(updateTooltip, true); + Action = saveScore; + } + + private void saveScore() + { + if (State.Value != DownloadState.LocallyAvailable) + OnSave?.Invoke(); + + State.Value = DownloadState.LocallyAvailable; + } + + private void updateTooltip(ValueChangedEvent state) + { + switch (state.NewValue) + { + case DownloadState.LocallyAvailable: + TooltipText = @"Score saved"; + break; + + default: + TooltipText = @"Save score"; + break; + } + } + } +} From bff35cb34895848daa8af880483e08ff9c59b8ed Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Wed, 22 Jun 2022 01:19:20 +0900 Subject: [PATCH 05/21] Shake button when replay already save --- .../Screens/Play/SaveFailedScoreButton.cs | 43 +++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/SaveFailedScoreButton.cs b/osu.Game/Screens/Play/SaveFailedScoreButton.cs index 1c198421abf2..3efd8fa4a3cf 100644 --- a/osu.Game/Screens/Play/SaveFailedScoreButton.cs +++ b/osu.Game/Screens/Play/SaveFailedScoreButton.cs @@ -4,20 +4,54 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online; +using osuTK; namespace osu.Game.Screens.Play { - public class SaveFailedScoreButton : DownloadButton + public class SaveFailedScoreButton : CompositeDrawable { public Action? OnSave; + protected readonly Bindable State = new Bindable(); + + private DownloadButton button; + private ShakeContainer shakeContainer; + + public SaveFailedScoreButton() + { + InternalChild = shakeContainer = new ShakeContainer + { + RelativeSizeAxes = Axes.Both, + Child = button = new DownloadButton + { + RelativeSizeAxes = Axes.Both, + } + }; + Size = new Vector2(50, 30); + } + [BackgroundDependencyLoader] private void load() { + button.Action = () => + { + switch (State.Value) + { + case DownloadState.LocallyAvailable: + shakeContainer.Shake(); + break; + + default: + saveScore(); + break; + } + }; State.BindValueChanged(updateTooltip, true); - Action = saveScore; } private void saveScore() @@ -26,6 +60,7 @@ private void saveScore() OnSave?.Invoke(); State.Value = DownloadState.LocallyAvailable; + button.State.Value = DownloadState.LocallyAvailable; } private void updateTooltip(ValueChangedEvent state) @@ -33,11 +68,11 @@ private void updateTooltip(ValueChangedEvent state) switch (state.NewValue) { case DownloadState.LocallyAvailable: - TooltipText = @"Score saved"; + button.TooltipText = @"Score saved"; break; default: - TooltipText = @"Save score"; + button.TooltipText = @"Save score"; break; } } From 946178ca412762ff871d8ea7f4d0579b8eebaf76 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Tue, 28 Jun 2022 20:03:21 +0900 Subject: [PATCH 06/21] Remove useless `LocalisableDescription` --- osu.Game/Scoring/ScoreRank.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Scoring/ScoreRank.cs b/osu.Game/Scoring/ScoreRank.cs index dc90e417cdd7..a4bb99add133 100644 --- a/osu.Game/Scoring/ScoreRank.cs +++ b/osu.Game/Scoring/ScoreRank.cs @@ -11,8 +11,6 @@ namespace osu.Game.Scoring { public enum ScoreRank { - // TODO: reconsider changing later on - [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankD))] [Description(@"F")] F = -1, From fd0d8b1ce3d5062b349484436d035d35a13a0e88 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Wed, 29 Jun 2022 22:50:47 +0900 Subject: [PATCH 07/21] Add button state, fix async issues, watch replay method Most borrowed from `ReplayDownloadButton` --- osu.Game/Screens/Play/FailOverlay.cs | 8 +- osu.Game/Screens/Play/Player.cs | 7 +- .../Screens/Play/SaveFailedScoreButton.cs | 93 +++++++++++++++---- 3 files changed, 86 insertions(+), 22 deletions(-) diff --git a/osu.Game/Screens/Play/FailOverlay.cs b/osu.Game/Screens/Play/FailOverlay.cs index 881791271b54..75b06f859d70 100644 --- a/osu.Game/Screens/Play/FailOverlay.cs +++ b/osu.Game/Screens/Play/FailOverlay.cs @@ -4,6 +4,8 @@ #nullable disable using System; +using System.Threading.Tasks; +using osu.Game.Scoring; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osuTK; @@ -18,7 +20,7 @@ namespace osu.Game.Screens.Play { public class FailOverlay : GameplayMenuOverlay { - public Action SaveReplay; + public Func> SaveReplay; public override string Header => "failed"; public override string Description => "you're dead, try again?"; @@ -52,10 +54,8 @@ private void load(OsuColour colours) Direction = FillDirection.Horizontal, Children = new Drawable[] { - new SaveFailedScoreButton() + new SaveFailedScoreButton(SaveReplay) { - OnSave = SaveReplay, - RelativeSizeAxes = Axes.Y, Width = 300 }, } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 8223ed1e3d30..f20cb74db6ad 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -1043,7 +1043,7 @@ public override bool OnExiting(ScreenExitEvent e) return base.OnExiting(e); } - private async void saveFailedReplay() + private async Task saveFailedReplay() { Score.ScoreInfo.Passed = false; Score.ScoreInfo.Rank = ScoreRank.F; @@ -1051,12 +1051,15 @@ private async void saveFailedReplay() try { - await ImportScore(scoreCopy).ConfigureAwait(false); + await ImportScore(scoreCopy); } catch (Exception ex) { Logger.Error(ex, @"Score import failed!"); + return null; } + + return scoreCopy.ScoreInfo; } /// diff --git a/osu.Game/Screens/Play/SaveFailedScoreButton.cs b/osu.Game/Screens/Play/SaveFailedScoreButton.cs index 3efd8fa4a3cf..b5727487dcf9 100644 --- a/osu.Game/Screens/Play/SaveFailedScoreButton.cs +++ b/osu.Game/Screens/Play/SaveFailedScoreButton.cs @@ -1,11 +1,16 @@ // 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 System; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Threading; +using osu.Game.Scoring; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online; @@ -15,14 +20,25 @@ namespace osu.Game.Screens.Play { public class SaveFailedScoreButton : CompositeDrawable { - public Action? OnSave; + public Func> SaveReplay; + private Task saveReplayAsync; + private ScoreInfo score; + + private ScheduledDelegate saveScoreDelegate; - protected readonly Bindable State = new Bindable(); + protected readonly Bindable State = new Bindable(); private DownloadButton button; private ShakeContainer shakeContainer; - public SaveFailedScoreButton() + public SaveFailedScoreButton(Func> sr) + { + Size = new Vector2(50, 30); + SaveReplay = sr; + } + + [BackgroundDependencyLoader(true)] + private void load(OsuGame game) { InternalChild = shakeContainer = new ShakeContainer { @@ -32,17 +48,16 @@ public SaveFailedScoreButton() RelativeSizeAxes = Axes.Both, } }; - Size = new Vector2(50, 30); - } - [BackgroundDependencyLoader] - private void load() - { button.Action = () => { switch (State.Value) { - case DownloadState.LocallyAvailable: + case ImportState.Imported: + game?.PresentScore(score, ScorePresentType.Gameplay); + break; + + case ImportState.Importing: shakeContainer.Shake(); break; @@ -52,23 +67,61 @@ private void load() } }; State.BindValueChanged(updateTooltip, true); + State.BindValueChanged(state => + { + switch (state.NewValue) + { + case ImportState.Imported: + button.State.Value = DownloadState.LocallyAvailable; + break; + + case ImportState.Importing: + button.State.Value = DownloadState.Importing; + break; + + case ImportState.Failed: + button.State.Value = DownloadState.NotDownloaded; + break; + } + }, true); } private void saveScore() { - if (State.Value != DownloadState.LocallyAvailable) - OnSave?.Invoke(); + State.Value = ImportState.Importing; + saveReplayAsync = Task.Run(SaveReplay); + + saveScoreDelegate = new ScheduledDelegate(() => + { + if (saveReplayAsync?.IsCompleted != true) + // If the asynchronous preparation has not completed, keep repeating this delegate. + return; + + saveScoreDelegate?.Cancel(); + + score = saveReplayAsync.GetAwaiter().GetResult(); + + State.Value = score != null ? ImportState.Imported : ImportState.Failed; + }, Time.Current, 50); - State.Value = DownloadState.LocallyAvailable; - button.State.Value = DownloadState.LocallyAvailable; + Scheduler.Add(saveScoreDelegate); } - private void updateTooltip(ValueChangedEvent state) + private void updateTooltip(ValueChangedEvent state) { switch (state.NewValue) { - case DownloadState.LocallyAvailable: - button.TooltipText = @"Score saved"; + case ImportState.Imported: + button.TooltipText = @"Watch replay"; + break; + + case ImportState.Importing: + button.TooltipText = @"Importing score"; + break; + + case ImportState.Failed: + button.State.Value = DownloadState.NotDownloaded; + button.TooltipText = @"Import failed, click button to re-import"; break; default: @@ -76,5 +129,13 @@ private void updateTooltip(ValueChangedEvent state) break; } } + + public enum ImportState + { + NotImported, + Failed, + Importing, + Imported + } } } From 33209ecd254f40f3d4dcc64be9fa7b470ac872a9 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Thu, 30 Jun 2022 19:51:58 +0900 Subject: [PATCH 08/21] remove useless value change --- osu.Game/Screens/Play/SaveFailedScoreButton.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/SaveFailedScoreButton.cs b/osu.Game/Screens/Play/SaveFailedScoreButton.cs index b5727487dcf9..9a30e23ddd02 100644 --- a/osu.Game/Screens/Play/SaveFailedScoreButton.cs +++ b/osu.Game/Screens/Play/SaveFailedScoreButton.cs @@ -66,7 +66,6 @@ private void load(OsuGame game) break; } }; - State.BindValueChanged(updateTooltip, true); State.BindValueChanged(state => { switch (state.NewValue) @@ -84,6 +83,7 @@ private void load(OsuGame game) break; } }, true); + State.BindValueChanged(updateTooltip, true); } private void saveScore() @@ -120,7 +120,6 @@ private void updateTooltip(ValueChangedEvent state) break; case ImportState.Failed: - button.State.Value = DownloadState.NotDownloaded; button.TooltipText = @"Import failed, click button to re-import"; break; From e4ebab92c6792253adf14d5aa0dd08b3c6bca44b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Jul 2022 18:36:46 +0900 Subject: [PATCH 09/21] Rename lots of weird variables --- osu.Game/Screens/Play/FailOverlay.cs | 1 + osu.Game/Screens/Play/SaveFailedScoreButton.cs | 15 ++++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/FailOverlay.cs b/osu.Game/Screens/Play/FailOverlay.cs index 75b06f859d70..2af5102369cb 100644 --- a/osu.Game/Screens/Play/FailOverlay.cs +++ b/osu.Game/Screens/Play/FailOverlay.cs @@ -21,6 +21,7 @@ namespace osu.Game.Screens.Play public class FailOverlay : GameplayMenuOverlay { public Func> SaveReplay; + public override string Header => "failed"; public override string Description => "you're dead, try again?"; diff --git a/osu.Game/Screens/Play/SaveFailedScoreButton.cs b/osu.Game/Screens/Play/SaveFailedScoreButton.cs index 9a30e23ddd02..29d2d0df2478 100644 --- a/osu.Game/Screens/Play/SaveFailedScoreButton.cs +++ b/osu.Game/Screens/Play/SaveFailedScoreButton.cs @@ -20,8 +20,8 @@ namespace osu.Game.Screens.Play { public class SaveFailedScoreButton : CompositeDrawable { - public Func> SaveReplay; - private Task saveReplayAsync; + public Func> ImportFailedScore; + private Task saveFailedScoreTask; private ScoreInfo score; private ScheduledDelegate saveScoreDelegate; @@ -31,10 +31,10 @@ public class SaveFailedScoreButton : CompositeDrawable private DownloadButton button; private ShakeContainer shakeContainer; - public SaveFailedScoreButton(Func> sr) + public SaveFailedScoreButton(Func> requestImportFailedScore) { Size = new Vector2(50, 30); - SaveReplay = sr; + ImportFailedScore = requestImportFailedScore; } [BackgroundDependencyLoader(true)] @@ -89,17 +89,18 @@ private void load(OsuGame game) private void saveScore() { State.Value = ImportState.Importing; - saveReplayAsync = Task.Run(SaveReplay); + + saveFailedScoreTask = Task.Run(ImportFailedScore); saveScoreDelegate = new ScheduledDelegate(() => { - if (saveReplayAsync?.IsCompleted != true) + if (saveFailedScoreTask?.IsCompleted != true) // If the asynchronous preparation has not completed, keep repeating this delegate. return; saveScoreDelegate?.Cancel(); - score = saveReplayAsync.GetAwaiter().GetResult(); + score = saveFailedScoreTask.GetAwaiter().GetResult(); State.Value = score != null ? ImportState.Imported : ImportState.Failed; }, Time.Current, 50); From a38c6704c2c44ba77d88e1e263ea327082c37274 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Fri, 8 Jul 2022 21:31:35 +0900 Subject: [PATCH 10/21] Use ContinueWith, Check is Task empty --- .../Screens/Play/SaveFailedScoreButton.cs | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/osu.Game/Screens/Play/SaveFailedScoreButton.cs b/osu.Game/Screens/Play/SaveFailedScoreButton.cs index 29d2d0df2478..98f81863d54f 100644 --- a/osu.Game/Screens/Play/SaveFailedScoreButton.cs +++ b/osu.Game/Screens/Play/SaveFailedScoreButton.cs @@ -9,7 +9,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Threading; using osu.Game.Scoring; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -24,8 +23,6 @@ public class SaveFailedScoreButton : CompositeDrawable private Task saveFailedScoreTask; private ScoreInfo score; - private ScheduledDelegate saveScoreDelegate; - protected readonly Bindable State = new Bindable(); private DownloadButton button; @@ -88,24 +85,19 @@ private void load(OsuGame game) private void saveScore() { + if (saveFailedScoreTask != null) + { + return; + } + State.Value = ImportState.Importing; saveFailedScoreTask = Task.Run(ImportFailedScore); - - saveScoreDelegate = new ScheduledDelegate(() => + saveFailedScoreTask.ContinueWith(s => Schedule(() => { - if (saveFailedScoreTask?.IsCompleted != true) - // If the asynchronous preparation has not completed, keep repeating this delegate. - return; - - saveScoreDelegate?.Cancel(); - - score = saveFailedScoreTask.GetAwaiter().GetResult(); - + score = s.GetAwaiter().GetResult(); State.Value = score != null ? ImportState.Imported : ImportState.Failed; - }, Time.Current, 50); - - Scheduler.Add(saveScoreDelegate); + })); } private void updateTooltip(ValueChangedEvent state) From 91f471ebe0747356b0f0224d703520510d9d035a Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Fri, 8 Jul 2022 21:42:34 +0900 Subject: [PATCH 11/21] disabled button instead of shake removed `ShakeContainer` --- osu.Game/Screens/Play/SaveFailedScoreButton.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Play/SaveFailedScoreButton.cs b/osu.Game/Screens/Play/SaveFailedScoreButton.cs index 98f81863d54f..bbe26ca7da20 100644 --- a/osu.Game/Screens/Play/SaveFailedScoreButton.cs +++ b/osu.Game/Screens/Play/SaveFailedScoreButton.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Scoring; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online; using osuTK; @@ -26,7 +25,6 @@ public class SaveFailedScoreButton : CompositeDrawable protected readonly Bindable State = new Bindable(); private DownloadButton button; - private ShakeContainer shakeContainer; public SaveFailedScoreButton(Func> requestImportFailedScore) { @@ -37,13 +35,9 @@ public SaveFailedScoreButton(Func> requestImportFailedScore) [BackgroundDependencyLoader(true)] private void load(OsuGame game) { - InternalChild = shakeContainer = new ShakeContainer + InternalChild = button = new DownloadButton { RelativeSizeAxes = Axes.Both, - Child = button = new DownloadButton - { - RelativeSizeAxes = Axes.Both, - } }; button.Action = () => @@ -55,7 +49,6 @@ private void load(OsuGame game) break; case ImportState.Importing: - shakeContainer.Shake(); break; default: @@ -106,18 +99,22 @@ private void updateTooltip(ValueChangedEvent state) { case ImportState.Imported: button.TooltipText = @"Watch replay"; + button.Enabled.Value = true; break; case ImportState.Importing: button.TooltipText = @"Importing score"; + button.Enabled.Value = false; break; case ImportState.Failed: button.TooltipText = @"Import failed, click button to re-import"; + button.Enabled.Value = true; break; default: button.TooltipText = @"Save score"; + button.Enabled.Value = true; break; } } From d2406242ae12c124aaa9e9f0c35c65e7fb90b7c1 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Sat, 9 Jul 2022 00:25:11 +0900 Subject: [PATCH 12/21] rename updateTooltip to updateState --- osu.Game/Screens/Play/SaveFailedScoreButton.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/SaveFailedScoreButton.cs b/osu.Game/Screens/Play/SaveFailedScoreButton.cs index bbe26ca7da20..256228a1cb6c 100644 --- a/osu.Game/Screens/Play/SaveFailedScoreButton.cs +++ b/osu.Game/Screens/Play/SaveFailedScoreButton.cs @@ -73,7 +73,7 @@ private void load(OsuGame game) break; } }, true); - State.BindValueChanged(updateTooltip, true); + State.BindValueChanged(updateState, true); } private void saveScore() @@ -93,7 +93,7 @@ private void saveScore() })); } - private void updateTooltip(ValueChangedEvent state) + private void updateState(ValueChangedEvent state) { switch (state.NewValue) { From 8a48cb701dc416ad9ad19ef4ad4d9e27799b2ceb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jul 2022 18:54:19 +0900 Subject: [PATCH 13/21] Tidy up implementation and remove unnecessary enum --- osu.Game/Online/DownloadState.cs | 2 - .../Screens/Play/SaveFailedScoreButton.cs | 61 ++++++++----------- 2 files changed, 24 insertions(+), 39 deletions(-) diff --git a/osu.Game/Online/DownloadState.cs b/osu.Game/Online/DownloadState.cs index 3d389d45f9f9..a58c40d16a6e 100644 --- a/osu.Game/Online/DownloadState.cs +++ b/osu.Game/Online/DownloadState.cs @@ -1,8 +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 - namespace osu.Game.Online { public enum DownloadState diff --git a/osu.Game/Screens/Play/SaveFailedScoreButton.cs b/osu.Game/Screens/Play/SaveFailedScoreButton.cs index 256228a1cb6c..6dfceec9f3db 100644 --- a/osu.Game/Screens/Play/SaveFailedScoreButton.cs +++ b/osu.Game/Screens/Play/SaveFailedScoreButton.cs @@ -1,8 +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 System; using System.Threading.Tasks; using osu.Framework.Allocation; @@ -18,22 +16,24 @@ namespace osu.Game.Screens.Play { public class SaveFailedScoreButton : CompositeDrawable { - public Func> ImportFailedScore; - private Task saveFailedScoreTask; - private ScoreInfo score; + private readonly Bindable state = new Bindable(); + + private readonly Func> importFailedScore; + + private Task? saveFailedScoreTask; - protected readonly Bindable State = new Bindable(); + private ScoreInfo? score; - private DownloadButton button; + private DownloadButton button = null!; public SaveFailedScoreButton(Func> requestImportFailedScore) { Size = new Vector2(50, 30); - ImportFailedScore = requestImportFailedScore; + importFailedScore = requestImportFailedScore; } - [BackgroundDependencyLoader(true)] - private void load(OsuGame game) + [BackgroundDependencyLoader] + private void load(OsuGame? game) { InternalChild = button = new DownloadButton { @@ -42,13 +42,13 @@ private void load(OsuGame game) button.Action = () => { - switch (State.Value) + switch (state.Value) { - case ImportState.Imported: + case DownloadState.LocallyAvailable: game?.PresentScore(score, ScorePresentType.Gameplay); break; - case ImportState.Importing: + case DownloadState.Importing: break; default: @@ -56,24 +56,24 @@ private void load(OsuGame game) break; } }; - State.BindValueChanged(state => + state.BindValueChanged(state => { switch (state.NewValue) { - case ImportState.Imported: + case DownloadState.LocallyAvailable: button.State.Value = DownloadState.LocallyAvailable; break; - case ImportState.Importing: + case DownloadState.Importing: button.State.Value = DownloadState.Importing; break; - case ImportState.Failed: + case DownloadState.NotDownloaded: button.State.Value = DownloadState.NotDownloaded; break; } }, true); - State.BindValueChanged(updateState, true); + state.BindValueChanged(updateState, true); } private void saveScore() @@ -83,48 +83,35 @@ private void saveScore() return; } - State.Value = ImportState.Importing; + state.Value = DownloadState.Importing; - saveFailedScoreTask = Task.Run(ImportFailedScore); + saveFailedScoreTask = Task.Run(importFailedScore); saveFailedScoreTask.ContinueWith(s => Schedule(() => { score = s.GetAwaiter().GetResult(); - State.Value = score != null ? ImportState.Imported : ImportState.Failed; + state.Value = score != null ? DownloadState.LocallyAvailable : DownloadState.NotDownloaded; })); } - private void updateState(ValueChangedEvent state) + private void updateState(ValueChangedEvent state) { switch (state.NewValue) { - case ImportState.Imported: + case DownloadState.LocallyAvailable: button.TooltipText = @"Watch replay"; button.Enabled.Value = true; break; - case ImportState.Importing: + case DownloadState.Importing: button.TooltipText = @"Importing score"; button.Enabled.Value = false; break; - case ImportState.Failed: - button.TooltipText = @"Import failed, click button to re-import"; - button.Enabled.Value = true; - break; - default: button.TooltipText = @"Save score"; button.Enabled.Value = true; break; } } - - public enum ImportState - { - NotImported, - Failed, - Importing, - Imported - } } } From 0e788ac7147c8c01f1cc72c6db6a74d0a072e33d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jul 2022 19:02:12 +0900 Subject: [PATCH 14/21] Simplify bindable logic greatly --- .../Screens/Play/SaveFailedScoreButton.cs | 92 ++++++------------- 1 file changed, 30 insertions(+), 62 deletions(-) diff --git a/osu.Game/Screens/Play/SaveFailedScoreButton.cs b/osu.Game/Screens/Play/SaveFailedScoreButton.cs index 6dfceec9f3db..ed4aa9697726 100644 --- a/osu.Game/Screens/Play/SaveFailedScoreButton.cs +++ b/osu.Game/Screens/Play/SaveFailedScoreButton.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Scoring; @@ -20,16 +21,15 @@ public class SaveFailedScoreButton : CompositeDrawable private readonly Func> importFailedScore; - private Task? saveFailedScoreTask; - private ScoreInfo? score; private DownloadButton button = null!; - public SaveFailedScoreButton(Func> requestImportFailedScore) + public SaveFailedScoreButton(Func> importFailedScore) { Size = new Vector2(50, 30); - importFailedScore = requestImportFailedScore; + + this.importFailedScore = importFailedScore; } [BackgroundDependencyLoader] @@ -38,80 +38,48 @@ private void load(OsuGame? game) InternalChild = button = new DownloadButton { RelativeSizeAxes = Axes.Both, - }; - - button.Action = () => - { - switch (state.Value) + State = { BindTarget = state }, + Action = () => { - case DownloadState.LocallyAvailable: - game?.PresentScore(score, ScorePresentType.Gameplay); - break; - - case DownloadState.Importing: - break; - - default: - saveScore(); - break; + switch (state.Value) + { + case DownloadState.LocallyAvailable: + game?.PresentScore(score, ScorePresentType.Gameplay); + break; + + case DownloadState.NotDownloaded: + state.Value = DownloadState.Importing; + + Task.Run(importFailedScore).ContinueWith(result => Schedule(() => + { + score = result.GetResultSafely(); + state.Value = score != null ? DownloadState.LocallyAvailable : DownloadState.NotDownloaded; + })); + break; + } } }; + state.BindValueChanged(state => { switch (state.NewValue) { case DownloadState.LocallyAvailable: - button.State.Value = DownloadState.LocallyAvailable; + button.TooltipText = @"watch replay"; + button.Enabled.Value = true; break; case DownloadState.Importing: - button.State.Value = DownloadState.Importing; + button.TooltipText = @"importing score"; + button.Enabled.Value = false; break; - case DownloadState.NotDownloaded: - button.State.Value = DownloadState.NotDownloaded; + default: + button.TooltipText = @"save score"; + button.Enabled.Value = true; break; } }, true); - state.BindValueChanged(updateState, true); - } - - private void saveScore() - { - if (saveFailedScoreTask != null) - { - return; - } - - state.Value = DownloadState.Importing; - - saveFailedScoreTask = Task.Run(importFailedScore); - saveFailedScoreTask.ContinueWith(s => Schedule(() => - { - score = s.GetAwaiter().GetResult(); - state.Value = score != null ? DownloadState.LocallyAvailable : DownloadState.NotDownloaded; - })); - } - - private void updateState(ValueChangedEvent state) - { - switch (state.NewValue) - { - case DownloadState.LocallyAvailable: - button.TooltipText = @"Watch replay"; - button.Enabled.Value = true; - break; - - case DownloadState.Importing: - button.TooltipText = @"Importing score"; - button.Enabled.Value = false; - break; - - default: - button.TooltipText = @"Save score"; - button.Enabled.Value = true; - break; - } } } } From 0200ef1d481dde0070c1d39109162892e98863e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jul 2022 19:06:44 +0900 Subject: [PATCH 15/21] Make delegate firing more safe to being set later than BDL --- osu.Game/Screens/Play/FailOverlay.cs | 2 +- osu.Game/Screens/Play/Player.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/FailOverlay.cs b/osu.Game/Screens/Play/FailOverlay.cs index 2af5102369cb..76cd030a8f36 100644 --- a/osu.Game/Screens/Play/FailOverlay.cs +++ b/osu.Game/Screens/Play/FailOverlay.cs @@ -55,7 +55,7 @@ private void load(OsuColour colours) Direction = FillDirection.Horizontal, Children = new Drawable[] { - new SaveFailedScoreButton(SaveReplay) + new SaveFailedScoreButton(() => SaveReplay()) { Width = 300 }, diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 19f0c903412d..17ab8f6710a0 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -1048,6 +1048,7 @@ private async Task saveFailedReplay() { Score.ScoreInfo.Passed = false; Score.ScoreInfo.Rank = ScoreRank.F; + var scoreCopy = Score.DeepClone(); try From 21433d4ecbfde8ca42cf95f8697fcc1ad4ace81b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jul 2022 20:14:58 +0900 Subject: [PATCH 16/21] Add test coverage of saving a failed score --- .../TestScenePlayerLocalScoreImport.cs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs index 5ec9e88728c3..113a0874ce6d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs @@ -8,14 +8,18 @@ using osu.Framework.Audio; using osu.Framework.Extensions; using osu.Framework.Extensions.ObjectExtensions; +using osu.Framework.Graphics.Containers; using osu.Framework.Platform; using osu.Framework.Screens; +using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Graphics.Containers; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; +using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; using osu.Game.Tests.Resources; @@ -58,13 +62,30 @@ public override void SetUpSteps() protected override bool HasCustomSteps => true; - protected override bool AllowFail => false; + protected override bool AllowFail => allowFail; + + private bool allowFail; + + [Test] + public void TestSaveFailedReplay() + { + AddStep("set fail", () => allowFail = true); + AddStep("set no custom ruleset", () => customRuleset = null); + + CreateTest(); + + AddUntilStep("fail screen displayed", () => Player.ChildrenOfType().First().State.Value == Visibility.Visible); + AddUntilStep("score not in database", () => Realm.Run(r => r.Find(Player.Score.ScoreInfo.ID) == null)); + AddStep("click save button", () => Player.ChildrenOfType().First().ChildrenOfType().First().TriggerClick()); + AddUntilStep("score not in database", () => Realm.Run(r => r.Find(Player.Score.ScoreInfo.ID) != null)); + } [Test] public void TestLastPlayedUpdated() { DateTimeOffset? getLastPlayed() => Realm.Run(r => r.Find(Beatmap.Value.BeatmapInfo.ID)?.LastPlayed); + AddStep("set no fail", () => allowFail = false); AddStep("set no custom ruleset", () => customRuleset = null); AddAssert("last played is null", () => getLastPlayed() == null); @@ -77,6 +98,7 @@ public void TestLastPlayedUpdated() [Test] public void TestScoreStoredLocally() { + AddStep("set no fail", () => allowFail = false); AddStep("set no custom ruleset", () => customRuleset = null); CreateTest(); @@ -94,6 +116,7 @@ public void TestScoreStoredLocallyCustomRuleset() { Ruleset createCustomRuleset() => new CustomRuleset(); + AddStep("set no fail", () => allowFail = false); AddStep("import custom ruleset", () => Realm.Write(r => r.Add(createCustomRuleset().RulesetInfo))); AddStep("set custom ruleset", () => customRuleset = createCustomRuleset()); From d325c534ab60b0b58bc36015e5e3f32915016c8e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jul 2022 20:45:37 +0900 Subject: [PATCH 17/21] Check whether score is already imported and show correct state for save button --- osu.Game/Screens/Play/SaveFailedScoreButton.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/SaveFailedScoreButton.cs b/osu.Game/Screens/Play/SaveFailedScoreButton.cs index ed4aa9697726..7eed2086ae57 100644 --- a/osu.Game/Screens/Play/SaveFailedScoreButton.cs +++ b/osu.Game/Screens/Play/SaveFailedScoreButton.cs @@ -8,6 +8,7 @@ using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Database; using osu.Game.Scoring; using osu.Game.Graphics.UserInterface; using osu.Game.Online; @@ -33,7 +34,7 @@ public SaveFailedScoreButton(Func> importFailedScore) } [BackgroundDependencyLoader] - private void load(OsuGame? game) + private void load(OsuGame? game, Player? player, RealmAccess realm) { InternalChild = button = new DownloadButton { @@ -60,6 +61,13 @@ private void load(OsuGame? game) } }; + if (player != null) + { + score = realm.Run(r => r.Find(player.Score.ScoreInfo.ID)?.Detach()); + if (score != null) + state.Value = DownloadState.LocallyAvailable; + } + state.BindValueChanged(state => { switch (state.NewValue) From 2beed6d7b77b9c2ecad01cda12befbacf5422d0c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jul 2022 20:45:48 +0900 Subject: [PATCH 18/21] Allow failed scores to fail in replay playback --- osu.Game/Screens/Play/ReplayPlayer.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index 64b4853a67ad..e82238945b18 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -22,12 +22,21 @@ public class ReplayPlayer : Player, IKeyBindingHandler { private readonly Func, Score> createScore; + private readonly bool replayIsFailedScore; + // Disallow replays from failing. (see https://github.com/ppy/osu/issues/6108) - protected override bool CheckModsAllowFailure() => false; + protected override bool CheckModsAllowFailure() + { + if (!replayIsFailedScore) + return false; + + return base.CheckModsAllowFailure(); + } public ReplayPlayer(Score score, PlayerConfiguration configuration = null) : this((_, _) => score, configuration) { + replayIsFailedScore = score.ScoreInfo.Rank == ScoreRank.F; } public ReplayPlayer(Func, Score> createScore, PlayerConfiguration configuration = null) From 68afb65affc2bd3313100eba9192ff7d7183ebc1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 15 Jul 2022 22:09:54 +0300 Subject: [PATCH 19/21] Move default state steps to `SetUp` rather than duplicating per test case --- .../Gameplay/TestScenePlayerLocalScoreImport.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs index 113a0874ce6d..6491987abe50 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs @@ -66,11 +66,17 @@ public override void SetUpSteps() private bool allowFail; + [SetUp] + public void SetUp() + { + allowFail = false; + customRuleset = null; + } + [Test] public void TestSaveFailedReplay() { - AddStep("set fail", () => allowFail = true); - AddStep("set no custom ruleset", () => customRuleset = null); + AddStep("allow fail", () => allowFail = true); CreateTest(); @@ -85,8 +91,6 @@ public void TestLastPlayedUpdated() { DateTimeOffset? getLastPlayed() => Realm.Run(r => r.Find(Beatmap.Value.BeatmapInfo.ID)?.LastPlayed); - AddStep("set no fail", () => allowFail = false); - AddStep("set no custom ruleset", () => customRuleset = null); AddAssert("last played is null", () => getLastPlayed() == null); CreateTest(); @@ -98,9 +102,6 @@ public void TestLastPlayedUpdated() [Test] public void TestScoreStoredLocally() { - AddStep("set no fail", () => allowFail = false); - AddStep("set no custom ruleset", () => customRuleset = null); - CreateTest(); AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning); @@ -116,7 +117,6 @@ public void TestScoreStoredLocallyCustomRuleset() { Ruleset createCustomRuleset() => new CustomRuleset(); - AddStep("set no fail", () => allowFail = false); AddStep("import custom ruleset", () => Realm.Write(r => r.Add(createCustomRuleset().RulesetInfo))); AddStep("set custom ruleset", () => customRuleset = createCustomRuleset()); From 6285442b7d997f9da0de8d2600310176b252b4fb Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 15 Jul 2022 22:28:09 +0300 Subject: [PATCH 20/21] Fix failed scores not prepared before import --- osu.Game/Screens/Play/Player.cs | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 17ab8f6710a0..516364e13ac8 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -267,7 +267,12 @@ private void load(AudioManager audio, OsuConfigManager config, OsuGameBase game, }, FailOverlay = new FailOverlay { - SaveReplay = saveFailedReplay, + SaveReplay = () => + { + Score.ScoreInfo.Passed = false; + Score.ScoreInfo.Rank = ScoreRank.F; + return prepareAndImportScore(); + }, OnRetry = Restart, OnQuit = () => PerformExit(true), }, @@ -721,7 +726,7 @@ private void scoreCompletionChanged(ValueChangedEvent completed) if (!Configuration.ShowResults) return; - prepareScoreForDisplayTask ??= Task.Run(prepareScoreForResults); + prepareScoreForDisplayTask ??= Task.Run(prepareAndImportScore); bool storyboardHasOutro = DimmableStoryboard.ContentDisplayed && !DimmableStoryboard.HasStoryboardEnded.Value; @@ -740,7 +745,7 @@ private void scoreCompletionChanged(ValueChangedEvent completed) /// Asynchronously run score preparation operations (database import, online submission etc.). /// /// The final score. - private async Task prepareScoreForResults() + private async Task prepareAndImportScore() { var scoreCopy = Score.DeepClone(); @@ -1044,26 +1049,6 @@ public override bool OnExiting(ScreenExitEvent e) return base.OnExiting(e); } - private async Task saveFailedReplay() - { - Score.ScoreInfo.Passed = false; - Score.ScoreInfo.Rank = ScoreRank.F; - - var scoreCopy = Score.DeepClone(); - - try - { - await ImportScore(scoreCopy); - } - catch (Exception ex) - { - Logger.Error(ex, @"Score import failed!"); - return null; - } - - return scoreCopy.ScoreInfo; - } - /// /// Creates the player's . /// From e6236ba088fddfceafacfae9df40a57b2cd833e5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 15 Jul 2022 23:39:04 +0300 Subject: [PATCH 21/21] Update save score button to check availability after import Previously was relying on whether `SaveReplay` returns null, but since I've changed it to use the standard "prepare score for import" path, the button has to check for local availability after import since that path doesn't return null on fail. --- osu.Game/Screens/Play/FailOverlay.cs | 2 +- osu.Game/Screens/Play/SaveFailedScoreButton.cs | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/FailOverlay.cs b/osu.Game/Screens/Play/FailOverlay.cs index 76cd030a8f36..2af5102369cb 100644 --- a/osu.Game/Screens/Play/FailOverlay.cs +++ b/osu.Game/Screens/Play/FailOverlay.cs @@ -55,7 +55,7 @@ private void load(OsuColour colours) Direction = FillDirection.Horizontal, Children = new Drawable[] { - new SaveFailedScoreButton(() => SaveReplay()) + new SaveFailedScoreButton(SaveReplay) { Width = 300 }, diff --git a/osu.Game/Screens/Play/SaveFailedScoreButton.cs b/osu.Game/Screens/Play/SaveFailedScoreButton.cs index 7eed2086ae57..3f6e741dff2d 100644 --- a/osu.Game/Screens/Play/SaveFailedScoreButton.cs +++ b/osu.Game/Screens/Play/SaveFailedScoreButton.cs @@ -22,7 +22,7 @@ public class SaveFailedScoreButton : CompositeDrawable private readonly Func> importFailedScore; - private ScoreInfo? score; + private ScoreInfo? importedScore; private DownloadButton button = null!; @@ -45,17 +45,16 @@ private void load(OsuGame? game, Player? player, RealmAccess realm) switch (state.Value) { case DownloadState.LocallyAvailable: - game?.PresentScore(score, ScorePresentType.Gameplay); + game?.PresentScore(importedScore, ScorePresentType.Gameplay); break; case DownloadState.NotDownloaded: state.Value = DownloadState.Importing; - - Task.Run(importFailedScore).ContinueWith(result => Schedule(() => + Task.Run(importFailedScore).ContinueWith(t => { - score = result.GetResultSafely(); - state.Value = score != null ? DownloadState.LocallyAvailable : DownloadState.NotDownloaded; - })); + importedScore = realm.Run(r => r.Find(t.GetResultSafely().ID)?.Detach()); + Schedule(() => state.Value = importedScore != null ? DownloadState.LocallyAvailable : DownloadState.NotDownloaded); + }); break; } } @@ -63,8 +62,8 @@ private void load(OsuGame? game, Player? player, RealmAccess realm) if (player != null) { - score = realm.Run(r => r.Find(player.Score.ScoreInfo.ID)?.Detach()); - if (score != null) + importedScore = realm.Run(r => r.Find(player.Score.ScoreInfo.ID)?.Detach()); + if (importedScore != null) state.Value = DownloadState.LocallyAvailable; }