mirror of
https://github.com/ppy/osu.git
synced 2026-05-24 05:29:57 +08:00
Ensure partial failed replays are played to their end
Closes https://github.com/ppy/osu/issues/24285. This is not a perfect solution, as it is still possible for a replay to play *beyond* its end if the HP system doesn't fail it after it runs out of frames, but it's probably the best that can be done at this time. Notably this removes existing F rank checks because they were really not reliable. - Scores coming from stable will never present F rank, because rank is not stored to the replay, and the lowest rank that can be produced by `StandardisedScoreMigrationTools` is D. - lazer scores set prior to https://github.com/ppy/osu/pull/28058 will present F rank as long as the user has kept them in their local database and never exported and reimported them, for the same reason as above (rank not stored to replay). Also there have been many mechanics changes since, so it's not impossible for the replay to fail *before* the user actually did even in this case. - lazer scores set after https://github.com/ppy/osu/pull/28058 could technically rely on F rank but making them rely on it is annoying for several reasons: - The PR in question didn't bump `LegacyScoreEncoder.LATEST_VERSION`, so any checks based on the replay version field would be half-reliable anyway. - *Even after* the above, the replay version is only stored to realm as `TotalScoreVersion`, which *then gets bumped* on score version upgrades. So it can't even be used for any checks from that angle, you'd have to decode it from the score. - You *could* use `ClientVersion` because that's somewhat reliable, but that's stored *as string*, so you'd have to do some snipping to split off the `-lazer` suffix, then parse the version, then compare it. I started going through the motions of that before deciding that this is an edge case of an edge case and probably not worth spending time over the simple and obvious solution of just doing away with the rank check. Until I'm proven wrong, I guess.
This commit is contained in:
@@ -29,8 +29,6 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private readonly Func<IBeatmap, IReadOnlyList<Mod>, Score> createScore;
|
||||
|
||||
private readonly bool replayIsFailedScore;
|
||||
|
||||
private PlaybackSettings playbackSettings;
|
||||
|
||||
[Cached(typeof(IGameplayLeaderboardProvider))]
|
||||
@@ -40,19 +38,28 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private bool isAutoplayPlayback => GameplayState.Mods.OfType<ModAutoplay>().Any();
|
||||
|
||||
// Disallow replays from failing. (see https://github.com/ppy/osu/issues/6108)
|
||||
private double? lastFrameTime;
|
||||
|
||||
protected override bool CheckModsAllowFailure()
|
||||
{
|
||||
if (!replayIsFailedScore && !isAutoplayPlayback)
|
||||
return false;
|
||||
// autoplay should be able to fail if the beatmap is not humanly beatable
|
||||
if (isAutoplayPlayback)
|
||||
return base.CheckModsAllowFailure();
|
||||
|
||||
return base.CheckModsAllowFailure();
|
||||
// non-autoplay replays should be able to fail, but only after they've exhausted their frames.
|
||||
// note that the rank isn't checked here - that's because it is generally unreliable.
|
||||
// stable replays, as well as lazer replays recorded prior to https://github.com/ppy/osu/pull/28058,
|
||||
// do not even *contain* the user's rank.
|
||||
// not to mention possible gameplay mechanics changes that could make a replay fail sooner than it really should.
|
||||
if (GameplayClockContainer.CurrentTime >= lastFrameTime)
|
||||
return base.CheckModsAllowFailure();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public ReplayPlayer(Score score, PlayerConfiguration configuration = null)
|
||||
: this((_, _) => score, configuration)
|
||||
{
|
||||
replayIsFailedScore = score.ScoreInfo.Rank == ScoreRank.F;
|
||||
}
|
||||
|
||||
public ReplayPlayer(Func<IBeatmap, IReadOnlyList<Mod>, Score> createScore, PlayerConfiguration configuration = null)
|
||||
@@ -95,6 +102,7 @@ namespace osu.Game.Screens.Play
|
||||
protected override void PrepareReplay()
|
||||
{
|
||||
DrawableRuleset?.SetReplayScore(Score);
|
||||
lastFrameTime = Score.Replay.Frames.LastOrDefault()?.Time;
|
||||
}
|
||||
|
||||
protected override Score CreateScore(IBeatmap beatmap) => createScore(beatmap, Mods.Value);
|
||||
|
||||
Reference in New Issue
Block a user