diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs
index 54ed7ba626..2844d84f31 100644
--- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs
+++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs
@@ -8,6 +8,7 @@ using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
+using osu.Framework.Logging;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
@@ -39,6 +40,14 @@ namespace osu.Game.Screens.Play
Precision = 0.1,
};
+ ///
+ /// Whether the audio playback is within acceptable ranges.
+ /// Will become false if audio playback is not going as expected.
+ ///
+ public IBindable PlaybackRateValid => playbackRateValid;
+
+ private readonly Bindable playbackRateValid = new Bindable(true);
+
private readonly WorkingBeatmap beatmap;
private Track track;
@@ -128,6 +137,7 @@ namespace osu.Game.Screens.Play
{
// Safety in case the clock is seeked while stopped.
LastStopTime = null;
+ elapsedValidationTime = null;
base.Seek(time);
}
@@ -197,6 +207,51 @@ namespace osu.Game.Screens.Play
addAdjustmentsToTrack();
}
+ protected override void Update()
+ {
+ base.Update();
+ checkPlaybackValidity();
+ }
+
+ #region Clock validation (ensure things are running correctly for local gameplay)
+
+ private double elapsedGameplayClockTime;
+ private double? elapsedValidationTime;
+ private int playbackDiscrepancyCount;
+
+ private const int allowed_playback_discrepancies = 5;
+
+ private void checkPlaybackValidity()
+ {
+ if (GameplayClock.IsRunning)
+ {
+ elapsedGameplayClockTime += GameplayClock.ElapsedFrameTime;
+
+ elapsedValidationTime ??= elapsedGameplayClockTime;
+ elapsedValidationTime += GameplayClock.Rate * Time.Elapsed;
+
+ if (Math.Abs(elapsedGameplayClockTime - elapsedValidationTime!.Value) > 300)
+ {
+ if (playbackDiscrepancyCount++ > allowed_playback_discrepancies)
+ {
+ if (playbackRateValid.Value)
+ {
+ playbackRateValid.Value = false;
+ Logger.Log("System audio playback is not working as expected. Some online functionality will not work.\n\nPlease check your audio drivers.", level: LogLevel.Important);
+ }
+ }
+ else
+ {
+ Logger.Log($"Playback discrepancy detected ({playbackDiscrepancyCount} of allowed {allowed_playback_discrepancies}): {elapsedGameplayClockTime:N1} vs {elapsedValidationTime:N1}");
+ }
+
+ elapsedValidationTime = null;
+ }
+ }
+ }
+
+ #endregion
+
private bool speedAdjustmentsApplied;
private void addAdjustmentsToTrack()
diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs
index 785164178a..f88526b8f9 100644
--- a/osu.Game/Screens/Play/SubmittingPlayer.cs
+++ b/osu.Game/Screens/Play/SubmittingPlayer.cs
@@ -208,6 +208,14 @@ namespace osu.Game.Screens.Play
private Task submitScore(Score score)
{
+ var masterClock = GameplayClockContainer as MasterGameplayClockContainer;
+
+ if (masterClock?.PlaybackRateValid.Value != true)
+ {
+ Logger.Log("Score submission cancelled due to audio playback rate discrepancy.");
+ return Task.CompletedTask;
+ }
+
// token may be null if the request failed but gameplay was still allowed (see HandleTokenRetrievalFailure).
if (token == null)
{