From e3ab5de8cd944a35228d47bba2623fbd5ee712ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Mar 2022 23:39:45 +0900 Subject: [PATCH] Tidy up constructor logic overlap with `gameplayStartTime` --- .../TestSceneMultiSpectatorScreen.cs | 2 +- .../Screens/Play/GameplayClockContainer.cs | 8 +-- .../Play/MasterGameplayClockContainer.cs | 69 ++++++++++--------- 3 files changed, 43 insertions(+), 36 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index e5e3fecd06..ed8b0bdd96 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -473,7 +473,7 @@ namespace osu.Game.Tests.Visual.Multiplayer } protected override MasterGameplayClockContainer CreateMasterGameplayClockContainer(WorkingBeatmap beatmap) - => new MasterGameplayClockContainer(beatmap, gameplayStartTime ?? 0, gameplayStartTime.HasValue); + => new MasterGameplayClockContainer(beatmap, gameplayStartTime ?? 0); } } } diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index b60a3e306e..66f5cb0bd4 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -42,13 +42,13 @@ namespace osu.Game.Screens.Play public event Action OnSeek; /// - /// The time from which gameplay should start. Will be seeked to on calling . + /// The time from which the clock should start. Will be seeked to on calling . /// /// /// If not set, a value of zero will be used. /// Importantly, the value will be inferred from the current ruleset in unless specified. /// - protected double? GameplayStartTime { get; private set; } + protected double? StartTime { get; set; } /// /// Creates a new . @@ -120,10 +120,10 @@ namespace osu.Game.Screens.Play public void Reset(bool startClock = false, double? gameplayStartTime = null) { if (gameplayStartTime != null) - GameplayStartTime = gameplayStartTime; + StartTime = gameplayStartTime; ensureSourceClockSet(); - Seek(GameplayStartTime ?? 0); + Seek(StartTime ?? 0); // Manually stop the source in order to not affect the IsPaused state. AdjustableSource.Stop(); diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index ce7b6ef7f0..58e8b5f1ad 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -49,9 +49,6 @@ namespace osu.Game.Screens.Play private readonly BindableDouble pauseFreqAdjust = new BindableDouble(1); private readonly WorkingBeatmap beatmap; - private readonly double gameplayStartTime; - private readonly bool startAtGameplayStart; - private readonly double firstHitObjectTime; private HardwareCorrectionOffsetClock userGlobalOffsetClock; private HardwareCorrectionOffsetClock userBeatmapOffsetClock; @@ -61,20 +58,29 @@ namespace osu.Game.Screens.Play private IDisposable beatmapOffsetSubscription; + private readonly double latestGameplayStartTime; + [Resolved] private RealmAccess realm { get; set; } [Resolved] private OsuConfigManager config { get; set; } - public MasterGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStartTime, bool startAtGameplayStart = false) + /// + /// Create a new master gameplay clock container. + /// + /// The beatmap to be used for time and metadata references. + /// The latest time which should be used when introducing gameplay. Will be used when skipping forward. + /// Whether to start from the provided latest start time rather than zero. + public MasterGameplayClockContainer(WorkingBeatmap beatmap, double latestGameplayStartTime, bool startFromLatestStartTime = false) : base(beatmap.Track) { this.beatmap = beatmap; - this.gameplayStartTime = gameplayStartTime; - this.startAtGameplayStart = startAtGameplayStart; - firstHitObjectTime = beatmap.Beatmap.HitObjects.First().StartTime; + this.latestGameplayStartTime = latestGameplayStartTime; + + if (startFromLatestStartTime) + StartTime = latestGameplayStartTime; } protected override void LoadComplete() @@ -89,33 +95,34 @@ namespace osu.Game.Screens.Play settings => settings.Offset, val => userBeatmapOffsetClock.Offset = val); - if (GameplayStartTime == null) - { - // sane default provided by ruleset. - double offset = gameplayStartTime; + // Reset may have been called externally before LoadComplete. + // If it was, and the clock is in a playing state, we want to ensure that it isn't stopped here. + bool isStarted = !IsPaused.Value; - if (!startAtGameplayStart) - { - offset = Math.Min(0, offset); + // If a custom start time was not specified, calculate the best value to use. + double gameplayStartTime = StartTime ?? findBeatmapStartTime(); - // if a storyboard is present, it may dictate the appropriate start time by having events in negative time space. - // this is commonly used to display an intro before the audio track start. - double? firstStoryboardEvent = beatmap.Storyboard.EarliestEventTime; - if (firstStoryboardEvent != null) - offset = Math.Min(offset, firstStoryboardEvent.Value); + Reset(startClock: isStarted, gameplayStartTime: gameplayStartTime); + } - // some beatmaps specify a current lead-in time which should be used instead of the ruleset-provided value when available. - // this is not available as an option in the live editor but can still be applied via .osu editing. - if (beatmap.BeatmapInfo.AudioLeadIn > 0) - offset = Math.Min(offset, firstHitObjectTime - beatmap.BeatmapInfo.AudioLeadIn); - } + private double findBeatmapStartTime() + { + // start with the originally provided latest time as a sane default. + double time = latestGameplayStartTime; - // Reset may have been called externally before LoadComplete. - // If it was and the clock is in a playing state, we want to ensure that it isn't stopped here. - bool isStarted = !IsPaused.Value; + // if a storyboard is present, it may dictate the appropriate start time by having events in negative time space. + // this is commonly used to display an intro before the audio track start. + double? firstStoryboardEvent = beatmap.Storyboard.EarliestEventTime; + if (firstStoryboardEvent != null) + time = Math.Min(time, firstStoryboardEvent.Value); - Reset(startClock: isStarted, gameplayStartTime: offset); - } + // some beatmaps specify a current lead-in time which should be used instead of the ruleset-provided value when available. + // this is not available as an option in the live editor but can still be applied via .osu editing. + double firstHitObjectTime = beatmap.Beatmap.HitObjects.First().StartTime; + if (beatmap.BeatmapInfo.AudioLeadIn > 0) + time = Math.Min(time, firstHitObjectTime - beatmap.BeatmapInfo.AudioLeadIn); + + return time; } protected override void OnIsPausedChanged(ValueChangedEvent isPaused) @@ -158,10 +165,10 @@ namespace osu.Game.Screens.Play /// public void Skip() { - if (GameplayClock.CurrentTime > gameplayStartTime - MINIMUM_SKIP_TIME) + if (GameplayClock.CurrentTime > latestGameplayStartTime - MINIMUM_SKIP_TIME) return; - double skipTarget = gameplayStartTime - MINIMUM_SKIP_TIME; + double skipTarget = latestGameplayStartTime - MINIMUM_SKIP_TIME; if (GameplayClock.CurrentTime < 0 && skipTarget > 6000) // double skip exception for storyboards with very long intros