From 73fb1851328c416c712937bb1be401ba958af47b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Feb 2018 16:00:50 +0900 Subject: [PATCH 01/17] Change the way ShouldProcessClock is specified in line with framework changes --- osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs | 1 + osu.Game/Rulesets/UI/RulesetInputManager.cs | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 37ca0c021b..3cbe37fed2 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -62,6 +62,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { // as we are currently very dependent on having a running clock, let's make our own clock for the time being. Clock = new FramedClock(); + ShouldProcessClock = true; RelativeSizeAxes = Axes.Both; diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index f465d0e202..5053d582e2 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -91,8 +91,6 @@ namespace osu.Game.Rulesets.UI #region Clock control - protected override bool ShouldProcessClock => false; // We handle processing the clock ourselves - private ManualClock clock; private IFrameBasedClock parentClock; From 9c0dfb7c8c3b4a5667e408904897f460a8d57093 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Feb 2018 16:01:30 +0900 Subject: [PATCH 02/17] Avoid creating an extra framed clock in SkipButton --- osu.Game/Screens/Play/Player.cs | 5 ++++- osu.Game/Screens/Play/SkipButton.cs | 10 ++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 4954618ef9..5ea070d4ff 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -185,9 +185,12 @@ namespace osu.Game.Screens.Play Clock = offsetClock, Child = RulesetContainer, }, - new SkipButton(firstObjectTime) { AudioClock = decoupledClock }, hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, decoupledClock, working, adjustableSourceClock) + new SkipButton(firstObjectTime) { + SeekableClock = decoupledClock, + FramedClock = offsetClock, + }, Anchor = Anchor.Centre, Origin = Anchor.Centre }, diff --git a/osu.Game/Screens/Play/SkipButton.cs b/osu.Game/Screens/Play/SkipButton.cs index f67a9b801e..463dcc1644 100644 --- a/osu.Game/Screens/Play/SkipButton.cs +++ b/osu.Game/Screens/Play/SkipButton.cs @@ -24,7 +24,9 @@ namespace osu.Game.Screens.Play public class SkipButton : OverlayContainer, IKeyBindingHandler { private readonly double startTime; - public IAdjustableClock AudioClock; + + public IAdjustableClock SeekableClock; + public IFrameBasedClock FramedClock; private Button button; private Box remainingTimeBox; @@ -60,8 +62,8 @@ namespace osu.Game.Screens.Play { var baseClock = Clock; - if (AudioClock != null) - Clock = new FramedClock(AudioClock) { ProcessSourceClockFrames = false }; + if (FramedClock != null) + Clock = FramedClock; Children = new Drawable[] { @@ -109,7 +111,7 @@ namespace osu.Game.Screens.Play using (BeginAbsoluteSequence(beginFadeTime)) this.FadeOut(fade_time); - button.Action = () => AudioClock?.Seek(startTime - skip_required_cutoff - fade_time); + button.Action = () => SeekableClock?.Seek(startTime - skip_required_cutoff - fade_time); displayTime = Time.Current; From 0635ae2293180d986f435570bd41f672dc906c9f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Feb 2018 16:01:55 +0900 Subject: [PATCH 03/17] Include missing offset --- osu.Game/Screens/Play/Player.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 5ea070d4ff..a0310a93ed 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -198,7 +198,7 @@ namespace osu.Game.Screens.Play { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Clock = decoupledClock, + Clock = offsetClock, Breaks = beatmap.Breaks } } From d4f1723ae63a6616cb3094e8ca9105e6af4ba2b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Feb 2018 16:02:48 +0900 Subject: [PATCH 04/17] Remove unnecessary secondary argument from HUDOverlay --- osu.Game/Screens/Play/HUDOverlay.cs | 8 ++++---- osu.Game/Screens/Play/Player.cs | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index e68a17f014..231fcfb3e2 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -41,7 +41,7 @@ namespace osu.Game.Screens.Play private static bool hasShownNotificationOnce; - public HUDOverlay(ScoreProcessor scoreProcessor, RulesetContainer rulesetContainer, DecoupleableInterpolatingFramedClock decoupledClock, WorkingBeatmap working, IAdjustableClock adjustableSourceClock) + public HUDOverlay(ScoreProcessor scoreProcessor, RulesetContainer rulesetContainer, IAdjustableClock seekableClock, WorkingBeatmap working) { RelativeSizeAxes = Axes.Both; @@ -66,13 +66,13 @@ namespace osu.Game.Screens.Play BindRulesetContainer(rulesetContainer); Progress.Objects = rulesetContainer.Objects; - Progress.AudioClock = decoupledClock; + Progress.AudioClock = seekableClock; Progress.AllowSeeking = rulesetContainer.HasReplayLoaded; - Progress.OnSeek = pos => decoupledClock.Seek(pos); + Progress.OnSeek = pos => seekableClock.Seek(pos); ModDisplay.Current.BindTo(working.Mods); - PlayerSettingsOverlay.PlaybackSettings.AdjustableClock = adjustableSourceClock; + PlayerSettingsOverlay.PlaybackSettings.AdjustableClock = seekableClock; } [BackgroundDependencyLoader(true)] diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a0310a93ed..a4fdc8a053 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -185,12 +185,13 @@ namespace osu.Game.Screens.Play Clock = offsetClock, Child = RulesetContainer, }, - hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, decoupledClock, working, adjustableSourceClock) new SkipButton(firstObjectTime) { SeekableClock = decoupledClock, FramedClock = offsetClock, }, + hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, decoupledClock, working) + { Anchor = Anchor.Centre, Origin = Anchor.Centre }, From fee258f2f210c3a31381c4cba89af480d5484cff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Feb 2018 16:02:57 +0900 Subject: [PATCH 05/17] Rework PauseContainer to better pause --- osu.Game/Screens/Play/PauseContainer.cs | 54 ++++++++++++------------- osu.Game/Screens/Play/Player.cs | 24 +++++------ 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/osu.Game/Screens/Play/PauseContainer.cs b/osu.Game/Screens/Play/PauseContainer.cs index 669bcd600c..b2f2fe2d4c 100644 --- a/osu.Game/Screens/Play/PauseContainer.cs +++ b/osu.Game/Screens/Play/PauseContainer.cs @@ -44,14 +44,21 @@ namespace osu.Game.Screens.Play public Action OnResume; public Action OnPause; - public IAdjustableClock AudioClock; - public FramedClock FramedClock; + public readonly IAdjustableClock SeekableClock; + public readonly FramedClock FramedClock; - public PauseContainer() + public PauseContainer(FramedClock framedClock, IAdjustableClock seekableClock) { + FramedClock = framedClock; + SeekableClock = seekableClock; + RelativeSizeAxes = Axes.Both; - AddInternal(content = new Container { RelativeSizeAxes = Axes.Both }); + AddInternal(content = new Container + { + Clock = FramedClock, + RelativeSizeAxes = Axes.Both + }); AddInternal(pauseOverlay = new PauseOverlay { @@ -65,47 +72,37 @@ namespace osu.Game.Screens.Play }); } - public void Pause(bool force = false) + public void Pause(bool force = false) => Schedule(() => // Scheduled to ensure a stable position in execution order, no matter how it was called. { if (!CanPause && !force) return; if (IsPaused) return; - // stop the decoupled clock (stops the audio eventually) - AudioClock.Stop(); - - // stop processing updatess on the offset clock (instantly freezes time for all our components) - FramedClock.ProcessSourceClockFrames = false; - + // stop the seekable clock (stops the audio eventually) + SeekableClock.Stop(); IsPaused = true; - // we need to do a final check after all of our children have processed up to the paused clock time. - // this is to cover cases where, for instance, the player fails in the current processing frame. - Schedule(() => - { - if (!CanPause) return; + OnPause?.Invoke(); + pauseOverlay.Show(); - lastPauseActionTime = Time.Current; - - OnPause?.Invoke(); - pauseOverlay.Show(); - }); - } + lastPauseActionTime = Time.Current; + }); public void Resume() { if (!IsPaused) return; IsPaused = false; - FramedClock.ProcessSourceClockFrames = true; - + IsResuming = false; lastPauseActionTime = Time.Current; - OnResume?.Invoke(); + // seek back to the time of the framed clock. + // this accounts for the audio clock potentially taking time to enter a completely stopped state. + SeekableClock.Seek(FramedClock.CurrentTime); + SeekableClock.Start(); + OnResume?.Invoke(); pauseOverlay.Hide(); - AudioClock.Start(); - IsResuming = false; } private OsuGameBase game; @@ -122,6 +119,9 @@ namespace osu.Game.Screens.Play if (!game.IsActive && CanPause) Pause(); + if (!IsPaused) + FramedClock.ProcessFrame(); + base.Update(); } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a4fdc8a053..e3ef0d333c 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -158,16 +158,8 @@ namespace osu.Game.Screens.Play Children = new Drawable[] { - storyboardContainer = new Container + pauseContainer = new PauseContainer(offsetClock, decoupledClock) { - RelativeSizeAxes = Axes.Both, - Clock = offsetClock, - Alpha = 0, - }, - pauseContainer = new PauseContainer - { - AudioClock = decoupledClock, - FramedClock = offsetClock, OnRetry = Restart, OnQuit = Exit, CheckCanPause = () => AllowPause && ValidForResume && !HasFailed && !RulesetContainer.HasReplayLoaded, @@ -183,7 +175,15 @@ namespace osu.Game.Screens.Play { RelativeSizeAxes = Axes.Both, Clock = offsetClock, - Child = RulesetContainer, + Children = new[] + { + storyboardContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + }, + RulesetContainer, + } }, new SkipButton(firstObjectTime) { @@ -338,7 +338,9 @@ namespace osu.Game.Screens.Play this.Delay(750).Schedule(() => { if (!pauseContainer.IsPaused) + { decoupledClock.Start(); + } }); }); }); @@ -365,9 +367,7 @@ namespace osu.Game.Screens.Play } if (loadedSuccessfully) - { pauseContainer?.Pause(); - } return true; } From 3d52ead213fe795cfaf0f7947a0ea2e6855a7156 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Feb 2018 16:05:21 +0900 Subject: [PATCH 06/17] Rename sourceClock --- osu.Game/Screens/Play/Player.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index e3ef0d333c..ce2ae08bed 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -54,8 +54,13 @@ namespace osu.Game.Screens.Play public CursorContainer Cursor => RulesetContainer.Cursor; public bool ProvidingUserCursor => RulesetContainer?.Cursor != null && !RulesetContainer.HasReplayLoaded.Value; - private IAdjustableClock adjustableSourceClock; + private IAdjustableClock sourceClock; + + /// + /// The final usable gameplay clock with user-set offsets applied. + /// private FramedOffsetClock offsetClock; + private DecoupleableInterpolatingFramedClock decoupledClock; private PauseContainer pauseContainer; @@ -138,7 +143,7 @@ namespace osu.Game.Screens.Play return; } - adjustableSourceClock = (IAdjustableClock)working.Track ?? new StopwatchClock(); + sourceClock = (IAdjustableClock)working.Track ?? new StopwatchClock(); decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; var firstObjectTime = RulesetContainer.Objects.First().StartTime; @@ -236,11 +241,11 @@ namespace osu.Game.Screens.Play private void applyRateFromMods() { - if (adjustableSourceClock == null) return; + if (sourceClock == null) return; - adjustableSourceClock.Rate = 1; + sourceClock.Rate = 1; foreach (var mod in Beatmap.Value.Mods.Value.OfType()) - mod.ApplyToClock(adjustableSourceClock); + mod.ApplyToClock(sourceClock); } private void initializeStoryboard(bool asyncLoad) @@ -328,11 +333,11 @@ namespace osu.Game.Screens.Play Task.Run(() => { - adjustableSourceClock.Reset(); + sourceClock.Reset(); Schedule(() => { - decoupledClock.ChangeSource(adjustableSourceClock); + decoupledClock.ChangeSource(sourceClock); applyRateFromMods(); this.Delay(750).Schedule(() => From 37d2a2c3ccbe280e92c41777ea2673ef93461126 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Feb 2018 16:10:12 +0900 Subject: [PATCH 07/17] Rename clock types to match across classes --- osu.Game/Screens/Play/PauseContainer.cs | 12 ++++++------ osu.Game/Screens/Play/Player.cs | 23 +++++++++++++---------- osu.Game/Screens/Play/SkipButton.cs | 4 ++-- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/Play/PauseContainer.cs b/osu.Game/Screens/Play/PauseContainer.cs index b2f2fe2d4c..8827b437ba 100644 --- a/osu.Game/Screens/Play/PauseContainer.cs +++ b/osu.Game/Screens/Play/PauseContainer.cs @@ -44,13 +44,13 @@ namespace osu.Game.Screens.Play public Action OnResume; public Action OnPause; - public readonly IAdjustableClock SeekableClock; + public readonly IAdjustableClock AdjustableClock; public readonly FramedClock FramedClock; - public PauseContainer(FramedClock framedClock, IAdjustableClock seekableClock) + public PauseContainer(FramedClock framedClock, IAdjustableClock adjustableClock) { FramedClock = framedClock; - SeekableClock = seekableClock; + AdjustableClock = adjustableClock; RelativeSizeAxes = Axes.Both; @@ -79,7 +79,7 @@ namespace osu.Game.Screens.Play if (IsPaused) return; // stop the seekable clock (stops the audio eventually) - SeekableClock.Stop(); + AdjustableClock.Stop(); IsPaused = true; OnPause?.Invoke(); @@ -98,8 +98,8 @@ namespace osu.Game.Screens.Play // seek back to the time of the framed clock. // this accounts for the audio clock potentially taking time to enter a completely stopped state. - SeekableClock.Seek(FramedClock.CurrentTime); - SeekableClock.Start(); + AdjustableClock.Seek(FramedClock.CurrentTime); + AdjustableClock.Start(); OnResume?.Invoke(); pauseOverlay.Hide(); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ce2ae08bed..a2e044b800 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -61,7 +61,10 @@ namespace osu.Game.Screens.Play /// private FramedOffsetClock offsetClock; - private DecoupleableInterpolatingFramedClock decoupledClock; + /// + /// The decoupled clock used for gameplay. Should be used for seeks and clock control. + /// + private DecoupleableInterpolatingFramedClock adjustableClock; private PauseContainer pauseContainer; @@ -144,16 +147,16 @@ namespace osu.Game.Screens.Play } sourceClock = (IAdjustableClock)working.Track ?? new StopwatchClock(); - decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; + adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; var firstObjectTime = RulesetContainer.Objects.First().StartTime; - decoupledClock.Seek(AllowLeadIn + adjustableClock.Seek(AllowLeadIn ? Math.Min(0, firstObjectTime - Math.Max(beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, beatmap.BeatmapInfo.AudioLeadIn)) : firstObjectTime); - decoupledClock.ProcessFrame(); + adjustableClock.ProcessFrame(); - offsetClock = new FramedOffsetClock(decoupledClock); + offsetClock = new FramedOffsetClock(adjustableClock); userAudioOffset = config.GetBindable(OsuSetting.AudioOffset); userAudioOffset.ValueChanged += v => offsetClock.Offset = v; @@ -163,7 +166,7 @@ namespace osu.Game.Screens.Play Children = new Drawable[] { - pauseContainer = new PauseContainer(offsetClock, decoupledClock) + pauseContainer = new PauseContainer(offsetClock, adjustableClock) { OnRetry = Restart, OnQuit = Exit, @@ -192,7 +195,7 @@ namespace osu.Game.Screens.Play }, new SkipButton(firstObjectTime) { - SeekableClock = decoupledClock, + AdjustableClock = adjustableClock, FramedClock = offsetClock, }, hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, decoupledClock, working) @@ -304,7 +307,7 @@ namespace osu.Game.Screens.Play if (Beatmap.Value.Mods.Value.OfType().Any(m => !m.AllowFail)) return false; - decoupledClock.Stop(); + adjustableClock.Stop(); HasFailed = true; failOverlay.Retries = RestartCount; @@ -337,14 +340,14 @@ namespace osu.Game.Screens.Play Schedule(() => { - decoupledClock.ChangeSource(sourceClock); + adjustableClock.ChangeSource(sourceClock); applyRateFromMods(); this.Delay(750).Schedule(() => { if (!pauseContainer.IsPaused) { - decoupledClock.Start(); + adjustableClock.Start(); } }); }); diff --git a/osu.Game/Screens/Play/SkipButton.cs b/osu.Game/Screens/Play/SkipButton.cs index 463dcc1644..b7e075d893 100644 --- a/osu.Game/Screens/Play/SkipButton.cs +++ b/osu.Game/Screens/Play/SkipButton.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Play { private readonly double startTime; - public IAdjustableClock SeekableClock; + public IAdjustableClock AdjustableClock; public IFrameBasedClock FramedClock; private Button button; @@ -111,7 +111,7 @@ namespace osu.Game.Screens.Play using (BeginAbsoluteSequence(beginFadeTime)) this.FadeOut(fade_time); - button.Action = () => SeekableClock?.Seek(startTime - skip_required_cutoff - fade_time); + button.Action = () => AdjustableClock?.Seek(startTime - skip_required_cutoff - fade_time); displayTime = Time.Current; From 8e78a7b1143a2dc898adf645a691200bf7bdb05c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Feb 2018 16:10:21 +0900 Subject: [PATCH 08/17] Fix HUD using incorrect clock for time display --- osu.Game/Screens/Play/HUDOverlay.cs | 8 ++++---- osu.Game/Screens/Play/Player.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 231fcfb3e2..b0fbde74d2 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -41,7 +41,7 @@ namespace osu.Game.Screens.Play private static bool hasShownNotificationOnce; - public HUDOverlay(ScoreProcessor scoreProcessor, RulesetContainer rulesetContainer, IAdjustableClock seekableClock, WorkingBeatmap working) + public HUDOverlay(ScoreProcessor scoreProcessor, RulesetContainer rulesetContainer, WorkingBeatmap working, IClock offsetClock, IAdjustableClock adjustableClock) { RelativeSizeAxes = Axes.Both; @@ -66,13 +66,13 @@ namespace osu.Game.Screens.Play BindRulesetContainer(rulesetContainer); Progress.Objects = rulesetContainer.Objects; - Progress.AudioClock = seekableClock; + Progress.AudioClock = offsetClock; Progress.AllowSeeking = rulesetContainer.HasReplayLoaded; - Progress.OnSeek = pos => seekableClock.Seek(pos); + Progress.OnSeek = pos => adjustableClock.Seek(pos); ModDisplay.Current.BindTo(working.Mods); - PlayerSettingsOverlay.PlaybackSettings.AdjustableClock = seekableClock; + PlayerSettingsOverlay.PlaybackSettings.AdjustableClock = adjustableClock; } [BackgroundDependencyLoader(true)] diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a2e044b800..938a67190c 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -198,7 +198,7 @@ namespace osu.Game.Screens.Play AdjustableClock = adjustableClock, FramedClock = offsetClock, }, - hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, decoupledClock, working) + hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, working, offsetClock, adjustableClock) { Anchor = Anchor.Centre, Origin = Anchor.Centre From cff17f18648f273c71f0339cf73d81b0c508e64f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Feb 2018 17:23:12 +0900 Subject: [PATCH 09/17] Update in line with inverse ShoudProcessClock default --- .../UI/Cursor/CursorTrail.cs | 1 - osu.Game/Rulesets/UI/RulesetInputManager.cs | 1 + osu.Game/Screens/Play/PauseContainer.cs | 19 ++++++++++--------- osu.Game/Screens/Play/Player.cs | 2 ++ osu.Game/Screens/Play/SkipButton.cs | 3 +++ 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 3cbe37fed2..37ca0c021b 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -62,7 +62,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { // as we are currently very dependent on having a running clock, let's make our own clock for the time being. Clock = new FramedClock(); - ShouldProcessClock = true; RelativeSizeAxes = Axes.Both; diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 5053d582e2..b00bbf1e3a 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -101,6 +101,7 @@ namespace osu.Game.Rulesets.UI //our clock will now be our parent's clock, but we want to replace this to allow manual control. parentClock = Clock; + ShouldProcessClock = false; Clock = new FramedClock(clock = new ManualClock { CurrentTime = parentClock.CurrentTime, diff --git a/osu.Game/Screens/Play/PauseContainer.cs b/osu.Game/Screens/Play/PauseContainer.cs index 8827b437ba..220a48e7c5 100644 --- a/osu.Game/Screens/Play/PauseContainer.cs +++ b/osu.Game/Screens/Play/PauseContainer.cs @@ -44,19 +44,20 @@ namespace osu.Game.Screens.Play public Action OnResume; public Action OnPause; - public readonly IAdjustableClock AdjustableClock; - public readonly FramedClock FramedClock; + private readonly IAdjustableClock adjustableClock; + private readonly FramedClock framedClock; public PauseContainer(FramedClock framedClock, IAdjustableClock adjustableClock) { - FramedClock = framedClock; - AdjustableClock = adjustableClock; + this.framedClock = framedClock; + this.adjustableClock = adjustableClock; RelativeSizeAxes = Axes.Both; AddInternal(content = new Container { - Clock = FramedClock, + Clock = this.framedClock, + ShouldProcessClock = false, RelativeSizeAxes = Axes.Both }); @@ -79,7 +80,7 @@ namespace osu.Game.Screens.Play if (IsPaused) return; // stop the seekable clock (stops the audio eventually) - AdjustableClock.Stop(); + adjustableClock.Stop(); IsPaused = true; OnPause?.Invoke(); @@ -98,8 +99,8 @@ namespace osu.Game.Screens.Play // seek back to the time of the framed clock. // this accounts for the audio clock potentially taking time to enter a completely stopped state. - AdjustableClock.Seek(FramedClock.CurrentTime); - AdjustableClock.Start(); + adjustableClock.Seek(framedClock.CurrentTime); + adjustableClock.Start(); OnResume?.Invoke(); pauseOverlay.Hide(); @@ -120,7 +121,7 @@ namespace osu.Game.Screens.Play Pause(); if (!IsPaused) - FramedClock.ProcessFrame(); + framedClock.ProcessFrame(); base.Update(); } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 938a67190c..a36d7e8e23 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -183,6 +183,7 @@ namespace osu.Game.Screens.Play { RelativeSizeAxes = Axes.Both, Clock = offsetClock, + ShouldProcessClock = false, Children = new[] { storyboardContainer = new Container @@ -208,6 +209,7 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, Origin = Anchor.Centre, Clock = offsetClock, + ShouldProcessClock = false, Breaks = beatmap.Breaks } } diff --git a/osu.Game/Screens/Play/SkipButton.cs b/osu.Game/Screens/Play/SkipButton.cs index b7e075d893..e8b43b7c4e 100644 --- a/osu.Game/Screens/Play/SkipButton.cs +++ b/osu.Game/Screens/Play/SkipButton.cs @@ -63,7 +63,10 @@ namespace osu.Game.Screens.Play var baseClock = Clock; if (FramedClock != null) + { Clock = FramedClock; + ShouldProcessClock = false; + } Children = new Drawable[] { From 01fcf9c813169f0bf2e90480d386aaa739838d18 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Feb 2018 21:50:52 +0900 Subject: [PATCH 10/17] Update in line with framework changes --- osu.Game/Rulesets/UI/RulesetInputManager.cs | 2 +- osu.Game/Screens/Play/PauseContainer.cs | 2 +- osu.Game/Screens/Play/Player.cs | 4 ++-- osu.Game/Screens/Play/SkipButton.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index b00bbf1e3a..3f8a17e23d 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -101,7 +101,7 @@ namespace osu.Game.Rulesets.UI //our clock will now be our parent's clock, but we want to replace this to allow manual control. parentClock = Clock; - ShouldProcessClock = false; + ProcessCustomClock = false; Clock = new FramedClock(clock = new ManualClock { CurrentTime = parentClock.CurrentTime, diff --git a/osu.Game/Screens/Play/PauseContainer.cs b/osu.Game/Screens/Play/PauseContainer.cs index 220a48e7c5..40e734b7df 100644 --- a/osu.Game/Screens/Play/PauseContainer.cs +++ b/osu.Game/Screens/Play/PauseContainer.cs @@ -57,7 +57,7 @@ namespace osu.Game.Screens.Play AddInternal(content = new Container { Clock = this.framedClock, - ShouldProcessClock = false, + ProcessCustomClock = false, RelativeSizeAxes = Axes.Both }); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a36d7e8e23..e89d522d90 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -183,7 +183,7 @@ namespace osu.Game.Screens.Play { RelativeSizeAxes = Axes.Both, Clock = offsetClock, - ShouldProcessClock = false, + ProcessCustomClock = false, Children = new[] { storyboardContainer = new Container @@ -209,7 +209,7 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, Origin = Anchor.Centre, Clock = offsetClock, - ShouldProcessClock = false, + ProcessCustomClock = false, Breaks = beatmap.Breaks } } diff --git a/osu.Game/Screens/Play/SkipButton.cs b/osu.Game/Screens/Play/SkipButton.cs index e8b43b7c4e..08bb26c72b 100644 --- a/osu.Game/Screens/Play/SkipButton.cs +++ b/osu.Game/Screens/Play/SkipButton.cs @@ -65,7 +65,7 @@ namespace osu.Game.Screens.Play if (FramedClock != null) { Clock = FramedClock; - ShouldProcessClock = false; + ProcessCustomClock = false; } Children = new Drawable[] From f9faf8e3d826610200e5d32a2e8f2c455a528883 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Feb 2018 22:01:52 +0900 Subject: [PATCH 11/17] Localise offset clock usage out of Player as much as possible --- osu.Game/Screens/Play/Player.cs | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index e89d522d90..cab791f3a5 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -56,11 +56,6 @@ namespace osu.Game.Screens.Play private IAdjustableClock sourceClock; - /// - /// The final usable gameplay clock with user-set offsets applied. - /// - private FramedOffsetClock offsetClock; - /// /// The decoupled clock used for gameplay. Should be used for seeks and clock control. /// @@ -156,7 +151,8 @@ namespace osu.Game.Screens.Play adjustableClock.ProcessFrame(); - offsetClock = new FramedOffsetClock(adjustableClock); + // the final usable gameplay clock with user-set offsets applied. + var offsetClock = new FramedOffsetClock(adjustableClock); userAudioOffset = config.GetBindable(OsuSetting.AudioOffset); userAudioOffset.ValueChanged += v => offsetClock.Offset = v; @@ -179,28 +175,23 @@ namespace osu.Game.Screens.Play OnResume = () => hudOverlay.KeyCounter.IsCounting = true, Children = new Drawable[] { - new Container + storyboardContainer = new Container { RelativeSizeAxes = Axes.Both, - Clock = offsetClock, - ProcessCustomClock = false, - Children = new[] - { - storyboardContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - }, - RulesetContainer, - } + Alpha = 0, }, + RulesetContainer, new SkipButton(firstObjectTime) { + Clock = Clock, // skip button doesn't want to use the audio clock directly + ProcessCustomClock = false, AdjustableClock = adjustableClock, FramedClock = offsetClock, }, hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, working, offsetClock, adjustableClock) { + Clock = Clock, // hud overlay doesn't want to use the audio clock directly + ProcessCustomClock = false, Anchor = Anchor.Centre, Origin = Anchor.Centre }, @@ -208,7 +199,6 @@ namespace osu.Game.Screens.Play { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Clock = offsetClock, ProcessCustomClock = false, Breaks = beatmap.Breaks } From a71dacc58acc4ce372d652809b1726f7ffeaa8c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 2 Mar 2018 21:02:10 +0900 Subject: [PATCH 12/17] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index e8ae207769..71900dc350 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit e8ae207769ec26fb7ddd67a2433514fcda354ecd +Subproject commit 71900dc350bcebbb60d912d4023a1d2a6bbbc3c1 From 26e50043b70a0c13eb769638fcccf08a7600529d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 3 Mar 2018 14:07:15 +0900 Subject: [PATCH 13/17] Fix parallax container during rewinds --- osu.Game/Graphics/Containers/ParallaxContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/ParallaxContainer.cs b/osu.Game/Graphics/Containers/ParallaxContainer.cs index f4400b7df2..febe52d775 100644 --- a/osu.Game/Graphics/Containers/ParallaxContainer.cs +++ b/osu.Game/Graphics/Containers/ParallaxContainer.cs @@ -66,7 +66,7 @@ namespace osu.Game.Graphics.Containers { Vector2 offset = (input.CurrentState.Mouse == null ? Vector2.Zero : ToLocalSpace(input.CurrentState.Mouse.NativeState.Position) - DrawSize / 2) * ParallaxAmount; - content.Position = Interpolation.ValueAt(Clock.ElapsedFrameTime, content.Position, offset, 0, 1000, Easing.OutQuint); + content.Position = Interpolation.ValueAt(MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000), content.Position, offset, 0, 1000, Easing.OutQuint); content.Scale = new Vector2(1 + ParallaxAmount); } From 659578e8fa8563b58ddda9e632f65ce443dafa70 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 4 Mar 2018 01:48:00 +0900 Subject: [PATCH 14/17] Add rewind support for storyboards --- osu.Game/Storyboards/Drawables/DrawableStoryboard.cs | 2 ++ osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs | 2 ++ osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs | 2 ++ 3 files changed, 6 insertions(+) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index aaeaaabd55..9da92d8cb4 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -33,6 +33,8 @@ namespace osu.Game.Storyboards.Drawables } } + public override bool RemoveCompletedTransforms => false; + private DependencyContainer dependencies; protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs index ef782abbe5..0b84ff3297 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs @@ -17,6 +17,8 @@ namespace osu.Game.Storyboards.Drawables public bool FlipH { get; set; } public bool FlipV { get; set; } + public override bool RemoveWhenNotAlive => false; + protected override Vector2 DrawScale => new Vector2(FlipH ? -base.DrawScale.X : base.DrawScale.X, FlipV ? -base.DrawScale.Y : base.DrawScale.Y); diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs index a39805f74e..c4b9a3d47e 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs @@ -17,6 +17,8 @@ namespace osu.Game.Storyboards.Drawables public bool FlipH { get; set; } public bool FlipV { get; set; } + public override bool RemoveWhenNotAlive => false; + protected override Vector2 DrawScale => new Vector2(FlipH ? -base.DrawScale.X : base.DrawScale.X, FlipV ? -base.DrawScale.Y : base.DrawScale.Y); From ea6e3938c039a27d29a13b17404fd8a582b197e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 4 Mar 2018 03:00:13 +0900 Subject: [PATCH 15/17] Fix hard crash due to spinner spin requirement being zero Resolves #2133. --- osu.Game.Rulesets.Osu/Objects/Spinner.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index 2f238bb74b..b30e4cb932 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects.Types; using osu.Game.Beatmaps.ControlPoints; @@ -26,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Objects SpinsRequired = (int)(Duration / 1000 * BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5)); // spinning doesn't match 1:1 with stable, so let's fudge them easier for the time being. - SpinsRequired = (int)(SpinsRequired * 0.6); + SpinsRequired = (int)Math.Max(1, SpinsRequired * 0.6); } } } From bc0bc8d459dc3f38098527fef9794b5326424036 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 4 Mar 2018 21:57:24 +0900 Subject: [PATCH 16/17] Add legacy timing offsets These have been in release builds since January, but implemented in a hacky way. This brings them with a sane implementation. --- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 3e7b36f324..7273fe999f 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -8,6 +8,7 @@ using OpenTK.Graphics; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Beatmaps.ControlPoints; +using osu.Framework; namespace osu.Game.Beatmaps.Formats { @@ -21,6 +22,8 @@ namespace osu.Game.Beatmaps.Formats private LegacySampleBank defaultSampleBank; private int defaultSampleVolume = 100; + private readonly int timeOffset; + public LegacyBeatmapDecoder() { } @@ -28,6 +31,14 @@ namespace osu.Game.Beatmaps.Formats public LegacyBeatmapDecoder(string header) { BeatmapVersion = int.Parse(header.Substring(17)); + + // BeatmapVersion 4 and lower had an incorrect offset (stable has this set as 24ms off) + timeOffset += BeatmapVersion < 5 ? 24 : 0; + + // lazer in general doesn't match stable. this is the result of user testing, albeit limited. + // only seems to be required on windows. + if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows) + timeOffset += -22; } protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap) @@ -102,7 +113,7 @@ namespace osu.Game.Beatmaps.Formats beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value); break; case @"PreviewTime": - metadata.PreviewTime = int.Parse(pair.Value); + metadata.PreviewTime = getOffsetTime(int.Parse(pair.Value)); break; case @"Countdown": beatmap.BeatmapInfo.Countdown = int.Parse(pair.Value) == 1; @@ -257,8 +268,8 @@ namespace osu.Game.Beatmaps.Formats case EventType.Break: var breakEvent = new BreakPeriod { - StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo), - EndTime = double.Parse(split[2], NumberFormatInfo.InvariantInfo) + StartTime = getOffsetTime(double.Parse(split[1], NumberFormatInfo.InvariantInfo)), + EndTime = getOffsetTime(double.Parse(split[2], NumberFormatInfo.InvariantInfo)) }; if (!breakEvent.HasEffect) @@ -273,7 +284,7 @@ namespace osu.Game.Beatmaps.Formats { string[] split = line.Split(','); - double time = double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo); + double time = getOffsetTime(double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo)); double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo); double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1; @@ -396,7 +407,14 @@ namespace osu.Game.Beatmaps.Formats var obj = parser.Parse(line); if (obj != null) + { + obj.StartTime = getOffsetTime(obj.StartTime); beatmap.HitObjects.Add(obj); + } } + + private int getOffsetTime(int time) => time + timeOffset; + + private double getOffsetTime(double time) => time + timeOffset; } } From e46f363fdc3ca492d8a0d1f4e68053276a49b491 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 4 Mar 2018 22:13:43 +0900 Subject: [PATCH 17/17] Fix failing unit test --- .../Formats/LegacyBeatmapDecoderTest.cs | 8 +++---- .../Beatmaps/Formats/OsuJsonDecoderTest.cs | 2 +- .../Beatmaps/IO/OszArchiveReaderTest.cs | 2 +- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 24 ++++++++++++------- .../Tests/Beatmaps/BeatmapConversionTest.cs | 2 +- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 21bbc4993c..2e774e0924 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeBeatmapGeneral() { - var decoder = new LegacyBeatmapDecoder(); + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { @@ -110,7 +110,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeBeatmapEvents() { - var decoder = new LegacyBeatmapDecoder(); + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { @@ -128,7 +128,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeBeatmapTimingPoints() { - var decoder = new LegacyBeatmapDecoder(); + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { @@ -187,7 +187,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeBeatmapHitObjects() { - var decoder = new LegacyBeatmapDecoder(); + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index 186bd44640..8168de091e 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -159,7 +159,7 @@ namespace osu.Game.Tests.Beatmaps.Formats using (var sr = new StreamReader(stream)) { - var legacyDecoded = new LegacyBeatmapDecoder().DecodeBeatmap(sr); + var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.DecodeBeatmap(sr); using (var ms = new MemoryStream()) using (var sw = new StreamWriter(ms)) using (var sr2 = new StreamReader(ms)) diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs index 7a1c6d9b89..1f7246a119 100644 --- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs @@ -58,7 +58,7 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile); Assert.AreEqual("Deif", meta.AuthorString); Assert.AreEqual("machinetop_background.jpg", meta.BackgroundFile); - Assert.AreEqual(164471, meta.PreviewTime); + Assert.AreEqual(164471 + LegacyBeatmapDecoder.UniversalOffset, meta.PreviewTime); Assert.AreEqual(string.Empty, meta.Source); Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", meta.Tags); Assert.AreEqual("Renatus", meta.Title); diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 7273fe999f..1d54bc4b0c 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -22,7 +22,18 @@ namespace osu.Game.Beatmaps.Formats private LegacySampleBank defaultSampleBank; private int defaultSampleVolume = 100; - private readonly int timeOffset; + /// + /// lazer's audio timings in general doesn't match stable. this is the result of user testing, albeit limited. + /// This only seems to be required on windows. We need to eventually figure out why, with a bit of luck. + /// + public static int UniversalOffset => RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? -22 : 0; + + /// + /// Whether or not beatmap or runtime offsets should be applied. Defaults on; only disable for testing purposes. + /// + public bool ApplyOffsets = true; + + private readonly int offset = UniversalOffset; public LegacyBeatmapDecoder() { @@ -33,12 +44,7 @@ namespace osu.Game.Beatmaps.Formats BeatmapVersion = int.Parse(header.Substring(17)); // BeatmapVersion 4 and lower had an incorrect offset (stable has this set as 24ms off) - timeOffset += BeatmapVersion < 5 ? 24 : 0; - - // lazer in general doesn't match stable. this is the result of user testing, albeit limited. - // only seems to be required on windows. - if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows) - timeOffset += -22; + offset += BeatmapVersion < 5 ? 24 : 0; } protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap) @@ -413,8 +419,8 @@ namespace osu.Game.Beatmaps.Formats } } - private int getOffsetTime(int time) => time + timeOffset; + private int getOffsetTime(int time) => time + (ApplyOffsets ? offset : 0); - private double getOffsetTime(double time) => time + timeOffset; + private double getOffsetTime(double time) => time + (ApplyOffsets ? offset : 0); } } diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index 596dbe84ba..a9b13e87bf 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -109,7 +109,7 @@ namespace osu.Game.Tests.Beatmaps private Beatmap getBeatmap(string name) { - var decoder = new LegacyBeatmapDecoder(); + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; using (var resStream = openResource($"{resource_namespace}.{name}.osu")) using (var stream = new StreamReader(resStream)) return decoder.DecodeBeatmap(stream);