Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix score no longer being saved when quick-restarting after pass #30937

Merged
merged 3 commits into from
Dec 5, 2024

Conversation

frenzibyte
Copy link
Member

The PerformExit path ensures the score is imported even if the user restarts after passing the beatmap during the one second completion delay RESULTS_DISPLAY_DELAY.

In #30623, Restart no longer calls PerformExit, therefore discarding the score for the scenario in question.

I've resolved the issue by still calling PerformExit rather than letting the PlayerLoader do the exiting process.

I wanted to write a test case for this but timing is critical, I'll have to change the constant to be overridable and make my test case set the delay to infinity, but it feels weird to break parts of the component itself in order to test it. I can still write a test case if wanted.

@peppy
Copy link
Member

peppy commented Dec 1, 2024

Tests failing.

Copy link
Member

@peppy peppy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic seems sound, but tests need addressing.

Thanks to tests for pointing that out :blobsweat:
Comment on lines 727 to 731
// if we're called externally (i.e. from results screen),
// use MakeCurrent to exit results screen as well as this player screen
// since ValidForResume = false in here
Debug.Assert(!ValidForResume);
this.MakeCurrent();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hurts my head. I guess at least there's an inline comment next to it but it's still a solution I'd classify as convoluted at best.

Can this not be done any simpler? I'd much rather have a duplicated PerformExit() call in the original code in the RestartRequested block or something than this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bdach does this read any better to you?

diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index 3a0a0613f3..1866ed26ce 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -646,7 +646,6 @@ protected bool PerformExit(bool skipTransition = false)
             // import current score if possible.
             prepareAndImportScoreAsync();
 
-            // Screen may not be current if a restart has been performed.
             if (this.IsCurrentScreen())
             {
                 skipExitTransition = skipTransition;
@@ -657,6 +656,12 @@ protected bool PerformExit(bool skipTransition = false)
                 // - the pause / fail dialog was requested but couldn't be displayed due to the type or state of this Player instance.
                 this.Exit();
             }
+            else
+            {
+                // May be restarting from results screen.
+                if (this.GetChildScreen() != null)
+                    this.MakeCurrent();
+            }
 
             return true;
         }
@@ -722,16 +727,6 @@ public bool Restart(bool quickRestart = false)
             skipExitTransition = quickRestart;
             PrepareLoaderForRestart?.Invoke(quickRestart);
 
-            if (!this.IsCurrentScreen())
-            {
-                // if we're called externally (i.e. from results screen),
-                // use MakeCurrent to exit results screen as well as this player screen
-                // since ValidForResume = false in here
-                Debug.Assert(!ValidForResume);
-                this.MakeCurrent();
-                return true;
-            }
-
             return PerformExit(quickRestart);
         }
 

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem I have is the whole "making a non-valid-for-resume screen current in order to implicitly exit it" shebang. Not the positioning of it in code. So I guess that's a no.

Copy link
Member Author

@frenzibyte frenzibyte Dec 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative option I can imagine would be to move the Restart method to PlayerLoader instead and choose between calling Player.PerformExit() and PlayerLoader.MakeCurrent() based on whether player is current screen. That will solve the hack of making a non-valid-for-resume screen current.

It also generally makes sense for PlayerLoader to expose restarting as that's what's really doing the "restarting" process.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative option I can imagine would be to move the Restart method to PlayerLoader instead and choose between calling Player.PerformExit() and PlayerLoader.MakeCurrent() based on whether player is current screen.

Yeah I don't know if I'd consider this better.

Maybe this is fine if there really is no saner alternative, I dunno.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I say we just choose one and go with it. Can reassess next time the code is touched.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will say that I have attempted my proposal above just to see where it goes and how bad/good would it be, but upon stumbling across some existing test code that relies on the current structure I halted down immediately as totally not worth the effort.

I like peppy's diff as it makes the logic look more appropriate than it is currently, pushed in fa87df6.

@@ -83,7 +83,7 @@ public abstract partial class Player : ScreenWithBeatmapBackground, ISamplePlayb
/// </summary>
protected virtual bool PauseOnFocusLost => true;

public Action<bool> RestartRequested;
public Action<bool> PrepareLoaderForRestart;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's with the rename? I preferred the old name.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say it's because the PlayerLoader is no longer responsible for performing the restart, ie. calling this.MakeCurrent(). the Requested terminology implies that another component is going to make the decision of whether a restart should actually occur.

@peppy peppy self-requested a review December 3, 2024 07:36
Comment on lines +661 to +663
// May be restarting from results screen.
if (this.GetChildScreen() != null)
this.MakeCurrent();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have ignored adding back the !ValidForResume assertion as I think it's a given since the screen is not current and the current screen is presumed to be the results screen.

@peppy peppy merged commit 791416c into ppy:master Dec 5, 2024
8 of 10 checks passed
@frenzibyte frenzibyte deleted the fix-quick-restart-not-saving-score branch December 5, 2024 07:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Replay is not saved when quick retrying at the end of a beatmap
3 participants