From 9da7eec0d91f4779c98ec094c321eba952440884 Mon Sep 17 00:00:00 2001 From: Sebastian Krajewski Date: Sat, 4 Jan 2020 08:21:48 +0100 Subject: [PATCH 01/43] Add pulse to slider reverse arrows --- .../Objects/Drawables/DrawableRepeatPoint.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index b81d94a673..db8ad98f8a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -65,12 +65,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void UpdateInitialTransforms() { - animDuration = Math.Min(150, repeatPoint.SpanDuration / 2); + animDuration = Math.Min(300, repeatPoint.SpanDuration); - this.Animate( - d => d.FadeIn(animDuration), - d => d.ScaleTo(0.5f).ScaleTo(1f, animDuration * 4, Easing.OutElasticHalf) - ); + this.FadeIn(animDuration); + + double fadeInStart = repeatPoint.StartTime - 2 * repeatPoint.SpanDuration; + + // We want first repeat arrow to start pulsing during snake in + if (repeatPoint.RepeatIndex == 0) + fadeInStart -= repeatPoint.TimePreempt; + + for (double pulseStartTime = fadeInStart; pulseStartTime < repeatPoint.StartTime; pulseStartTime += 300) + this.Delay(pulseStartTime - LifetimeStart).ScaleTo(1.3f).ScaleTo(1f, Math.Min(300, repeatPoint.StartTime - pulseStartTime)); } protected override void UpdateStateTransforms(ArmedState state) @@ -88,7 +94,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables break; case ArmedState.Hit: - this.FadeOut(animDuration, Easing.OutQuint) + this.FadeOut(animDuration, Easing.Out) .ScaleTo(Scale * 1.5f, animDuration, Easing.Out); break; } From fc0b622a69dc20e0104536d58b15f5a5f67cf45d Mon Sep 17 00:00:00 2001 From: Sebastian Krajewski Date: Sat, 4 Jan 2020 10:35:10 +0100 Subject: [PATCH 02/43] Change reverse arrow pulse easing to OutQuad --- .../Objects/Drawables/DrawableRepeatPoint.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index db8ad98f8a..e1cacfaaff 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables fadeInStart -= repeatPoint.TimePreempt; for (double pulseStartTime = fadeInStart; pulseStartTime < repeatPoint.StartTime; pulseStartTime += 300) - this.Delay(pulseStartTime - LifetimeStart).ScaleTo(1.3f).ScaleTo(1f, Math.Min(300, repeatPoint.StartTime - pulseStartTime)); + this.Delay(pulseStartTime - LifetimeStart).ScaleTo(1.3f).ScaleTo(1f, Math.Min(300, repeatPoint.StartTime - pulseStartTime), Easing.Out); } protected override void UpdateStateTransforms(ArmedState state) From 46271ccbc89e2a55059cda1070d1599321427bca Mon Sep 17 00:00:00 2001 From: Sebastian Krajewski Date: Sat, 4 Jan 2020 13:01:42 +0100 Subject: [PATCH 03/43] Add slider reverse arrow pulse settings --- .../Configuration/OsuRulesetConfigManager.cs | 5 +- .../Objects/Drawables/DrawableRepeatPoint.cs | 43 +++++++------- .../Drawables/Pieces/ReverseArrowPiece.cs | 58 +++++++++++++++++++ .../UI/OsuSettingsSubsection.cs | 5 ++ .../UI/ReverseArrowPulseMode.cs | 18 ++++++ 5 files changed, 108 insertions(+), 21 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs create mode 100644 osu.Game.Rulesets.Osu/UI/ReverseArrowPulseMode.cs diff --git a/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs index f76635a932..77b0c728b0 100644 --- a/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs +++ b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs @@ -3,6 +3,7 @@ using osu.Game.Configuration; using osu.Game.Rulesets.Configuration; +using osu.Game.Rulesets.Osu.UI; namespace osu.Game.Rulesets.Osu.Configuration { @@ -19,6 +20,7 @@ namespace osu.Game.Rulesets.Osu.Configuration Set(OsuRulesetSetting.SnakingInSliders, true); Set(OsuRulesetSetting.SnakingOutSliders, true); Set(OsuRulesetSetting.ShowCursorTrail, true); + Set(OsuRulesetSetting.ReverseArrowPulse, ReverseArrowPulseMode.Synced); } } @@ -26,6 +28,7 @@ namespace osu.Game.Rulesets.Osu.Configuration { SnakingInSliders, SnakingOutSliders, - ShowCursorTrail + ShowCursorTrail, + ReverseArrowPulse, } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index e1cacfaaff..1f3c5d54bc 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -6,13 +6,13 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; using osu.Framework.MathUtils; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; +using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Scoring; using osuTK; -using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -21,9 +21,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private readonly RepeatPoint repeatPoint; private readonly DrawableSlider drawableSlider; + public readonly Bindable PulseMode = new Bindable(ReverseArrowPulseMode.Synced); + private double animDuration; - private readonly SkinnableDrawable scaleContainer; + private readonly Drawable scaleContainer; + + [Resolved(CanBeNull = true)] + private OsuRulesetConfigManager config { get; set; } public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider) : base(repeatPoint) @@ -36,16 +41,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Blending = BlendingParameters.Additive; Origin = Anchor.Centre; - InternalChild = scaleContainer = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.ReverseArrow), _ => new SpriteIcon - { - RelativeSizeAxes = Axes.Both, - Icon = FontAwesome.Solid.ChevronRight, - Size = new Vector2(0.35f) - }, confineMode: ConfineMode.NoScaling) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }; + InternalChild = scaleContainer = new ReverseArrowPiece(); } private readonly IBindable scaleBindable = new Bindable(); @@ -55,6 +51,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { scaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true); scaleBindable.BindTo(HitObject.ScaleBindable); + + config?.BindWith(OsuRulesetSetting.ReverseArrowPulse, PulseMode); } protected override void CheckForResult(bool userTriggered, double timeOffset) @@ -69,14 +67,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables this.FadeIn(animDuration); - double fadeInStart = repeatPoint.StartTime - 2 * repeatPoint.SpanDuration; + if (PulseMode.Value == ReverseArrowPulseMode.Stable) + { + double fadeInStart = repeatPoint.StartTime - 2 * repeatPoint.SpanDuration; - // We want first repeat arrow to start pulsing during snake in - if (repeatPoint.RepeatIndex == 0) - fadeInStart -= repeatPoint.TimePreempt; + // We want first repeat arrow to start pulsing during snake in + if (repeatPoint.RepeatIndex == 0) + fadeInStart -= repeatPoint.TimePreempt; - for (double pulseStartTime = fadeInStart; pulseStartTime < repeatPoint.StartTime; pulseStartTime += 300) - this.Delay(pulseStartTime - LifetimeStart).ScaleTo(1.3f).ScaleTo(1f, Math.Min(300, repeatPoint.StartTime - pulseStartTime), Easing.Out); + for (double pulseStartTime = fadeInStart; pulseStartTime < repeatPoint.StartTime; pulseStartTime += 300) + this.Delay(pulseStartTime - LifetimeStart).ScaleTo(1.3f).ScaleTo(1f, Math.Min(300, repeatPoint.StartTime - pulseStartTime), Easing.Out); + } + else if (PulseMode.Value == ReverseArrowPulseMode.Off) + this.ScaleTo(0.5f).ScaleTo(1f, animDuration * 2, Easing.OutElasticHalf); } protected override void UpdateStateTransforms(ArmedState state) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs new file mode 100644 index 0000000000..1ec175274e --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs @@ -0,0 +1,58 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Audio.Track; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osuTK; +using osu.Framework.Graphics.Sprites; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.Containers; +using osu.Game.Rulesets.Osu.Configuration; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class ReverseArrowPiece : BeatSyncedContainer + { + public readonly Bindable PulseMode = new Bindable(ReverseArrowPulseMode.Synced); + + [Resolved(CanBeNull = true)] + private OsuRulesetConfigManager config { get; set; } + + public ReverseArrowPiece() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + Blending = BlendingParameters.Additive; + + Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); + + InternalChild = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.ReverseArrow), _ => new SpriteIcon + { + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.Solid.ChevronRight, + Size = new Vector2(0.35f) + }, confineMode: ConfineMode.NoScaling) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) + { + if (PulseMode.Value == ReverseArrowPulseMode.Synced) + InternalChild.ScaleTo(1.3f).ScaleTo(1f, timingPoint.BeatLength, Easing.Out); + } + + [BackgroundDependencyLoader] + private void load() + { + config?.BindWith(OsuRulesetSetting.ReverseArrowPulse, PulseMode); + } + } +} diff --git a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs index 88adf72551..afde693316 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs @@ -39,6 +39,11 @@ namespace osu.Game.Rulesets.Osu.UI LabelText = "Cursor trail", Bindable = config.GetBindable(OsuRulesetSetting.ShowCursorTrail) }, + new SettingsEnumDropdown + { + LabelText = "Slider reverse arrow pulse", + Bindable = config.GetBindable(OsuRulesetSetting.ReverseArrowPulse) + }, }; } } diff --git a/osu.Game.Rulesets.Osu/UI/ReverseArrowPulseMode.cs b/osu.Game.Rulesets.Osu/UI/ReverseArrowPulseMode.cs new file mode 100644 index 0000000000..d3261e71db --- /dev/null +++ b/osu.Game.Rulesets.Osu/UI/ReverseArrowPulseMode.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.ComponentModel; + +namespace osu.Game.Rulesets.Osu.UI +{ + public enum ReverseArrowPulseMode + { + Off, + + [Description("Match osu!stable")] + Stable, + + [Description("Sync to beatmap")] + Synced + } +} From 319465899824dfde4a6ef3150c86651c3afc98f9 Mon Sep 17 00:00:00 2001 From: Sebastian Krajewski Date: Sat, 4 Jan 2020 13:12:37 +0100 Subject: [PATCH 04/43] Fix repeat point pulsing when it is in fade out state --- .../Objects/Drawables/DrawableRepeatPoint.cs | 2 +- .../Objects/Drawables/Pieces/ReverseArrowPiece.cs | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index 1f3c5d54bc..6bf2f95f41 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Blending = BlendingParameters.Additive; Origin = Anchor.Centre; - InternalChild = scaleContainer = new ReverseArrowPiece(); + InternalChild = scaleContainer = new ReverseArrowPiece(repeatPoint); } private readonly IBindable scaleBindable = new Bindable(); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs index 1ec175274e..114cf9d27e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Bindables; @@ -22,8 +23,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces [Resolved(CanBeNull = true)] private OsuRulesetConfigManager config { get; set; } - public ReverseArrowPiece() + private readonly RepeatPoint repeatPoint; + + public ReverseArrowPiece(RepeatPoint repeatPoint) { + this.repeatPoint = repeatPoint; + Anchor = Anchor.Centre; Origin = Anchor.Centre; @@ -45,8 +50,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) { - if (PulseMode.Value == ReverseArrowPulseMode.Synced) - InternalChild.ScaleTo(1.3f).ScaleTo(1f, timingPoint.BeatLength, Easing.Out); + if (PulseMode.Value == ReverseArrowPulseMode.Synced && Clock.CurrentTime < repeatPoint.StartTime) + InternalChild.ScaleTo(1.3f).ScaleTo(1f, Math.Min(timingPoint.BeatLength, repeatPoint.StartTime - Clock.CurrentTime), Easing.Out); } [BackgroundDependencyLoader] From 21468eb07011c8774f9de838a5a19d602fe01fc6 Mon Sep 17 00:00:00 2001 From: Sebastian Krajewski Date: Tue, 7 Jan 2020 04:55:05 +0100 Subject: [PATCH 05/43] Remove settings related to reverse arrow --- .../Configuration/OsuRulesetConfigManager.cs | 5 +--- .../Objects/Drawables/DrawableRepeatPoint.cs | 28 +++---------------- .../Drawables/Pieces/ReverseArrowPiece.cs | 17 +---------- .../UI/OsuSettingsSubsection.cs | 5 ---- .../UI/ReverseArrowPulseMode.cs | 18 ------------ 5 files changed, 6 insertions(+), 67 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu/UI/ReverseArrowPulseMode.cs diff --git a/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs index 77b0c728b0..f76635a932 100644 --- a/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs +++ b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs @@ -3,7 +3,6 @@ using osu.Game.Configuration; using osu.Game.Rulesets.Configuration; -using osu.Game.Rulesets.Osu.UI; namespace osu.Game.Rulesets.Osu.Configuration { @@ -20,7 +19,6 @@ namespace osu.Game.Rulesets.Osu.Configuration Set(OsuRulesetSetting.SnakingInSliders, true); Set(OsuRulesetSetting.SnakingOutSliders, true); Set(OsuRulesetSetting.ShowCursorTrail, true); - Set(OsuRulesetSetting.ReverseArrowPulse, ReverseArrowPulseMode.Synced); } } @@ -28,7 +26,6 @@ namespace osu.Game.Rulesets.Osu.Configuration { SnakingInSliders, SnakingOutSliders, - ShowCursorTrail, - ReverseArrowPulse, + ShowCursorTrail } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index 6bf2f95f41..4873160af0 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -8,9 +8,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.MathUtils; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; -using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Scoring; using osuTK; @@ -21,15 +19,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private readonly RepeatPoint repeatPoint; private readonly DrawableSlider drawableSlider; - public readonly Bindable PulseMode = new Bindable(ReverseArrowPulseMode.Synced); - private double animDuration; private readonly Drawable scaleContainer; - [Resolved(CanBeNull = true)] - private OsuRulesetConfigManager config { get; set; } - public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider) : base(repeatPoint) { @@ -51,8 +44,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { scaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true); scaleBindable.BindTo(HitObject.ScaleBindable); - - config?.BindWith(OsuRulesetSetting.ReverseArrowPulse, PulseMode); } protected override void CheckForResult(bool userTriggered, double timeOffset) @@ -65,21 +56,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { animDuration = Math.Min(300, repeatPoint.SpanDuration); - this.FadeIn(animDuration); - - if (PulseMode.Value == ReverseArrowPulseMode.Stable) - { - double fadeInStart = repeatPoint.StartTime - 2 * repeatPoint.SpanDuration; - - // We want first repeat arrow to start pulsing during snake in - if (repeatPoint.RepeatIndex == 0) - fadeInStart -= repeatPoint.TimePreempt; - - for (double pulseStartTime = fadeInStart; pulseStartTime < repeatPoint.StartTime; pulseStartTime += 300) - this.Delay(pulseStartTime - LifetimeStart).ScaleTo(1.3f).ScaleTo(1f, Math.Min(300, repeatPoint.StartTime - pulseStartTime), Easing.Out); - } - else if (PulseMode.Value == ReverseArrowPulseMode.Off) - this.ScaleTo(0.5f).ScaleTo(1f, animDuration * 2, Easing.OutElasticHalf); + this.Animate( + d => d.FadeIn(animDuration), + d => d.ScaleTo(0.5f).ScaleTo(1f, animDuration * 2, Easing.OutElasticHalf) + ); } protected override void UpdateStateTransforms(ArmedState state) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs index 114cf9d27e..2b9a3aa197 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs @@ -2,27 +2,18 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Framework.Allocation; using osu.Framework.Audio.Track; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osuTK; using osu.Framework.Graphics.Sprites; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.Containers; -using osu.Game.Rulesets.Osu.Configuration; -using osu.Game.Rulesets.Osu.UI; using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public class ReverseArrowPiece : BeatSyncedContainer { - public readonly Bindable PulseMode = new Bindable(ReverseArrowPulseMode.Synced); - - [Resolved(CanBeNull = true)] - private OsuRulesetConfigManager config { get; set; } - private readonly RepeatPoint repeatPoint; public ReverseArrowPiece(RepeatPoint repeatPoint) @@ -50,14 +41,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) { - if (PulseMode.Value == ReverseArrowPulseMode.Synced && Clock.CurrentTime < repeatPoint.StartTime) + if (Clock.CurrentTime < repeatPoint.StartTime) InternalChild.ScaleTo(1.3f).ScaleTo(1f, Math.Min(timingPoint.BeatLength, repeatPoint.StartTime - Clock.CurrentTime), Easing.Out); } - - [BackgroundDependencyLoader] - private void load() - { - config?.BindWith(OsuRulesetSetting.ReverseArrowPulse, PulseMode); - } } } diff --git a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs index afde693316..88adf72551 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs @@ -39,11 +39,6 @@ namespace osu.Game.Rulesets.Osu.UI LabelText = "Cursor trail", Bindable = config.GetBindable(OsuRulesetSetting.ShowCursorTrail) }, - new SettingsEnumDropdown - { - LabelText = "Slider reverse arrow pulse", - Bindable = config.GetBindable(OsuRulesetSetting.ReverseArrowPulse) - }, }; } } diff --git a/osu.Game.Rulesets.Osu/UI/ReverseArrowPulseMode.cs b/osu.Game.Rulesets.Osu/UI/ReverseArrowPulseMode.cs deleted file mode 100644 index d3261e71db..0000000000 --- a/osu.Game.Rulesets.Osu/UI/ReverseArrowPulseMode.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.ComponentModel; - -namespace osu.Game.Rulesets.Osu.UI -{ - public enum ReverseArrowPulseMode - { - Off, - - [Description("Match osu!stable")] - Stable, - - [Description("Sync to beatmap")] - Synced - } -} From 14289523777302fbcb3aa3c524964f9d43a365af Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 8 Jan 2020 18:59:13 +0300 Subject: [PATCH 06/43] Implement CountryFilter component for RankingsOverlay --- .../Online/TestSceneRankingsCountryFilter.cs | 45 ++++ osu.Game/Overlays/Rankings/CountryFilter.cs | 231 ++++++++++++++++++ 2 files changed, 276 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs create mode 100644 osu.Game/Overlays/Rankings/CountryFilter.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs new file mode 100644 index 0000000000..968be62a7c --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs @@ -0,0 +1,45 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Bindables; +using osu.Game.Overlays.Rankings; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneRankingsCountryFilter : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(CountryFilter), + }; + + public TestSceneRankingsCountryFilter() + { + var countryBindable = new Bindable(); + CountryFilter filter; + + Add(filter = new CountryFilter + { + Country = { BindTarget = countryBindable } + }); + + var country = new Country + { + FlagName = "BY", + FullName = "Belarus" + }; + var unknownCountry = new Country + { + FlagName = "CK", + FullName = "Cook Islands" + }; + + AddStep("Set country", () => countryBindable.Value = country); + AddStep("Set null country", () => countryBindable.Value = null); + AddStep("Set country with no flag", () => countryBindable.Value = unknownCountry); + } + } +} diff --git a/osu.Game/Overlays/Rankings/CountryFilter.cs b/osu.Game/Overlays/Rankings/CountryFilter.cs new file mode 100644 index 0000000000..4a24a440cc --- /dev/null +++ b/osu.Game/Overlays/Rankings/CountryFilter.cs @@ -0,0 +1,231 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Users; +using osu.Game.Users.Drawables; +using osuTK; + +namespace osu.Game.Overlays.Rankings +{ + public class CountryFilter : Container + { + private const int duration = 200; + private const int height = 50; + + public readonly Bindable Country = new Bindable(); + + private readonly Box background; + private readonly CountryPill countryPill; + private readonly Container content; + + public CountryFilter() + { + RelativeSizeAxes = Axes.X; + Child = content = new Container + { + RelativeSizeAxes = Axes.X, + Height = height, + Alpha = 0, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both + }, + new FillFlowContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10, 0), + Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN }, + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = @"filtered by country:", + Font = OsuFont.GetFont(size: 14) + }, + countryPill = new CountryPill + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Alpha = 0, + Country = { BindTarget = Country } + } + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = colours.GreySeafoam; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Country.BindValueChanged(onCountryChanged, true); + } + + private void onCountryChanged(ValueChangedEvent country) + { + countryPill.ClearTransforms(); + + if (country.NewValue == null) + { + countryPill.Collapse(); + this.ResizeHeightTo(0, duration, Easing.OutQuint); + content.FadeOut(duration, Easing.OutQuint); + return; + } + + this.ResizeHeightTo(height, duration, Easing.OutQuint); + content.FadeIn(duration, Easing.OutQuint).Finally(_ => countryPill.Expand()); + } + + private class CountryPill : CircularContainer + { + private readonly Box background; + private readonly UpdateableFlag flag; + private readonly OsuSpriteText countryName; + + public readonly Bindable Country = new Bindable(); + + public CountryPill() + { + AutoSizeDuration = duration; + AutoSizeEasing = Easing.OutQuint; + Height = 25; + Masking = true; + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both + }, + new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Margin = new MarginPadding { Horizontal = 10 }, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(8, 0), + Children = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(3, 0), + Children = new Drawable[] + { + flag = new UpdateableFlag + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(22, 15) + }, + countryName = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(size: 14) + } + } + }, + new CloseButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + ClickAction = () => Country.Value = null + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = colours.GreySeafoamDarker; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Country.BindValueChanged(onCountryChanged, true); + } + + public void Expand() + { + AutoSizeAxes = Axes.X; + this.FadeIn(duration, Easing.OutQuint); + } + + public void Collapse() + { + AutoSizeAxes = Axes.None; + this.ResizeWidthTo(0, duration, Easing.OutQuint); + this.FadeOut(duration, Easing.OutQuint); + } + + private void onCountryChanged(ValueChangedEvent country) + { + if (country.NewValue == null) + return; + + flag.Country = country.NewValue; + countryName.Text = country.NewValue.FullName; + } + + private class CloseButton : Container + { + public Action ClickAction; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AutoSizeAxes = Axes.Both; + Children = new Drawable[] + { + new SpriteIcon + { + Size = new Vector2(8), + Icon = FontAwesome.Solid.Times, + Colour = colours.GreySeafoamLighter, + }, + new HoverClickSounds(), + }; + } + + protected override bool OnClick(ClickEvent e) + { + ClickAction?.Invoke(); + return base.OnClick(e); + } + } + } + } +} From dc64ba8ed81d21aef0c4368f53c5fcfda795161a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 8 Jan 2020 19:22:07 +0300 Subject: [PATCH 07/43] Remove unused variable --- osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs index 968be62a7c..9a8ddf9cad 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs @@ -19,9 +19,8 @@ namespace osu.Game.Tests.Visual.Online public TestSceneRankingsCountryFilter() { var countryBindable = new Bindable(); - CountryFilter filter; - Add(filter = new CountryFilter + Add(new CountryFilter { Country = { BindTarget = countryBindable } }); From 1dbae21f981852f878154d73af241d7e44e0073a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 8 Jan 2020 19:40:28 +0300 Subject: [PATCH 08/43] Fix crashing test --- osu.Game/Overlays/Rankings/CountryFilter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Rankings/CountryFilter.cs b/osu.Game/Overlays/Rankings/CountryFilter.cs index 4a24a440cc..008214a9a4 100644 --- a/osu.Game/Overlays/Rankings/CountryFilter.cs +++ b/osu.Game/Overlays/Rankings/CountryFilter.cs @@ -86,6 +86,7 @@ namespace osu.Game.Overlays.Rankings private void onCountryChanged(ValueChangedEvent country) { + content.ClearTransforms(); countryPill.ClearTransforms(); if (country.NewValue == null) From 6cb763a019b3ecc34d47d683390a5b476ee94334 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 9 Jan 2020 00:06:28 +0300 Subject: [PATCH 09/43] Improve animations --- osu.Game/Overlays/Rankings/CountryFilter.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Rankings/CountryFilter.cs b/osu.Game/Overlays/Rankings/CountryFilter.cs index 008214a9a4..5452bc9327 100644 --- a/osu.Game/Overlays/Rankings/CountryFilter.cs +++ b/osu.Game/Overlays/Rankings/CountryFilter.cs @@ -86,9 +86,6 @@ namespace osu.Game.Overlays.Rankings private void onCountryChanged(ValueChangedEvent country) { - content.ClearTransforms(); - countryPill.ClearTransforms(); - if (country.NewValue == null) { countryPill.Collapse(); @@ -98,7 +95,8 @@ namespace osu.Game.Overlays.Rankings } this.ResizeHeightTo(height, duration, Easing.OutQuint); - content.FadeIn(duration, Easing.OutQuint).Finally(_ => countryPill.Expand()); + content.FadeIn(duration, Easing.OutQuint); + countryPill.Expand(); } private class CountryPill : CircularContainer @@ -181,12 +179,14 @@ namespace osu.Game.Overlays.Rankings public void Expand() { + ClearTransforms(); AutoSizeAxes = Axes.X; this.FadeIn(duration, Easing.OutQuint); } public void Collapse() { + ClearTransforms(); AutoSizeAxes = Axes.None; this.ResizeWidthTo(0, duration, Easing.OutQuint); this.FadeOut(duration, Easing.OutQuint); From 29c4ae68d91e8bb06f6197eb7da2ee24dc2725bb Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 9 Jan 2020 00:14:29 +0300 Subject: [PATCH 10/43] Add some content to test scene for better visual representation --- .../Online/TestSceneRankingsCountryFilter.cs | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs index 9a8ddf9cad..360bb81715 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs @@ -4,8 +4,13 @@ using System; using System.Collections.Generic; using osu.Framework.Bindables; +using osu.Framework.Graphics.Containers; using osu.Game.Overlays.Rankings; using osu.Game.Users; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osuTK.Graphics; +using osu.Game.Graphics.Sprites; namespace osu.Game.Tests.Visual.Online { @@ -20,9 +25,35 @@ namespace osu.Game.Tests.Visual.Online { var countryBindable = new Bindable(); - Add(new CountryFilter + AddRange(new Drawable[] { - Country = { BindTarget = countryBindable } + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Gray, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new CountryFilter + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Country = { BindTarget = countryBindable } + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = "Some content", + Margin = new MarginPadding { Vertical = 20 } + } + } + } }); var country = new Country From 0d9fb065da462b36074c9b292cc50c001b39236a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 9 Jan 2020 00:27:22 +0300 Subject: [PATCH 11/43] Move CountryPill to it's own class --- .../Online/TestSceneRankingsCountryFilter.cs | 1 + osu.Game/Overlays/Rankings/CountryFilter.cs | 135 ---------------- osu.Game/Overlays/Rankings/CountryPill.cs | 149 ++++++++++++++++++ 3 files changed, 150 insertions(+), 135 deletions(-) create mode 100644 osu.Game/Overlays/Rankings/CountryPill.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs index 360bb81715..3d38710b59 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs @@ -19,6 +19,7 @@ namespace osu.Game.Tests.Visual.Online public override IReadOnlyList RequiredTypes => new[] { typeof(CountryFilter), + typeof(CountryPill) }; public TestSceneRankingsCountryFilter() diff --git a/osu.Game/Overlays/Rankings/CountryFilter.cs b/osu.Game/Overlays/Rankings/CountryFilter.cs index 5452bc9327..7cd56100db 100644 --- a/osu.Game/Overlays/Rankings/CountryFilter.cs +++ b/osu.Game/Overlays/Rankings/CountryFilter.cs @@ -1,19 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; using osu.Game.Users; -using osu.Game.Users.Drawables; using osuTK; namespace osu.Game.Overlays.Rankings @@ -98,135 +93,5 @@ namespace osu.Game.Overlays.Rankings content.FadeIn(duration, Easing.OutQuint); countryPill.Expand(); } - - private class CountryPill : CircularContainer - { - private readonly Box background; - private readonly UpdateableFlag flag; - private readonly OsuSpriteText countryName; - - public readonly Bindable Country = new Bindable(); - - public CountryPill() - { - AutoSizeDuration = duration; - AutoSizeEasing = Easing.OutQuint; - Height = 25; - Masking = true; - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both - }, - new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Margin = new MarginPadding { Horizontal = 10 }, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(8, 0), - Children = new Drawable[] - { - new FillFlowContainer - { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(3, 0), - Children = new Drawable[] - { - flag = new UpdateableFlag - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(22, 15) - }, - countryName = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.GetFont(size: 14) - } - } - }, - new CloseButton - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - ClickAction = () => Country.Value = null - } - } - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - background.Colour = colours.GreySeafoamDarker; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - Country.BindValueChanged(onCountryChanged, true); - } - - public void Expand() - { - ClearTransforms(); - AutoSizeAxes = Axes.X; - this.FadeIn(duration, Easing.OutQuint); - } - - public void Collapse() - { - ClearTransforms(); - AutoSizeAxes = Axes.None; - this.ResizeWidthTo(0, duration, Easing.OutQuint); - this.FadeOut(duration, Easing.OutQuint); - } - - private void onCountryChanged(ValueChangedEvent country) - { - if (country.NewValue == null) - return; - - flag.Country = country.NewValue; - countryName.Text = country.NewValue.FullName; - } - - private class CloseButton : Container - { - public Action ClickAction; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - AutoSizeAxes = Axes.Both; - Children = new Drawable[] - { - new SpriteIcon - { - Size = new Vector2(8), - Icon = FontAwesome.Solid.Times, - Colour = colours.GreySeafoamLighter, - }, - new HoverClickSounds(), - }; - } - - protected override bool OnClick(ClickEvent e) - { - ClickAction?.Invoke(); - return base.OnClick(e); - } - } - } } } diff --git a/osu.Game/Overlays/Rankings/CountryPill.cs b/osu.Game/Overlays/Rankings/CountryPill.cs new file mode 100644 index 0000000000..65fce3b909 --- /dev/null +++ b/osu.Game/Overlays/Rankings/CountryPill.cs @@ -0,0 +1,149 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Users; +using osu.Game.Users.Drawables; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Rankings +{ + public class CountryPill : CircularContainer + { + private const int duration = 200; + + private readonly Box background; + private readonly UpdateableFlag flag; + private readonly OsuSpriteText countryName; + + public readonly Bindable Country = new Bindable(); + + public CountryPill() + { + AutoSizeDuration = duration; + AutoSizeEasing = Easing.OutQuint; + Height = 25; + Masking = true; + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both + }, + new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Margin = new MarginPadding { Horizontal = 10 }, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(8, 0), + Children = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(3, 0), + Children = new Drawable[] + { + flag = new UpdateableFlag + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(22, 15) + }, + countryName = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(size: 14) + } + } + }, + new CloseButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Action = () => Country.Value = null + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = colours.GreySeafoamDarker; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Country.BindValueChanged(onCountryChanged, true); + } + + public void Expand() + { + ClearTransforms(); + AutoSizeAxes = Axes.X; + this.FadeIn(duration, Easing.OutQuint); + } + + public void Collapse() + { + ClearTransforms(); + AutoSizeAxes = Axes.None; + this.ResizeWidthTo(0, duration, Easing.OutQuint); + this.FadeOut(duration, Easing.OutQuint); + } + + private void onCountryChanged(ValueChangedEvent country) + { + if (country.NewValue == null) + return; + + flag.Country = country.NewValue; + countryName.Text = country.NewValue.FullName; + } + + private class CloseButton : OsuHoverContainer + { + private readonly SpriteIcon icon; + + protected override IEnumerable EffectTargets => new[] { icon }; + + public CloseButton() + { + AutoSizeAxes = Axes.Both; + Add(icon = new SpriteIcon + { + Size = new Vector2(8), + Icon = FontAwesome.Solid.Times + }); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + IdleColour = colours.GreySeafoamLighter; + HoverColour = Color4.White; + } + } + } +} From 619fe29871b7e2797b9e0c15003842bb70e4059f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Jan 2020 01:39:45 +0900 Subject: [PATCH 12/43] Make reverse arrow animate faster via divisor specification Adds MinimumBeatLength to BeatSyncedContainer to make sure things don't get out of hand. --- .../Objects/Drawables/Pieces/ReverseArrowPiece.cs | 3 +++ osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs index 2b9a3aa197..2c6e5b7c18 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs @@ -20,6 +20,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { this.repeatPoint = repeatPoint; + Divisor = 2; + MinimumBeatLength = 200; + Anchor = Anchor.Centre; Origin = Anchor.Centre; diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index b9ef279f5c..be9aefa359 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -38,6 +38,11 @@ namespace osu.Game.Graphics.Containers /// public int Divisor { get; set; } = 1; + /// + /// An optional minimum beat length. Any beat length below this will be multiplied by two until valid. + /// + public double MinimumBeatLength { get; set; } + /// /// Default length of a beat in milliseconds. Used whenever there is no beatmap or track playing. /// @@ -89,6 +94,9 @@ namespace osu.Game.Graphics.Containers double beatLength = timingPoint.BeatLength / Divisor; + while (beatLength < MinimumBeatLength) + beatLength *= 2; + int beatIndex = (int)((currentTrackTime - timingPoint.Time) / beatLength) - (effectPoint.OmitFirstBarLine ? 1 : 0); // The beats before the start of the first control point are off by 1, this should do the trick From c5085aea24ae4b08890baac8e894ce208d71af7e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Jan 2020 01:45:10 +0900 Subject: [PATCH 13/43] Use Child, not InternalChild --- .../Objects/Drawables/Pieces/ReverseArrowPiece.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs index 2c6e5b7c18..ee7cdefa13 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); - InternalChild = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.ReverseArrow), _ => new SpriteIcon + Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.ReverseArrow), _ => new SpriteIcon { RelativeSizeAxes = Axes.Both, Icon = FontAwesome.Solid.ChevronRight, @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) { if (Clock.CurrentTime < repeatPoint.StartTime) - InternalChild.ScaleTo(1.3f).ScaleTo(1f, Math.Min(timingPoint.BeatLength, repeatPoint.StartTime - Clock.CurrentTime), Easing.Out); + Child.ScaleTo(1.3f).ScaleTo(1f, Math.Min(timingPoint.BeatLength, repeatPoint.StartTime - Clock.CurrentTime), Easing.Out); } } } From 210d06b75ed27209d6d8721a8d3b131004a57bb5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Jan 2020 01:45:32 +0900 Subject: [PATCH 14/43] Remove default value --- .../Objects/Drawables/Pieces/ReverseArrowPiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs index ee7cdefa13..37bbfbe806 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces RelativeSizeAxes = Axes.Both, Icon = FontAwesome.Solid.ChevronRight, Size = new Vector2(0.35f) - }, confineMode: ConfineMode.NoScaling) + }) { Anchor = Anchor.Centre, Origin = Anchor.Centre, From ab4f31639d9e2ccfd8ac4cc61091c024aa0c6827 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Jan 2020 01:47:44 +0900 Subject: [PATCH 15/43] Remove weird time clause --- .../Objects/Drawables/Pieces/ReverseArrowPiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs index 37bbfbe806..0403fe8196 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) { if (Clock.CurrentTime < repeatPoint.StartTime) - Child.ScaleTo(1.3f).ScaleTo(1f, Math.Min(timingPoint.BeatLength, repeatPoint.StartTime - Clock.CurrentTime), Easing.Out); + Child.ScaleTo(1.3f).ScaleTo(1f, timingPoint.BeatLength, Easing.Out); } } } From fe09e34f1bf8a9ed9d7479c0792ac871ae3b5020 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Jan 2020 01:48:20 +0900 Subject: [PATCH 16/43] Remove limiting clause --- .../Objects/Drawables/Pieces/ReverseArrowPiece.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs index 0403fe8196..08dd97b0d8 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs @@ -42,10 +42,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces }; } - protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) - { - if (Clock.CurrentTime < repeatPoint.StartTime) - Child.ScaleTo(1.3f).ScaleTo(1f, timingPoint.BeatLength, Easing.Out); - } + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) => + Child.ScaleTo(1.3f).ScaleTo(1f, timingPoint.BeatLength, Easing.Out); } } From c196e83e75f495f533f82e867d7d8cd3e9165738 Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 13 Jan 2020 20:48:39 -0800 Subject: [PATCH 17/43] Allow changing volume in song select with arrow keys when pressing alt --- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index bf2382c4ae..da01ac5f95 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -436,7 +436,7 @@ namespace osu.Game.Screens.Select break; } - if (direction == 0) + if (direction == 0 || e.AltPressed) return base.OnKeyDown(e); SelectNext(direction, skipDifficulties); From 1367c18d3f2a471c5d8a303990362b181337911d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Jan 2020 15:07:25 +0900 Subject: [PATCH 18/43] General refactorings --- .../Online/TestSceneRankingsCountryFilter.cs | 2 +- osu.Game/Overlays/Rankings/CountryFilter.cs | 18 ++- osu.Game/Overlays/Rankings/CountryPill.cs | 117 ++++++++++-------- 3 files changed, 80 insertions(+), 57 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs index 3d38710b59..7ac65181f9 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.Online { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Country = { BindTarget = countryBindable } + Current = { BindTarget = countryBindable } }, new OsuSpriteText { diff --git a/osu.Game/Overlays/Rankings/CountryFilter.cs b/osu.Game/Overlays/Rankings/CountryFilter.cs index 7cd56100db..2b12457ccc 100644 --- a/osu.Game/Overlays/Rankings/CountryFilter.cs +++ b/osu.Game/Overlays/Rankings/CountryFilter.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Users; @@ -13,12 +14,18 @@ using osuTK; namespace osu.Game.Overlays.Rankings { - public class CountryFilter : Container + public class CountryFilter : CompositeDrawable, IHasCurrentValue { private const int duration = 200; private const int height = 50; - public readonly Bindable Country = new Bindable(); + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current + { + get => current.Current; + set => current.Current = value; + } private readonly Box background; private readonly CountryPill countryPill; @@ -27,7 +34,8 @@ namespace osu.Game.Overlays.Rankings public CountryFilter() { RelativeSizeAxes = Axes.X; - Child = content = new Container + + InternalChild = content = new Container { RelativeSizeAxes = Axes.X, Height = height, @@ -59,7 +67,7 @@ namespace osu.Game.Overlays.Rankings Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Alpha = 0, - Country = { BindTarget = Country } + Current = Current } } } @@ -76,7 +84,7 @@ namespace osu.Game.Overlays.Rankings protected override void LoadComplete() { base.LoadComplete(); - Country.BindValueChanged(onCountryChanged, true); + Current.BindValueChanged(onCountryChanged, true); } private void onCountryChanged(ValueChangedEvent country) diff --git a/osu.Game/Overlays/Rankings/CountryPill.cs b/osu.Game/Overlays/Rankings/CountryPill.cs index 65fce3b909..410d316006 100644 --- a/osu.Game/Overlays/Rankings/CountryPill.cs +++ b/osu.Game/Overlays/Rankings/CountryPill.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -18,68 +19,80 @@ using osuTK.Graphics; namespace osu.Game.Overlays.Rankings { - public class CountryPill : CircularContainer + public class CountryPill : CompositeDrawable, IHasCurrentValue { private const int duration = 200; + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current + { + get => current.Current; + set => current.Current = value; + } + + private readonly Container content; private readonly Box background; private readonly UpdateableFlag flag; private readonly OsuSpriteText countryName; - public readonly Bindable Country = new Bindable(); - public CountryPill() { - AutoSizeDuration = duration; - AutoSizeEasing = Easing.OutQuint; - Height = 25; - Masking = true; - Children = new Drawable[] + AutoSizeAxes = Axes.Both; + + InternalChild = content = new CircularContainer { - background = new Box + Height = 25, + AutoSizeDuration = duration, + AutoSizeEasing = Easing.OutQuint, + Masking = true, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both - }, - new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Margin = new MarginPadding { Horizontal = 10 }, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(8, 0), - Children = new Drawable[] + background = new Box { - new FillFlowContainer + RelativeSizeAxes = Axes.Both + }, + new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Margin = new MarginPadding { Horizontal = 10 }, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(8, 0), + Children = new Drawable[] { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(3, 0), - Children = new Drawable[] + new FillFlowContainer { - flag = new UpdateableFlag + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(3, 0), + Children = new Drawable[] { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(22, 15) - }, - countryName = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.GetFont(size: 14) + flag = new UpdateableFlag + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(22, 15) + }, + countryName = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(size: 14) + } } + }, + new CloseButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Action = () => Current.Value = null } - }, - new CloseButton - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Action = () => Country.Value = null } } } @@ -95,21 +108,23 @@ namespace osu.Game.Overlays.Rankings protected override void LoadComplete() { base.LoadComplete(); - Country.BindValueChanged(onCountryChanged, true); + Current.BindValueChanged(onCountryChanged, true); } public void Expand() { - ClearTransforms(); - AutoSizeAxes = Axes.X; + content.ClearTransforms(); + content.AutoSizeAxes = Axes.X; + this.FadeIn(duration, Easing.OutQuint); } public void Collapse() { - ClearTransforms(); - AutoSizeAxes = Axes.None; - this.ResizeWidthTo(0, duration, Easing.OutQuint); + content.ClearTransforms(); + content.AutoSizeAxes = Axes.None; + content.ResizeWidthTo(0, duration, Easing.OutQuint); + this.FadeOut(duration, Easing.OutQuint); } From 92daf9b8f194ad789b4c9de9c6fe132ef3d67b7f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Jan 2020 18:14:31 +0900 Subject: [PATCH 19/43] Cleanup --- .../Objects/Drawables/DrawableRepeatPoint.cs | 2 +- .../Objects/Drawables/Pieces/ReverseArrowPiece.cs | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index 055a09fcb8..20b31c68f2 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Blending = BlendingParameters.Additive; Origin = Anchor.Centre; - InternalChild = scaleContainer = new ReverseArrowPiece(repeatPoint); + InternalChild = scaleContainer = new ReverseArrowPiece(); } private readonly IBindable scaleBindable = new Bindable(); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs index 08dd97b0d8..35a27bb0a6 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Audio.Track; using osu.Framework.Graphics; using osuTK; @@ -14,12 +13,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public class ReverseArrowPiece : BeatSyncedContainer { - private readonly RepeatPoint repeatPoint; - - public ReverseArrowPiece(RepeatPoint repeatPoint) + public ReverseArrowPiece() { - this.repeatPoint = repeatPoint; - Divisor = 2; MinimumBeatLength = 200; From 681a43a318c42149f773360401d5afd55668357d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Jan 2020 13:20:38 +0900 Subject: [PATCH 20/43] Refactor to support beatmap updates --- .../Beatmaps/IO/ImportBeatmapTest.cs | 96 +++++++++++++++++++ .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 2 +- osu.Game/Database/ArchiveModelManager.cs | 8 +- 3 files changed, 101 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 4766411cbd..538e7fa4b7 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -11,9 +11,14 @@ using NUnit.Framework; using osu.Framework.Platform; using osu.Game.IPC; using osu.Framework.Allocation; +using osu.Framework.Extensions; +using osu.Framework.IO.Stores; using osu.Framework.Logging; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Formats; +using osu.Game.Database; using osu.Game.IO; +using osu.Game.IO.Archives; using osu.Game.Tests.Resources; using SharpCompress.Archives; using SharpCompress.Archives.Zip; @@ -552,6 +557,46 @@ namespace osu.Game.Tests.Beatmaps.IO } } + [Test] + public async Task TestUpdateFile() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestUpdateFile))) + { + try + { + var osu = loadOsu(host); + var manager = osu.Dependencies.Get(); + + var temp = TestResources.GetTestBeatmapForImport(); + await osu.Dependencies.Get().Import(temp); + + BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0]; + Beatmap beatmapToUpdate = (Beatmap)manager.GetWorkingBeatmap(setToUpdate.Beatmaps.First(b => b.RulesetID == 0)).Beatmap; + BeatmapSetFileInfo fileToUpdate = setToUpdate.Files.First(f => beatmapToUpdate.BeatmapInfo.Path.Contains(f.Filename)); + + using (var stream = new MemoryStream()) + { + using (var writer = new StreamWriter(stream, leaveOpen: true)) + { + beatmapToUpdate.BeatmapInfo.Version = "updated"; + new LegacyBeatmapEncoder(beatmapToUpdate).Encode(writer); + } + + stream.Seek(0, SeekOrigin.Begin); + + using (var reader = new UpdateArchiveReader(manager.Files.Store, setToUpdate, fileToUpdate, stream)) + await manager.Import(setToUpdate, reader); + } + + var allBeatmaps = manager.GetAllUsableBeatmapSets(); + } + finally + { + host.Exit(); + } + } + } + public static async Task LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false) { var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack); @@ -655,5 +700,56 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.IsTrue(task.Wait(timeout), failureMessage); } + + private class UpdateArchiveReader : ArchiveReader + where TModel : class, IHasFiles + where TFileModel : INamedFileInfo, new() + { + private readonly IResourceStore store; + private readonly TModel modelToUpdate; + private readonly TFileModel fileToUpdate; + private readonly Stream newContents; + + public UpdateArchiveReader(IResourceStore store, TModel modelToUpdate, TFileModel fileToUpdate, Stream newContents) + : base(string.Empty) + { + this.store = store; + this.modelToUpdate = modelToUpdate; + this.fileToUpdate = fileToUpdate; + this.newContents = newContents; + } + + public override Stream GetStream(string name) + { + name = name.ToStandardisedPath(); + + if (name.Contains(fileToUpdate.Filename, StringComparison.Ordinal)) + { + var stream = new MemoryStream(); + + newContents.Seek(0, SeekOrigin.Begin); + newContents.CopyTo(stream); + + stream.Seek(0, SeekOrigin.Begin); + + return stream; + } + + TFileModel existing = modelToUpdate.Files.FirstOrDefault(m => name.Contains(m.Filename, StringComparison.Ordinal)); + + if (!string.IsNullOrEmpty(existing?.FileInfo?.StoragePath)) + return store.GetStream(existing.FileInfo.StoragePath); + + return null; + } + + public override void Dispose() + { + } + + public override IEnumerable Filenames => Enumerable.Empty(); // modelToUpdate.Files.Select(f => f.Filename); + + public override Stream GetUnderlyingStream() => null; + } } } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 433becd8cc..09f40ce7b6 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -63,7 +63,7 @@ namespace osu.Game.Beatmaps.Formats writer.WriteLine(FormattableString.Invariant($"PreviewTime: {beatmap.Metadata.PreviewTime}")); // Todo: Not all countdown types are supported by lazer yet writer.WriteLine(FormattableString.Invariant($"Countdown: {(beatmap.BeatmapInfo.Countdown ? '1' : '0')}")); - writer.WriteLine(FormattableString.Invariant($"SampleSet: {toLegacySampleBank(beatmap.ControlPointInfo.SamplePoints[0].SampleBank)}")); + writer.WriteLine(FormattableString.Invariant($"SampleSet: {toLegacySampleBank(beatmap.ControlPointInfo.SamplePointAt(double.MinValue).SampleBank)}")); writer.WriteLine(FormattableString.Invariant($"StackLeniency: {beatmap.BeatmapInfo.StackLeniency}")); writer.WriteLine(FormattableString.Invariant($"Mode: {beatmap.BeatmapInfo.RulesetID}")); writer.WriteLine(FormattableString.Invariant($"LetterboxInBreaks: {(beatmap.BeatmapInfo.LetterboxInBreaks ? '1' : '0')}")); diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 45fe034a70..3838fca282 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -68,7 +68,7 @@ namespace osu.Game.Database public virtual bool SupportsImportFromStable => RuntimeInfo.IsDesktop; - protected readonly FileStore Files; + public readonly FileStore Files; protected readonly IDatabaseContextFactory ContextFactory; @@ -222,9 +222,8 @@ namespace osu.Game.Database { model = CreateModel(archive); - if (model == null) return Task.FromResult(null); - - model.Hash = computeHash(archive); + if (model == null) + return Task.FromResult(null); } catch (TaskCanceledException) { @@ -303,6 +302,7 @@ namespace osu.Game.Database LogForModel(item, "Beginning import..."); item.Files = archive != null ? createFileInfos(archive, Files) : new List(); + item.Hash = archive != null ? computeHash(archive) : item.Hash; await Populate(item, archive, cancellationToken); From 609102bea3dd9fc2bf92c2344c145c9543fc2b38 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 8 Jan 2020 12:36:07 +0900 Subject: [PATCH 21/43] Initial file updating implementation --- .../Beatmaps/IO/ImportBeatmapTest.cs | 14 ++++-- osu.Game/Database/ArchiveModelManager.cs | 45 ++++++++++++++++--- 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 538e7fa4b7..52a705edfd 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -19,6 +19,7 @@ using osu.Game.Beatmaps.Formats; using osu.Game.Database; using osu.Game.IO; using osu.Game.IO.Archives; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Tests.Resources; using SharpCompress.Archives; using SharpCompress.Archives.Zip; @@ -579,16 +580,23 @@ namespace osu.Game.Tests.Beatmaps.IO using (var writer = new StreamWriter(stream, leaveOpen: true)) { beatmapToUpdate.BeatmapInfo.Version = "updated"; + beatmapToUpdate.HitObjects.Clear(); + beatmapToUpdate.HitObjects.Add(new HitCircle { StartTime = 5000 }); + new LegacyBeatmapEncoder(beatmapToUpdate).Encode(writer); } stream.Seek(0, SeekOrigin.Begin); - using (var reader = new UpdateArchiveReader(manager.Files.Store, setToUpdate, fileToUpdate, stream)) - await manager.Import(setToUpdate, reader); + manager.Update(setToUpdate); + manager.UpdateFile(fileToUpdate, stream); } - var allBeatmaps = manager.GetAllUsableBeatmapSets(); + Beatmap updatedBeatmap = (Beatmap)manager.GetWorkingBeatmap(manager.QueryBeatmap(b => b.ID == beatmapToUpdate.BeatmapInfo.ID)).Beatmap; + + Assert.That(updatedBeatmap.BeatmapInfo.Version, Is.EqualTo("updated")); + Assert.That(updatedBeatmap.HitObjects.Count, Is.EqualTo(1)); + Assert.That(updatedBeatmap.HitObjects[0].StartTime, Is.EqualTo(5000)); } finally { diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 3838fca282..af21567610 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -261,18 +261,24 @@ namespace osu.Game.Database /// /// In the case of no matching files, a hash will be generated from the passed archive's . /// - private string computeHash(ArchiveReader reader) + private string computeHash(TModel item, ArchiveReader reader = null) { // for now, concatenate all .osu files in the set to create a unique hash. MemoryStream hashable = new MemoryStream(); - foreach (string file in reader.Filenames.Where(f => HashableFileTypes.Any(f.EndsWith))) + foreach (TFileModel file in item.Files.Where(f => HashableFileTypes.Any(f.Filename.EndsWith))) { - using (Stream s = reader.GetStream(file)) + using (Stream s = Files.Store.GetStream(file.FileInfo.StoragePath)) s.CopyTo(hashable); } - return hashable.Length > 0 ? hashable.ComputeSHA2Hash() : reader.Name.ComputeSHA2Hash(); + if (hashable.Length > 0) + return hashable.ComputeSHA2Hash(); + + if (reader != null) + return reader.Name.ComputeSHA2Hash(); + + return item.Hash; } /// @@ -302,7 +308,7 @@ namespace osu.Game.Database LogForModel(item, "Beginning import..."); item.Files = archive != null ? createFileInfos(archive, Files) : new List(); - item.Hash = archive != null ? computeHash(archive) : item.Hash; + item.Hash = computeHash(item, archive); await Populate(item, archive, cancellationToken); @@ -358,12 +364,37 @@ namespace osu.Game.Database return item; }, cancellationToken, TaskCreationOptions.HideScheduler, import_scheduler).Unwrap(); + public void UpdateFile(TFileModel file, Stream contents) + { + using (ContextFactory.GetForWrite()) // used to share a context for full import. keep in mind this will block all writes. + { + var existingModels = ModelStore.ConsumableItems.Where(b => b.Files.Any(f => f.FileInfoID == file.FileInfoID)).ToList(); + + if (existingModels.Count == 0) + throw new InvalidOperationException($"Cannot update files of models not contained by this {nameof(ArchiveModelManager)}."); + + using (var stream = Files.Storage.GetStream(file.FileInfo.StoragePath, FileAccess.Write, FileMode.Create)) + contents.CopyTo(stream); + + foreach (var model in existingModels) + Update(model); + } + } + /// /// Perform an update of the specified item. - /// TODO: Support file changes. + /// TODO: Support file additions/removals. /// /// The item to update. - public void Update(TModel item) => ModelStore.Update(item); + public void Update(TModel item) + { + using (ContextFactory.GetForWrite()) + { + item.Hash = computeHash(item); + + ModelStore.Update(item); + } + } /// /// Delete an item from the manager. From dce93dfe7c4bcbc4b8d8a4f8b90eceaff0e3a48c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 9 Jan 2020 18:56:45 +0900 Subject: [PATCH 22/43] Split test --- .../Beatmaps/IO/ImportBeatmapTest.cs | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 52a705edfd..3db5a2b459 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -559,9 +559,39 @@ namespace osu.Game.Tests.Beatmaps.IO } [Test] - public async Task TestUpdateFile() + public async Task TestUpdateBeatmapInfoContents() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestUpdateFile))) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestUpdateBeatmapInfoContents))) + { + try + { + var osu = loadOsu(host); + var manager = osu.Dependencies.Get(); + + var temp = TestResources.GetTestBeatmapForImport(); + await osu.Dependencies.Get().Import(temp); + + // Update via the beatmap, not the beatmap info, to ensure correct linking + BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0]; + Beatmap beatmapToUpdate = (Beatmap)manager.GetWorkingBeatmap(setToUpdate.Beatmaps.First(b => b.RulesetID == 0)).Beatmap; + beatmapToUpdate.BeatmapInfo.Version = "updated"; + + manager.Update(setToUpdate); + + BeatmapInfo updatedInfo = manager.QueryBeatmap(b => b.ID == beatmapToUpdate.BeatmapInfo.ID); + Assert.That(updatedInfo.Version, Is.EqualTo("updated")); + } + finally + { + host.Exit(); + } + } + } + + [Test] + public async Task TestUpdateBeatmapFileContents() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestUpdateBeatmapFileContents))) { try { @@ -579,7 +609,6 @@ namespace osu.Game.Tests.Beatmaps.IO { using (var writer = new StreamWriter(stream, leaveOpen: true)) { - beatmapToUpdate.BeatmapInfo.Version = "updated"; beatmapToUpdate.HitObjects.Clear(); beatmapToUpdate.HitObjects.Add(new HitCircle { StartTime = 5000 }); @@ -588,13 +617,11 @@ namespace osu.Game.Tests.Beatmaps.IO stream.Seek(0, SeekOrigin.Begin); - manager.Update(setToUpdate); manager.UpdateFile(fileToUpdate, stream); } Beatmap updatedBeatmap = (Beatmap)manager.GetWorkingBeatmap(manager.QueryBeatmap(b => b.ID == beatmapToUpdate.BeatmapInfo.ID)).Beatmap; - Assert.That(updatedBeatmap.BeatmapInfo.Version, Is.EqualTo("updated")); Assert.That(updatedBeatmap.HitObjects.Count, Is.EqualTo(1)); Assert.That(updatedBeatmap.HitObjects[0].StartTime, Is.EqualTo(5000)); } From 000fc836b2600108aa8d4571f11a04c6d759c9e3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 9 Jan 2020 18:56:53 +0900 Subject: [PATCH 23/43] Remove unused class --- .../Beatmaps/IO/ImportBeatmapTest.cs | 51 ------------------- 1 file changed, 51 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 3db5a2b459..33fdd75de7 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -735,56 +735,5 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.IsTrue(task.Wait(timeout), failureMessage); } - - private class UpdateArchiveReader : ArchiveReader - where TModel : class, IHasFiles - where TFileModel : INamedFileInfo, new() - { - private readonly IResourceStore store; - private readonly TModel modelToUpdate; - private readonly TFileModel fileToUpdate; - private readonly Stream newContents; - - public UpdateArchiveReader(IResourceStore store, TModel modelToUpdate, TFileModel fileToUpdate, Stream newContents) - : base(string.Empty) - { - this.store = store; - this.modelToUpdate = modelToUpdate; - this.fileToUpdate = fileToUpdate; - this.newContents = newContents; - } - - public override Stream GetStream(string name) - { - name = name.ToStandardisedPath(); - - if (name.Contains(fileToUpdate.Filename, StringComparison.Ordinal)) - { - var stream = new MemoryStream(); - - newContents.Seek(0, SeekOrigin.Begin); - newContents.CopyTo(stream); - - stream.Seek(0, SeekOrigin.Begin); - - return stream; - } - - TFileModel existing = modelToUpdate.Files.FirstOrDefault(m => name.Contains(m.Filename, StringComparison.Ordinal)); - - if (!string.IsNullOrEmpty(existing?.FileInfo?.StoragePath)) - return store.GetStream(existing.FileInfo.StoragePath); - - return null; - } - - public override void Dispose() - { - } - - public override IEnumerable Filenames => Enumerable.Empty(); // modelToUpdate.Files.Select(f => f.Filename); - - public override Stream GetUnderlyingStream() => null; - } } } From b592b6d80dc3445c1b0f5d2169c358abac0e912a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 9 Jan 2020 19:42:47 +0900 Subject: [PATCH 24/43] Improve robustness by always creating a new file --- .../Beatmaps/IO/ImportBeatmapTest.cs | 58 ++++++++++++++++--- osu.Game/Database/ArchiveModelManager.cs | 18 +++--- 2 files changed, 57 insertions(+), 19 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 33fdd75de7..47eab01699 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -11,14 +11,10 @@ using NUnit.Framework; using osu.Framework.Platform; using osu.Game.IPC; using osu.Framework.Allocation; -using osu.Framework.Extensions; -using osu.Framework.IO.Stores; using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; -using osu.Game.Database; using osu.Game.IO; -using osu.Game.IO.Archives; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Tests.Resources; using SharpCompress.Archives; @@ -559,9 +555,9 @@ namespace osu.Game.Tests.Beatmaps.IO } [Test] - public async Task TestUpdateBeatmapInfoContents() + public async Task TestUpdateBeatmapInfo() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestUpdateBeatmapInfoContents))) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestUpdateBeatmapInfo))) { try { @@ -589,9 +585,9 @@ namespace osu.Game.Tests.Beatmaps.IO } [Test] - public async Task TestUpdateBeatmapFileContents() + public async Task TestUpdateBeatmapFile() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestUpdateBeatmapFileContents))) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestUpdateBeatmapFile))) { try { @@ -617,7 +613,51 @@ namespace osu.Game.Tests.Beatmaps.IO stream.Seek(0, SeekOrigin.Begin); - manager.UpdateFile(fileToUpdate, stream); + manager.UpdateFile(setToUpdate, fileToUpdate, stream); + } + + Beatmap updatedBeatmap = (Beatmap)manager.GetWorkingBeatmap(manager.QueryBeatmap(b => b.ID == beatmapToUpdate.BeatmapInfo.ID)).Beatmap; + + Assert.That(updatedBeatmap.HitObjects.Count, Is.EqualTo(1)); + Assert.That(updatedBeatmap.HitObjects[0].StartTime, Is.EqualTo(5000)); + } + finally + { + host.Exit(); + } + } + } + + [Test] + public async Task TestAddFile() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestAddFile))) + { + try + { + var osu = loadOsu(host); + var manager = osu.Dependencies.Get(); + + var temp = TestResources.GetTestBeatmapForImport(); + await osu.Dependencies.Get().Import(temp); + + BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0]; + Beatmap beatmapToUpdate = (Beatmap)manager.GetWorkingBeatmap(setToUpdate.Beatmaps.First(b => b.RulesetID == 0)).Beatmap; + BeatmapSetFileInfo fileToUpdate = setToUpdate.Files.First(f => beatmapToUpdate.BeatmapInfo.Path.Contains(f.Filename)); + + using (var stream = new MemoryStream()) + { + using (var writer = new StreamWriter(stream, leaveOpen: true)) + { + beatmapToUpdate.HitObjects.Clear(); + beatmapToUpdate.HitObjects.Add(new HitCircle { StartTime = 5000 }); + + new LegacyBeatmapEncoder(beatmapToUpdate).Encode(writer); + } + + stream.Seek(0, SeekOrigin.Begin); + + manager.UpdateFile(setToUpdate, fileToUpdate, stream); } Beatmap updatedBeatmap = (Beatmap)manager.GetWorkingBeatmap(manager.QueryBeatmap(b => b.ID == beatmapToUpdate.BeatmapInfo.ID)).Beatmap; diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index af21567610..fb64a4da15 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -364,20 +364,18 @@ namespace osu.Game.Database return item; }, cancellationToken, TaskCreationOptions.HideScheduler, import_scheduler).Unwrap(); - public void UpdateFile(TFileModel file, Stream contents) + public void UpdateFile(TModel model, TFileModel file, Stream contents) { using (ContextFactory.GetForWrite()) // used to share a context for full import. keep in mind this will block all writes. { - var existingModels = ModelStore.ConsumableItems.Where(b => b.Files.Any(f => f.FileInfoID == file.FileInfoID)).ToList(); + model.Files.Remove(file); + model.Files.Add(new TFileModel + { + Filename = file.Filename, + FileInfo = Files.Add(contents) + }); - if (existingModels.Count == 0) - throw new InvalidOperationException($"Cannot update files of models not contained by this {nameof(ArchiveModelManager)}."); - - using (var stream = Files.Storage.GetStream(file.FileInfo.StoragePath, FileAccess.Write, FileMode.Create)) - contents.CopyTo(stream); - - foreach (var model in existingModels) - Update(model); + Update(model); } } From 5f5878e37e889a126dc5f3084e123a1c0efe02ce Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 10 Jan 2020 11:55:47 +0900 Subject: [PATCH 25/43] Dereference existing file --- osu.Game/Database/ArchiveModelManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index fb64a4da15..66e1b43e58 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -375,6 +375,8 @@ namespace osu.Game.Database FileInfo = Files.Add(contents) }); + Files.Dereference(file.FileInfo); + Update(model); } } From 524ff419314c45bd2526df614cacee75599ae6b9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 10 Jan 2020 12:39:45 +0900 Subject: [PATCH 26/43] Add more assertions --- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 47eab01699..4a6e1f1caa 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -616,8 +616,15 @@ namespace osu.Game.Tests.Beatmaps.IO manager.UpdateFile(setToUpdate, fileToUpdate, stream); } - Beatmap updatedBeatmap = (Beatmap)manager.GetWorkingBeatmap(manager.QueryBeatmap(b => b.ID == beatmapToUpdate.BeatmapInfo.ID)).Beatmap; + // Check that the old file has been dereferenced + Assert.That(manager.Files.QueryFiles(f => f.ID == fileToUpdate.FileInfoID).SingleOrDefault(), Is.Null); + // Ensure that the old file is deleted upon a cleanup + manager.Files.Cleanup(); + Assert.That(new System.IO.FileInfo(manager.Files.Storage.GetFullPath(fileToUpdate.FileInfo.StoragePath)), Does.Not.Exist); + + // Check that the new file is referenced correctly by attempting a retrieval + Beatmap updatedBeatmap = (Beatmap)manager.GetWorkingBeatmap(manager.QueryBeatmap(b => b.ID == beatmapToUpdate.BeatmapInfo.ID)).Beatmap; Assert.That(updatedBeatmap.HitObjects.Count, Is.EqualTo(1)); Assert.That(updatedBeatmap.HitObjects[0].StartTime, Is.EqualTo(5000)); } From b91bcaccba30db2426a67dc58d110adc9889fb45 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 10 Jan 2020 12:40:07 +0900 Subject: [PATCH 27/43] Remove outdated comment --- osu.Game/Database/ArchiveModelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 66e1b43e58..a4c930d888 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -366,7 +366,7 @@ namespace osu.Game.Database public void UpdateFile(TModel model, TFileModel file, Stream contents) { - using (ContextFactory.GetForWrite()) // used to share a context for full import. keep in mind this will block all writes. + using (ContextFactory.GetForWrite()) { model.Files.Remove(file); model.Files.Add(new TFileModel From a25583219551fa6316cd7d71f8ea2e6fce88f507 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 10 Jan 2020 19:57:34 +0900 Subject: [PATCH 28/43] Add beatmapmanager method for saving --- osu.Game/Beatmaps/BeatmapManager.cs | 22 ++++++++++++++++++++++ osu.Game/Beatmaps/WorkingBeatmap.cs | 17 ----------------- osu.Game/Screens/Edit/Editor.cs | 10 ++++------ 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index a10ad73817..395a72ef18 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Linq.Expressions; +using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; @@ -26,6 +27,7 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; +using Decoder = osu.Game.Beatmaps.Formats.Decoder; namespace osu.Game.Beatmaps { @@ -174,6 +176,26 @@ namespace osu.Game.Beatmaps /// The beatmap difficulty to restore. public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap); + /// + /// Saves an file against a given . + /// + /// The to save the content against. The file referenced by will be replaced. + /// The content to write. + public void Save(BeatmapInfo info, IBeatmap beatmapContent) + { + var setInfo = QueryBeatmapSet(s => s.Beatmaps.Any(b => b.ID == info.ID)); + + using (var stream = new MemoryStream()) + { + using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) + new LegacyBeatmapEncoder(beatmapContent).Encode(sw); + + stream.Seek(0, SeekOrigin.Begin); + + UpdateFile(setInfo, setInfo.Files.Single(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase)), stream); + } + } + private readonly WeakList workingCache = new WeakList(); /// diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 6aba5257f5..05c344b199 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -7,13 +7,11 @@ using osu.Game.Rulesets.Mods; using System; using System.Collections.Generic; using osu.Game.Storyboards; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using osu.Framework.Audio; using osu.Framework.Statistics; -using osu.Game.IO.Serialization; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.UI; @@ -76,21 +74,6 @@ namespace osu.Game.Beatmaps return AudioManager.Tracks.GetVirtual(length); } - /// - /// Saves the . - /// - /// The absolute path of the output file. - public string Save() - { - string directory = Path.Combine(Path.GetTempPath(), @"osu!"); - Directory.CreateDirectory(directory); - - var path = Path.Combine(directory, Guid.NewGuid().ToString().Replace("-", string.Empty) + ".json"); - using (var sw = new StreamWriter(path)) - sw.WriteLine(Beatmap.Serialize()); - return path; - } - /// /// Creates a to convert a for a specified . /// diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 14d69cddd1..ef17e5ebd8 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -44,6 +44,9 @@ namespace osu.Game.Screens.Edit public override bool DisallowExternalBeatmapRulesetChanges => true; + [Resolved] + private BeatmapManager beatmapManager { get; set; } + private Box bottomBackground; private Container screenContainer; @@ -56,7 +59,6 @@ namespace osu.Game.Screens.Edit private EditorBeatmap editorBeatmap; private DependencyContainer dependencies; - private GameHost host; protected override UserActivity InitialActivity => new UserActivity.Editing(Beatmap.Value.BeatmapInfo); @@ -66,8 +68,6 @@ namespace osu.Game.Screens.Edit [BackgroundDependencyLoader] private void load(OsuColour colours, GameHost host) { - this.host = host; - beatDivisor.Value = Beatmap.Value.BeatmapInfo.BeatDivisor; beatDivisor.BindValueChanged(divisor => Beatmap.Value.BeatmapInfo.BeatDivisor = divisor.NewValue); @@ -90,7 +90,7 @@ namespace osu.Game.Screens.Edit if (RuntimeInfo.IsDesktop) { - fileMenuItems.Add(new EditorMenuItem("Export", MenuItemType.Standard, exportBeatmap)); + fileMenuItems.Add(new EditorMenuItem("Export", MenuItemType.Standard, () => beatmapManager.Save(playableBeatmap.BeatmapInfo, editorBeatmap))); fileMenuItems.Add(new EditorMenuItemSpacer()); } @@ -292,8 +292,6 @@ namespace osu.Game.Screens.Edit } } - private void exportBeatmap() => host.OpenFileExternally(Beatmap.Value.Save()); - private void onModeChanged(ValueChangedEvent e) { currentScreen?.Exit(); From 1b3bff6fa5e023c3202a6358fcf57b7cf7328005 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Jan 2020 18:43:06 +0900 Subject: [PATCH 29/43] Fix model file infos not being removed --- osu.Game/Database/ArchiveModelManager.cs | 13 +++++++++---- .../Database/DownloadableArchiveModelManager.cs | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index a4c930d888..be20b2c2e9 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -34,7 +34,7 @@ namespace osu.Game.Database /// The associated file join type. public abstract class ArchiveModelManager : ICanAcceptFiles, IModelManager where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete - where TFileModel : INamedFileInfo, new() + where TFileModel : class, INamedFileInfo, new() { private const int import_queue_request_concurrency = 1; @@ -366,8 +366,15 @@ namespace osu.Game.Database public void UpdateFile(TModel model, TFileModel file, Stream contents) { - using (ContextFactory.GetForWrite()) + using (var usage = ContextFactory.GetForWrite()) { + // Dereference the existing file info, since the file model will be removed. + Files.Dereference(file.FileInfo); + + // Remove the file model. + usage.Context.Set().Remove(file); + + // Add the new file info and containing file model. model.Files.Remove(file); model.Files.Add(new TFileModel { @@ -375,8 +382,6 @@ namespace osu.Game.Database FileInfo = Files.Add(contents) }); - Files.Dereference(file.FileInfo); - Update(model); } } diff --git a/osu.Game/Database/DownloadableArchiveModelManager.cs b/osu.Game/Database/DownloadableArchiveModelManager.cs index 71bf195a8d..4bd9df5f36 100644 --- a/osu.Game/Database/DownloadableArchiveModelManager.cs +++ b/osu.Game/Database/DownloadableArchiveModelManager.cs @@ -21,7 +21,7 @@ namespace osu.Game.Database /// The associated file join type. public abstract class DownloadableArchiveModelManager : ArchiveModelManager, IModelDownloader where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable - where TFileModel : INamedFileInfo, new() + where TFileModel : class, INamedFileInfo, new() { public event Action> DownloadBegan; From ea7510d2f53ed89b55a49fcdf3984fc4e9018bf3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Jan 2020 18:43:29 +0900 Subject: [PATCH 30/43] Rename menu option --- osu.Game/Screens/Edit/Editor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index ef17e5ebd8..e1a6143ba4 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -90,7 +90,7 @@ namespace osu.Game.Screens.Edit if (RuntimeInfo.IsDesktop) { - fileMenuItems.Add(new EditorMenuItem("Export", MenuItemType.Standard, () => beatmapManager.Save(playableBeatmap.BeatmapInfo, editorBeatmap))); + fileMenuItems.Add(new EditorMenuItem("Save", MenuItemType.Standard, () => beatmapManager.Save(playableBeatmap.BeatmapInfo, editorBeatmap))); fileMenuItems.Add(new EditorMenuItemSpacer()); } From 4fe5ea8e92ef2c44be7665f12c193b99bc8f9f49 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Jan 2020 19:03:50 +0900 Subject: [PATCH 31/43] Clear working beatmap cache on save --- osu.Game/Beatmaps/BeatmapManager.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 395a72ef18..eeb1c3478a 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -194,6 +194,10 @@ namespace osu.Game.Beatmaps UpdateFile(setInfo, setInfo.Files.Single(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase)), stream); } + + var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == info.ID); + if (working != null) + workingCache.Remove(working); } private readonly WeakList workingCache = new WeakList(); From b51575b433e89185d6691ddd2c93bef75da451b8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Jan 2020 19:05:52 +0900 Subject: [PATCH 32/43] Add temporary keybind for saving --- osu.Game/Screens/Edit/Editor.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index e1a6143ba4..c6bdd2c39b 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -90,7 +90,7 @@ namespace osu.Game.Screens.Edit if (RuntimeInfo.IsDesktop) { - fileMenuItems.Add(new EditorMenuItem("Save", MenuItemType.Standard, () => beatmapManager.Save(playableBeatmap.BeatmapInfo, editorBeatmap))); + fileMenuItems.Add(new EditorMenuItem("Save", MenuItemType.Standard, saveBeatmap)); fileMenuItems.Add(new EditorMenuItemSpacer()); } @@ -205,6 +205,15 @@ namespace osu.Game.Screens.Edit case Key.Right: seek(e, 1); return true; + + case Key.S: + if (e.ControlPressed) + { + saveBeatmap(); + return true; + } + + break; } return base.OnKeyDown(e); @@ -327,5 +336,7 @@ namespace osu.Game.Screens.Edit else clock.SeekForward(!clock.IsRunning, amount); } + + private void saveBeatmap() => beatmapManager.Save(playableBeatmap.BeatmapInfo, editorBeatmap); } } From 4b8d62800026c0e587d7a5970b76cbd74826d24f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Jan 2020 19:08:47 +0900 Subject: [PATCH 33/43] Remove file addition test --- .../Beatmaps/IO/ImportBeatmapTest.cs | 44 ------------------- 1 file changed, 44 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 4a6e1f1caa..d6499760a7 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -635,50 +635,6 @@ namespace osu.Game.Tests.Beatmaps.IO } } - [Test] - public async Task TestAddFile() - { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestAddFile))) - { - try - { - var osu = loadOsu(host); - var manager = osu.Dependencies.Get(); - - var temp = TestResources.GetTestBeatmapForImport(); - await osu.Dependencies.Get().Import(temp); - - BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0]; - Beatmap beatmapToUpdate = (Beatmap)manager.GetWorkingBeatmap(setToUpdate.Beatmaps.First(b => b.RulesetID == 0)).Beatmap; - BeatmapSetFileInfo fileToUpdate = setToUpdate.Files.First(f => beatmapToUpdate.BeatmapInfo.Path.Contains(f.Filename)); - - using (var stream = new MemoryStream()) - { - using (var writer = new StreamWriter(stream, leaveOpen: true)) - { - beatmapToUpdate.HitObjects.Clear(); - beatmapToUpdate.HitObjects.Add(new HitCircle { StartTime = 5000 }); - - new LegacyBeatmapEncoder(beatmapToUpdate).Encode(writer); - } - - stream.Seek(0, SeekOrigin.Begin); - - manager.UpdateFile(setToUpdate, fileToUpdate, stream); - } - - Beatmap updatedBeatmap = (Beatmap)manager.GetWorkingBeatmap(manager.QueryBeatmap(b => b.ID == beatmapToUpdate.BeatmapInfo.ID)).Beatmap; - - Assert.That(updatedBeatmap.HitObjects.Count, Is.EqualTo(1)); - Assert.That(updatedBeatmap.HitObjects[0].StartTime, Is.EqualTo(5000)); - } - finally - { - host.Exit(); - } - } - } - public static async Task LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false) { var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack); From 0e844b30395e856d25e11997e94e46a27867b3c6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Jan 2020 19:23:34 +0900 Subject: [PATCH 34/43] Cleanup / re-protect file store --- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 8 ++------ osu.Game/Database/ArchiveModelManager.cs | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index d6499760a7..fb7164062c 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -616,12 +616,8 @@ namespace osu.Game.Tests.Beatmaps.IO manager.UpdateFile(setToUpdate, fileToUpdate, stream); } - // Check that the old file has been dereferenced - Assert.That(manager.Files.QueryFiles(f => f.ID == fileToUpdate.FileInfoID).SingleOrDefault(), Is.Null); - - // Ensure that the old file is deleted upon a cleanup - manager.Files.Cleanup(); - Assert.That(new System.IO.FileInfo(manager.Files.Storage.GetFullPath(fileToUpdate.FileInfo.StoragePath)), Does.Not.Exist); + // Check that the old file reference has been removed + Assert.That(manager.QueryBeatmapSet(s => s.ID == setToUpdate.ID).Files.All(f => f.ID != fileToUpdate.ID)); // Check that the new file is referenced correctly by attempting a retrieval Beatmap updatedBeatmap = (Beatmap)manager.GetWorkingBeatmap(manager.QueryBeatmap(b => b.ID == beatmapToUpdate.BeatmapInfo.ID)).Beatmap; diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index be20b2c2e9..5e237d2ecb 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -68,7 +68,7 @@ namespace osu.Game.Database public virtual bool SupportsImportFromStable => RuntimeInfo.IsDesktop; - public readonly FileStore Files; + protected readonly FileStore Files; protected readonly IDatabaseContextFactory ContextFactory; From a86fb2113e66b9915de13b562c041a6f90a5ec8a Mon Sep 17 00:00:00 2001 From: Revel Date: Tue, 14 Jan 2020 14:26:54 -0500 Subject: [PATCH 35/43] fix tooltip capitalization --- osu.Game/Graphics/Containers/LinkFlowContainer.cs | 2 +- osu.Game/Graphics/UserInterface/ExternalLinkButton.cs | 2 +- osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs | 2 +- .../Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs | 2 +- osu.Game/Overlays/Chat/ChatLine.cs | 2 +- osu.Game/Overlays/Direct/PanelDownloadButton.cs | 2 +- osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs | 2 +- .../Overlays/Profile/Header/Components/LevelProgressBar.cs | 2 +- .../Overlays/Settings/Sections/Input/KeyboardSettings.cs | 2 +- osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs | 2 +- osu.Game/Overlays/Settings/SettingsItem.cs | 2 +- osu.Game/Screens/Ranking/Pages/ReplayDownloadButton.cs | 6 +++--- osu.Game/Screens/Ranking/Pages/RetryButton.cs | 2 +- osu.Game/Users/Drawables/DrawableAvatar.cs | 2 +- osu.Game/Users/UserPanel.cs | 2 +- 15 files changed, 17 insertions(+), 17 deletions(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 9735f6373d..e3a9a5fe9d 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -63,7 +63,7 @@ namespace osu.Game.Graphics.Containers } public void AddUserLink(User user, Action creationParameters = null) - => createLink(AddText(user.Username, creationParameters), new LinkDetails(LinkAction.OpenUserProfile, user.Id.ToString()), "View Profile"); + => createLink(AddText(user.Username, creationParameters), new LinkDetails(LinkAction.OpenUserProfile, user.Id.ToString()), "view profile"); private void createLink(IEnumerable drawables, LinkDetails link, string tooltipText, Action action = null) { diff --git a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs index 8c00cae08a..7225dbc66f 100644 --- a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs +++ b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs @@ -57,6 +57,6 @@ namespace osu.Game.Graphics.UserInterface return true; } - public string TooltipText => "View in browser"; + public string TooltipText => "view in browser"; } } diff --git a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs index 418ad038f7..e4a4b1c50e 100644 --- a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs +++ b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs @@ -104,7 +104,7 @@ namespace osu.Game.Graphics.UserInterface private class CapsWarning : SpriteIcon, IHasTooltip { - public string TooltipText => @"Caps lock is active"; + public string TooltipText => @"caps lock is active"; public CapsWarning() { diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs index fe10287491..e0360c6312 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons { private readonly bool noVideo; - public string TooltipText => button.Enabled.Value ? "Download this beatmap" : "Login to download"; + public string TooltipText => button.Enabled.Value ? "download this beatmap" : "login to download"; private readonly IBindable localUser = new Bindable(); diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index 8abde8a24f..1493d11950 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -236,7 +236,7 @@ namespace osu.Game.Overlays.Chat { List items = new List { - new OsuMenuItem("View Profile", MenuItemType.Highlighted, Action) + new OsuMenuItem("view profile", MenuItemType.Highlighted, Action) }; if (sender.Id != api.LocalUser.Value.Id) diff --git a/osu.Game/Overlays/Direct/PanelDownloadButton.cs b/osu.Game/Overlays/Direct/PanelDownloadButton.cs index 4037cd46f3..ed44f1e960 100644 --- a/osu.Game/Overlays/Direct/PanelDownloadButton.cs +++ b/osu.Game/Overlays/Direct/PanelDownloadButton.cs @@ -48,7 +48,7 @@ namespace osu.Game.Overlays.Direct if (BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false) { button.Enabled.Value = false; - button.TooltipText = "This beatmap is currently not available for download."; + button.TooltipText = "this beatmap is currently not available for download."; return; } diff --git a/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs b/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs index 8069937810..29471375b5 100644 --- a/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs +++ b/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Profile.Header.Components public LevelBadge() { - TooltipText = "Level"; + TooltipText = "level"; } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs index 6a6532764f..a73ce56a2b 100644 --- a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs +++ b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Profile.Header.Components public LevelProgressBar() { - TooltipText = "Progress to next level"; + TooltipText = "progress to next level"; } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs index 55c7210d6c..db6f24a954 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input new SettingsButton { Text = "Key configuration", - TooltipText = "Change global shortcut keys and gameplay bindings", + TooltipText = "change global shortcut keys and gameplay bindings", Action = keyConfig.ToggleVisibility }, }; diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index 4f2f3dfd1a..59d39a1c3c 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -87,7 +87,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input private class SensitivitySlider : OsuSliderBar { - public override string TooltipText => Current.Disabled ? "Enable raw input to adjust sensitivity" : $"{base.TooltipText}x"; + public override string TooltipText => Current.Disabled ? "enable raw input to adjust sensitivity" : $"{base.TooltipText}x"; } } } diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 55e1937d83..e89f2adf0b 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -161,7 +161,7 @@ namespace osu.Game.Overlays.Settings UpdateState(); } - public string TooltipText => "Revert to default"; + public string TooltipText => "revert to default"; protected override bool OnClick(ClickEvent e) { diff --git a/osu.Game/Screens/Ranking/Pages/ReplayDownloadButton.cs b/osu.Game/Screens/Ranking/Pages/ReplayDownloadButton.cs index 9cc6ea2628..62213720aa 100644 --- a/osu.Game/Screens/Ranking/Pages/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Ranking/Pages/ReplayDownloadButton.cs @@ -74,15 +74,15 @@ namespace osu.Game.Screens.Ranking.Pages switch (replayAvailability) { case ReplayAvailability.Local: - button.TooltipText = @"Watch replay"; + button.TooltipText = @"watch replay"; break; case ReplayAvailability.Online: - button.TooltipText = @"Download replay"; + button.TooltipText = @"download replay"; break; default: - button.TooltipText = @"Replay unavailable"; + button.TooltipText = @"replay unavailable"; break; } }, true); diff --git a/osu.Game/Screens/Ranking/Pages/RetryButton.cs b/osu.Game/Screens/Ranking/Pages/RetryButton.cs index 2a281224c1..06d0440b30 100644 --- a/osu.Game/Screens/Ranking/Pages/RetryButton.cs +++ b/osu.Game/Screens/Ranking/Pages/RetryButton.cs @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Ranking.Pages }, }; - TooltipText = "Retry"; + TooltipText = "retry"; } [BackgroundDependencyLoader] diff --git a/osu.Game/Users/Drawables/DrawableAvatar.cs b/osu.Game/Users/Drawables/DrawableAvatar.cs index ee9af15863..93136e88a0 100644 --- a/osu.Game/Users/Drawables/DrawableAvatar.cs +++ b/osu.Game/Users/Drawables/DrawableAvatar.cs @@ -74,7 +74,7 @@ namespace osu.Game.Users.Drawables private class ClickableArea : OsuClickableContainer { - public override string TooltipText => Enabled.Value ? @"View Profile" : null; + public override string TooltipText => Enabled.Value ? @"view profile" : null; protected override bool OnClick(ClickEvent e) { diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index 6ddbc13a06..98c088f013 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -247,7 +247,7 @@ namespace osu.Game.Users public MenuItem[] ContextMenuItems => new MenuItem[] { - new OsuMenuItem("View Profile", MenuItemType.Highlighted, ViewProfile), + new OsuMenuItem("view profile", MenuItemType.Highlighted, ViewProfile), }; } } From 443dedfd07dec0b645e4da08aeab670d4954293a Mon Sep 17 00:00:00 2001 From: Revel Date: Tue, 14 Jan 2020 14:54:06 -0500 Subject: [PATCH 36/43] undo menu item capitalization --- osu.Game/Overlays/Chat/ChatLine.cs | 2 +- osu.Game/Users/UserPanel.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index 1493d11950..8abde8a24f 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -236,7 +236,7 @@ namespace osu.Game.Overlays.Chat { List items = new List { - new OsuMenuItem("view profile", MenuItemType.Highlighted, Action) + new OsuMenuItem("View Profile", MenuItemType.Highlighted, Action) }; if (sender.Id != api.LocalUser.Value.Id) diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index 98c088f013..6ddbc13a06 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -247,7 +247,7 @@ namespace osu.Game.Users public MenuItem[] ContextMenuItems => new MenuItem[] { - new OsuMenuItem("view profile", MenuItemType.Highlighted, ViewProfile), + new OsuMenuItem("View Profile", MenuItemType.Highlighted, ViewProfile), }; } } From c25fefa3cab630688e689d5240dc546ada4d1ba8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 15 Jan 2020 13:44:38 +0900 Subject: [PATCH 37/43] Add beatmap package export --- osu.Game/Beatmaps/BeatmapManager.cs | 27 +++++++++++++++++++++++---- osu.Game/Screens/Edit/Editor.cs | 1 + 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index eeb1c3478a..31869f9310 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -28,6 +28,7 @@ using osu.Game.Online.API.Requests; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; using Decoder = osu.Game.Beatmaps.Formats.Decoder; +using ZipArchive = SharpCompress.Archives.Zip.ZipArchive; namespace osu.Game.Beatmaps { @@ -58,14 +59,11 @@ namespace osu.Game.Beatmaps protected override string ImportFromStablePath => "Songs"; private readonly RulesetStore rulesets; - private readonly BeatmapStore beatmaps; - private readonly AudioManager audioManager; - private readonly GameHost host; - private readonly BeatmapUpdateQueue updateQueue; + private readonly Storage exportStorage; public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, AudioManager audioManager, GameHost host = null, WorkingBeatmap defaultBeatmap = null) @@ -82,6 +80,7 @@ namespace osu.Game.Beatmaps beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b); updateQueue = new BeatmapUpdateQueue(api); + exportStorage = storage.GetStorageForDirectory("exports"); } protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) => @@ -200,6 +199,26 @@ namespace osu.Game.Beatmaps workingCache.Remove(working); } + /// + /// Exports a to an .osz package. + /// + /// The to export. + public void Export(BeatmapSetInfo set) + { + var localSet = QueryBeatmapSet(s => s.ID == set.ID); + + using (var archive = ZipArchive.Create()) + { + foreach (var file in localSet.Files) + archive.AddEntry(file.Filename, Files.Storage.GetStream(file.FileInfo.StoragePath)); + + using (var outputStream = exportStorage.GetStream($"{set}.osz", FileAccess.Write, FileMode.Create)) + archive.SaveTo(outputStream); + + exportStorage.OpenInNativeExplorer(); + } + } + private readonly WeakList workingCache = new WeakList(); /// diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index c6bdd2c39b..a6f9963f67 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -91,6 +91,7 @@ namespace osu.Game.Screens.Edit if (RuntimeInfo.IsDesktop) { fileMenuItems.Add(new EditorMenuItem("Save", MenuItemType.Standard, saveBeatmap)); + fileMenuItems.Add(new EditorMenuItem("Export package", MenuItemType.Standard, () => beatmapManager.Export(Beatmap.Value.BeatmapSetInfo))); fileMenuItems.Add(new EditorMenuItemSpacer()); } From 2060be345d43a558926ad0d8edbe41efb423989b Mon Sep 17 00:00:00 2001 From: Joehu Date: Tue, 14 Jan 2020 20:44:55 -0800 Subject: [PATCH 38/43] Move alt pressed condition to top of method --- osu.Game/Screens/Select/BeatmapCarousel.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index da01ac5f95..49c7381f90 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -412,6 +412,9 @@ namespace osu.Game.Screens.Select protected override bool OnKeyDown(KeyDownEvent e) { + if (e.AltPressed) + return base.OnKeyDown(e); + int direction = 0; bool skipDifficulties = false; @@ -436,7 +439,7 @@ namespace osu.Game.Screens.Select break; } - if (direction == 0 || e.AltPressed) + if (direction == 0) return base.OnKeyDown(e); SelectNext(direction, skipDifficulties); From e49701a215d12e1c45fbc59179fce53242eb1a39 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 15 Jan 2020 13:48:28 +0900 Subject: [PATCH 39/43] Save before exporting --- osu.Game/Screens/Edit/Editor.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index a6f9963f67..e212b429b9 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -91,7 +91,7 @@ namespace osu.Game.Screens.Edit if (RuntimeInfo.IsDesktop) { fileMenuItems.Add(new EditorMenuItem("Save", MenuItemType.Standard, saveBeatmap)); - fileMenuItems.Add(new EditorMenuItem("Export package", MenuItemType.Standard, () => beatmapManager.Export(Beatmap.Value.BeatmapSetInfo))); + fileMenuItems.Add(new EditorMenuItem("Export package", MenuItemType.Standard, exportBeatmap)); fileMenuItems.Add(new EditorMenuItemSpacer()); } @@ -339,5 +339,11 @@ namespace osu.Game.Screens.Edit } private void saveBeatmap() => beatmapManager.Save(playableBeatmap.BeatmapInfo, editorBeatmap); + + private void exportBeatmap() + { + saveBeatmap(); + beatmapManager.Export(Beatmap.Value.BeatmapSetInfo); + } } } From b42b23d65bf974d41799bed94fbf3639d8f6232a Mon Sep 17 00:00:00 2001 From: Joehu Date: Tue, 14 Jan 2020 20:52:15 -0800 Subject: [PATCH 40/43] Add comment for alt pressed Taken from OsuScrollContainer --- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 49c7381f90..76ca97d074 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -412,6 +412,8 @@ namespace osu.Game.Screens.Select protected override bool OnKeyDown(KeyDownEvent e) { + // allow for controlling volume when alt is held. + // mostly for compatibility with osu-stable. if (e.AltPressed) return base.OnKeyDown(e); From 363d7d724af0c195159da3b4c01151d11c7225c1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 15 Jan 2020 14:02:29 +0900 Subject: [PATCH 41/43] Use old ctor params --- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index fb7164062c..c1bd73ef05 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -5,6 +5,7 @@ using System; using System.IO; using System.Collections.Generic; using System.Linq; +using System.Text; using System.Threading; using System.Threading.Tasks; using NUnit.Framework; @@ -603,7 +604,7 @@ namespace osu.Game.Tests.Beatmaps.IO using (var stream = new MemoryStream()) { - using (var writer = new StreamWriter(stream, leaveOpen: true)) + using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true)) { beatmapToUpdate.HitObjects.Clear(); beatmapToUpdate.HitObjects.Add(new HitCircle { StartTime = 5000 }); From e88e40eec4edd073ceb9ad9ba6b912785b961f91 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Jan 2020 15:11:36 +0900 Subject: [PATCH 42/43] Expand on comment --- osu.Game/Screens/Select/BeatmapCarousel.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 76ca97d074..4433543ca1 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -413,7 +413,8 @@ namespace osu.Game.Screens.Select protected override bool OnKeyDown(KeyDownEvent e) { // allow for controlling volume when alt is held. - // mostly for compatibility with osu-stable. + // this is required as the VolumeControlReceptor uses OnPressed, which is + // executed after all OnKeyDown events. if (e.AltPressed) return base.OnKeyDown(e); From 0d8b4e7c3e7a51a9223928b2e13eca67f8d8e6f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Jan 2020 12:51:43 +0900 Subject: [PATCH 43/43] Fix user agent missing in registration --- osu.Game/Online/API/APIRequest.cs | 10 ---------- osu.Game/Online/API/OsuWebRequest.cs | 21 +++++++++++++++++++++ osu.Game/Online/API/RegistrationRequest.cs | 3 +-- 3 files changed, 22 insertions(+), 12 deletions(-) create mode 100644 osu.Game/Online/API/OsuWebRequest.cs diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 0956c749a2..6c9356a9b7 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -162,16 +162,6 @@ namespace osu.Game.Online.API [JsonProperty("error")] public string ErrorMessage { get; set; } } - - private class OsuWebRequest : WebRequest - { - public OsuWebRequest(string uri) - : base(uri) - { - } - - protected override string UserAgent => "osu!"; - } } public class APIException : InvalidOperationException diff --git a/osu.Game/Online/API/OsuWebRequest.cs b/osu.Game/Online/API/OsuWebRequest.cs new file mode 100644 index 0000000000..1d27899473 --- /dev/null +++ b/osu.Game/Online/API/OsuWebRequest.cs @@ -0,0 +1,21 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.IO.Network; + +namespace osu.Game.Online.API +{ + public class OsuWebRequest : WebRequest + { + public OsuWebRequest(string uri) + : base(uri) + { + } + + public OsuWebRequest() + { + } + + protected override string UserAgent => "osu!"; + } +} diff --git a/osu.Game/Online/API/RegistrationRequest.cs b/osu.Game/Online/API/RegistrationRequest.cs index 349cd4de0c..f650e5c93b 100644 --- a/osu.Game/Online/API/RegistrationRequest.cs +++ b/osu.Game/Online/API/RegistrationRequest.cs @@ -2,11 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using Newtonsoft.Json; -using osu.Framework.IO.Network; namespace osu.Game.Online.API { - public class RegistrationRequest : WebRequest + public class RegistrationRequest : OsuWebRequest { internal string Username; internal string Email;