From eda9f838366cd46f09e3bb3401cfaf7775e21daf Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Sun, 29 Apr 2018 15:32:22 +0300 Subject: [PATCH 01/63] Add activeBeatmapPreview variable for tracking beatmap preview playback --- osu.Game/Overlays/Direct/PlayButton.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index 4913b11ae1..dae3b63a8f 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -18,6 +18,7 @@ namespace osu.Game.Overlays.Direct { public class PlayButton : Container { + private static PlayButton activeBeatmapPreview; public readonly Bindable Playing = new Bindable(); public Track Preview { get; private set; } @@ -131,6 +132,9 @@ namespace osu.Game.Overlays.Direct if (playing) { + if (activeBeatmapPreview != null) + activeBeatmapPreview.Playing.Value = false; + if (Preview == null) { beginAudioLoad(); @@ -140,6 +144,7 @@ namespace osu.Game.Overlays.Direct Preview.Restart(); audio.Track.AddAdjustment(AdjustableProperty.Volume, muteBindable); + activeBeatmapPreview = this; } else { From 56c1c2beca733efdeee32e7d9a0779e0dc4ca524 Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Sun, 29 Apr 2018 15:50:02 +0300 Subject: [PATCH 02/63] Remove redundant multiple previews handling from PaginatedBeatmapContainer --- .../Beatmaps/PaginatedBeatmapContainer.cs | 27 ++----------------- .../Profile/Sections/BeatmapsSection.cs | 10 ------- 2 files changed, 2 insertions(+), 35 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index 3fec9d8697..0b06acd426 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -1,14 +1,13 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; -using OpenTK; +using System.Linq; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Direct; using osu.Game.Users; -using System.Linq; +using OpenTK; namespace osu.Game.Overlays.Profile.Sections.Beatmaps { @@ -18,10 +17,6 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps private readonly BeatmapSetType type; - private DirectPanel currentlyPlaying; - - public event Action BeganPlayingPreview; - public PaginatedBeatmapContainer(BeatmapSetType type, Bindable user, string header, string missing = "None... yet.") : base(user, header, missing) { @@ -56,28 +51,10 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps var panel = new DirectGridPanel(s.ToBeatmapSet(Rulesets)); ItemsContainer.Add(panel); - - panel.PreviewPlaying.ValueChanged += isPlaying => - { - StopPlayingPreview(); - - if (isPlaying) - { - BeganPlayingPreview?.Invoke(this); - currentlyPlaying = panel; - } - }; } }; Api.Queue(req); } - - public void StopPlayingPreview() - { - if (currentlyPlaying == null) return; - currentlyPlaying.PreviewPlaying.Value = false; - currentlyPlaying = null; - } } } diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs index 92abd20f93..367d096c16 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Linq; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Profile.Sections.Beatmaps; @@ -22,15 +21,6 @@ namespace osu.Game.Overlays.Profile.Sections new PaginatedBeatmapContainer(BeatmapSetType.Unranked, User, "Pending Beatmaps"), new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, "Graveyarded Beatmaps"), }; - - foreach (var paginatedBeatmapContainer in Children.OfType()) - { - paginatedBeatmapContainer.BeganPlayingPreview += _ => - { - foreach (var bc in Children.OfType()) - bc.StopPlayingPreview(); - }; - } } } } From 7cffabf7f931c367eb02ce8a0fc40342d5f6122d Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Tue, 8 May 2018 22:55:48 +0300 Subject: [PATCH 03/63] Introduce PreviewTrackManager --- osu.Game/Audio/PreviewTrackManager.cs | 60 +++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 osu.Game/Audio/PreviewTrackManager.cs diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs new file mode 100644 index 0000000000..55addac8ad --- /dev/null +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -0,0 +1,60 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Track; +using osu.Framework.Configuration; +using osu.Framework.IO.Stores; + +namespace osu.Game.Audio +{ + public class PreviewTrackManager : TrackManager + { + private AudioManager audio; + private Track currentTrack; + private readonly BindableDouble muteBindable; + + public PreviewTrackManager() + : base(new OnlineStore()) + { + muteBindable = new BindableDouble(); + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio, FrameworkConfigManager config) + { + this.audio = audio; + + audio.AddItem(this); + + config.BindWith(FrameworkSetting.VolumeMusic, Volume); + } + + protected override void UpdateState() + { + if (currentTrack?.HasCompleted ?? false) + onStop(); + + base.UpdateState(); + } + + public void Play(Track track) + { + currentTrack?.Stop(); + currentTrack = track; + currentTrack.Restart(); + onPlay(); + } + + private void onPlay() => audio.Track.AddAdjustment(AdjustableProperty.Volume, muteBindable); + + public void Stop() + { + currentTrack?.Stop(); + onStop(); + } + + private void onStop() => audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable); + } +} From ad50f7faf10cdc5c4090d5ff58db5cc906debe72 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Wed, 9 May 2018 14:51:04 +0300 Subject: [PATCH 04/63] Make PreviewTrackManager a Component in order to use DI --- osu.Game/Audio/PreviewTrackManager.cs | 60 +++++++++++++++------------ 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs index 55addac8ad..2e5f26bcf4 100644 --- a/osu.Game/Audio/PreviewTrackManager.cs +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -1,60 +1,66 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Configuration; +using osu.Framework.Graphics; using osu.Framework.IO.Stores; +using osu.Game.Beatmaps; namespace osu.Game.Audio { - public class PreviewTrackManager : TrackManager + public class PreviewTrackManager : Component { - private AudioManager audio; - private Track currentTrack; - private readonly BindableDouble muteBindable; + public event Action PlaybackStarted; + public event Action PlaybackStopped; - public PreviewTrackManager() - : base(new OnlineStore()) - { - muteBindable = new BindableDouble(); - } + private TrackManager trackManager; + private BindableDouble muteBindable; + + public Track CurrentTrack { get; private set; } [BackgroundDependencyLoader] private void load(AudioManager audio, FrameworkConfigManager config) { - this.audio = audio; + trackManager = new TrackManager(new OnlineStore()); - audio.AddItem(this); + muteBindable = new BindableDouble(); - config.BindWith(FrameworkSetting.VolumeMusic, Volume); + audio.AddItem(trackManager); + config.BindWith(FrameworkSetting.VolumeMusic, trackManager.Volume); + + PlaybackStarted += () => audio.Track.AddAdjustment(AdjustableProperty.Volume, muteBindable); + PlaybackStopped += () => audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable); } - protected override void UpdateState() - { - if (currentTrack?.HasCompleted ?? false) - onStop(); + public Track Get(BeatmapSetInfo beatmapSetInfo) => trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo.OnlineBeatmapSetID}.mp3"); - base.UpdateState(); + protected override void Update() + { + if (CurrentTrack?.HasCompleted ?? false) + PlaybackStopped?.Invoke(); + + base.Update(); } public void Play(Track track) { - currentTrack?.Stop(); - currentTrack = track; - currentTrack.Restart(); - onPlay(); + Stop(); + CurrentTrack = track; + track.Restart(); + PlaybackStarted?.Invoke(); } - private void onPlay() => audio.Track.AddAdjustment(AdjustableProperty.Volume, muteBindable); - public void Stop() { - currentTrack?.Stop(); - onStop(); + if (CurrentTrack?.IsRunning ?? false) + { + CurrentTrack?.Stop(); + PlaybackStopped?.Invoke(); + } } - - private void onStop() => audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable); } } From c492f62edaac7b92d505c8d0eaf5fa2112522e4e Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Wed, 9 May 2018 14:52:46 +0300 Subject: [PATCH 05/63] Use PreviewTrackManager in the PlayButton --- osu.Game/OsuGameBase.cs | 5 ++ osu.Game/Overlays/Direct/PlayButton.cs | 112 +++++-------------------- 2 files changed, 24 insertions(+), 93 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 487cb50c9a..4d2d4d05a7 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -23,6 +23,7 @@ using osu.Game.Online.API; using osu.Framework.Graphics.Performance; using osu.Framework.Graphics.Textures; using osu.Framework.Logging; +using osu.Game.Audio; using osu.Game.Database; using osu.Game.Graphics.Textures; using osu.Game.Input; @@ -202,6 +203,10 @@ namespace osu.Game KeyBindingStore.Register(globalBinding); dependencies.Cache(globalBinding); + + PreviewTrackManager previewTrackManager; + dependencies.Cache(previewTrackManager = new PreviewTrackManager()); + Add(previewTrackManager); } private void runMigrations() diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index dae3b63a8f..082cf34d8e 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -1,24 +1,22 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK.Graphics; using osu.Framework.Allocation; -using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; -using osu.Framework.IO.Stores; +using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; +using OpenTK.Graphics; namespace osu.Game.Overlays.Direct { public class PlayButton : Container { - private static PlayButton activeBeatmapPreview; public readonly Bindable Playing = new Bindable(); public Track Preview { get; private set; } @@ -33,17 +31,16 @@ namespace osu.Game.Overlays.Direct beatmapSet = value; Playing.Value = false; - trackLoader = null; Preview = null; } } + private PreviewTrackManager previewTrackManager; + private Color4 hoverColour; private readonly SpriteIcon icon; private readonly LoadingAnimation loadingAnimation; - private readonly BindableDouble muteBindable = new BindableDouble(); - private const float transition_duration = 500; private bool loading @@ -79,14 +76,20 @@ namespace osu.Game.Overlays.Direct loadingAnimation = new LoadingAnimation(), }); - Playing.ValueChanged += updatePreviewTrack; + Playing.ValueChanged += playingStateChanged; } [BackgroundDependencyLoader] - private void load(OsuColour colour, AudioManager audio) + private void load(OsuColour colour, PreviewTrackManager previewTrackManager) { hoverColour = colour.Yellow; - this.audio = audio; + this.previewTrackManager = previewTrackManager; + + previewTrackManager.PlaybackStopped += () => + { + if (Preview == previewTrackManager.CurrentTrack) + Playing.Value = false; + }; } protected override bool OnClick(InputState state) @@ -108,18 +111,7 @@ namespace osu.Game.Overlays.Direct base.OnHoverLost(state); } - protected override void Update() - { - base.Update(); - - if (Preview?.HasCompleted ?? false) - { - Playing.Value = false; - Preview = null; - } - } - - private void updatePreviewTrack(bool playing) + private void playingStateChanged(bool playing) { if (playing && BeatmapSet == null) { @@ -132,25 +124,18 @@ namespace osu.Game.Overlays.Direct if (playing) { - if (activeBeatmapPreview != null) - activeBeatmapPreview.Playing.Value = false; - if (Preview == null) { - beginAudioLoad(); - return; + loading = true; + Preview = previewTrackManager.Get(beatmapSet); + loading = false; } - Preview.Restart(); - - audio.Track.AddAdjustment(AdjustableProperty.Volume, muteBindable); - activeBeatmapPreview = this; + previewTrackManager.Play(Preview); } else { - audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable); - - Preview?.Stop(); + previewTrackManager.Stop(); loading = false; } } @@ -160,64 +145,5 @@ namespace osu.Game.Overlays.Direct base.Dispose(isDisposing); Playing.Value = false; } - - private TrackLoader trackLoader; - private AudioManager audio; - - private void beginAudioLoad() - { - if (trackLoader != null) - { - Preview = trackLoader.Preview; - Playing.TriggerChange(); - return; - } - - loading = true; - - LoadComponentAsync(trackLoader = new TrackLoader($"https://b.ppy.sh/preview/{BeatmapSet.OnlineBeatmapSetID}.mp3"), - d => - { - // We may have been replaced by another loader - if (trackLoader != d) return; - - Preview = d?.Preview; - updatePreviewTrack(Playing); - loading = false; - - Add(trackLoader); - }); - } - - private class TrackLoader : Drawable - { - private readonly string preview; - - public Track Preview; - private TrackManager trackManager; - - public TrackLoader(string preview) - { - this.preview = preview; - } - - [BackgroundDependencyLoader] - private void load(AudioManager audio, FrameworkConfigManager config) - { - // create a local trackManager to bypass the mute we are applying above. - audio.AddItem(trackManager = new TrackManager(new OnlineStore())); - - // add back the user's music volume setting (since we are no longer in the global TrackManager's hierarchy). - config.BindWith(FrameworkSetting.VolumeMusic, trackManager.Volume); - - Preview = trackManager.Get(preview); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - trackManager?.Dispose(); - } - } } } From 95dd2125ee91dbda04d527d1c34d236a9190c1f9 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Wed, 9 May 2018 16:22:52 +0300 Subject: [PATCH 06/63] Load PlayButton.Preview asynchronously --- osu.Game/Overlays/Direct/PlayButton.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index 082cf34d8e..77c904050b 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.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.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Configuration; @@ -126,9 +127,17 @@ namespace osu.Game.Overlays.Direct { if (Preview == null) { - loading = true; - Preview = previewTrackManager.Get(beatmapSet); - loading = false; + Task.Run(() => + { + loading = true; + return Preview = previewTrackManager.Get(beatmapSet); + }) + .ContinueWith(t => + { + playingStateChanged(true); + loading = false; + }); + return; } previewTrackManager.Play(Preview); From 95a207ef56a9907fff4c892f7e36c47fbf6e843d Mon Sep 17 00:00:00 2001 From: KoziLord Date: Tue, 15 May 2018 00:17:42 +0200 Subject: [PATCH 07/63] Fixed the parallax background missing from the space occupied by the top toolbar. --- osu.Game/Screens/Ranking/Results.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index 32161a0b8e..38dbf1a65d 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -160,7 +160,7 @@ namespace osu.Game.Screens.Ranking { RelativeSizeAxes = Axes.Both, ParallaxAmount = 0.01f, - Scale = new Vector2(1 / circle_outer_scale / overscan), + Scale = new Vector2(1 / circle_outer_scale), Anchor = Anchor.Centre, Origin = Anchor.Centre, Children = new Drawable[] From ca05fde14c7c9f984caf6c7fcbbdbe3ce78ed010 Mon Sep 17 00:00:00 2001 From: KoziLord Date: Tue, 15 May 2018 00:47:03 +0200 Subject: [PATCH 08/63] Fix the Parallax Background not filling the entire Circle Container. --- osu.Game/Screens/Ranking/Results.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index 38dbf1a65d..a2990d7902 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -159,8 +159,11 @@ namespace osu.Game.Screens.Ranking backgroundParallax = new ParallaxContainer { RelativeSizeAxes = Axes.Both, - ParallaxAmount = 0.01f, - Scale = new Vector2(1 / circle_outer_scale), + ParallaxAmount = 0.02f, + //Compensate for the Toolbar + Scale = new Vector2(1.06f / circle_outer_scale / overscan), + //Offset the background to account for the toolbar. + Position = new Vector2(0, -20), Anchor = Anchor.Centre, Origin = Anchor.Centre, Children = new Drawable[] From be56d8cb8253a7c710579f54377f5563e9c01170 Mon Sep 17 00:00:00 2001 From: KoziLord Date: Tue, 15 May 2018 00:53:31 +0200 Subject: [PATCH 09/63] Change the parallax amount --- osu.Game/Screens/Ranking/Results.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index a2990d7902..17e8666ebf 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -159,7 +159,7 @@ namespace osu.Game.Screens.Ranking backgroundParallax = new ParallaxContainer { RelativeSizeAxes = Axes.Both, - ParallaxAmount = 0.02f, + ParallaxAmount = 0.01f, //Compensate for the Toolbar Scale = new Vector2(1.06f / circle_outer_scale / overscan), //Offset the background to account for the toolbar. From be1159444d2fb9cb15f0019b275fa9f6a6df48e4 Mon Sep 17 00:00:00 2001 From: KoziLord Date: Tue, 15 May 2018 15:56:49 +0200 Subject: [PATCH 10/63] Use the absolute value of ParallaxAmount to make sure we're not scaling stuff down when using a negative ParallaxAmount --- osu.Game/Graphics/Containers/ParallaxContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/ParallaxContainer.cs b/osu.Game/Graphics/Containers/ParallaxContainer.cs index dc635ce7e7..51be40c1e8 100644 --- a/osu.Game/Graphics/Containers/ParallaxContainer.cs +++ b/osu.Game/Graphics/Containers/ParallaxContainer.cs @@ -45,7 +45,7 @@ namespace osu.Game.Graphics.Containers if (!parallaxEnabled) { content.MoveTo(Vector2.Zero, firstUpdate ? 0 : 1000, Easing.OutQuint); - content.Scale = new Vector2(1 + ParallaxAmount); + content.Scale = new Vector2(1 + System.Math.Abs(ParallaxAmount)); } }; } @@ -69,7 +69,7 @@ namespace osu.Game.Graphics.Containers double elapsed = MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000); content.Position = Interpolation.ValueAt(elapsed, content.Position, offset, 0, 1000, Easing.OutQuint); - content.Scale = Interpolation.ValueAt(elapsed, content.Scale, new Vector2(1 + ParallaxAmount), 0, 1000, Easing.OutQuint); + content.Scale = Interpolation.ValueAt(elapsed, content.Scale, new Vector2(1 + System.Math.Abs(ParallaxAmount)), 0, 1000, Easing.OutQuint); } firstUpdate = false; From 1e88f216f27831a8c5bc76cf09ce271be61e0580 Mon Sep 17 00:00:00 2001 From: KoziLord Date: Wed, 16 May 2018 01:27:18 +0200 Subject: [PATCH 11/63] Revert the ParallaxContainer changes I made by accident back. --- osu.Game/Graphics/Containers/ParallaxContainer.cs | 4 ++-- osu.Game/Screens/Ranking/Results.cs | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/Containers/ParallaxContainer.cs b/osu.Game/Graphics/Containers/ParallaxContainer.cs index 51be40c1e8..dc635ce7e7 100644 --- a/osu.Game/Graphics/Containers/ParallaxContainer.cs +++ b/osu.Game/Graphics/Containers/ParallaxContainer.cs @@ -45,7 +45,7 @@ namespace osu.Game.Graphics.Containers if (!parallaxEnabled) { content.MoveTo(Vector2.Zero, firstUpdate ? 0 : 1000, Easing.OutQuint); - content.Scale = new Vector2(1 + System.Math.Abs(ParallaxAmount)); + content.Scale = new Vector2(1 + ParallaxAmount); } }; } @@ -69,7 +69,7 @@ namespace osu.Game.Graphics.Containers double elapsed = MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000); content.Position = Interpolation.ValueAt(elapsed, content.Position, offset, 0, 1000, Easing.OutQuint); - content.Scale = Interpolation.ValueAt(elapsed, content.Scale, new Vector2(1 + System.Math.Abs(ParallaxAmount)), 0, 1000, Easing.OutQuint); + content.Scale = Interpolation.ValueAt(elapsed, content.Scale, new Vector2(1 + ParallaxAmount), 0, 1000, Easing.OutQuint); } firstUpdate = false; diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index 17e8666ebf..32161a0b8e 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -160,10 +160,7 @@ namespace osu.Game.Screens.Ranking { RelativeSizeAxes = Axes.Both, ParallaxAmount = 0.01f, - //Compensate for the Toolbar - Scale = new Vector2(1.06f / circle_outer_scale / overscan), - //Offset the background to account for the toolbar. - Position = new Vector2(0, -20), + Scale = new Vector2(1 / circle_outer_scale / overscan), Anchor = Anchor.Centre, Origin = Anchor.Centre, Children = new Drawable[] From 428d0f5deec85cfb2153d8246301f3f269865346 Mon Sep 17 00:00:00 2001 From: KoziLord Date: Wed, 16 May 2018 01:30:16 +0200 Subject: [PATCH 12/63] Re-implement the fix after it got deleted in the previous commit. --- osu.Game/Screens/Ranking/Results.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index 32161a0b8e..162c428444 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -160,8 +160,11 @@ namespace osu.Game.Screens.Ranking { RelativeSizeAxes = Axes.Both, ParallaxAmount = 0.01f, - Scale = new Vector2(1 / circle_outer_scale / overscan), + //Compensate for the Toolbar + Scale = new Vector2(1.06f / circle_outer_scale / overscan), + //Offset the background to account for the toolbar. Anchor = Anchor.Centre, + Position = new Vector2(0, -20), Origin = Anchor.Centre, Children = new Drawable[] { From 3b708f650591a015ccd1e54721981f4316fbce4b Mon Sep 17 00:00:00 2001 From: KoziLord Date: Wed, 16 May 2018 01:34:08 +0200 Subject: [PATCH 13/63] make it equal with the original codebase. --- osu.Game/Screens/Ranking/Results.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index 162c428444..67949f8e14 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -160,17 +160,14 @@ namespace osu.Game.Screens.Ranking { RelativeSizeAxes = Axes.Both, ParallaxAmount = 0.01f, - //Compensate for the Toolbar - Scale = new Vector2(1.06f / circle_outer_scale / overscan), - //Offset the background to account for the toolbar. + Scale = new Vector2(1 / circle_outer_scale / overscan), Anchor = Anchor.Centre, - Position = new Vector2(0, -20), Origin = Anchor.Centre, Children = new Drawable[] { new Sprite { - RelativeSizeAxes = Axes.Both, + RelativeSsizeAxes = Axes.Both, Alpha = 0.2f, Texture = Beatmap.Value.Background, Anchor = Anchor.Centre, From 705960dabbc32591ccd36841e66e101e41133189 Mon Sep 17 00:00:00 2001 From: KoziLord Date: Wed, 16 May 2018 01:36:26 +0200 Subject: [PATCH 14/63] Fix typo --- osu.Game/Screens/Ranking/Results.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index 67949f8e14..32161a0b8e 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -167,7 +167,7 @@ namespace osu.Game.Screens.Ranking { new Sprite { - RelativeSsizeAxes = Axes.Both, + RelativeSizeAxes = Axes.Both, Alpha = 0.2f, Texture = Beatmap.Value.Background, Anchor = Anchor.Centre, From d2b2d3e2e078de26188030f79c1722e2f30d2589 Mon Sep 17 00:00:00 2001 From: KoziLord Date: Wed, 16 May 2018 01:38:46 +0200 Subject: [PATCH 15/63] Use the absolute value of ParallaxAmount to make sure we're not scaling things down when using a negative value for the Parallax (moving against the cursor instead of with it) --- osu.Game/Graphics/Containers/ParallaxContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/ParallaxContainer.cs b/osu.Game/Graphics/Containers/ParallaxContainer.cs index dc635ce7e7..51be40c1e8 100644 --- a/osu.Game/Graphics/Containers/ParallaxContainer.cs +++ b/osu.Game/Graphics/Containers/ParallaxContainer.cs @@ -45,7 +45,7 @@ namespace osu.Game.Graphics.Containers if (!parallaxEnabled) { content.MoveTo(Vector2.Zero, firstUpdate ? 0 : 1000, Easing.OutQuint); - content.Scale = new Vector2(1 + ParallaxAmount); + content.Scale = new Vector2(1 + System.Math.Abs(ParallaxAmount)); } }; } @@ -69,7 +69,7 @@ namespace osu.Game.Graphics.Containers double elapsed = MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000); content.Position = Interpolation.ValueAt(elapsed, content.Position, offset, 0, 1000, Easing.OutQuint); - content.Scale = Interpolation.ValueAt(elapsed, content.Scale, new Vector2(1 + ParallaxAmount), 0, 1000, Easing.OutQuint); + content.Scale = Interpolation.ValueAt(elapsed, content.Scale, new Vector2(1 + System.Math.Abs(ParallaxAmount)), 0, 1000, Easing.OutQuint); } firstUpdate = false; From 79c64d16e43da33e3e21fdcefdd192da9eaba2b7 Mon Sep 17 00:00:00 2001 From: frankhjwx Date: Mon, 21 May 2018 09:49:23 +0800 Subject: [PATCH 16/63] Realization on Catch Star Rating --- .../Difficulty/CatchDifficultyCalculator.cs | 160 +++++++++++++++++- .../Difficulty/CatchDifficultyHitObject.cs | 127 ++++++++++++++ 2 files changed, 285 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index f8351b7519..5351bd746d 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -1,18 +1,174 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.Generic; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Catch.Difficulty { public class CatchDifficultyCalculator : DifficultyCalculator { - public CatchDifficultyCalculator(IBeatmap beatmap) : base(beatmap) + private const double STAR_SCALING_FACTOR = 0.145; + private const float PLAYFIELD_WIDTH = CatchPlayfield.BASE_WIDTH; + + private readonly List difficultyHitObjects = new List(); + + public CatchDifficultyCalculator(IBeatmap beatmap) + : base(beatmap) { } - public override double Calculate(Dictionary categoryDifficulty = null) => 0; + public CatchDifficultyCalculator(IBeatmap beatmap, Mod[] mods) + : base(beatmap, mods) + { + } + + public override double Calculate(Dictionary categoryDifficulty = null) + { + + difficultyHitObjects.Clear(); + + float circleSize = Beatmap.BeatmapInfo.BaseDifficulty.CircleSize; + float catcherWidth = (1.0f - 0.7f * (circleSize - 5) / 5) * 0.62064f * 172; + //float catcherWidth = (float)(305.0f / 1.6f * ((102.4f * (1.0f - 0.7 * (circleSize - 5.0f)) / 5.0f) / 128.0f * 0.7f)); + float catcherWidthHalf = catcherWidth / 2; + catcherWidthHalf *= 0.8f; + + foreach (var hitObject in Beatmap.HitObjects) + { + // We want to only consider fruits that contribute to the combo. Droplets are addressed as accuracy and spinners are not relevant for "skill" calculations. + if (hitObject is Fruit) + { + difficultyHitObjects.Add(new CatchDifficultyHitObject((CatchHitObject)hitObject, catcherWidthHalf)); + } + if (hitObject is JuiceStream) + { + IEnumerator nestedHitObjectsEnumerator = hitObject.NestedHitObjects.GetEnumerator(); + while (nestedHitObjectsEnumerator.MoveNext()) + { + CatchHitObject objectInJuiceStream = (CatchHitObject)nestedHitObjectsEnumerator.Current; + if (!(objectInJuiceStream is TinyDroplet)) + difficultyHitObjects.Add(new CatchDifficultyHitObject(objectInJuiceStream, catcherWidthHalf)); + } + } + } + + difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime)); + + if (!CalculateStrainValues()) return 0; + + double starRating = Math.Sqrt(CalculateDifficulty()) * STAR_SCALING_FACTOR; + + if (categoryDifficulty != null) + { + categoryDifficulty["Aim"] = starRating; + + double ar = Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate; + double preEmpt = BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450) / TimeRate; + + categoryDifficulty["AR"] = preEmpt > 1200.0 ? -(preEmpt - 1800.0) / 120.0 : -(preEmpt - 1200.0) / 150.0 + 5.0; + + //categoryDifficulty.Add("AR", (preEmpt > 1200.0 ? -(preEmpt - 1800.0) / 120.0 : -(preEmpt - 1200.0) / 150.0 + 5.0).ToString("0.00", GameBase.nfi)); + //categoryDifficulty.Add("Max combo", DifficultyHitObjects.Count.ToString(GameBase.nfi)); + } + + return starRating; + } + + protected bool CalculateStrainValues() + { + // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment. + using (List.Enumerator hitObjectsEnumerator = difficultyHitObjects.GetEnumerator()) + { + + if (!hitObjectsEnumerator.MoveNext()) return false; + + CatchDifficultyHitObject currentHitObject = hitObjectsEnumerator.Current; + CatchDifficultyHitObject nextHitObject; + + // First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject. + while (hitObjectsEnumerator.MoveNext()) + { + nextHitObject = hitObjectsEnumerator.Current; + nextHitObject.CalculateStrains(currentHitObject, TimeRate); + currentHitObject = nextHitObject; + } + + return true; + } + } + + /// + /// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP. + /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain. + /// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage. + /// + protected const double STRAIN_STEP = 750; + + /// + /// The weighting of each strain value decays to this number * it's previous value + /// + protected const double DECAY_WEIGHT = 0.94; + + protected double CalculateDifficulty() + { + // The strain step needs to be adjusted for the algorithm to be considered equal with speed changing mods + double actualStrainStep = STRAIN_STEP * TimeRate; + + // Find the highest strain value within each strain step + List highestStrains = new List(); + double intervalEndTime = actualStrainStep; + double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval + + CatchDifficultyHitObject previousHitObject = null; + foreach (CatchDifficultyHitObject hitObject in difficultyHitObjects) + { + // While we are beyond the current interval push the currently available maximum to our strain list + while (hitObject.BaseHitObject.StartTime > intervalEndTime) + { + highestStrains.Add(maximumStrain); + + // The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay + // until the beginning of the next interval. + if (previousHitObject == null) + { + maximumStrain = 0; + } + else + { + double decay = Math.Pow(CatchDifficultyHitObject.DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); + maximumStrain = previousHitObject.Strain * decay; + } + + // Go to the next time interval + intervalEndTime += actualStrainStep; + } + + // Obtain maximum strain + maximumStrain = Math.Max(hitObject.Strain, maximumStrain); + + previousHitObject = hitObject; + } + + + // Build the weighted sum over the highest strains for each interval + double difficulty = 0; + double weight = 1; + highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. + + foreach (double strain in highestStrains) + { + difficulty += weight * strain; + weight *= DECAY_WEIGHT; + } + + return difficulty; + } } } diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs new file mode 100644 index 0000000000..14f9445c75 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs @@ -0,0 +1,127 @@ +using System; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; +using OpenTK; + +namespace osu.Game.Rulesets.Catch.Difficulty +{ + class CatchDifficultyHitObject + { + internal static readonly double DECAY_BASE = 0.20; + private const float NORMALIZED_HITOBJECT_RADIUS = 41.0f; + private const float ABSOLUTE_PLAYER_POSITIONING_ERROR = 16f; + private float playerPositioningError; + + internal CatchHitObject BaseHitObject; + + /// + /// Measures jump difficulty. CtB doesn't have something like button pressing speed or accuracy + /// + internal double Strain = 1; + + /// + /// This is required to keep track of lazy player movement (always moving only as far as necessary) + /// Without this quick repeat sliders / weirdly shaped streams might become ridiculously overrated + /// + internal float PlayerPositionOffset; + internal float LastMovement; + + internal float NormalizedPosition; + internal float ActualNormalizedPosition => NormalizedPosition + PlayerPositionOffset; + + internal CatchDifficultyHitObject(CatchHitObject baseHitObject, float catcherWidthHalf) + { + BaseHitObject = baseHitObject; + + // We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps. + float scalingFactor = NORMALIZED_HITOBJECT_RADIUS / catcherWidthHalf; + + playerPositioningError = ABSOLUTE_PLAYER_POSITIONING_ERROR;// * scalingFactor; + NormalizedPosition = baseHitObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor; + } + + private const double DIRECTION_CHANGE_BONUS = 12.5; + internal void CalculateStrains(CatchDifficultyHitObject previousHitObject, double timeRate) + { + // Rather simple, but more specialized things are inherently inaccurate due to the big difference playstyles and opinions make. + // See Taiko feedback thread. + double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate; + double decay = Math.Pow(DECAY_BASE, timeElapsed / 1000); + + // Update new position with lazy movement. + PlayerPositionOffset = + MathHelper.Clamp( + previousHitObject.ActualNormalizedPosition, + NormalizedPosition - (NORMALIZED_HITOBJECT_RADIUS - playerPositioningError), + NormalizedPosition + (NORMALIZED_HITOBJECT_RADIUS - playerPositioningError)) // Obtain new lazy position, but be stricter by allowing for an error of a certain degree of the player. + - NormalizedPosition; // Subtract HitObject position to obtain offset + + LastMovement = DistanceTo(previousHitObject); + double addition = spacingWeight(LastMovement); + + if (NormalizedPosition < previousHitObject.NormalizedPosition) + { + LastMovement = -LastMovement; + } + + CatchHitObject previousHitCircle = previousHitObject.BaseHitObject; + + double additionBonus = 0; + double sqrtTime = Math.Sqrt(Math.Max(timeElapsed, 25)); + + // Direction changes give an extra point! + if (Math.Abs(LastMovement) > 0.1) + { + if (Math.Abs(previousHitObject.LastMovement) > 0.1 && Math.Sign(LastMovement) != Math.Sign(previousHitObject.LastMovement)) + { + double bonus = DIRECTION_CHANGE_BONUS / sqrtTime; + + // Weight bonus by how + double bonusFactor = Math.Min(playerPositioningError, Math.Abs(LastMovement)) / playerPositioningError; + + // We want time to play a role twice here! + addition += bonus * bonusFactor; + + // Bonus for tougher direction switches and "almost" hyperdashes at this point + if (previousHitCircle != null && previousHitCircle.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH) + { + additionBonus += 0.3 * bonusFactor; + } + } + + // Base bonus for every movement, giving some weight to streams. + addition += 7.5 * Math.Min(Math.Abs(LastMovement), NORMALIZED_HITOBJECT_RADIUS * 2) / (NORMALIZED_HITOBJECT_RADIUS * 6) / sqrtTime; + } + + // Bonus for "almost" hyperdashes at corner points + if (previousHitCircle != null && previousHitCircle.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH) + { + if (!previousHitCircle.HyperDash) + { + additionBonus += 1.0; + } + else + { + // After a hyperdash we ARE in the correct position. Always! + PlayerPositionOffset = 0; + } + + addition *= 1.0 + additionBonus * ((10 - previousHitCircle.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 10); + } + + addition *= 850.0 / Math.Max(timeElapsed, 25); + + Strain = previousHitObject.Strain * decay + addition; + } + + private static double spacingWeight(float distance) + { + return Math.Pow(distance, 1.3) / 500; + } + + internal float DistanceTo(CatchDifficultyHitObject other) + { + return Math.Abs(ActualNormalizedPosition - other.ActualNormalizedPosition); + } + } +} From 6bf5ea73d5f19ba4d706f9754a7169c5ae8e5f8e Mon Sep 17 00:00:00 2001 From: frankhjwx Date: Mon, 21 May 2018 09:53:20 +0800 Subject: [PATCH 17/63] Fix CatcherWidth --- .../Difficulty/CatchDifficultyCalculator.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 5351bd746d..59c512c469 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -35,8 +35,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty difficultyHitObjects.Clear(); float circleSize = Beatmap.BeatmapInfo.BaseDifficulty.CircleSize; - float catcherWidth = (1.0f - 0.7f * (circleSize - 5) / 5) * 0.62064f * 172; - //float catcherWidth = (float)(305.0f / 1.6f * ((102.4f * (1.0f - 0.7 * (circleSize - 5.0f)) / 5.0f) / 128.0f * 0.7f)); + float catcherWidth = (1.0f - 0.7f * (circleSize - 5) / 5) * 0.62064f * CatcherArea.CATCHER_SIZE; float catcherWidthHalf = catcherWidth / 2; catcherWidthHalf *= 0.8f; From 0ef718a09e6269343b6a3a04b1b3f9bfaaaf7d3a Mon Sep 17 00:00:00 2001 From: frankhjwx Date: Mon, 21 May 2018 09:55:07 +0800 Subject: [PATCH 18/63] Fixes on categoryDifficulty --- .../Difficulty/CatchDifficultyCalculator.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 59c512c469..63570d44b0 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -72,9 +72,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty double preEmpt = BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450) / TimeRate; categoryDifficulty["AR"] = preEmpt > 1200.0 ? -(preEmpt - 1800.0) / 120.0 : -(preEmpt - 1200.0) / 150.0 + 5.0; - - //categoryDifficulty.Add("AR", (preEmpt > 1200.0 ? -(preEmpt - 1800.0) / 120.0 : -(preEmpt - 1200.0) / 150.0 + 5.0).ToString("0.00", GameBase.nfi)); - //categoryDifficulty.Add("Max combo", DifficultyHitObjects.Count.ToString(GameBase.nfi)); + categoryDifficulty["Max combo"] = difficultyHitObjects.Count; } return starRating; From 44fd4b95bd7ce7c7ec58e6dc82cd57f5447ac2b1 Mon Sep 17 00:00:00 2001 From: frankhjwx Date: Mon, 21 May 2018 09:58:46 +0800 Subject: [PATCH 19/63] We need DistanceToHyperDash for star rating calculation --- osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index 548813fbd2..d55cdac115 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -24,6 +24,11 @@ namespace osu.Game.Rulesets.Catch.Objects public int ComboIndex { get; set; } + /// + /// The distance for a fruit to to next hyper if it's not a hyper. + /// + public float DistanceToHyperDash { get; set; } + /// /// The next fruit starts a new combo. Used for explodey. /// From 0405383e4e3eb352b2e78e428ab473f00e69fca6 Mon Sep 17 00:00:00 2001 From: frankhjwx Date: Mon, 21 May 2018 10:12:18 +0800 Subject: [PATCH 20/63] Fixing code style --- .../Difficulty/CatchDifficultyCalculator.cs | 21 +++++-------------- .../Difficulty/CatchDifficultyHitObject.cs | 2 +- .../Difficulty/TaikoDifficultyCalculator.cs | 12 ++--------- .../Difficulty/DifficultyCalculator.cs | 15 +++++++++++++ 4 files changed, 23 insertions(+), 27 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 63570d44b0..83b5b3bb5c 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -31,7 +31,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty public override double Calculate(Dictionary categoryDifficulty = null) { - difficultyHitObjects.Clear(); float circleSize = Beatmap.BeatmapInfo.BaseDifficulty.CircleSize; @@ -83,7 +82,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment. using (List.Enumerator hitObjectsEnumerator = difficultyHitObjects.GetEnumerator()) { - if (!hitObjectsEnumerator.MoveNext()) return false; CatchDifficultyHitObject currentHitObject = hitObjectsEnumerator.Current; @@ -106,17 +104,17 @@ namespace osu.Game.Rulesets.Catch.Difficulty /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain. /// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage. /// - protected const double STRAIN_STEP = 750; + protected const double strain_step = 750; /// /// The weighting of each strain value decays to this number * it's previous value /// - protected const double DECAY_WEIGHT = 0.94; + protected const double decay_weight = 0.94; protected double CalculateDifficulty() { // The strain step needs to be adjusted for the algorithm to be considered equal with speed changing mods - double actualStrainStep = STRAIN_STEP * TimeRate; + double actualStrainStep = strain_step * TimeRate; // Find the highest strain value within each strain step List highestStrains = new List(); @@ -153,17 +151,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty previousHitObject = hitObject; } - - // Build the weighted sum over the highest strains for each interval - double difficulty = 0; - double weight = 1; - highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. - - foreach (double strain in highestStrains) - { - difficulty += weight * strain; - weight *= DECAY_WEIGHT; - } + // calculate maximun strain difficulty + double difficulty = StrainCalculator(highestStrains, decay_weight); return difficulty; } diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs index 14f9445c75..f4eefdd7cd 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty // We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps. float scalingFactor = NORMALIZED_HITOBJECT_RADIUS / catcherWidthHalf; - playerPositioningError = ABSOLUTE_PLAYER_POSITIONING_ERROR;// * scalingFactor; + playerPositioningError = ABSOLUTE_PLAYER_POSITIONING_ERROR; // * scalingFactor; NormalizedPosition = baseHitObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 57e1e65064..197464c10a 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -122,16 +122,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty previousHitObject = hitObject; } - // Build the weighted sum over the highest strains for each interval - double difficulty = 0; - double weight = 1; - highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. - - foreach (double strain in highestStrains) - { - difficulty += weight * strain; - weight *= decay_weight; - } + // calculate maximun strain difficulty + double difficulty = StrainCalculator(highestStrains, decay_weight); return difficulty; } diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 070bc7ddb0..348435b0d0 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -37,5 +37,20 @@ namespace osu.Game.Rulesets.Difficulty } public abstract double Calculate(Dictionary categoryDifficulty = null); + + protected double StrainCalculator(List highestStrains, double decayWeight) + { + // Build the weighted sum over the highest strains for each interval + double difficulty = 0; + double weight = 1; + highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. + + foreach (double strain in highestStrains) + { + difficulty += weight * strain; + weight *= decayWeight; + } + return difficulty; + } } } From 886be8ce1f7c29d558e3a34f42863740c7ca6515 Mon Sep 17 00:00:00 2001 From: frankhjwx Date: Mon, 21 May 2018 10:16:32 +0800 Subject: [PATCH 21/63] Adding lisence header --- .../Difficulty/CatchDifficultyCalculator.cs | 2 +- .../Difficulty/CatchDifficultyHitObject.cs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 83b5b3bb5c..19d1bab0ac 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty return true; } } - + /// /// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP. /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain. diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs index f4eefdd7cd..778ab9df28 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs @@ -1,4 +1,7 @@ -using System; +// 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.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; using OpenTK; @@ -76,7 +79,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty { double bonus = DIRECTION_CHANGE_BONUS / sqrtTime; - // Weight bonus by how + // Weight bonus by how double bonusFactor = Math.Min(playerPositioningError, Math.Abs(LastMovement)) / playerPositioningError; // We want time to play a role twice here! From 68a929eb0c1d78e38687f55b42b61c05d6a1e9a1 Mon Sep 17 00:00:00 2001 From: frankhjwx Date: Mon, 21 May 2018 10:39:16 +0800 Subject: [PATCH 22/63] Fixing parameters naming --- .../Difficulty/CatchDifficultyCalculator.cs | 17 +++++------ .../Difficulty/CatchDifficultyHitObject.cs | 28 +++++++++---------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 19d1bab0ac..31de9cfc8d 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -14,8 +14,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty { public class CatchDifficultyCalculator : DifficultyCalculator { - private const double STAR_SCALING_FACTOR = 0.145; - private const float PLAYFIELD_WIDTH = CatchPlayfield.BASE_WIDTH; + private const double star_scaling_factor = 0.145; + private const float playfield_width = CatchPlayfield.BASE_WIDTH; private readonly List difficultyHitObjects = new List(); @@ -54,6 +54,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty if (!(objectInJuiceStream is TinyDroplet)) difficultyHitObjects.Add(new CatchDifficultyHitObject(objectInJuiceStream, catcherWidthHalf)); } + // Dispose the enumerator after counting all fruits. + nestedHitObjectsEnumerator.Dispose(); } } @@ -61,7 +63,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty if (!CalculateStrainValues()) return 0; - double starRating = Math.Sqrt(CalculateDifficulty()) * STAR_SCALING_FACTOR; + double starRating = Math.Sqrt(CalculateDifficulty()) * star_scaling_factor; if (categoryDifficulty != null) { @@ -85,12 +87,11 @@ namespace osu.Game.Rulesets.Catch.Difficulty if (!hitObjectsEnumerator.MoveNext()) return false; CatchDifficultyHitObject currentHitObject = hitObjectsEnumerator.Current; - CatchDifficultyHitObject nextHitObject; // First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject. while (hitObjectsEnumerator.MoveNext()) { - nextHitObject = hitObjectsEnumerator.Current; + CatchDifficultyHitObject nextHitObject = hitObjectsEnumerator.Current; nextHitObject.CalculateStrains(currentHitObject, TimeRate); currentHitObject = nextHitObject; } @@ -104,12 +105,12 @@ namespace osu.Game.Rulesets.Catch.Difficulty /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain. /// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage. /// - protected const double strain_step = 750; + private const double strain_step = 750; /// /// The weighting of each strain value decays to this number * it's previous value /// - protected const double decay_weight = 0.94; + private const double decay_weight = 0.94; protected double CalculateDifficulty() { @@ -137,7 +138,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty } else { - double decay = Math.Pow(CatchDifficultyHitObject.DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); + double decay = Math.Pow(CatchDifficultyHitObject.decay_base, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); maximumStrain = previousHitObject.Strain * decay; } diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs index 778ab9df28..190c92b319 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs @@ -8,12 +8,12 @@ using OpenTK; namespace osu.Game.Rulesets.Catch.Difficulty { - class CatchDifficultyHitObject + internal class CatchDifficultyHitObject { - internal static readonly double DECAY_BASE = 0.20; - private const float NORMALIZED_HITOBJECT_RADIUS = 41.0f; - private const float ABSOLUTE_PLAYER_POSITIONING_ERROR = 16f; - private float playerPositioningError; + internal static readonly double decay_base = 0.20; + private const float normalized_hitobject_radius = 41.0f; + private const float absolute_player_positioning_error = 16f; + private readonly float playerPositioningError; internal CatchHitObject BaseHitObject; @@ -37,26 +37,26 @@ namespace osu.Game.Rulesets.Catch.Difficulty BaseHitObject = baseHitObject; // We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps. - float scalingFactor = NORMALIZED_HITOBJECT_RADIUS / catcherWidthHalf; + float scalingFactor = normalized_hitobject_radius / catcherWidthHalf; - playerPositioningError = ABSOLUTE_PLAYER_POSITIONING_ERROR; // * scalingFactor; + playerPositioningError = absolute_player_positioning_error; // * scalingFactor; NormalizedPosition = baseHitObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor; } - private const double DIRECTION_CHANGE_BONUS = 12.5; + private const double direction_change_bonus = 12.5; internal void CalculateStrains(CatchDifficultyHitObject previousHitObject, double timeRate) { // Rather simple, but more specialized things are inherently inaccurate due to the big difference playstyles and opinions make. // See Taiko feedback thread. double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate; - double decay = Math.Pow(DECAY_BASE, timeElapsed / 1000); + double decay = Math.Pow(decay_base, timeElapsed / 1000); // Update new position with lazy movement. PlayerPositionOffset = MathHelper.Clamp( previousHitObject.ActualNormalizedPosition, - NormalizedPosition - (NORMALIZED_HITOBJECT_RADIUS - playerPositioningError), - NormalizedPosition + (NORMALIZED_HITOBJECT_RADIUS - playerPositioningError)) // Obtain new lazy position, but be stricter by allowing for an error of a certain degree of the player. + NormalizedPosition - (normalized_hitobject_radius - playerPositioningError), + NormalizedPosition + (normalized_hitobject_radius - playerPositioningError)) // Obtain new lazy position, but be stricter by allowing for an error of a certain degree of the player. - NormalizedPosition; // Subtract HitObject position to obtain offset LastMovement = DistanceTo(previousHitObject); @@ -77,9 +77,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty { if (Math.Abs(previousHitObject.LastMovement) > 0.1 && Math.Sign(LastMovement) != Math.Sign(previousHitObject.LastMovement)) { - double bonus = DIRECTION_CHANGE_BONUS / sqrtTime; + double bonus = direction_change_bonus / sqrtTime; - // Weight bonus by how + // Weight bonus by how double bonusFactor = Math.Min(playerPositioningError, Math.Abs(LastMovement)) / playerPositioningError; // We want time to play a role twice here! @@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty } // Base bonus for every movement, giving some weight to streams. - addition += 7.5 * Math.Min(Math.Abs(LastMovement), NORMALIZED_HITOBJECT_RADIUS * 2) / (NORMALIZED_HITOBJECT_RADIUS * 6) / sqrtTime; + addition += 7.5 * Math.Min(Math.Abs(LastMovement), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtTime; } // Bonus for "almost" hyperdashes at corner points From 871743204b00983179fff782745cdea1bf341b5a Mon Sep 17 00:00:00 2001 From: frankhjwx Date: Mon, 21 May 2018 10:41:58 +0800 Subject: [PATCH 23/63] Fixing parameters naming --- osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs index 190c92b319..d9b16c2a0b 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty { double bonus = direction_change_bonus / sqrtTime; - // Weight bonus by how + // Weight bonus by how double bonusFactor = Math.Min(playerPositioningError, Math.Abs(LastMovement)) / playerPositioningError; // We want time to play a role twice here! From 2e5bc4323ad553b8f85fef881b552b8e8c9e956d Mon Sep 17 00:00:00 2001 From: frankhjwx Date: Mon, 21 May 2018 13:31:00 +0800 Subject: [PATCH 24/63] Fixing null reference exception bugs --- .../Difficulty/CatchDifficultyCalculator.cs | 9 +++++---- .../Difficulty/CatchDifficultyHitObject.cs | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 31de9cfc8d..2fa946f41c 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty difficultyHitObjects.Clear(); float circleSize = Beatmap.BeatmapInfo.BaseDifficulty.CircleSize; - float catcherWidth = (1.0f - 0.7f * (circleSize - 5) / 5) * 0.62064f * CatcherArea.CATCHER_SIZE; + float catcherWidth = ((1.0f - 0.7f * (circleSize - 5) / 5) * 0.62064f) * CatcherArea.CATCHER_SIZE; float catcherWidthHalf = catcherWidth / 2; catcherWidthHalf *= 0.8f; @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty double ar = Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate; double preEmpt = BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450) / TimeRate; - categoryDifficulty["AR"] = preEmpt > 1200.0 ? -(preEmpt - 1800.0) / 120.0 : -(preEmpt - 1200.0) / 150.0 + 5.0; + categoryDifficulty["AR"] = (preEmpt > 1200.0) ? -(preEmpt - 1800.0) / 120.0 : (-(preEmpt - 1200.0) / 150.0) + 5.0; categoryDifficulty["Max combo"] = difficultyHitObjects.Count; } @@ -92,7 +92,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty while (hitObjectsEnumerator.MoveNext()) { CatchDifficultyHitObject nextHitObject = hitObjectsEnumerator.Current; - nextHitObject.CalculateStrains(currentHitObject, TimeRate); + if (nextHitObject != null) + nextHitObject.CalculateStrains(currentHitObject, TimeRate); currentHitObject = nextHitObject; } @@ -111,7 +112,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty /// The weighting of each strain value decays to this number * it's previous value /// private const double decay_weight = 0.94; - + protected double CalculateDifficulty() { // The strain step needs to be adjusted for the algorithm to be considered equal with speed changing mods diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs index d9b16c2a0b..72fc6d211b 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs @@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty { internal class CatchDifficultyHitObject { - internal static readonly double decay_base = 0.20; + internal static readonly double DECAY_BASE = 0.20; private const float normalized_hitobject_radius = 41.0f; private const float absolute_player_positioning_error = 16f; private readonly float playerPositioningError; @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty // Rather simple, but more specialized things are inherently inaccurate due to the big difference playstyles and opinions make. // See Taiko feedback thread. double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate; - double decay = Math.Pow(decay_base, timeElapsed / 1000); + double decay = Math.Pow(DECAY_BASE, timeElapsed / 1000); // Update new position with lazy movement. PlayerPositionOffset = From 6d71f7f220940eb8bcc2750dd3e192b85fa6e777 Mon Sep 17 00:00:00 2001 From: frankhjwx Date: Mon, 21 May 2018 13:35:02 +0800 Subject: [PATCH 25/63] Minor fixes --- osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 2fa946f41c..beee332504 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty /// The weighting of each strain value decays to this number * it's previous value /// private const double decay_weight = 0.94; - + protected double CalculateDifficulty() { // The strain step needs to be adjusted for the algorithm to be considered equal with speed changing mods From 193a29860144f3e5c41b8b09c5492271ff07a2fd Mon Sep 17 00:00:00 2001 From: frankhjwx Date: Mon, 21 May 2018 13:36:57 +0800 Subject: [PATCH 26/63] Fixing minor bugs --- osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index beee332504..43c157df06 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -139,7 +139,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty } else { - double decay = Math.Pow(CatchDifficultyHitObject.decay_base, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); + double decay = Math.Pow(CatchDifficultyHitObject.DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); maximumStrain = previousHitObject.Strain * decay; } From 4784538d7349a2e1fcef059141c992524a2e2bd6 Mon Sep 17 00:00:00 2001 From: frankhjwx Date: Mon, 21 May 2018 13:53:48 +0800 Subject: [PATCH 27/63] Fixing minor bugs --- osu-framework | 2 +- .../Difficulty/CatchDifficultyCalculator.cs | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/osu-framework b/osu-framework index 80e78fd45b..fac688633b 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 80e78fd45bb79ca4bc46ecc05deb6058f3879faa +Subproject commit fac688633b8fcf34ae5d0514c26b03e217161eb4 diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 43c157df06..975e8eed58 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty difficultyHitObjects.Clear(); float circleSize = Beatmap.BeatmapInfo.BaseDifficulty.CircleSize; - float catcherWidth = ((1.0f - 0.7f * (circleSize - 5) / 5) * 0.62064f) * CatcherArea.CATCHER_SIZE; + float catcherWidth = (1.0f - 0.7f * (circleSize - 5) / 5) * 0.62064f * CatcherArea.CATCHER_SIZE; float catcherWidthHalf = catcherWidth / 2; catcherWidthHalf *= 0.8f; @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty double ar = Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate; double preEmpt = BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450) / TimeRate; - categoryDifficulty["AR"] = (preEmpt > 1200.0) ? -(preEmpt - 1800.0) / 120.0 : (-(preEmpt - 1200.0) / 150.0) + 5.0; + categoryDifficulty["AR"] = preEmpt > 1200.0 ? -(preEmpt - 1800.0) / 120.0 : -(preEmpt - 1200.0) / 150.0 + 5.0; categoryDifficulty["Max combo"] = difficultyHitObjects.Count; } @@ -92,8 +92,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty while (hitObjectsEnumerator.MoveNext()) { CatchDifficultyHitObject nextHitObject = hitObjectsEnumerator.Current; - if (nextHitObject != null) - nextHitObject.CalculateStrains(currentHitObject, TimeRate); + nextHitObject?.CalculateStrains(currentHitObject, TimeRate); currentHitObject = nextHitObject; } From 498244a3089827cbd58aff593d0ec3963cf32303 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Fri, 25 May 2018 00:37:53 +0300 Subject: [PATCH 28/63] Introduce PreviewTrack class --- osu.Game/Audio/PreviewTrack.cs | 39 +++++++++++++++++ osu.Game/Audio/PreviewTrackManager.cs | 43 ++++++++----------- .../BeatmapSet/Buttons/PreviewButton.cs | 6 +-- osu.Game/Overlays/Direct/DirectPanel.cs | 16 +++---- osu.Game/Overlays/Direct/PlayButton.cs | 18 ++++---- 5 files changed, 76 insertions(+), 46 deletions(-) create mode 100644 osu.Game/Audio/PreviewTrack.cs diff --git a/osu.Game/Audio/PreviewTrack.cs b/osu.Game/Audio/PreviewTrack.cs new file mode 100644 index 0000000000..10dec7c394 --- /dev/null +++ b/osu.Game/Audio/PreviewTrack.cs @@ -0,0 +1,39 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Audio.Track; + +namespace osu.Game.Audio +{ + public class PreviewTrack + { + public readonly Track Track; + private readonly Action onStart; + private readonly Action onStop; + + public event Action Stopped; + public event Action Started; + + public PreviewTrack(Track track, Action onStart, Action onStop) + { + Track = track; + this.onStart = onStart; + this.onStop = onStop; + } + + public void Start() + { + onStart?.Invoke(this); + Track.Restart(); + Started?.Invoke(); + } + + public void Stop() + { + onStop?.Invoke(); + Track.Stop(); + Stopped?.Invoke(); + } + } +} diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs index 2e5f26bcf4..5c7f580ca8 100644 --- a/osu.Game/Audio/PreviewTrackManager.cs +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -14,13 +14,13 @@ namespace osu.Game.Audio { public class PreviewTrackManager : Component { - public event Action PlaybackStarted; - public event Action PlaybackStopped; + private Action onTrackStart; + private Action onTrackStop; private TrackManager trackManager; private BindableDouble muteBindable; - public Track CurrentTrack { get; private set; } + public PreviewTrack CurrentTrack { get; private set; } [BackgroundDependencyLoader] private void load(AudioManager audio, FrameworkConfigManager config) @@ -32,35 +32,28 @@ namespace osu.Game.Audio audio.AddItem(trackManager); config.BindWith(FrameworkSetting.VolumeMusic, trackManager.Volume); - PlaybackStarted += () => audio.Track.AddAdjustment(AdjustableProperty.Volume, muteBindable); - PlaybackStopped += () => audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable); + onTrackStart = track => + { + CurrentTrack?.Stop(); + audio.Track.AddAdjustment(AdjustableProperty.Volume, muteBindable); + CurrentTrack = track; + CurrentTrack.Stopped += () => CurrentTrack = null; + }; + onTrackStop = () => audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable); } - public Track Get(BeatmapSetInfo beatmapSetInfo) => trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo.OnlineBeatmapSetID}.mp3"); + public PreviewTrack Get(BeatmapSetInfo beatmapSetInfo) => + new PreviewTrack( + trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo.OnlineBeatmapSetID}.mp3"), + onTrackStart, + onTrackStop); protected override void Update() { - if (CurrentTrack?.HasCompleted ?? false) - PlaybackStopped?.Invoke(); + if (CurrentTrack?.Track.HasCompleted ?? false) + CurrentTrack.Stop(); base.Update(); } - - public void Play(Track track) - { - Stop(); - CurrentTrack = track; - track.Restart(); - PlaybackStarted?.Invoke(); - } - - public void Stop() - { - if (CurrentTrack?.IsRunning ?? false) - { - CurrentTrack?.Stop(); - PlaybackStopped?.Invoke(); - } - } } } diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs index 08a99f1aea..78628675ac 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs @@ -2,13 +2,13 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; -using osu.Framework.Audio.Track; using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input; +using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons private readonly Box bg, progress; private readonly PlayButton playButton; - private Track preview => playButton.Preview; + private PreviewTrack preview => playButton.Preview; public Bindable Playing => playButton.Playing; public BeatmapSetInfo BeatmapSet @@ -83,7 +83,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons if (Playing.Value && preview != null) { // prevent negative (potential infinite) width if a track without length was loaded - progress.Width = preview.Length > 0 ? (float)(preview.CurrentTime / preview.Length) : 0f; + progress.Width = preview.Track.Length > 0 ? (float)(preview.Track.CurrentTime / preview.Track.Length) : 0f; } else progress.Width = 0; diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index cc0123dabc..2ceefe31ef 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -4,22 +4,22 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; -using OpenTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; +using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using OpenTK.Graphics; -using osu.Framework.Input; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests; -using osu.Framework.Configuration; -using osu.Framework.Audio.Track; +using OpenTK; +using OpenTK.Graphics; namespace osu.Game.Overlays.Direct { @@ -37,7 +37,7 @@ namespace osu.Game.Overlays.Direct private BeatmapManager beatmaps; private BeatmapSetOverlay beatmapSetOverlay; - public Track Preview => PlayButton.Preview; + public PreviewTrack Preview => PlayButton.Preview; public Bindable PreviewPlaying => PlayButton.Playing; protected abstract PlayButton PlayButton { get; } protected abstract Box PreviewBar { get; } @@ -121,9 +121,9 @@ namespace osu.Game.Overlays.Direct { base.Update(); - if (PreviewPlaying && Preview != null && Preview.IsLoaded) + if (PreviewPlaying && Preview != null && Preview.Track.IsLoaded) { - PreviewBar.Width = (float)(Preview.CurrentTime / Preview.Length); + PreviewBar.Width = (float)(Preview.Track.CurrentTime / Preview.Track.Length); } } diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index 77c904050b..1f18faaf14 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using osu.Framework.Allocation; -using osu.Framework.Audio.Track; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -19,7 +18,7 @@ namespace osu.Game.Overlays.Direct public class PlayButton : Container { public readonly Bindable Playing = new Bindable(); - public Track Preview { get; private set; } + public PreviewTrack Preview { get; private set; } private BeatmapSetInfo beatmapSet; @@ -32,6 +31,8 @@ namespace osu.Game.Overlays.Direct beatmapSet = value; Playing.Value = false; + if (Preview != null) + Preview.Stopped -= preview_Stopped; Preview = null; } } @@ -85,12 +86,6 @@ namespace osu.Game.Overlays.Direct { hoverColour = colour.Yellow; this.previewTrackManager = previewTrackManager; - - previewTrackManager.PlaybackStopped += () => - { - if (Preview == previewTrackManager.CurrentTrack) - Playing.Value = false; - }; } protected override bool OnClick(InputState state) @@ -134,21 +129,24 @@ namespace osu.Game.Overlays.Direct }) .ContinueWith(t => { + Preview.Stopped += preview_Stopped; playingStateChanged(true); loading = false; }); return; } - previewTrackManager.Play(Preview); + Preview.Start(); } else { - previewTrackManager.Stop(); + Preview.Stop(); loading = false; } } + private void preview_Stopped() => Playing.Value = false; + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From a24589f5836d87b758ba3cafcc694676fb65d208 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Fri, 25 May 2018 22:35:15 +0300 Subject: [PATCH 29/63] Cleanup external PlayButton.Playing usage --- osu.Game/Audio/PreviewTrackManager.cs | 9 ++-- .../BeatmapSet/Buttons/PreviewButton.cs | 8 +-- osu.Game/Overlays/BeatmapSet/Details.cs | 6 ++- osu.Game/Overlays/Direct/DirectPanel.cs | 1 - osu.Game/Overlays/Direct/PlayButton.cs | 52 +++++++++---------- osu.Game/Overlays/DirectOverlay.cs | 26 ---------- 6 files changed, 37 insertions(+), 65 deletions(-) diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs index 5c7f580ca8..5c2656b322 100644 --- a/osu.Game/Audio/PreviewTrackManager.cs +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -37,14 +37,17 @@ namespace osu.Game.Audio CurrentTrack?.Stop(); audio.Track.AddAdjustment(AdjustableProperty.Volume, muteBindable); CurrentTrack = track; - CurrentTrack.Stopped += () => CurrentTrack = null; }; - onTrackStop = () => audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable); + onTrackStop = () => + { + audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable); + CurrentTrack = null; + }; } public PreviewTrack Get(BeatmapSetInfo beatmapSetInfo) => new PreviewTrack( - trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo.OnlineBeatmapSetID}.mp3"), + trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo?.OnlineBeatmapSetID}.mp3"), onTrackStart, onTrackStop); diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs index 78628675ac..78bc77efe8 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs @@ -66,7 +66,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons }, }; - Action = () => Playing.Value = !Playing.Value; + Action = () => playButton.TriggerOnClick(); Playing.ValueChanged += newValue => progress.FadeTo(newValue ? 1 : 0, 100); } @@ -89,12 +89,6 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons progress.Width = 0; } - protected override void Dispose(bool isDisposing) - { - Playing.Value = false; - base.Dispose(isDisposing); - } - protected override bool OnHover(InputState state) { bg.FadeColour(Color4.Black.Opacity(0.5f), 100); diff --git a/osu.Game/Overlays/BeatmapSet/Details.cs b/osu.Game/Overlays/BeatmapSet/Details.cs index 5264caf936..8c16822cfc 100644 --- a/osu.Game/Overlays/BeatmapSet/Details.cs +++ b/osu.Game/Overlays/BeatmapSet/Details.cs @@ -102,7 +102,11 @@ namespace osu.Game.Overlays.BeatmapSet updateDisplay(); } - public void StopPreview() => preview.Playing.Value = false; + public void StopPreview() + { + if (preview.Playing) + preview.TriggerOnClick(); + } private class DetailBox : Container { diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 2ceefe31ef..1c7abd4cdd 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -149,7 +149,6 @@ namespace osu.Game.Overlays.Direct protected override bool OnClick(InputState state) { ShowInformation(); - PreviewPlaying.Value = false; return true; } diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index 1f18faaf14..677f74540b 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -31,8 +31,6 @@ namespace osu.Game.Overlays.Direct beatmapSet = value; Playing.Value = false; - if (Preview != null) - Preview.Stopped -= preview_Stopped; Preview = null; } } @@ -90,7 +88,30 @@ namespace osu.Game.Overlays.Direct protected override bool OnClick(InputState state) { - Playing.Value = !Playing.Value; + if (!Playing.Value) + { + if (Preview == null) + { + Task.Run(() => + { + loading = true; + return Preview = previewTrackManager.Get(beatmapSet); + }) + .ContinueWith(t => + { + Preview.Started += () => Playing.Value = true; + Preview.Stopped += () => Playing.Value = false; + Preview.Start(); + loading = false; + }); + return true; + } + + Preview.Start(); + } + else + Preview?.Stop(); + return true; } @@ -118,35 +139,12 @@ namespace osu.Game.Overlays.Direct icon.Icon = playing ? FontAwesome.fa_pause : FontAwesome.fa_play; icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint); - if (playing) + if (!playing) { - if (Preview == null) - { - Task.Run(() => - { - loading = true; - return Preview = previewTrackManager.Get(beatmapSet); - }) - .ContinueWith(t => - { - Preview.Stopped += preview_Stopped; - playingStateChanged(true); - loading = false; - }); - return; - } - - Preview.Start(); - } - else - { - Preview.Stop(); loading = false; } } - private void preview_Stopped() => Playing.Value = false; - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index f437546888..35a6e5fc48 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -33,7 +33,6 @@ namespace osu.Game.Overlays private readonly FillFlowContainer resultCountsContainer; private readonly OsuSpriteText resultCountsText; private FillFlowContainer panels; - private DirectPanel playing; protected override Color4 BackgroundColour => OsuColour.FromHex(@"485e74"); protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"465b71"); @@ -217,12 +216,6 @@ namespace osu.Game.Overlays panels.FadeOut(200); panels.Expire(); panels = null; - - if (playing != null) - { - playing.PreviewPlaying.Value = false; - playing = null; - } } if (BeatmapSets == null) return; @@ -253,17 +246,6 @@ namespace osu.Game.Overlays { if (panels != null) ScrollFlow.Remove(panels); ScrollFlow.Add(panels = newPanels); - - foreach (DirectPanel panel in p.Children) - panel.PreviewPlaying.ValueChanged += newValue => - { - if (newValue) - { - if (playing != null && playing != panel) - playing.PreviewPlaying.Value = false; - playing = panel; - } - }; }); } @@ -313,14 +295,6 @@ namespace osu.Game.Overlays api.Queue(getSetsRequest); } - protected override void PopOut() - { - base.PopOut(); - - if (playing != null) - playing.PreviewPlaying.Value = false; - } - private int distinctCount(List list) => list.Distinct().ToArray().Length; protected override void Dispose(bool isDisposing) From 572c3f518948a1729a34e24646f252d7d65d6e1e Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Fri, 25 May 2018 23:51:05 +0300 Subject: [PATCH 30/63] Stop preview playback when an overlay is popping out --- osu.Game/Overlays/BeatmapSet/Details.cs | 6 ------ osu.Game/Overlays/BeatmapSetOverlay.cs | 15 +++++++++------ osu.Game/Overlays/DirectOverlay.cs | 13 +++++++++++-- osu.Game/Overlays/UserProfileOverlay.cs | 10 +++++++--- 4 files changed, 27 insertions(+), 17 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Details.cs b/osu.Game/Overlays/BeatmapSet/Details.cs index 8c16822cfc..ccd0fa04ab 100644 --- a/osu.Game/Overlays/BeatmapSet/Details.cs +++ b/osu.Game/Overlays/BeatmapSet/Details.cs @@ -102,12 +102,6 @@ namespace osu.Game.Overlays.BeatmapSet updateDisplay(); } - public void StopPreview() - { - if (preview.Playing) - preview.TriggerOnClick(); - } - private class DetailBox : Container { private readonly Container content; diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 096f7bb63c..53bae54379 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -1,23 +1,24 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Linq; using osu.Framework.Allocation; -using OpenTK; -using OpenTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input; +using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.BeatmapSet; -using osu.Game.Rulesets; using osu.Game.Overlays.BeatmapSet.Scores; -using System.Linq; +using osu.Game.Rulesets; +using OpenTK; +using OpenTK.Graphics; namespace osu.Game.Overlays { @@ -37,6 +38,7 @@ namespace osu.Game.Overlays private readonly ScrollContainer scroll; private BeatmapSetInfo beatmapSet; + private PreviewTrackManager previewTrackManager; public BeatmapSetInfo BeatmapSet { @@ -109,10 +111,11 @@ namespace osu.Game.Overlays } [BackgroundDependencyLoader] - private void load(APIAccess api, RulesetStore rulesets) + private void load(APIAccess api, RulesetStore rulesets, PreviewTrackManager previewTrackManager) { this.api = api; this.rulesets = rulesets; + this.previewTrackManager = previewTrackManager; } protected override void PopIn() @@ -124,7 +127,7 @@ namespace osu.Game.Overlays protected override void PopOut() { base.PopOut(); - header.Details.StopPreview(); + previewTrackManager.CurrentTrack?.Stop(); FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out).OnComplete(_ => BeatmapSet = null); } diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 35a6e5fc48..c69dc57342 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -4,12 +4,12 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using OpenTK; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Threading; +using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -18,6 +18,7 @@ using osu.Game.Online.API.Requests; using osu.Game.Overlays.Direct; using osu.Game.Overlays.SearchableList; using osu.Game.Rulesets; +using OpenTK; using OpenTK.Graphics; namespace osu.Game.Overlays @@ -176,11 +177,12 @@ namespace osu.Game.Overlays } [BackgroundDependencyLoader] - private void load(OsuColour colours, APIAccess api, RulesetStore rulesets, BeatmapManager beatmaps) + private void load(OsuColour colours, APIAccess api, RulesetStore rulesets, BeatmapManager beatmaps, PreviewTrackManager previewTrackManager) { this.api = api; this.rulesets = rulesets; this.beatmaps = beatmaps; + this.previewTrackManager = previewTrackManager; resultCountsContainer.Colour = colours.Yellow; @@ -254,6 +256,7 @@ namespace osu.Game.Overlays private readonly Bindable currentQuery = new Bindable(); private ScheduledDelegate queryChangedDebounce; + private PreviewTrackManager previewTrackManager; private void updateSearch() { @@ -297,6 +300,12 @@ namespace osu.Game.Overlays private int distinctCount(List list) => list.Distinct().ToArray().Length; + protected override void PopOut() + { + previewTrackManager.CurrentTrack?.Stop(); + base.PopOut(); + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index a4dd0c9ec3..04a93dea23 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -2,14 +2,13 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Linq; -using OpenTK; -using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; +using osu.Game.Audio; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -18,6 +17,8 @@ using osu.Game.Online.API.Requests; using osu.Game.Overlays.Profile; using osu.Game.Overlays.Profile.Sections; using osu.Game.Users; +using OpenTK; +using OpenTK.Graphics; namespace osu.Game.Overlays { @@ -30,6 +31,7 @@ namespace osu.Game.Overlays protected ProfileHeader Header; private SectionsContainer sectionsContainer; private ProfileTabControl tabs; + private PreviewTrackManager previewTrackManager; public const float CONTENT_X_MARGIN = 50; @@ -56,9 +58,10 @@ namespace osu.Game.Overlays } [BackgroundDependencyLoader] - private void load(APIAccess api) + private void load(APIAccess api, PreviewTrackManager previewTrackManager) { this.api = api; + this.previewTrackManager = previewTrackManager; } protected override void PopIn() @@ -70,6 +73,7 @@ namespace osu.Game.Overlays protected override void PopOut() { base.PopOut(); + previewTrackManager.CurrentTrack?.Stop(); FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out); } From 88ac427ba67a216dff3dc654279fa95cac013df4 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Fri, 1 Jun 2018 21:06:37 +0300 Subject: [PATCH 31/63] PreviewTrack.Owner --- osu.Game/Audio/PreviewTrack.cs | 6 +++++- osu.Game/Audio/PreviewTrackManager.cs | 6 ++++-- osu.Game/Overlays/BeatmapSetOverlay.cs | 3 ++- osu.Game/Overlays/Direct/PlayButton.cs | 14 +++++++++++++- osu.Game/Overlays/DirectOverlay.cs | 3 ++- 5 files changed, 26 insertions(+), 6 deletions(-) diff --git a/osu.Game/Audio/PreviewTrack.cs b/osu.Game/Audio/PreviewTrack.cs index 10dec7c394..d182df42e4 100644 --- a/osu.Game/Audio/PreviewTrack.cs +++ b/osu.Game/Audio/PreviewTrack.cs @@ -3,23 +3,27 @@ using System; using osu.Framework.Audio.Track; +using osu.Framework.Graphics.Containers; namespace osu.Game.Audio { public class PreviewTrack { public readonly Track Track; + public readonly OverlayContainer Owner; + private readonly Action onStart; private readonly Action onStop; public event Action Stopped; public event Action Started; - public PreviewTrack(Track track, Action onStart, Action onStop) + public PreviewTrack(Track track, Action onStart, Action onStop, OverlayContainer owner) { Track = track; this.onStart = onStart; this.onStop = onStop; + Owner = owner; } public void Start() diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs index 5c2656b322..59a088d954 100644 --- a/osu.Game/Audio/PreviewTrackManager.cs +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -7,6 +7,7 @@ using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.IO.Stores; using osu.Game.Beatmaps; @@ -45,11 +46,12 @@ namespace osu.Game.Audio }; } - public PreviewTrack Get(BeatmapSetInfo beatmapSetInfo) => + public PreviewTrack Get(BeatmapSetInfo beatmapSetInfo, OverlayContainer previewOwner) => new PreviewTrack( trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo?.OnlineBeatmapSetID}.mp3"), onTrackStart, - onTrackStop); + onTrackStop, + previewOwner); protected override void Update() { diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 53bae54379..cccd51a621 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -127,7 +127,8 @@ namespace osu.Game.Overlays protected override void PopOut() { base.PopOut(); - previewTrackManager.CurrentTrack?.Stop(); + if (previewTrackManager.CurrentTrack?.Owner == this) + previewTrackManager.CurrentTrack?.Stop(); FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out).OnComplete(_ => BeatmapSet = null); } diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index 677f74540b..a5c9da4fba 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -35,6 +35,18 @@ namespace osu.Game.Overlays.Direct } } + private OverlayContainer parentOverlayContainer + { + get + { + var d = Parent; + while (!(d is OverlayContainer)) + d = d.Parent; + + return (OverlayContainer)d; + } + } + private PreviewTrackManager previewTrackManager; private Color4 hoverColour; @@ -95,7 +107,7 @@ namespace osu.Game.Overlays.Direct Task.Run(() => { loading = true; - return Preview = previewTrackManager.Get(beatmapSet); + return Preview = previewTrackManager.Get(beatmapSet, parentOverlayContainer); }) .ContinueWith(t => { diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index c69dc57342..72de8ebc84 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -302,7 +302,8 @@ namespace osu.Game.Overlays protected override void PopOut() { - previewTrackManager.CurrentTrack?.Stop(); + if (previewTrackManager.CurrentTrack?.Owner == this) + previewTrackManager.CurrentTrack?.Stop(); base.PopOut(); } From 556673266427af65322f62e4893dd224df330165 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Fri, 1 Jun 2018 21:36:30 +0300 Subject: [PATCH 32/63] Get rid of PreviewTrackManager.onTrackStart and PreviewTrackManager.onTrackStop delegates --- osu.Game/Audio/PreviewTrack.cs | 9 +----- osu.Game/Audio/PreviewTrackManager.cs | 43 ++++++++++++++------------- 2 files changed, 23 insertions(+), 29 deletions(-) diff --git a/osu.Game/Audio/PreviewTrack.cs b/osu.Game/Audio/PreviewTrack.cs index d182df42e4..1250d4864e 100644 --- a/osu.Game/Audio/PreviewTrack.cs +++ b/osu.Game/Audio/PreviewTrack.cs @@ -12,30 +12,23 @@ namespace osu.Game.Audio public readonly Track Track; public readonly OverlayContainer Owner; - private readonly Action onStart; - private readonly Action onStop; - public event Action Stopped; public event Action Started; - public PreviewTrack(Track track, Action onStart, Action onStop, OverlayContainer owner) + public PreviewTrack(Track track, OverlayContainer owner) { Track = track; - this.onStart = onStart; - this.onStop = onStop; Owner = owner; } public void Start() { - onStart?.Invoke(this); Track.Restart(); Started?.Invoke(); } public void Stop() { - onStop?.Invoke(); Track.Stop(); Stopped?.Invoke(); } diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs index 59a088d954..4189468b77 100644 --- a/osu.Game/Audio/PreviewTrackManager.cs +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Track; @@ -15,9 +14,7 @@ namespace osu.Game.Audio { public class PreviewTrackManager : Component { - private Action onTrackStart; - private Action onTrackStop; - + private AudioManager audio; private TrackManager trackManager; private BindableDouble muteBindable; @@ -27,32 +24,36 @@ namespace osu.Game.Audio private void load(AudioManager audio, FrameworkConfigManager config) { trackManager = new TrackManager(new OnlineStore()); - muteBindable = new BindableDouble(); + this.audio = audio; audio.AddItem(trackManager); - config.BindWith(FrameworkSetting.VolumeMusic, trackManager.Volume); - onTrackStart = track => - { - CurrentTrack?.Stop(); - audio.Track.AddAdjustment(AdjustableProperty.Volume, muteBindable); - CurrentTrack = track; - }; - onTrackStop = () => - { - audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable); - CurrentTrack = null; - }; + config.BindWith(FrameworkSetting.VolumeMusic, trackManager.Volume); } - public PreviewTrack Get(BeatmapSetInfo beatmapSetInfo, OverlayContainer previewOwner) => - new PreviewTrack( + public PreviewTrack Get(BeatmapSetInfo beatmapSetInfo, OverlayContainer previewOwner) + { + var previewTrack = new PreviewTrack( trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo?.OnlineBeatmapSetID}.mp3"), - onTrackStart, - onTrackStop, previewOwner); + previewTrack.Started += () => + { + CurrentTrack?.Stop(); + CurrentTrack = previewTrack; + audio.Track.AddAdjustment(AdjustableProperty.Volume, muteBindable); + }; + + previewTrack.Stopped += () => + { + CurrentTrack = null; + audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable); + }; + + return previewTrack; + } + protected override void Update() { if (CurrentTrack?.Track.HasCompleted ?? false) From 330ce1904122d4169104ae500ce5338e3ba584e5 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Fri, 1 Jun 2018 23:36:25 +0300 Subject: [PATCH 33/63] Make PreviewTrack a component and use LoadComponentAsync --- osu.Game/Audio/PreviewTrack.cs | 19 +++++++++++++++---- osu.Game/Audio/PreviewTrackManager.cs | 23 +++++++++-------------- osu.Game/Overlays/Direct/PlayButton.cs | 13 ++++++------- 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/osu.Game/Audio/PreviewTrack.cs b/osu.Game/Audio/PreviewTrack.cs index 1250d4864e..d8898dfede 100644 --- a/osu.Game/Audio/PreviewTrack.cs +++ b/osu.Game/Audio/PreviewTrack.cs @@ -2,25 +2,36 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using osu.Framework.Allocation; using osu.Framework.Audio.Track; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; namespace osu.Game.Audio { - public class PreviewTrack + public class PreviewTrack : Component { - public readonly Track Track; + public Track Track { get; private set; } public readonly OverlayContainer Owner; + private readonly BeatmapSetInfo beatmapSetInfo; + public event Action Stopped; public event Action Started; - public PreviewTrack(Track track, OverlayContainer owner) + public PreviewTrack(BeatmapSetInfo beatmapSetInfo, OverlayContainer owner) { - Track = track; + this.beatmapSetInfo = beatmapSetInfo; Owner = owner; } + [BackgroundDependencyLoader] + private void load(PreviewTrackManager previewTrackManager) + { + Track = previewTrackManager.Get(this, beatmapSetInfo); + } + public void Start() { Track.Restart(); diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs index 4189468b77..a3da930af0 100644 --- a/osu.Game/Audio/PreviewTrackManager.cs +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -6,7 +6,6 @@ using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Configuration; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.IO.Stores; using osu.Game.Beatmaps; @@ -32,12 +31,16 @@ namespace osu.Game.Audio config.BindWith(FrameworkSetting.VolumeMusic, trackManager.Volume); } - public PreviewTrack Get(BeatmapSetInfo beatmapSetInfo, OverlayContainer previewOwner) + protected override void Update() { - var previewTrack = new PreviewTrack( - trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo?.OnlineBeatmapSetID}.mp3"), - previewOwner); + if (CurrentTrack?.Track.HasCompleted ?? false) + CurrentTrack.Stop(); + base.Update(); + } + + public Track Get(PreviewTrack previewTrack, BeatmapSetInfo beatmapSetInfo) + { previewTrack.Started += () => { CurrentTrack?.Stop(); @@ -51,15 +54,7 @@ namespace osu.Game.Audio audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable); }; - return previewTrack; - } - - protected override void Update() - { - if (CurrentTrack?.Track.HasCompleted ?? false) - CurrentTrack.Stop(); - - base.Update(); + return trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo?.OnlineBeatmapSetID}.mp3"); } } } diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index a5c9da4fba..36d1170380 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; @@ -104,18 +103,18 @@ namespace osu.Game.Overlays.Direct { if (Preview == null) { - Task.Run(() => - { - loading = true; - return Preview = previewTrackManager.Get(beatmapSet, parentOverlayContainer); - }) - .ContinueWith(t => + loading = true; + + LoadComponentAsync( + Preview = new PreviewTrack(beatmapSet, parentOverlayContainer), + t => { Preview.Started += () => Playing.Value = true; Preview.Stopped += () => Playing.Value = false; Preview.Start(); loading = false; }); + return true; } From 70120aa14c1018f1ecb18ecc5ed50852673f7fa3 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Sat, 2 Jun 2018 12:28:16 +0300 Subject: [PATCH 34/63] Remove unused previewTrackManager variable --- osu.Game/Overlays/Direct/PlayButton.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index 36d1170380..92d1acbaa4 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -46,8 +46,6 @@ namespace osu.Game.Overlays.Direct } } - private PreviewTrackManager previewTrackManager; - private Color4 hoverColour; private readonly SpriteIcon icon; private readonly LoadingAnimation loadingAnimation; @@ -94,7 +92,6 @@ namespace osu.Game.Overlays.Direct private void load(OsuColour colour, PreviewTrackManager previewTrackManager) { hoverColour = colour.Yellow; - this.previewTrackManager = previewTrackManager; } protected override bool OnClick(InputState state) From 5bb6757cbdbc24cc130cb2cbea643353032c7ae6 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Sat, 2 Jun 2018 13:03:17 +0300 Subject: [PATCH 35/63] Remove unused previewTrackManager parameter --- osu.Game/Overlays/Direct/PlayButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index 92d1acbaa4..dc8042a137 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -89,7 +89,7 @@ namespace osu.Game.Overlays.Direct } [BackgroundDependencyLoader] - private void load(OsuColour colour, PreviewTrackManager previewTrackManager) + private void load(OsuColour colour) { hoverColour = colour.Yellow; } From 9b69e1825da552b934c539ee6b499fc2feb0864a Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Sat, 2 Jun 2018 22:06:45 +0300 Subject: [PATCH 36/63] Make PreviewTrack.owner private --- osu.Game/Audio/PreviewTrack.cs | 12 +++++++++--- osu.Game/Overlays/BeatmapSetOverlay.cs | 3 +-- osu.Game/Overlays/DirectOverlay.cs | 3 +-- osu.Game/Overlays/UserProfileOverlay.cs | 2 +- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/osu.Game/Audio/PreviewTrack.cs b/osu.Game/Audio/PreviewTrack.cs index d8898dfede..cfc47497d0 100644 --- a/osu.Game/Audio/PreviewTrack.cs +++ b/osu.Game/Audio/PreviewTrack.cs @@ -13,7 +13,7 @@ namespace osu.Game.Audio public class PreviewTrack : Component { public Track Track { get; private set; } - public readonly OverlayContainer Owner; + private readonly OverlayContainer owner; private readonly BeatmapSetInfo beatmapSetInfo; @@ -23,7 +23,7 @@ namespace osu.Game.Audio public PreviewTrack(BeatmapSetInfo beatmapSetInfo, OverlayContainer owner) { this.beatmapSetInfo = beatmapSetInfo; - Owner = owner; + this.owner = owner; } [BackgroundDependencyLoader] @@ -38,8 +38,14 @@ namespace osu.Game.Audio Started?.Invoke(); } - public void Stop() + /// + /// Stop preview playback + /// + /// An which is probably the owner of this + public void Stop(OverlayContainer source = null) { + if (source != null && owner != source) + return; Track.Stop(); Stopped?.Invoke(); } diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index cccd51a621..234d91b9e6 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -127,8 +127,7 @@ namespace osu.Game.Overlays protected override void PopOut() { base.PopOut(); - if (previewTrackManager.CurrentTrack?.Owner == this) - previewTrackManager.CurrentTrack?.Stop(); + previewTrackManager.CurrentTrack?.Stop(this); FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out).OnComplete(_ => BeatmapSet = null); } diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 72de8ebc84..406554eb5e 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -302,8 +302,7 @@ namespace osu.Game.Overlays protected override void PopOut() { - if (previewTrackManager.CurrentTrack?.Owner == this) - previewTrackManager.CurrentTrack?.Stop(); + previewTrackManager.CurrentTrack?.Stop(this); base.PopOut(); } diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 04a93dea23..6a8dd30890 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -73,7 +73,7 @@ namespace osu.Game.Overlays protected override void PopOut() { base.PopOut(); - previewTrackManager.CurrentTrack?.Stop(); + previewTrackManager.CurrentTrack?.Stop(this); FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out); } From 3033294df1a583097d64b0b35f954bba13d3b2b3 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Thu, 7 Jun 2018 14:30:11 +0300 Subject: [PATCH 37/63] Stop Preview on BeatmapSet change --- osu.Game/Overlays/Direct/PlayButton.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index dc8042a137..607d6d218f 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -29,6 +29,7 @@ namespace osu.Game.Overlays.Direct if (value == beatmapSet) return; beatmapSet = value; + Preview.Stop(parentOverlayContainer); Playing.Value = false; Preview = null; } From ab6699b1fb599c7d8d6da4f30afb7bd986c17494 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Thu, 7 Jun 2018 14:40:41 +0300 Subject: [PATCH 38/63] Stop DirectOverlay preview on search update --- osu.Game/Overlays/DirectOverlay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 406554eb5e..fb1be8e05e 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -273,6 +273,8 @@ namespace osu.Game.Overlays if (Header.Tabs.Current.Value == DirectTab.Search && (Filter.Search.Text == string.Empty || currentQuery == string.Empty)) return; + previewTrackManager.CurrentTrack?.Stop(this); + getSetsRequest = new SearchBeatmapSetsRequest(currentQuery.Value ?? string.Empty, ((FilterControl)Filter).Ruleset.Value, Filter.DisplayStyleControl.Dropdown.Current.Value, From 1fdbd2047151f42fb1f59fdb15fabfd423bf78e4 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Thu, 7 Jun 2018 14:50:21 +0300 Subject: [PATCH 39/63] Nullref quickfix in PlayButton --- osu.Game/Overlays/Direct/PlayButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index 607d6d218f..94c3cb748f 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Direct if (value == beatmapSet) return; beatmapSet = value; - Preview.Stop(parentOverlayContainer); + Preview?.Stop(parentOverlayContainer); Playing.Value = false; Preview = null; } From 8c671f93fc9476439b4e306a0c0c7ba2299603be Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Jun 2018 19:32:04 +0900 Subject: [PATCH 40/63] Always perform non-seeked scroll in the editor while audio is playing --- osu.Game/Screens/Edit/Editor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index d4f66c2f09..379484bae6 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -181,9 +181,9 @@ namespace osu.Game.Screens.Edit protected override bool OnScroll(InputState state) { if (state.Mouse.ScrollDelta.X + state.Mouse.ScrollDelta.Y > 0) - clock.SeekBackward(true); + clock.SeekBackward(!clock.IsRunning); else - clock.SeekForward(true); + clock.SeekForward(!clock.IsRunning); return true; } From d753df65a57bb1bc20e02138dc60f393133ad596 Mon Sep 17 00:00:00 2001 From: Tom Arrow Date: Tue, 19 Jun 2018 18:04:53 +0200 Subject: [PATCH 41/63] Remove default bindings for right actions --- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 7b4978694b..b41a6f3c38 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -32,9 +32,7 @@ namespace osu.Game.Rulesets.Taiko new KeyBinding(InputKey.J, TaikoAction.RightCentre), new KeyBinding(InputKey.K, TaikoAction.RightRim), new KeyBinding(InputKey.MouseLeft, TaikoAction.LeftCentre), - new KeyBinding(InputKey.MouseLeft, TaikoAction.RightCentre), new KeyBinding(InputKey.MouseRight, TaikoAction.LeftRim), - new KeyBinding(InputKey.MouseRight, TaikoAction.RightRim), }; public override IEnumerable ConvertLegacyMods(LegacyMods mods) From 5d0c84783528fb3acead1f75327de5ddd413314a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 21 Jun 2018 12:26:15 +0900 Subject: [PATCH 42/63] Fix post-merge errors --- .../Difficulty/CatchDifficultyAttributes.cs | 20 ++++ .../Difficulty/CatchDifficultyCalculator.cs | 98 ++++++++++--------- .../Difficulty/CatchDifficultyHitObject.cs | 2 +- .../Difficulty/TaikoDifficultyCalculator.cs | 12 ++- 4 files changed, 82 insertions(+), 50 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs new file mode 100644 index 0000000000..687cd03152 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Catch.Difficulty +{ + public class CatchDifficultyAttributes : DifficultyAttributes + { + public double AimRating; + public double ApproachRate; + public int MaxCombo; + + public CatchDifficultyAttributes(Mod[] mods, double starRating) + : base(mods, starRating) + { + } + } +} diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index acdec851d8..520a980d26 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -2,6 +2,8 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Internal; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; @@ -13,31 +15,39 @@ namespace osu.Game.Rulesets.Catch.Difficulty { public class CatchDifficultyCalculator : DifficultyCalculator { + + /// + /// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP. + /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain. + /// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage. + /// + private const double strain_step = 750; + + /// + /// The weighting of each strain value decays to this number * it's previous value + /// + private const double decay_weight = 0.94; + private const double star_scaling_factor = 0.145; - private const float playfield_width = CatchPlayfield.BASE_WIDTH; - private readonly List difficultyHitObjects = new List(); - - public CatchDifficultyCalculator(IBeatmap beatmap) - : base(beatmap) + public CatchDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) + : base(ruleset, beatmap) { } - public CatchDifficultyCalculator(IBeatmap beatmap, Mod[] mods) - : base(beatmap, mods) + protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) { - } + if (!beatmap.HitObjects.Any()) + return new CatchDifficultyAttributes(mods, 0); - public override double Calculate(Dictionary categoryDifficulty = null) - { - difficultyHitObjects.Clear(); - - float circleSize = Beatmap.BeatmapInfo.BaseDifficulty.CircleSize; + float circleSize = beatmap.BeatmapInfo.BaseDifficulty.CircleSize; float catcherWidth = (1.0f - 0.7f * (circleSize - 5) / 5) * 0.62064f * CatcherArea.CATCHER_SIZE; float catcherWidthHalf = catcherWidth / 2; catcherWidthHalf *= 0.8f; - foreach (var hitObject in Beatmap.HitObjects) + var difficultyHitObjects = new List(); + + foreach (var hitObject in beatmap.HitObjects) { // We want to only consider fruits that contribute to the combo. Droplets are addressed as accuracy and spinners are not relevant for "skill" calculations. if (hitObject is Fruit) @@ -60,28 +70,26 @@ namespace osu.Game.Rulesets.Catch.Difficulty difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime)); - if (!CalculateStrainValues()) return 0; + if (!calculateStrainValues(difficultyHitObjects, timeRate)) + return new CatchDifficultyAttributes(mods, 0); - double starRating = Math.Sqrt(CalculateDifficulty()) * star_scaling_factor; + double ar = beatmap.BeatmapInfo.BaseDifficulty.ApproachRate; + double preEmpt = BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450) / timeRate; - if (categoryDifficulty != null) + double starRating = Math.Sqrt(calculateDifficulty(difficultyHitObjects, timeRate)) * star_scaling_factor; + + return new CatchDifficultyAttributes(mods, starRating) { - categoryDifficulty["Aim"] = starRating; - - double ar = Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate; - double preEmpt = BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450) / TimeRate; - - categoryDifficulty["AR"] = preEmpt > 1200.0 ? -(preEmpt - 1800.0) / 120.0 : -(preEmpt - 1200.0) / 150.0 + 5.0; - categoryDifficulty["Max combo"] = difficultyHitObjects.Count; - } - - return starRating; + AimRating = starRating, + ApproachRate = preEmpt > 1200.0 ? -(preEmpt - 1800.0) / 120.0 : -(preEmpt - 1200.0) / 150.0 + 5.0, + MaxCombo = difficultyHitObjects.Count + }; } - protected bool CalculateStrainValues() + private bool calculateStrainValues(List objects, double timeRate) { // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment. - using (List.Enumerator hitObjectsEnumerator = difficultyHitObjects.GetEnumerator()) + using (var hitObjectsEnumerator = objects.GetEnumerator()) { if (!hitObjectsEnumerator.MoveNext()) return false; @@ -91,7 +99,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty while (hitObjectsEnumerator.MoveNext()) { CatchDifficultyHitObject nextHitObject = hitObjectsEnumerator.Current; - nextHitObject?.CalculateStrains(currentHitObject, TimeRate); + nextHitObject?.CalculateStrains(currentHitObject, timeRate); currentHitObject = nextHitObject; } @@ -99,22 +107,10 @@ namespace osu.Game.Rulesets.Catch.Difficulty } } - /// - /// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP. - /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain. - /// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage. - /// - private const double strain_step = 750; - - /// - /// The weighting of each strain value decays to this number * it's previous value - /// - private const double decay_weight = 0.94; - - protected double CalculateDifficulty() + private double calculateDifficulty(List objects, double timeRate) { // The strain step needs to be adjusted for the algorithm to be considered equal with speed changing mods - double actualStrainStep = strain_step * TimeRate; + double actualStrainStep = strain_step * timeRate; // Find the highest strain value within each strain step List highestStrains = new List(); @@ -122,7 +118,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval CatchDifficultyHitObject previousHitObject = null; - foreach (CatchDifficultyHitObject hitObject in difficultyHitObjects) + foreach (CatchDifficultyHitObject hitObject in objects) { // While we are beyond the current interval push the currently available maximum to our strain list while (hitObject.BaseHitObject.StartTime > intervalEndTime) @@ -151,8 +147,16 @@ namespace osu.Game.Rulesets.Catch.Difficulty previousHitObject = hitObject; } - // calculate maximun strain difficulty - double difficulty = StrainCalculator(highestStrains, decay_weight); + // Build the weighted sum over the highest strains for each interval + double difficulty = 0; + double weight = 1; + highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. + + foreach (double strain in highestStrains) + { + difficulty += weight * strain; + weight *= decay_weight; + } return difficulty; } diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs index 72fc6d211b..720c1d8653 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs @@ -8,7 +8,7 @@ using OpenTK; namespace osu.Game.Rulesets.Catch.Difficulty { - internal class CatchDifficultyHitObject + public class CatchDifficultyHitObject { internal static readonly double DECAY_BASE = 0.20; private const float normalized_hitobject_radius = 41.0f; diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 446dcfd0b4..473c205293 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -110,8 +110,16 @@ namespace osu.Game.Rulesets.Taiko.Difficulty previousHitObject = hitObject; } - // calculate maximun strain difficulty - double difficulty = StrainCalculator(highestStrains, decay_weight); + // Build the weighted sum over the highest strains for each interval + double difficulty = 0; + double weight = 1; + highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. + + foreach (double strain in highestStrains) + { + difficulty += weight * strain; + weight *= decay_weight; + } return difficulty; } From 5f2d1d4a1e93289b55aad46fa7ddf74ff39798f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jun 2018 12:48:44 +0900 Subject: [PATCH 43/63] Dim TestCase background a bit --- osu.Game/Tests/OsuTestBrowser.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/OsuTestBrowser.cs b/osu.Game/Tests/OsuTestBrowser.cs index 738bb2d642..217af8eb77 100644 --- a/osu.Game/Tests/OsuTestBrowser.cs +++ b/osu.Game/Tests/OsuTestBrowser.cs @@ -3,6 +3,7 @@ using osu.Framework.Platform; using osu.Framework.Testing; +using osu.Game.Graphics; using osu.Game.Screens.Backgrounds; namespace osu.Game.Tests @@ -13,7 +14,11 @@ namespace osu.Game.Tests { base.LoadComplete(); - LoadComponentAsync(new BackgroundScreenDefault { Depth = 10 }, AddInternal); + LoadComponentAsync(new BackgroundScreenDefault + { + Colour = OsuColour.Gray(0.5f), + Depth = 10 + }, AddInternal); // Have to construct this here, rather than in the constructor, because // we depend on some dependencies to be loaded within OsuGameBase.load(). From df0083e3d938d404855a73be9895656a3efb95f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jun 2018 12:48:57 +0900 Subject: [PATCH 44/63] Add tests for ParallaxContainer --- .../Visual/TestCaseParallaxContainer.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 osu.Game.Tests/Visual/TestCaseParallaxContainer.cs diff --git a/osu.Game.Tests/Visual/TestCaseParallaxContainer.cs b/osu.Game.Tests/Visual/TestCaseParallaxContainer.cs new file mode 100644 index 0000000000..8c12589c6f --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseParallaxContainer.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics.Containers; +using osu.Game.Screens.Backgrounds; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseParallaxContainer : OsuTestCase + { + public TestCaseParallaxContainer() + { + ParallaxContainer parallax; + + Add(parallax = new ParallaxContainer + { + Child = new BackgroundScreenDefault { Alpha = 0.8f } + }); + + AddStep("default parallax", () => parallax.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT); + AddStep("high parallax", () => parallax.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * 10); + AddStep("no parallax", () => parallax.ParallaxAmount = 0); + AddStep("negative parallax", () => parallax.ParallaxAmount = -ParallaxContainer.DEFAULT_PARALLAX_AMOUNT); + } + } +} From 74f732038f7542fe489bf6fe409e2a2db93a3a79 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jun 2018 12:49:07 +0900 Subject: [PATCH 45/63] Add documentation for negative values of ParallaxAmount --- osu.Game/Graphics/Containers/ParallaxContainer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Graphics/Containers/ParallaxContainer.cs b/osu.Game/Graphics/Containers/ParallaxContainer.cs index 51be40c1e8..8e1e5d54fa 100644 --- a/osu.Game/Graphics/Containers/ParallaxContainer.cs +++ b/osu.Game/Graphics/Containers/ParallaxContainer.cs @@ -16,6 +16,9 @@ namespace osu.Game.Graphics.Containers { public const float DEFAULT_PARALLAX_AMOUNT = 0.02f; + /// + /// The amount of parallax movement. Negative values will reverse the direction of parallax relative to user input. + /// public float ParallaxAmount = DEFAULT_PARALLAX_AMOUNT; private Bindable parallaxEnabled; From 9314f49bc384774479f6ee9e92e6b5c1731fbed1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 21 Jun 2018 12:57:59 +0900 Subject: [PATCH 46/63] Expose the catch width from the Catcher --- .../Difficulty/CatchDifficultyCalculator.cs | 10 ++++------ osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 12 +++++++++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 520a980d26..33ee1c0184 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -40,10 +40,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty if (!beatmap.HitObjects.Any()) return new CatchDifficultyAttributes(mods, 0); - float circleSize = beatmap.BeatmapInfo.BaseDifficulty.CircleSize; - float catcherWidth = (1.0f - 0.7f * (circleSize - 5) / 5) * 0.62064f * CatcherArea.CATCHER_SIZE; - float catcherWidthHalf = catcherWidth / 2; - catcherWidthHalf *= 0.8f; + var catcher = new CatcherArea.Catcher(beatmap.BeatmapInfo.BaseDifficulty); + float halfCatchWidth = catcher.CatchWidth * 0.5f; var difficultyHitObjects = new List(); @@ -52,7 +50,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty // We want to only consider fruits that contribute to the combo. Droplets are addressed as accuracy and spinners are not relevant for "skill" calculations. if (hitObject is Fruit) { - difficultyHitObjects.Add(new CatchDifficultyHitObject((CatchHitObject)hitObject, catcherWidthHalf)); + difficultyHitObjects.Add(new CatchDifficultyHitObject((CatchHitObject)hitObject, halfCatchWidth)); } if (hitObject is JuiceStream) { @@ -61,7 +59,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty { CatchHitObject objectInJuiceStream = (CatchHitObject)nestedHitObjectsEnumerator.Current; if (!(objectInJuiceStream is TinyDroplet)) - difficultyHitObjects.Add(new CatchDifficultyHitObject(objectInJuiceStream, catcherWidthHalf)); + difficultyHitObjects.Add(new CatchDifficultyHitObject(objectInJuiceStream, halfCatchWidth)); } // Dispose the enumerator after counting all fruits. nestedHitObjectsEnumerator.Dispose(); diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index b62e9997d4..8e61e8cabe 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -105,6 +106,11 @@ namespace osu.Game.Rulesets.Catch.UI public class Catcher : Container, IKeyBindingHandler { + /// + /// Width of the area that can be used to attempt catches during gameplay. + /// + internal float CatchWidth => CATCHER_SIZE * Math.Abs(Scale.X); + private Container caughtFruit; public Container ExplodingFruitTarget; @@ -232,15 +238,15 @@ namespace osu.Game.Rulesets.Catch.UI /// Whether the catch is possible. public bool AttemptCatch(CatchHitObject fruit) { - double halfCatcherWidth = CATCHER_SIZE * Math.Abs(Scale.X) * 0.5f; + float halfCatchWidth = CatchWidth * 0.5f; // this stuff wil disappear once we move fruit to non-relative coordinate space in the future. var catchObjectPosition = fruit.X * CatchPlayfield.BASE_WIDTH; var catcherPosition = Position.X * CatchPlayfield.BASE_WIDTH; var validCatch = - catchObjectPosition >= catcherPosition - halfCatcherWidth && - catchObjectPosition <= catcherPosition + halfCatcherWidth; + catchObjectPosition >= catcherPosition - halfCatchWidth && + catchObjectPosition <= catcherPosition + halfCatchWidth; if (validCatch && fruit.HyperDash) { From afcf91a4c591980c5d633b07f60a46015931743c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 21 Jun 2018 13:12:22 +0900 Subject: [PATCH 47/63] What the... --- osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 33ee1c0184..4dd9cd10db 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; -using Microsoft.EntityFrameworkCore.Internal; +using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 8e61e8cabe..3b4a7b13e7 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -3,7 +3,6 @@ using System; using System.Linq; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; From 6aa0ca5cc98dd2d245f05fe30c461616798940ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jun 2018 14:13:20 +0900 Subject: [PATCH 48/63] Add (one-way) migration for existing users --- osu.Desktop/osu.Desktop.csproj | 1 + ...111_UpdateTaikoDefaultBindings.Designer.cs | 376 ++++++++++++++++++ ...180621044111_UpdateTaikoDefaultBindings.cs | 19 + .../Migrations/OsuDbContextModelSnapshot.cs | 8 +- 4 files changed, 399 insertions(+), 5 deletions(-) create mode 100644 osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.Designer.cs create mode 100644 osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 3cf95e9b3e..29d3b0e394 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -30,6 +30,7 @@ + diff --git a/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.Designer.cs b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.Designer.cs new file mode 100644 index 0000000000..aaa11e88b6 --- /dev/null +++ b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.Designer.cs @@ -0,0 +1,376 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using osu.Game.Database; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20180621044111_UpdateTaikoDefaultBindings")] + partial class UpdateTaikoDefaultBindings + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.1-rtm-30846"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash"); + + b.HasIndex("MD5Hash"); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntKey") + .HasColumnName("Key"); + + b.Property("RulesetID"); + + b.Property("StringValue") + .HasColumnName("Value"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("SkinInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("SkinInfoID"); + + b.ToTable("SkinFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Creator"); + + b.Property("DeletePending"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.ToTable("SkinInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Files") + .HasForeignKey("SkinInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs new file mode 100644 index 0000000000..98ce5def08 --- /dev/null +++ b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs @@ -0,0 +1,19 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using osu.Framework.Logging; + +namespace osu.Game.Migrations +{ + public partial class UpdateTaikoDefaultBindings : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DELETE FROM KeyBinding WHERE RulesetID = 1"); + Logger.Log("osu!taiko bindings have been reset due to new defaults", LoggingTarget.Runtime, LogLevel.Important); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + // we can't really tell if these should be restored or not, so let's just not do so. + } + } +} diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index d750d50b5b..bd80cb743b 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -1,11 +1,9 @@ // +using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using osu.Game.Database; -using System; namespace osu.Game.Migrations { @@ -16,7 +14,7 @@ namespace osu.Game.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.0.3-rtm-10026"); + .HasAnnotation("ProductVersion", "2.1.1-rtm-30846"); modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => { From 566d125658a8e1071b27ee2e2a70babd94f9c8ed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jun 2018 14:28:40 +0900 Subject: [PATCH 49/63] Change order of default initialisation to look better in settings --- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index b41a6f3c38..609fd27bb4 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -27,12 +27,12 @@ namespace osu.Game.Rulesets.Taiko public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] { + new KeyBinding(InputKey.MouseLeft, TaikoAction.LeftCentre), + new KeyBinding(InputKey.MouseRight, TaikoAction.LeftRim), new KeyBinding(InputKey.D, TaikoAction.LeftRim), new KeyBinding(InputKey.F, TaikoAction.LeftCentre), new KeyBinding(InputKey.J, TaikoAction.RightCentre), new KeyBinding(InputKey.K, TaikoAction.RightRim), - new KeyBinding(InputKey.MouseLeft, TaikoAction.LeftCentre), - new KeyBinding(InputKey.MouseRight, TaikoAction.LeftRim), }; public override IEnumerable ConvertLegacyMods(LegacyMods mods) From ed17a241f5551172df87be804c094e951a694ab8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jun 2018 14:43:02 +0900 Subject: [PATCH 50/63] Decrease notification font size --- osu.Game/Overlays/Notifications/SimpleNotification.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Notifications/SimpleNotification.cs b/osu.Game/Overlays/Notifications/SimpleNotification.cs index a78bc8da81..25a832941e 100644 --- a/osu.Game/Overlays/Notifications/SimpleNotification.cs +++ b/osu.Game/Overlays/Notifications/SimpleNotification.cs @@ -59,7 +59,7 @@ namespace osu.Game.Overlays.Notifications } }); - Content.Add(textDrawable = new OsuTextFlowContainer(t => t.TextSize = 16) + Content.Add(textDrawable = new OsuTextFlowContainer(t => t.TextSize = 14) { Colour = OsuColour.Gray(128), AutoSizeAxes = Axes.Y, From 87d94591e466e5ced6570d640dc0257d4d9486f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jun 2018 14:43:38 +0900 Subject: [PATCH 51/63] Start displaying notifications earlier Also show important notifications and more in total. --- osu.Game/OsuGame.cs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 36c76851c6..7f66c9fe8c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -101,6 +101,8 @@ namespace osu.Game public OsuGame(string[] args = null) { this.args = args; + + forwardLoggedErrorsToNotifications(); } public void ToggleSettings() => settings.ToggleVisibility(); @@ -305,8 +307,6 @@ namespace osu.Game Depth = -6, }, overlayContent.Add); - forwardLoggedErrorsToNotifications(); - dependencies.Cache(settings); dependencies.Cache(onscreenDisplay); dependencies.Cache(social); @@ -394,31 +394,33 @@ namespace osu.Game private void forwardLoggedErrorsToNotifications() { - int recentErrorCount = 0; + int recentLogCount = 0; const double debounce = 5000; Logger.NewEntry += entry => { - if (entry.Level < LogLevel.Error || entry.Target == null) return; + if (entry.Level < LogLevel.Important || entry.Target == null) return; - if (recentErrorCount < 2) + if (recentLogCount < 3) { - notifications.Post(new SimpleNotification + bool lastDisplayable = recentLogCount == recentLogCount - 1; + + Schedule(() => notifications.Post(new SimpleNotification { - Icon = FontAwesome.fa_bomb, - Text = (recentErrorCount == 0 ? entry.Message : "Subsequent errors occurred and have been logged.") + "\nClick to view log files.", + Icon = entry.Level == LogLevel.Important ? FontAwesome.fa_exclamation_circle : FontAwesome.fa_bomb, + Text = (!lastDisplayable ? entry.Message : "Subsequent messages have been logged.") + "\n\nClick to view log files.", Activated = () => { Host.Storage.GetStorageForDirectory("logs").OpenInNativeExplorer(); return true; } - }); + })); } - Interlocked.Increment(ref recentErrorCount); + Interlocked.Increment(ref recentLogCount); - Scheduler.AddDelayed(() => Interlocked.Decrement(ref recentErrorCount), debounce); + Scheduler.AddDelayed(() => Interlocked.Decrement(ref recentLogCount), debounce); }; } From 6ec0aaee91d4713d730f66ce6e70ab763053fc15 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jun 2018 14:50:42 +0900 Subject: [PATCH 52/63] Split out logic for subsequent log message and apply action to only itself --- osu.Game/OsuGame.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 7f66c9fe8c..ba8685b5b2 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -402,14 +402,22 @@ namespace osu.Game { if (entry.Level < LogLevel.Important || entry.Target == null) return; - if (recentLogCount < 3) - { - bool lastDisplayable = recentLogCount == recentLogCount - 1; + const int short_term_display_limit = 3; + if (recentLogCount < short_term_display_limit) + { Schedule(() => notifications.Post(new SimpleNotification { Icon = entry.Level == LogLevel.Important ? FontAwesome.fa_exclamation_circle : FontAwesome.fa_bomb, - Text = (!lastDisplayable ? entry.Message : "Subsequent messages have been logged.") + "\n\nClick to view log files.", + Text = entry.Message, + })); + } + else if (recentLogCount == short_term_display_limit) + { + Schedule(() => notifications.Post(new SimpleNotification + { + Icon = FontAwesome.fa_ellipsis_h, + Text = "Subsequent messages have been logged. Click to view log files.", Activated = () => { Host.Storage.GetStorageForDirectory("logs").OpenInNativeExplorer(); @@ -419,7 +427,6 @@ namespace osu.Game } Interlocked.Increment(ref recentLogCount); - Scheduler.AddDelayed(() => Interlocked.Decrement(ref recentLogCount), debounce); }; } From a9cb214aa908a2fbbce3742d34c2f69b9f4093f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jun 2018 16:21:08 +0900 Subject: [PATCH 53/63] Replace usage of GetEnumerator --- .../Difficulty/CatchDifficultyCalculator.cs | 36 ++++++------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 4dd9cd10db..20bdaf875d 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -9,7 +9,6 @@ using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; -using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Catch.Difficulty { @@ -53,17 +52,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty difficultyHitObjects.Add(new CatchDifficultyHitObject((CatchHitObject)hitObject, halfCatchWidth)); } if (hitObject is JuiceStream) - { - IEnumerator nestedHitObjectsEnumerator = hitObject.NestedHitObjects.GetEnumerator(); - while (nestedHitObjectsEnumerator.MoveNext()) - { - CatchHitObject objectInJuiceStream = (CatchHitObject)nestedHitObjectsEnumerator.Current; - if (!(objectInJuiceStream is TinyDroplet)) - difficultyHitObjects.Add(new CatchDifficultyHitObject(objectInJuiceStream, halfCatchWidth)); - } - // Dispose the enumerator after counting all fruits. - nestedHitObjectsEnumerator.Dispose(); - } + difficultyHitObjects.AddRange(hitObject.NestedHitObjects.OfType().Where(o => !(o is TinyDroplet)).Select(o => new CatchDifficultyHitObject(o, halfCatchWidth))); } difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime)); @@ -86,23 +75,20 @@ namespace osu.Game.Rulesets.Catch.Difficulty private bool calculateStrainValues(List objects, double timeRate) { + CatchDifficultyHitObject lastObject = null; + + if (!objects.Any()) return false; + // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment. - using (var hitObjectsEnumerator = objects.GetEnumerator()) + foreach (var currentObject in objects) { - if (!hitObjectsEnumerator.MoveNext()) return false; + if (lastObject != null) + currentObject.CalculateStrains(lastObject, timeRate); - CatchDifficultyHitObject currentHitObject = hitObjectsEnumerator.Current; - - // First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject. - while (hitObjectsEnumerator.MoveNext()) - { - CatchDifficultyHitObject nextHitObject = hitObjectsEnumerator.Current; - nextHitObject?.CalculateStrains(currentHitObject, timeRate); - currentHitObject = nextHitObject; - } - - return true; + lastObject = currentObject; } + + return true; } private double calculateDifficulty(List objects, double timeRate) From 4982efce889d82dd2a026e640c23097b9789f8c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jun 2018 17:22:59 +0900 Subject: [PATCH 54/63] Remove vscode files from long-gone deploy project --- osu.Desktop.Deploy/.vscode/launch.json | 29 ------------ osu.Desktop.Deploy/.vscode/tasks.json | 64 -------------------------- 2 files changed, 93 deletions(-) delete mode 100644 osu.Desktop.Deploy/.vscode/launch.json delete mode 100644 osu.Desktop.Deploy/.vscode/tasks.json diff --git a/osu.Desktop.Deploy/.vscode/launch.json b/osu.Desktop.Deploy/.vscode/launch.json deleted file mode 100644 index 8c35d211bd..0000000000 --- a/osu.Desktop.Deploy/.vscode/launch.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [{ - "name": "Deploy (Debug)", - "request": "launch", - "type": "mono", - "program": "${workspaceRoot}/bin/Debug/net471/osu.Desktop.Deploy.exe", - "cwd": "${workspaceRoot}", - "preLaunchTask": "Build (Debug)", - "runtimeExecutable": null, - "env": {}, - "console": "internalConsole" - }, - { - "name": "Deploy (Release)", - "request": "launch", - "type": "clr", - "program": "${workspaceRoot}/bin/Release/net471/osu.Desktop.Deploy.exe", - "cwd": "${workspaceRoot}", - "preLaunchTask": "Build (Release)", - "runtimeExecutable": null, - "env": {}, - "console": "internalConsole" - } - ] -} \ No newline at end of file diff --git a/osu.Desktop.Deploy/.vscode/tasks.json b/osu.Desktop.Deploy/.vscode/tasks.json deleted file mode 100644 index 35bf9e7a0e..0000000000 --- a/osu.Desktop.Deploy/.vscode/tasks.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "2.0.0", - "command": "msbuild", - "type": "shell", - "suppressTaskName": true, - "args": [ - "/property:GenerateFullPaths=true", - "/property:DebugType=portable", - "/verbosity:minimal", - "/m" //parallel compiling support. - ], - "tasks": [{ - "taskName": "Build (Debug)", - "group": { - "kind": "build", - "isDefault": true - }, - "problemMatcher": [ - "$msCompile" - ] - }, - { - "taskName": "Build (Release)", - "group": "build", - "args": [ - "/property:Configuration=Release" - ], - "problemMatcher": [ - "$msCompile" - ] - }, - { - "taskName": "Clean (Debug)", - "args": [ - "/target:Clean" - ], - "problemMatcher": [ - "$msCompile" - ] - }, - { - "taskName": "Clean (Release)", - "args": [ - "/target:Clean", - "/property:Configuration=Release" - ], - "problemMatcher": [ - "$msCompile" - ] - }, - { - "taskName": "Clean All", - "dependsOn": [ - "Clean (Debug)", - "Clean (Release)" - ], - "problemMatcher": [ - "$msCompile" - ] - } - ] -} \ No newline at end of file From c64f64814f2a44bce3856fbcdfdcb530a474c7b9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jun 2018 17:32:10 +0900 Subject: [PATCH 55/63] Remove unnecessary AimRating --- .../Difficulty/CatchDifficultyAttributes.cs | 1 - .../Difficulty/CatchDifficultyCalculator.cs | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs index 687cd03152..f6535380c8 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs @@ -8,7 +8,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty { public class CatchDifficultyAttributes : DifficultyAttributes { - public double AimRating; public double ApproachRate; public int MaxCombo; diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 20bdaf875d..108c9ada14 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -60,14 +60,12 @@ namespace osu.Game.Rulesets.Catch.Difficulty if (!calculateStrainValues(difficultyHitObjects, timeRate)) return new CatchDifficultyAttributes(mods, 0); - double ar = beatmap.BeatmapInfo.BaseDifficulty.ApproachRate; - double preEmpt = BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450) / timeRate; - + // this is the same as osu!, so there's potential to share the implementation... maybe + double preEmpt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate; double starRating = Math.Sqrt(calculateDifficulty(difficultyHitObjects, timeRate)) * star_scaling_factor; return new CatchDifficultyAttributes(mods, starRating) { - AimRating = starRating, ApproachRate = preEmpt > 1200.0 ? -(preEmpt - 1800.0) / 120.0 : -(preEmpt - 1200.0) / 150.0 + 5.0, MaxCombo = difficultyHitObjects.Count }; From 34498f7f86eb6e0baa7113d6ba59122c608febdc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jun 2018 17:49:04 +0900 Subject: [PATCH 56/63] Use var where possible --- osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 108c9ada14..3d1013aad3 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty double actualStrainStep = strain_step * timeRate; // Find the highest strain value within each strain step - List highestStrains = new List(); + var highestStrains = new List(); double intervalEndTime = actualStrainStep; double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval From b2066c5d739d55e05d8a6dfb9f77f9e84f91fb29 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 21 Jun 2018 16:19:07 +0900 Subject: [PATCH 57/63] Rework preview tracks to reduce usage complexities --- osu.Game/Audio/IPreviewTrackOwner.cs | 16 ++++ osu.Game/Audio/PreviewTrack.cs | 88 +++++++++++++------ osu.Game/Audio/PreviewTrackManager.cs | 59 +++++++++---- .../Containers/OsuFocusedOverlayContainer.cs | 24 ++++- .../BeatmapSet/Buttons/PreviewButton.cs | 2 +- osu.Game/Overlays/BeatmapSetOverlay.cs | 7 +- osu.Game/Overlays/Direct/DirectPanel.cs | 4 +- osu.Game/Overlays/Direct/PlayButton.cs | 48 +++++----- osu.Game/Overlays/DirectOverlay.cs | 8 +- osu.Game/Overlays/UserProfileOverlay.cs | 6 +- 10 files changed, 172 insertions(+), 90 deletions(-) create mode 100644 osu.Game/Audio/IPreviewTrackOwner.cs diff --git a/osu.Game/Audio/IPreviewTrackOwner.cs b/osu.Game/Audio/IPreviewTrackOwner.cs new file mode 100644 index 0000000000..f166096601 --- /dev/null +++ b/osu.Game/Audio/IPreviewTrackOwner.cs @@ -0,0 +1,16 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Audio +{ + /// + /// Interface for objects that can own s. + /// + /// + /// s can cancel the currently playing through the + /// global if they're the owner of the playing . + /// + public interface IPreviewTrackOwner + { + } +} diff --git a/osu.Game/Audio/PreviewTrack.cs b/osu.Game/Audio/PreviewTrack.cs index cfc47497d0..59baa86f80 100644 --- a/osu.Game/Audio/PreviewTrack.cs +++ b/osu.Game/Audio/PreviewTrack.cs @@ -5,49 +5,87 @@ using System; using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; +using osu.Framework.Threading; namespace osu.Game.Audio { - public class PreviewTrack : Component + public abstract class PreviewTrack : Component { - public Track Track { get; private set; } - private readonly OverlayContainer owner; - - private readonly BeatmapSetInfo beatmapSetInfo; - public event Action Stopped; public event Action Started; - public PreviewTrack(BeatmapSetInfo beatmapSetInfo, OverlayContainer owner) - { - this.beatmapSetInfo = beatmapSetInfo; - this.owner = owner; - } + private Track track; + private bool wasPlaying; [BackgroundDependencyLoader] - private void load(PreviewTrackManager previewTrackManager) + private void load() { - Track = previewTrackManager.Get(this, beatmapSetInfo); - } + track = GetTrack(); - public void Start() - { - Track.Restart(); - Started?.Invoke(); + if (track != null) + track.Looping = false; } /// - /// Stop preview playback + /// Length of the track. /// - /// An which is probably the owner of this - public void Stop(OverlayContainer source = null) + public double Length => track?.Length ?? 0; + + /// + /// The current track time. + /// + public double CurrentTime => track?.CurrentTime ?? 0; + + /// + /// Whether the track is loaded. + /// + public bool TrackLoaded => track?.IsLoaded ?? false; + + protected override void Update() { - if (source != null && owner != source) + base.Update(); + + // Todo: Track currently doesn't signal its completion, so we have to handle it manually + if (track != null && wasPlaying && track.HasCompleted) + Stop(); + } + + private ScheduledDelegate startDelegate; + + public void Start() => startDelegate = Schedule(() => + { + if (!IsLoaded) return; - Track.Stop(); + + if (track == null) + return; + + if (wasPlaying) + return; + wasPlaying = true; + + track.Restart(); + Started?.Invoke(); + }); + + public void Stop() + { + startDelegate?.Cancel(); + + if (!IsLoaded) + return; + + if (track == null) + return; + + if (!wasPlaying) + return; + wasPlaying = false; + + track.Stop(); Stopped?.Invoke(); } + + protected abstract Track GetTrack(); } } diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs index a3da930af0..499e4a1eea 100644 --- a/osu.Game/Audio/PreviewTrackManager.cs +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -13,17 +13,17 @@ namespace osu.Game.Audio { public class PreviewTrackManager : Component { + private readonly BindableDouble muteBindable = new BindableDouble(); + private AudioManager audio; private TrackManager trackManager; - private BindableDouble muteBindable; - public PreviewTrack CurrentTrack { get; private set; } + private TrackManagerPreviewTrack current; [BackgroundDependencyLoader] private void load(AudioManager audio, FrameworkConfigManager config) { trackManager = new TrackManager(new OnlineStore()); - muteBindable = new BindableDouble(); this.audio = audio; audio.AddItem(trackManager); @@ -31,30 +31,55 @@ namespace osu.Game.Audio config.BindWith(FrameworkSetting.VolumeMusic, trackManager.Volume); } - protected override void Update() + public PreviewTrack Get(BeatmapSetInfo beatmapSetInfo) { - if (CurrentTrack?.Track.HasCompleted ?? false) - CurrentTrack.Stop(); + var track = new TrackManagerPreviewTrack(beatmapSetInfo, trackManager); - base.Update(); - } - - public Track Get(PreviewTrack previewTrack, BeatmapSetInfo beatmapSetInfo) - { - previewTrack.Started += () => + track.Started += () => { - CurrentTrack?.Stop(); - CurrentTrack = previewTrack; + current?.Stop(); + current = track; audio.Track.AddAdjustment(AdjustableProperty.Volume, muteBindable); }; - previewTrack.Stopped += () => + track.Stopped += () => { - CurrentTrack = null; + current = null; audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable); }; - return trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo?.OnlineBeatmapSetID}.mp3"); + return track; + } + + public void Stop(IPreviewTrackOwner source) + { + if (current?.Owner != source) + return; + + current?.Stop(); + current = null; + } + + private class TrackManagerPreviewTrack : PreviewTrack + { + public IPreviewTrackOwner Owner { get; private set; } + + private readonly BeatmapSetInfo beatmapSetInfo; + private readonly TrackManager trackManager; + + public TrackManagerPreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager) + { + this.beatmapSetInfo = beatmapSetInfo; + this.trackManager = trackManager; + } + + [BackgroundDependencyLoader] + private void load(IPreviewTrackOwner owner) + { + Owner = owner; + } + + protected override Track GetTrack() => trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo?.OnlineBeatmapSetID}.mp3"); } } } diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 0528f7b3ae..e3179b5631 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -8,20 +8,32 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input; using OpenTK; using osu.Framework.Configuration; +using osu.Game.Audio; using osu.Game.Overlays; namespace osu.Game.Graphics.Containers { - public class OsuFocusedOverlayContainer : FocusedOverlayContainer + public class OsuFocusedOverlayContainer : FocusedOverlayContainer, IPreviewTrackOwner { private SampleChannel samplePopIn; private SampleChannel samplePopOut; + private PreviewTrackManager previewTrackManager; + protected readonly Bindable OverlayActivationMode = new Bindable(OverlayActivation.All); - [BackgroundDependencyLoader(true)] - private void load(OsuGame osuGame, AudioManager audio) + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) { + var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); + dependencies.CacheAs(this); + return dependencies; + } + + [BackgroundDependencyLoader(true)] + private void load(OsuGame osuGame, AudioManager audio, PreviewTrackManager previewTrackManager) + { + this.previewTrackManager = previewTrackManager; + if (osuGame != null) OverlayActivationMode.BindTo(osuGame.OverlayActivationMode); @@ -66,5 +78,11 @@ namespace osu.Game.Graphics.Containers break; } } + + protected override void PopOut() + { + base.PopOut(); + previewTrackManager.Stop(this); + } } } diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs index 78bc77efe8..505b7a7540 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs @@ -83,7 +83,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons if (Playing.Value && preview != null) { // prevent negative (potential infinite) width if a track without length was loaded - progress.Width = preview.Track.Length > 0 ? (float)(preview.Track.CurrentTime / preview.Track.Length) : 0f; + progress.Width = preview.Length > 0 ? (float)(preview.CurrentTime / preview.Length) : 0f; } else progress.Width = 0; diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 234d91b9e6..88f0a72ddf 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input; -using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -38,7 +37,6 @@ namespace osu.Game.Overlays private readonly ScrollContainer scroll; private BeatmapSetInfo beatmapSet; - private PreviewTrackManager previewTrackManager; public BeatmapSetInfo BeatmapSet { @@ -111,11 +109,10 @@ namespace osu.Game.Overlays } [BackgroundDependencyLoader] - private void load(APIAccess api, RulesetStore rulesets, PreviewTrackManager previewTrackManager) + private void load(APIAccess api, RulesetStore rulesets) { this.api = api; this.rulesets = rulesets; - this.previewTrackManager = previewTrackManager; } protected override void PopIn() @@ -127,8 +124,6 @@ namespace osu.Game.Overlays protected override void PopOut() { base.PopOut(); - previewTrackManager.CurrentTrack?.Stop(this); - FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out).OnComplete(_ => BeatmapSet = null); } diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 93f28194e4..e63c290ce5 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -113,9 +113,9 @@ namespace osu.Game.Overlays.Direct { base.Update(); - if (PreviewPlaying && Preview != null && Preview.Track.IsLoaded) + if (PreviewPlaying && Preview != null && Preview.TrackLoaded) { - PreviewBar.Width = (float)(Preview.Track.CurrentTime / Preview.Track.Length); + PreviewBar.Width = (float)(Preview.CurrentTime / Preview.Length); } } diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index e6f409cbce..d9881f2b23 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -29,21 +29,14 @@ namespace osu.Game.Overlays.Direct if (value == beatmapSet) return; beatmapSet = value; - Preview?.Stop(parentOverlayContainer); + if (Preview != null) + { + Preview.Stop(); + RemoveInternal(Preview); + Preview = null; + } + Playing.Value = false; - Preview = null; - } - } - - private OverlayContainer parentOverlayContainer - { - get - { - var d = Parent; - while (!(d is OverlayContainer)) - d = d.Parent; - - return (OverlayContainer)d; } } @@ -89,9 +82,13 @@ namespace osu.Game.Overlays.Direct Playing.ValueChanged += playingStateChanged; } + private PreviewTrackManager previewTrackManager; + [BackgroundDependencyLoader] - private void load(OsuColour colour) + private void load(OsuColour colour, PreviewTrackManager previewTrackManager) { + this.previewTrackManager = previewTrackManager; + hoverColour = colour.Yellow; } @@ -103,15 +100,18 @@ namespace osu.Game.Overlays.Direct { loading = true; - LoadComponentAsync( - Preview = new PreviewTrack(beatmapSet, parentOverlayContainer), - t => - { - Preview.Started += () => Playing.Value = true; - Preview.Stopped += () => Playing.Value = false; - Preview.Start(); - loading = false; - }); + Preview = previewTrackManager.Get(beatmapSet); + Preview.Started += () => Playing.Value = true; + Preview.Stopped += () => Playing.Value = false; + + LoadComponentAsync(Preview, t => + { + AddInternal(t); + + Preview.Start(); + + loading = false; + }); return true; } diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 5f5ef44949..c28ecb3c9f 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -262,7 +262,7 @@ namespace osu.Game.Overlays if (Header.Tabs.Current.Value == DirectTab.Search && (Filter.Search.Text == string.Empty || currentQuery == string.Empty)) return; - previewTrackManager.CurrentTrack?.Stop(this); + previewTrackManager.Stop(this); getSetsRequest = new SearchBeatmapSetsRequest(currentQuery.Value ?? string.Empty, ((FilterControl)Filter).Ruleset.Value, @@ -289,12 +289,6 @@ namespace osu.Game.Overlays private int distinctCount(List list) => list.Distinct().ToArray().Length; - protected override void PopOut() - { - previewTrackManager.CurrentTrack?.Stop(this); - base.PopOut(); - } - public class ResultCounts { public readonly int Artists; diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 6a8dd30890..745f2f3def 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; -using osu.Game.Audio; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -31,7 +30,6 @@ namespace osu.Game.Overlays protected ProfileHeader Header; private SectionsContainer sectionsContainer; private ProfileTabControl tabs; - private PreviewTrackManager previewTrackManager; public const float CONTENT_X_MARGIN = 50; @@ -58,10 +56,9 @@ namespace osu.Game.Overlays } [BackgroundDependencyLoader] - private void load(APIAccess api, PreviewTrackManager previewTrackManager) + private void load(APIAccess api) { this.api = api; - this.previewTrackManager = previewTrackManager; } protected override void PopIn() @@ -73,7 +70,6 @@ namespace osu.Game.Overlays protected override void PopOut() { base.PopOut(); - previewTrackManager.CurrentTrack?.Stop(this); FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out); } From 40ed3fdd05df4d11d01edbac79373b91c03bcbf3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 21 Jun 2018 18:45:32 +0900 Subject: [PATCH 58/63] Fix possible incorrect order of events due to threaded load --- osu.Game/Overlays/Direct/PlayButton.cs | 36 ++++++++++++++------------ 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index d9881f2b23..4bf6cf2bf7 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -96,27 +96,29 @@ namespace osu.Game.Overlays.Direct { if (!Playing.Value) { - if (Preview == null) + if (Preview != null) { - loading = true; - - Preview = previewTrackManager.Get(beatmapSet); - Preview.Started += () => Playing.Value = true; - Preview.Stopped += () => Playing.Value = false; - - LoadComponentAsync(Preview, t => - { - AddInternal(t); - - Preview.Start(); - - loading = false; - }); - + Preview.Start(); return true; } - Preview.Start(); + loading = true; + + var loadingPreview = previewTrackManager.Get(beatmapSet); + loadingPreview.Started += () => Playing.Value = true; + loadingPreview.Stopped += () => Playing.Value = false; + + LoadComponentAsync(Preview = loadingPreview, t => + { + if (Preview != loadingPreview) + return; + + AddInternal(t); + + Preview.Start(); + + loading = false; + }); } else Preview?.Stop(); From 63a6fc25963ef227429f02ba7bce093e0a441a0d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 21 Jun 2018 18:54:42 +0900 Subject: [PATCH 59/63] XMLDoc all the things --- osu.Game/Audio/PreviewTrack.cs | 16 ++++++++++++++++ osu.Game/Audio/PreviewTrackManager.cs | 17 +++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/osu.Game/Audio/PreviewTrack.cs b/osu.Game/Audio/PreviewTrack.cs index 59baa86f80..5fd3dbfe8f 100644 --- a/osu.Game/Audio/PreviewTrack.cs +++ b/osu.Game/Audio/PreviewTrack.cs @@ -11,7 +11,14 @@ namespace osu.Game.Audio { public abstract class PreviewTrack : Component { + /// + /// Invoked when this has stopped playing. + /// public event Action Stopped; + + /// + /// Invoked when this has started playing. + /// public event Action Started; private Track track; @@ -52,6 +59,9 @@ namespace osu.Game.Audio private ScheduledDelegate startDelegate; + /// + /// Starts playing this . + /// public void Start() => startDelegate = Schedule(() => { if (!IsLoaded) @@ -68,6 +78,9 @@ namespace osu.Game.Audio Started?.Invoke(); }); + /// + /// Stops playing this . + /// public void Stop() { startDelegate?.Cancel(); @@ -86,6 +99,9 @@ namespace osu.Game.Audio Stopped?.Invoke(); } + /// + /// Retrieves the audio track. + /// protected abstract Track GetTrack(); } } diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs index 499e4a1eea..8febf8e621 100644 --- a/osu.Game/Audio/PreviewTrackManager.cs +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -11,6 +11,9 @@ using osu.Game.Beatmaps; namespace osu.Game.Audio { + /// + /// A central store for the retrieval of s. + /// public class PreviewTrackManager : Component { private readonly BindableDouble muteBindable = new BindableDouble(); @@ -31,6 +34,11 @@ namespace osu.Game.Audio config.BindWith(FrameworkSetting.VolumeMusic, trackManager.Volume); } + /// + /// Retrieves a for a . + /// + /// The to retrieve the preview track for. + /// The playable . public PreviewTrack Get(BeatmapSetInfo beatmapSetInfo) { var track = new TrackManagerPreviewTrack(beatmapSetInfo, trackManager); @@ -51,6 +59,15 @@ namespace osu.Game.Audio return track; } + /// + /// Stops the currently playing . + /// + /// + /// Only the immediate owner (an object that implements ) of the playing + /// can globally stop the currently playing . The object holding a reference to the + /// can always stop the themselves through . + /// + /// The which may be the owner of the . public void Stop(IPreviewTrackOwner source) { if (current?.Owner != source) From 1932399521c87a672f657422a853bfbfec44fc7d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 21 Jun 2018 19:31:07 +0900 Subject: [PATCH 60/63] Add testcases --- .../Visual/TestCasePreviewTrackManager.cs | 131 ++++++++++++++++++ osu.Game/Audio/PreviewTrack.cs | 5 + osu.Game/Audio/PreviewTrackManager.cs | 9 +- 3 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs diff --git a/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs b/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs new file mode 100644 index 0000000000..b394202706 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs @@ -0,0 +1,131 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics.Containers; +using osu.Game.Audio; +using osu.Game.Beatmaps; + +namespace osu.Game.Tests.Visual +{ + public class TestCasePreviewTrackManager : OsuTestCase, IPreviewTrackOwner + { + private readonly PreviewTrackManager trackManager = new TestPreviewTrackManager(); + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + { + var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); + dependencies.CacheAs(trackManager); + dependencies.CacheAs(this); + return dependencies; + } + + [BackgroundDependencyLoader] + private void load() + { + AddInternal(trackManager); + } + + [Test] + public void TestStartStop() + { + PreviewTrack track = null; + + AddStep("get track", () => track = getOwnedTrack()); + AddStep("start", () => track.Start()); + AddAssert("started", () => track.IsRunning); + AddStep("stop", () => track.Stop()); + AddAssert("stopped", () => !track.IsRunning); + } + + [Test] + public void TestStartMultipleTracks() + { + PreviewTrack track1 = null; + PreviewTrack track2 = null; + + AddStep("get tracks", () => + { + track1 = getOwnedTrack(); + track2 = getOwnedTrack(); + }); + + AddStep("start track 1", () => track1.Start()); + AddStep("start track 2", () => track2.Start()); + AddAssert("track 1 stopped", () => !track1.IsRunning); + AddAssert("track 2 started", () => track2.IsRunning); + } + + [Test] + public void TestCancelFromOwner() + { + PreviewTrack track = null; + + AddStep("get track", () => track = getOwnedTrack()); + AddStep("start", () => track.Start()); + AddStep("stop by owner", () => trackManager.Stop(this)); + AddAssert("stopped", () => !track.IsRunning); + } + + [Test] + public void TestCancelFromNonOwner() + { + TestTrackOwner owner = null; + PreviewTrack track = null; + + AddStep("get track", () => AddInternal(owner = new TestTrackOwner(track = getTrack()))); + AddStep("start", () => track.Start()); + AddStep("attempt stop", () => trackManager.Stop(this)); + AddAssert("not stopped", () => track.IsRunning); + AddStep("stop by true owner", () => trackManager.Stop(owner)); + AddAssert("stopped", () => !track.IsRunning); + } + + private PreviewTrack getTrack() => trackManager.Get(null); + + private PreviewTrack getOwnedTrack() + { + var track = getTrack(); + + AddInternal(track); + + return track; + } + + private class TestTrackOwner : CompositeDrawable, IPreviewTrackOwner + { + private readonly PreviewTrack track; + + public TestTrackOwner(PreviewTrack track) + { + this.track = track; + + AddInternal(track); + } + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + { + var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); + dependencies.CacheAs(this); + return dependencies; + } + } + + private class TestPreviewTrackManager : PreviewTrackManager + { + protected override TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager) => new TestPreviewTrack(beatmapSetInfo, trackManager); + + protected class TestPreviewTrack : TrackManagerPreviewTrack + { + public TestPreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager) + : base(beatmapSetInfo, trackManager) + { + } + + protected override Track GetTrack() => new TrackVirtual { Length = 100000 }; + } + } + } +} diff --git a/osu.Game/Audio/PreviewTrack.cs b/osu.Game/Audio/PreviewTrack.cs index 5fd3dbfe8f..50366e35eb 100644 --- a/osu.Game/Audio/PreviewTrack.cs +++ b/osu.Game/Audio/PreviewTrack.cs @@ -48,6 +48,11 @@ namespace osu.Game.Audio /// public bool TrackLoaded => track?.IsLoaded ?? false; + /// + /// Whether the track is playing. + /// + public bool IsRunning => track?.IsRunning ?? false; + protected override void Update() { base.Update(); diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs index 8febf8e621..56bedc9dd2 100644 --- a/osu.Game/Audio/PreviewTrackManager.cs +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -41,7 +41,7 @@ namespace osu.Game.Audio /// The playable . public PreviewTrack Get(BeatmapSetInfo beatmapSetInfo) { - var track = new TrackManagerPreviewTrack(beatmapSetInfo, trackManager); + var track = CreatePreviewTrack(beatmapSetInfo, trackManager); track.Started += () => { @@ -77,7 +77,12 @@ namespace osu.Game.Audio current = null; } - private class TrackManagerPreviewTrack : PreviewTrack + /// + /// Creates the . + /// + protected virtual TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager) => new TrackManagerPreviewTrack(beatmapSetInfo, trackManager); + + protected class TrackManagerPreviewTrack : PreviewTrack { public IPreviewTrackOwner Owner { get; private set; } From 15bb301d14bf0a6c0ee7a1c9fa3e6dfe22f68c5f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 21 Jun 2018 19:37:42 +0900 Subject: [PATCH 61/63] Remove unused field --- osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs b/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs index b394202706..7d02519007 100644 --- a/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs +++ b/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs @@ -96,12 +96,8 @@ namespace osu.Game.Tests.Visual private class TestTrackOwner : CompositeDrawable, IPreviewTrackOwner { - private readonly PreviewTrack track; - public TestTrackOwner(PreviewTrack track) { - this.track = track; - AddInternal(track); } From 4b2b1f51f946d858aa44dd65872cb3dbe068e272 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Jun 2018 12:12:59 +0900 Subject: [PATCH 62/63] Apply some code review --- osu.Game/Audio/PreviewTrack.cs | 21 ++----- osu.Game/Overlays/Direct/PlayButton.cs | 87 +++++++++++--------------- 2 files changed, 44 insertions(+), 64 deletions(-) diff --git a/osu.Game/Audio/PreviewTrack.cs b/osu.Game/Audio/PreviewTrack.cs index 50366e35eb..3c9122b941 100644 --- a/osu.Game/Audio/PreviewTrack.cs +++ b/osu.Game/Audio/PreviewTrack.cs @@ -22,15 +22,12 @@ namespace osu.Game.Audio public event Action Started; private Track track; - private bool wasPlaying; + private bool hasStarted; [BackgroundDependencyLoader] private void load() { track = GetTrack(); - - if (track != null) - track.Looping = false; } /// @@ -58,7 +55,7 @@ namespace osu.Game.Audio base.Update(); // Todo: Track currently doesn't signal its completion, so we have to handle it manually - if (track != null && wasPlaying && track.HasCompleted) + if (hasStarted && track.HasCompleted) Stop(); } @@ -69,15 +66,12 @@ namespace osu.Game.Audio /// public void Start() => startDelegate = Schedule(() => { - if (!IsLoaded) - return; - if (track == null) return; - if (wasPlaying) + if (hasStarted) return; - wasPlaying = true; + hasStarted = true; track.Restart(); Started?.Invoke(); @@ -90,15 +84,12 @@ namespace osu.Game.Audio { startDelegate?.Cancel(); - if (!IsLoaded) - return; - if (track == null) return; - if (!wasPlaying) + if (!hasStarted) return; - wasPlaying = false; + hasStarted = false; track.Stop(); Stopped?.Invoke(); diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index 4bf6cf2bf7..4b91a3d700 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Direct { public class PlayButton : Container { - public readonly Bindable Playing = new Bindable(); + public readonly BindableBool Playing = new BindableBool(); public PreviewTrack Preview { get; private set; } private BeatmapSetInfo beatmapSet; @@ -29,12 +29,9 @@ namespace osu.Game.Overlays.Direct if (value == beatmapSet) return; beatmapSet = value; - if (Preview != null) - { - Preview.Stop(); - RemoveInternal(Preview); - Preview = null; - } + Preview?.Stop(); + Preview?.Expire(); + Preview = null; Playing.Value = false; } @@ -51,15 +48,9 @@ namespace osu.Game.Overlays.Direct set { if (value) - { loadingAnimation.Show(); - icon.FadeOut(transition_duration * 5, Easing.OutQuint); - } else - { loadingAnimation.Hide(); - icon.FadeIn(transition_duration, Easing.OutQuint); - } } } @@ -94,35 +85,7 @@ namespace osu.Game.Overlays.Direct protected override bool OnClick(InputState state) { - if (!Playing.Value) - { - if (Preview != null) - { - Preview.Start(); - return true; - } - - loading = true; - - var loadingPreview = previewTrackManager.Get(beatmapSet); - loadingPreview.Started += () => Playing.Value = true; - loadingPreview.Stopped += () => Playing.Value = false; - - LoadComponentAsync(Preview = loadingPreview, t => - { - if (Preview != loadingPreview) - return; - - AddInternal(t); - - Preview.Start(); - - loading = false; - }); - } - else - Preview?.Stop(); - + Playing.Toggle(); return true; } @@ -141,17 +104,43 @@ namespace osu.Game.Overlays.Direct private void playingStateChanged(bool playing) { - if (playing && BeatmapSet == null) - { - Playing.Value = false; - return; - } - icon.Icon = playing ? FontAwesome.fa_stop : FontAwesome.fa_play; icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint); - if (!playing) + if (playing) { + if (BeatmapSet == null) + { + Playing.Value = false; + return; + } + + if (Preview != null) + { + Preview.Start(); + return; + } + + loading = true; + + LoadComponentAsync(Preview = previewTrackManager.Get(beatmapSet), preview => + { + // beatmapset may have changed. + if (Preview != preview) + return; + + AddInternal(preview); + loading = false; + preview.Stopped += () => Playing.Value = false; + + // user may have changed their mind. + if (Playing) + preview.Start(); + }); + } + else + { + Preview?.Stop(); loading = false; } } From 73e13e2d63c3d9a6509cb0c664399f458781b3a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Jun 2018 12:35:43 +0900 Subject: [PATCH 63/63] Rename Stop to StopAnyPlaying for clarity --- osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs | 6 +++--- osu.Game/Audio/PreviewTrackManager.cs | 8 ++++---- .../Graphics/Containers/OsuFocusedOverlayContainer.cs | 2 +- osu.Game/Overlays/DirectOverlay.cs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs b/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs index 7d02519007..d711d501fe 100644 --- a/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs +++ b/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs @@ -65,7 +65,7 @@ namespace osu.Game.Tests.Visual AddStep("get track", () => track = getOwnedTrack()); AddStep("start", () => track.Start()); - AddStep("stop by owner", () => trackManager.Stop(this)); + AddStep("stop by owner", () => trackManager.StopAnyPlaying(this)); AddAssert("stopped", () => !track.IsRunning); } @@ -77,9 +77,9 @@ namespace osu.Game.Tests.Visual AddStep("get track", () => AddInternal(owner = new TestTrackOwner(track = getTrack()))); AddStep("start", () => track.Start()); - AddStep("attempt stop", () => trackManager.Stop(this)); + AddStep("attempt stop", () => trackManager.StopAnyPlaying(this)); AddAssert("not stopped", () => track.IsRunning); - AddStep("stop by true owner", () => trackManager.Stop(owner)); + AddStep("stop by true owner", () => trackManager.StopAnyPlaying(owner)); AddAssert("stopped", () => !track.IsRunning); } diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs index 56bedc9dd2..07fbe86ff4 100644 --- a/osu.Game/Audio/PreviewTrackManager.cs +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -60,7 +60,7 @@ namespace osu.Game.Audio } /// - /// Stops the currently playing . + /// Stops any currently playing . /// /// /// Only the immediate owner (an object that implements ) of the playing @@ -68,12 +68,12 @@ namespace osu.Game.Audio /// can always stop the themselves through . /// /// The which may be the owner of the . - public void Stop(IPreviewTrackOwner source) + public void StopAnyPlaying(IPreviewTrackOwner source) { - if (current?.Owner != source) + if (current == null || current.Owner != source) return; - current?.Stop(); + current.Stop(); current = null; } diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index e3179b5631..d6b6595b69 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -82,7 +82,7 @@ namespace osu.Game.Graphics.Containers protected override void PopOut() { base.PopOut(); - previewTrackManager.Stop(this); + previewTrackManager.StopAnyPlaying(this); } } } diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index c28ecb3c9f..423211659d 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -262,7 +262,7 @@ namespace osu.Game.Overlays if (Header.Tabs.Current.Value == DirectTab.Search && (Filter.Search.Text == string.Empty || currentQuery == string.Empty)) return; - previewTrackManager.Stop(this); + previewTrackManager.StopAnyPlaying(this); getSetsRequest = new SearchBeatmapSetsRequest(currentQuery.Value ?? string.Empty, ((FilterControl)Filter).Ruleset.Value,