From 7311ccabfbb18f9d53b5a695381022b3e05151e1 Mon Sep 17 00:00:00 2001 From: Joehu Date: Fri, 8 Mar 2019 20:27:50 -0800 Subject: [PATCH 01/27] Fix direct panel stats being misaligned by one pixel --- osu.Game/Overlays/Direct/DirectGridPanel.cs | 5 +---- osu.Game/Overlays/Direct/DirectListPanel.cs | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs index 1413f0f885..b35dbde639 100644 --- a/osu.Game/Overlays/Direct/DirectGridPanel.cs +++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs @@ -185,10 +185,7 @@ namespace osu.Game.Overlays.Direct Margin = new MarginPadding { Top = vertical_padding, Right = vertical_padding }, Children = new[] { - new Statistic(FontAwesome.fa_play_circle, SetInfo.OnlineInfo?.PlayCount ?? 0) - { - Margin = new MarginPadding { Right = 1 }, - }, + new Statistic(FontAwesome.fa_play_circle, SetInfo.OnlineInfo?.PlayCount ?? 0), new Statistic(FontAwesome.fa_heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0), }, }, diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs index 01393ad98b..d857a0f042 100644 --- a/osu.Game/Overlays/Direct/DirectListPanel.cs +++ b/osu.Game/Overlays/Direct/DirectListPanel.cs @@ -160,10 +160,7 @@ namespace osu.Game.Overlays.Direct Direction = FillDirection.Vertical, Children = new Drawable[] { - new Statistic(FontAwesome.fa_play_circle, SetInfo.OnlineInfo?.PlayCount ?? 0) - { - Margin = new MarginPadding { Right = 1 }, - }, + new Statistic(FontAwesome.fa_play_circle, SetInfo.OnlineInfo?.PlayCount ?? 0), new Statistic(FontAwesome.fa_heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0), new FillFlowContainer { From 13b3036ec6c6d21b5b2278010ac184efe09f4e8f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Mar 2019 02:45:45 +0900 Subject: [PATCH 02/27] Fix "wind" mods adjusting rate twice This is a hotfix for incorrect framework behaviour. Closes #4442 --- osu.Game/Screens/Play/GameplayClockContainer.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 594df63420..0a6453beeb 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -141,11 +141,10 @@ namespace osu.Game.Screens.Play { if (sourceClock == null) return; - sourceClock.Rate = 1; + sourceClock.Rate = UserPlaybackRate.Value; + foreach (var mod in beatmap.Mods.Value.OfType()) mod.ApplyToClock(sourceClock); - - sourceClock.Rate *= UserPlaybackRate.Value; } } } From cdeaa80fea548a2be72ef7675976e73d0fed9f65 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Mar 2019 11:35:25 +0900 Subject: [PATCH 03/27] Call ResetSpeedAdjustments before adjusting clock --- osu.Game/Screens/Play/GameplayClockContainer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 0a6453beeb..207627140d 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -141,6 +141,7 @@ namespace osu.Game.Screens.Play { if (sourceClock == null) return; + sourceClock.ResetSpeedAdjustments(); sourceClock.Rate = UserPlaybackRate.Value; foreach (var mod in beatmap.Mods.Value.OfType()) From c1a356161f08749b75bd3338206557f296bf1b6e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Mar 2019 18:14:01 +0900 Subject: [PATCH 04/27] Allow use rate adjust to fallback to clock rate adjust --- osu.Game/Screens/Play/GameplayClockContainer.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 207627140d..3c2cec1d94 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Threading.Tasks; using osu.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -142,7 +143,11 @@ namespace osu.Game.Screens.Play if (sourceClock == null) return; sourceClock.ResetSpeedAdjustments(); - sourceClock.Rate = UserPlaybackRate.Value; + + if (sourceClock is IHasTempoAdjust tempo) + tempo.TempoAdjust = UserPlaybackRate.Value; + else + sourceClock.Rate = UserPlaybackRate.Value; foreach (var mod in beatmap.Mods.Value.OfType()) mod.ApplyToClock(sourceClock); From 6832e384a9a63c050b5e66d8b702aa12633ef1e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Mar 2019 18:14:41 +0900 Subject: [PATCH 05/27] Simplify DT/HT/NC/DC --- osu.Game/Rulesets/Mods/ModDaycore.cs | 2 +- osu.Game/Rulesets/Mods/ModDoubleTime.cs | 12 +++++------- osu.Game/Rulesets/Mods/ModHalfTime.cs | 12 +++++------- osu.Game/Rulesets/Mods/ModNightcore.cs | 2 +- osu.Game/Rulesets/Mods/ModTimeAdjust.cs | 24 ++++++++++++++++++++++++ 5 files changed, 36 insertions(+), 16 deletions(-) create mode 100644 osu.Game/Rulesets/Mods/ModTimeAdjust.cs diff --git a/osu.Game/Rulesets/Mods/ModDaycore.cs b/osu.Game/Rulesets/Mods/ModDaycore.cs index 2eea6a237c..dded688e80 100644 --- a/osu.Game/Rulesets/Mods/ModDaycore.cs +++ b/osu.Game/Rulesets/Mods/ModDaycore.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mods public override void ApplyToClock(IAdjustableClock clock) { if (clock is IHasPitchAdjust pitchAdjust) - pitchAdjust.PitchAdjust = 0.75; + pitchAdjust.PitchAdjust *= RateAdjust; else base.ApplyToClock(clock); } diff --git a/osu.Game/Rulesets/Mods/ModDoubleTime.cs b/osu.Game/Rulesets/Mods/ModDoubleTime.cs index e59654c60d..9ea9eb76bc 100644 --- a/osu.Game/Rulesets/Mods/ModDoubleTime.cs +++ b/osu.Game/Rulesets/Mods/ModDoubleTime.cs @@ -2,12 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Framework.Timing; +using System.Linq; using osu.Game.Graphics; namespace osu.Game.Rulesets.Mods { - public abstract class ModDoubleTime : Mod, IApplicableToClock + public abstract class ModDoubleTime : ModTimeAdjust, IApplicableToClock { public override string Name => "Double Time"; public override string Acronym => "DT"; @@ -15,11 +15,9 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.DifficultyIncrease; public override string Description => "Zoooooooooom..."; public override bool Ranked => true; - public override Type[] IncompatibleMods => new[] { typeof(ModHalfTime), typeof(ModTimeRamp) }; - public virtual void ApplyToClock(IAdjustableClock clock) - { - clock.Rate = 1.5; - } + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModHalfTime)).ToArray(); + + protected override double RateAdjust => 1.5; } } diff --git a/osu.Game/Rulesets/Mods/ModHalfTime.cs b/osu.Game/Rulesets/Mods/ModHalfTime.cs index 07cceb6f49..fe26c96214 100644 --- a/osu.Game/Rulesets/Mods/ModHalfTime.cs +++ b/osu.Game/Rulesets/Mods/ModHalfTime.cs @@ -2,12 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Framework.Timing; +using System.Linq; using osu.Game.Graphics; namespace osu.Game.Rulesets.Mods { - public abstract class ModHalfTime : Mod, IApplicableToClock + public abstract class ModHalfTime : ModTimeAdjust, IApplicableToClock { public override string Name => "Half Time"; public override string Acronym => "HT"; @@ -15,11 +15,9 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.DifficultyReduction; public override string Description => "Less zoom..."; public override bool Ranked => true; - public override Type[] IncompatibleMods => new[] { typeof(ModDoubleTime), typeof(ModTimeRamp) }; - public virtual void ApplyToClock(IAdjustableClock clock) - { - clock.Rate = 0.75; - } + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModDoubleTime)).ToArray(); + + protected override double RateAdjust => 0.75; } } diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index e6bd532f72..a689292ed7 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mods public override void ApplyToClock(IAdjustableClock clock) { if (clock is IHasPitchAdjust pitchAdjust) - pitchAdjust.PitchAdjust = 1.5; + pitchAdjust.PitchAdjust *= RateAdjust; else base.ApplyToClock(clock); } diff --git a/osu.Game/Rulesets/Mods/ModTimeAdjust.cs b/osu.Game/Rulesets/Mods/ModTimeAdjust.cs new file mode 100644 index 0000000000..513883f552 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModTimeAdjust.cs @@ -0,0 +1,24 @@ +// 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; +using osu.Framework.Timing; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModTimeAdjust : Mod + { + public override Type[] IncompatibleMods => new[] { typeof(ModTimeRamp) }; + + protected abstract double RateAdjust { get; } + + public virtual void ApplyToClock(IAdjustableClock clock) + { + if (clock is IHasTempoAdjust tempo) + tempo.TempoAdjust *= RateAdjust; + else + clock.Rate *= RateAdjust; + } + } +} From a10cd2288dc4433196a931ee5a85dd4a6a8e909a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Mar 2019 18:15:18 +0900 Subject: [PATCH 06/27] Improve ModRamp's handling of external rate changes --- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 30 ++++++++++++++++++++------- osu.Game/Rulesets/Mods/ModWindDown.cs | 5 +++++ osu.Game/Rulesets/Mods/ModWindUp.cs | 7 ++++++- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index 4a0ed0f9bb..1573418d95 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mods { public abstract class ModTimeRamp : Mod { - public override Type[] IncompatibleMods => new[] { typeof(ModDoubleTime), typeof(ModHalfTime) }; + public override Type[] IncompatibleMods => new[] { typeof(ModTimeAdjust) }; protected abstract double FinalRateAdjustment { get; } } @@ -29,20 +29,26 @@ namespace osu.Game.Rulesets.Mods private IAdjustableClock clock; - private IHasPitchAdjust pitchAdjust; - /// /// The point in the beatmap at which the final ramping rate should be reached. /// private const double final_rate_progress = 0.75f; + /// + /// The adjustment applied on entering this mod's application method. + /// + private double baseAdjust; + public virtual void ApplyToClock(IAdjustableClock clock) { this.clock = clock; - pitchAdjust = (IHasPitchAdjust)clock; - // for preview purposes - pitchAdjust.PitchAdjust = 1.0 + FinalRateAdjustment; + // we capture the adjustment applied before entering our application method. + // this will cover external changes, which should re-fire this method. + baseAdjust = (clock as IHasPitchAdjust)?.PitchAdjust ?? clock.Rate; + + // for preview purposes. during gameplay, Update will overwrite this setting. + applyAdjustment(1); } public virtual void ApplyToBeatmap(Beatmap beatmap) @@ -56,9 +62,17 @@ namespace osu.Game.Rulesets.Mods public virtual void Update(Playfield playfield) { var absRate = Math.Abs(FinalRateAdjustment); - var adjustment = MathHelper.Clamp(absRate * ((clock.CurrentTime - beginRampTime) / finalRateTime), 0, absRate); + applyAdjustment(MathHelper.Clamp(absRate * ((clock.CurrentTime - beginRampTime) / finalRateTime), 0, absRate)); + } - pitchAdjust.PitchAdjust = 1 + Math.Sign(FinalRateAdjustment) * adjustment; + private void applyAdjustment(double adjustment) + { + var localAdjust = 1 + Math.Sign(FinalRateAdjustment) * adjustment; + + if (clock is IHasPitchAdjust tempo) + tempo.PitchAdjust = baseAdjust * localAdjust; + else + clock.Rate *= baseAdjust * localAdjust; } } } diff --git a/osu.Game/Rulesets/Mods/ModWindDown.cs b/osu.Game/Rulesets/Mods/ModWindDown.cs index 646c5c64e4..174070eb85 100644 --- a/osu.Game/Rulesets/Mods/ModWindDown.cs +++ b/osu.Game/Rulesets/Mods/ModWindDown.cs @@ -1,6 +1,8 @@ // 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.Linq; using osu.Game.Graphics; using osu.Game.Rulesets.Objects; @@ -14,6 +16,9 @@ namespace osu.Game.Rulesets.Mods public override string Description => "Sloooow doooown..."; public override FontAwesome Icon => FontAwesome.fa_chevron_circle_down; public override double ScoreMultiplier => 1.0; + protected override double FinalRateAdjustment => -0.25; + + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindUp)).ToArray(); } } diff --git a/osu.Game/Rulesets/Mods/ModWindUp.cs b/osu.Game/Rulesets/Mods/ModWindUp.cs index 9050b5591a..bf9af8a51d 100644 --- a/osu.Game/Rulesets/Mods/ModWindUp.cs +++ b/osu.Game/Rulesets/Mods/ModWindUp.cs @@ -1,6 +1,8 @@ // 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.Linq; using osu.Game.Graphics; using osu.Game.Rulesets.Objects; @@ -14,6 +16,9 @@ namespace osu.Game.Rulesets.Mods public override string Description => "Can you keep up?"; public override FontAwesome Icon => FontAwesome.fa_chevron_circle_up; public override double ScoreMultiplier => 1.0; + protected override double FinalRateAdjustment => 0.5; + + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindDown)).ToArray(); } -} \ No newline at end of file +} From 12f334035fb1b52d3328671ef270f8a591f2b760 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 12 Mar 2019 19:23:24 +0900 Subject: [PATCH 07/27] Create properties for use in DrawableManiaJudgement --- .../UI/DrawableManiaJudgement.cs | 22 +++++++--------- .../Rulesets/Judgements/DrawableJudgement.cs | 26 +++++++++++++------ 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs index 5874bac7f6..3073b1dd19 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs @@ -22,22 +22,20 @@ namespace osu.Game.Rulesets.Mania.UI JudgementText.Font = JudgementText.Font.With(size: 25); } + protected override double FadeInDuration => 50; + + protected override float InitialHitScale => 0.8f; + + protected override double HitFadeOutDuration => 200; + + protected override float HitScaleDuration => 250; + protected override void LoadComplete() { - base.LoadComplete(); - - this.FadeInFromZero(50, Easing.OutQuint); - if (Result.IsHit) - { - JudgementBody.ScaleTo(0.8f); - JudgementBody.ScaleTo(1, 250, Easing.OutElastic); + JudgementBody.Delay(FadeInDuration).ScaleTo(0.75f, HitScaleDuration); - JudgementBody.Delay(50).ScaleTo(0.75f, 250); - this.Delay(50).FadeOut(200); - } - - Expire(); + base.LoadComplete(); } } } diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 0d6e11c649..e25676700d 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -32,6 +32,14 @@ namespace osu.Game.Rulesets.Judgements protected Container JudgementBody; protected SpriteText JudgementText; + protected virtual double FadeInDuration => 100; + + protected virtual float InitialHitScale => 0.9f; + + protected virtual float HitScaleDuration => 500; + + protected virtual double HitFadeOutDuration => 400; + /// /// Creates a drawable which visualises a . /// @@ -65,11 +73,13 @@ namespace osu.Game.Rulesets.Judgements }; } + private const double MISS_ANIMATION_DURATION = 800; + protected override void LoadComplete() { base.LoadComplete(); - this.FadeInFromZero(100, Easing.OutQuint); + this.FadeInFromZero(FadeInDuration, Easing.OutQuint); switch (Result.Type) { @@ -77,18 +87,18 @@ namespace osu.Game.Rulesets.Judgements break; case HitResult.Miss: JudgementBody.ScaleTo(1.6f); - JudgementBody.ScaleTo(1, 100, Easing.In); + JudgementBody.ScaleTo(1, FadeInDuration, Easing.In); - JudgementBody.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint); - JudgementBody.RotateTo(40, 800, Easing.InQuint); + JudgementBody.MoveToOffset(new Vector2(0, 100), MISS_ANIMATION_DURATION, Easing.InQuint); + JudgementBody.RotateTo(40, MISS_ANIMATION_DURATION, Easing.InQuint); - this.Delay(600).FadeOut(200); + this.Delay(500).FadeOut(200); break; default: - JudgementBody.ScaleTo(0.9f); - JudgementBody.ScaleTo(1, 500, Easing.OutElastic); + JudgementBody.ScaleTo(InitialHitScale); + JudgementBody.ScaleTo(1, HitScaleDuration, Easing.OutElastic); - this.Delay(100).FadeOut(400); + this.Delay(FadeInDuration).FadeOut(HitFadeOutDuration); break; } From 9850122245670545d1dde1dd5df132423fb07f2e Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 12 Mar 2019 19:24:46 +0900 Subject: [PATCH 08/27] Fix incorrect delay --- osu.Game/Rulesets/Judgements/DrawableJudgement.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index e25676700d..308a2b00f9 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Judgements JudgementBody.MoveToOffset(new Vector2(0, 100), MISS_ANIMATION_DURATION, Easing.InQuint); JudgementBody.RotateTo(40, MISS_ANIMATION_DURATION, Easing.InQuint); - this.Delay(500).FadeOut(200); + this.Delay(600).FadeOut(200); break; default: JudgementBody.ScaleTo(InitialHitScale); From 3b3b1e51ee43953bf713768ac89cca5e3b6e2d31 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 12 Mar 2019 19:41:33 +0900 Subject: [PATCH 09/27] Move hit animation logic into virtual method --- .../UI/DrawableManiaJudgement.cs | 15 ++++------- .../Rulesets/Judgements/DrawableJudgement.cs | 27 ++++++++++--------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs index 3073b1dd19..8797f014df 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs @@ -24,18 +24,13 @@ namespace osu.Game.Rulesets.Mania.UI protected override double FadeInDuration => 50; - protected override float InitialHitScale => 0.8f; - - protected override double HitFadeOutDuration => 200; - - protected override float HitScaleDuration => 250; - - protected override void LoadComplete() + protected override void ApplyHitAnimations() { - if (Result.IsHit) - JudgementBody.Delay(FadeInDuration).ScaleTo(0.75f, HitScaleDuration); + JudgementBody.ScaleTo(0.8f); + JudgementBody.ScaleTo(1, 250, Easing.OutElastic); - base.LoadComplete(); + JudgementBody.Delay(FadeInDuration).ScaleTo(0.75f, 250); + this.Delay(FadeInDuration).FadeOut(200); } } } diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 308a2b00f9..16e7fd4a15 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -32,14 +32,12 @@ namespace osu.Game.Rulesets.Judgements protected Container JudgementBody; protected SpriteText JudgementText; + /// + /// The amount of time for this judgement to initially fade in. + /// + /// Override this to change fade in times of mode-specific judgements protected virtual double FadeInDuration => 100; - protected virtual float InitialHitScale => 0.9f; - - protected virtual float HitScaleDuration => 500; - - protected virtual double HitFadeOutDuration => 400; - /// /// Creates a drawable which visualises a . /// @@ -73,7 +71,13 @@ namespace osu.Game.Rulesets.Judgements }; } - private const double MISS_ANIMATION_DURATION = 800; + protected virtual void ApplyHitAnimations() + { + JudgementBody.ScaleTo(0.9f); + JudgementBody.ScaleTo(1, 500, Easing.OutElastic); + + this.Delay(FadeInDuration).FadeOut(400); + } protected override void LoadComplete() { @@ -89,16 +93,13 @@ namespace osu.Game.Rulesets.Judgements JudgementBody.ScaleTo(1.6f); JudgementBody.ScaleTo(1, FadeInDuration, Easing.In); - JudgementBody.MoveToOffset(new Vector2(0, 100), MISS_ANIMATION_DURATION, Easing.InQuint); - JudgementBody.RotateTo(40, MISS_ANIMATION_DURATION, Easing.InQuint); + JudgementBody.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint); + JudgementBody.RotateTo(40, 800, Easing.InQuint); this.Delay(600).FadeOut(200); break; default: - JudgementBody.ScaleTo(InitialHitScale); - JudgementBody.ScaleTo(1, HitScaleDuration, Easing.OutElastic); - - this.Delay(FadeInDuration).FadeOut(HitFadeOutDuration); + ApplyHitAnimations(); break; } From 7eb253b6872acc10ad646f1e3d184cef0b51bbf5 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 12 Mar 2019 19:44:53 +0900 Subject: [PATCH 10/27] Fix incorrect scale duration --- osu.Game/Rulesets/Judgements/DrawableJudgement.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 16e7fd4a15..b273faec8f 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Judgements break; case HitResult.Miss: JudgementBody.ScaleTo(1.6f); - JudgementBody.ScaleTo(1, FadeInDuration, Easing.In); + JudgementBody.ScaleTo(1, 100, Easing.In); JudgementBody.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint); JudgementBody.RotateTo(40, 800, Easing.InQuint); From 527b77b07de94ac2477923b5f27bab29ea7e899a Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 12 Mar 2019 19:52:44 +0900 Subject: [PATCH 11/27] Make all inheritors of drawablejudgement use new method --- .../Objects/Drawables/DrawableOsuJudgement.cs | 9 +++------ osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs | 8 +++----- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs index 2512e74da7..938a2293ba 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs @@ -5,7 +5,6 @@ using osu.Framework.Graphics; using osuTK; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -16,12 +15,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { } - protected override void LoadComplete() + protected override void ApplyHitAnimations() { - if (Result.Type != HitResult.Miss) - JudgementText?.TransformSpacingTo(new Vector2(14, 0), 1800, Easing.OutQuint); - - base.LoadComplete(); + JudgementText?.TransformSpacingTo(new Vector2(14, 0), 1800, Easing.OutQuint); + base.ApplyHitAnimations(); } } } diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs index 90841f11f5..943adaed4b 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs @@ -39,12 +39,10 @@ namespace osu.Game.Rulesets.Taiko.UI } } - protected override void LoadComplete() + protected override void ApplyHitAnimations() { - if (Result.IsHit) - this.MoveToY(-100, 500); - - base.LoadComplete(); + this.MoveToY(-100, 500); + base.ApplyHitAnimations(); } } } From cc09ecbfcf27d2d220b8828f527bb1994444c341 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Mar 2019 20:31:15 +0900 Subject: [PATCH 12/27] Increase validation performed on .osu files to avoid hard crashes --- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 4 +- osu.Game/Beatmaps/Formats/LegacyParser.cs | 45 +++++++++++++++++++ .../Objects/Legacy/ConvertHitObjectParser.cs | 35 ++++++++------- 3 files changed, 66 insertions(+), 18 deletions(-) create mode 100644 osu.Game/Beatmaps/Formats/LegacyParser.cs diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 4f19fbd323..5988b05084 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -280,8 +280,8 @@ namespace osu.Game.Beatmaps.Formats { string[] split = line.Split(','); - double time = getOffsetTime(double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo)); - double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo); + double time = getOffsetTime(Parsing.ParseDouble(split[0].Trim())); + double beatLength = Parsing.ParseDouble(split[1].Trim()); double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1; TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple; diff --git a/osu.Game/Beatmaps/Formats/LegacyParser.cs b/osu.Game/Beatmaps/Formats/LegacyParser.cs new file mode 100644 index 0000000000..777a85dc93 --- /dev/null +++ b/osu.Game/Beatmaps/Formats/LegacyParser.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.Globalization; + +namespace osu.Game.Beatmaps.Formats +{ + /// + /// Helper methods to parse from string to number and perform very basic validation. + /// + public static class Parsing + { + public const int MAX_COORDINATE_VALUE = 65536; + + public const double MAX_PARSE_VALUE = int.MaxValue; + + public static double ParseFloat(string input, float parseLimit = (float)MAX_PARSE_VALUE) + { + var output = float.Parse(input, CultureInfo.InvariantCulture); + if (output < -parseLimit) throw new FormatException("Value is too low"); + if (output > parseLimit) throw new FormatException("Value is too high"); + + return output; + } + + public static double ParseDouble(string input, double parseLimit = MAX_PARSE_VALUE) + { + var output = double.Parse(input, CultureInfo.InvariantCulture); + if (output < -parseLimit) throw new FormatException("Value is too low"); + if (output > parseLimit) throw new FormatException("Value is too high"); + + return output; + } + + public static int ParseInt(string input, int parseLimit = (int)MAX_PARSE_VALUE) + { + var output = int.Parse(input, CultureInfo.InvariantCulture); + if (output < -parseLimit) throw new FormatException("Value is too low"); + if (output > parseLimit) throw new FormatException("Value is too high"); + + return output; + } + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index e397139843..4d36b45989 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -5,7 +5,6 @@ using osuTK; using osu.Game.Rulesets.Objects.Types; using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using osu.Game.Beatmaps.Formats; using osu.Game.Audio; @@ -46,9 +45,11 @@ namespace osu.Game.Rulesets.Objects.Legacy { string[] split = text.Split(','); - Vector2 pos = new Vector2((int)Convert.ToSingle(split[0], CultureInfo.InvariantCulture), (int)Convert.ToSingle(split[1], CultureInfo.InvariantCulture)); + Vector2 pos = new Vector2((int)Parsing.ParseFloat(split[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE)); - ConvertHitObjectType type = (ConvertHitObjectType)int.Parse(split[3]); + double startTime = Parsing.ParseDouble(split[2]) + Offset; + + ConvertHitObjectType type = (ConvertHitObjectType)Parsing.ParseInt(split[3]); int comboOffset = (int)(type & ConvertHitObjectType.ComboOffset) >> 4; type &= ~ConvertHitObjectType.ComboOffset; @@ -56,7 +57,7 @@ namespace osu.Game.Rulesets.Objects.Legacy bool combo = type.HasFlag(ConvertHitObjectType.NewCombo); type &= ~ConvertHitObjectType.NewCombo; - var soundType = (LegacySoundType)int.Parse(split[4]); + var soundType = (LegacySoundType)Parsing.ParseInt(split[4]); var bankInfo = new SampleBankInfo(); HitObject result = null; @@ -107,7 +108,7 @@ namespace osu.Game.Rulesets.Objects.Legacy } string[] temp = t.Split(':'); - points[pointIndex++] = new Vector2((int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture), (int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture)) - pos; + points[pointIndex++] = new Vector2((int)Parsing.ParseDouble(temp[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseDouble(temp[1], Parsing.MAX_COORDINATE_VALUE)) - pos; } // osu-stable special-cased colinear perfect curves to a CurveType.Linear @@ -116,7 +117,7 @@ namespace osu.Game.Rulesets.Objects.Legacy if (points.Length == 3 && pathType == PathType.PerfectCurve && isLinear(points)) pathType = PathType.Linear; - int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture); + int repeatCount = Parsing.ParseInt(split[6]); if (repeatCount > 9000) throw new ArgumentOutOfRangeException(nameof(repeatCount), @"Repeat count is way too high"); @@ -125,7 +126,7 @@ namespace osu.Game.Rulesets.Objects.Legacy repeatCount = Math.Max(0, repeatCount - 1); if (split.Length > 7) - length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture); + length = Math.Max(0, Parsing.ParseDouble(split[7])); if (split.Length > 10) readCustomSampleBanks(split[10], bankInfo); @@ -184,7 +185,9 @@ namespace osu.Game.Rulesets.Objects.Legacy } else if (type.HasFlag(ConvertHitObjectType.Spinner)) { - result = CreateSpinner(new Vector2(512, 384) / 2, combo, comboOffset, Convert.ToDouble(split[5], CultureInfo.InvariantCulture) + Offset); + double endTime = Math.Max(startTime, Parsing.ParseDouble(split[5]) + Offset); + + result = CreateSpinner(new Vector2(512, 384) / 2, combo, comboOffset, endTime); if (split.Length > 6) readCustomSampleBanks(split[6], bankInfo); @@ -193,12 +196,12 @@ namespace osu.Game.Rulesets.Objects.Legacy { // Note: Hold is generated by BMS converts - double endTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture); + double endTime = Math.Max(0, Parsing.ParseDouble(split[2])); if (split.Length > 5 && !string.IsNullOrEmpty(split[5])) { string[] ss = split[5].Split(':'); - endTime = Convert.ToDouble(ss[0], CultureInfo.InvariantCulture); + endTime = Parsing.ParseDouble(ss[0]); readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo); } @@ -211,7 +214,7 @@ namespace osu.Game.Rulesets.Objects.Legacy return null; } - result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture) + Offset; + result.StartTime = startTime; if (result.Samples.Count == 0) result.Samples = convertSoundType(soundType, bankInfo); @@ -222,7 +225,7 @@ namespace osu.Game.Rulesets.Objects.Legacy } catch (FormatException) { - throw new FormatException("One or more hit objects were malformed."); + return null; } } @@ -233,8 +236,8 @@ namespace osu.Game.Rulesets.Objects.Legacy string[] split = str.Split(':'); - var bank = (LegacyBeatmapDecoder.LegacySampleBank)int.Parse(split[0]); - var addbank = (LegacyBeatmapDecoder.LegacySampleBank)int.Parse(split[1]); + var bank = (LegacyBeatmapDecoder.LegacySampleBank)Parsing.ParseInt(split[0]); + var addbank = (LegacyBeatmapDecoder.LegacySampleBank)Parsing.ParseInt(split[1]); string stringBank = bank.ToString().ToLowerInvariant(); if (stringBank == @"none") @@ -247,10 +250,10 @@ namespace osu.Game.Rulesets.Objects.Legacy bankInfo.Add = string.IsNullOrEmpty(stringAddBank) ? stringBank : stringAddBank; if (split.Length > 2) - bankInfo.CustomSampleBank = int.Parse(split[2]); + bankInfo.CustomSampleBank = Parsing.ParseInt(split[2]); if (split.Length > 3) - bankInfo.Volume = int.Parse(split[3]); + bankInfo.Volume = Parsing.ParseInt(split[3]); bankInfo.Filename = split.Length > 4 ? split[4] : null; } From 73d9aef5bdf70ee6c359a4909bb70cb29d2302ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Mar 2019 20:38:04 +0900 Subject: [PATCH 13/27] Fix filename --- osu.Game/Beatmaps/Formats/{LegacyParser.cs => Parsing.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename osu.Game/Beatmaps/Formats/{LegacyParser.cs => Parsing.cs} (100%) diff --git a/osu.Game/Beatmaps/Formats/LegacyParser.cs b/osu.Game/Beatmaps/Formats/Parsing.cs similarity index 100% rename from osu.Game/Beatmaps/Formats/LegacyParser.cs rename to osu.Game/Beatmaps/Formats/Parsing.cs From 93b774104afab8644d8736d97549d391b2932f78 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Mar 2019 11:30:33 +0900 Subject: [PATCH 14/27] Fix NaN handling --- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 5 ++++- osu.Game/Beatmaps/Formats/Parsing.cs | 21 ++++++++++++------- .../Objects/Legacy/ConvertHitObjectParser.cs | 6 +++++- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 5988b05084..84bd953068 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -348,7 +348,10 @@ namespace osu.Game.Beatmaps.Formats CustomSampleBank = customSampleBank }); } - catch (FormatException e) + catch (FormatException) + { + } + catch (OverflowException) { } } diff --git a/osu.Game/Beatmaps/Formats/Parsing.cs b/osu.Game/Beatmaps/Formats/Parsing.cs index 777a85dc93..c3efb8c760 100644 --- a/osu.Game/Beatmaps/Formats/Parsing.cs +++ b/osu.Game/Beatmaps/Formats/Parsing.cs @@ -15,11 +15,14 @@ namespace osu.Game.Beatmaps.Formats public const double MAX_PARSE_VALUE = int.MaxValue; - public static double ParseFloat(string input, float parseLimit = (float)MAX_PARSE_VALUE) + public static float ParseFloat(string input, float parseLimit = (float)MAX_PARSE_VALUE) { var output = float.Parse(input, CultureInfo.InvariantCulture); - if (output < -parseLimit) throw new FormatException("Value is too low"); - if (output > parseLimit) throw new FormatException("Value is too high"); + + if (output < -parseLimit) throw new OverflowException("Value is too low"); + if (output > parseLimit) throw new OverflowException("Value is too high"); + + if (float.IsNaN(output)) throw new FormatException("Not a number"); return output; } @@ -27,8 +30,11 @@ namespace osu.Game.Beatmaps.Formats public static double ParseDouble(string input, double parseLimit = MAX_PARSE_VALUE) { var output = double.Parse(input, CultureInfo.InvariantCulture); - if (output < -parseLimit) throw new FormatException("Value is too low"); - if (output > parseLimit) throw new FormatException("Value is too high"); + + if (output < -parseLimit) throw new OverflowException("Value is too low"); + if (output > parseLimit) throw new OverflowException("Value is too high"); + + if (double.IsNaN(output)) throw new FormatException("Not a number"); return output; } @@ -36,8 +42,9 @@ namespace osu.Game.Beatmaps.Formats public static int ParseInt(string input, int parseLimit = (int)MAX_PARSE_VALUE) { var output = int.Parse(input, CultureInfo.InvariantCulture); - if (output < -parseLimit) throw new FormatException("Value is too low"); - if (output > parseLimit) throw new FormatException("Value is too high"); + + if (output < -parseLimit) throw new OverflowException("Value is too low"); + if (output > parseLimit) throw new OverflowException("Value is too high"); return output; } diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 4d36b45989..d738b1028e 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -225,8 +225,12 @@ namespace osu.Game.Rulesets.Objects.Legacy } catch (FormatException) { - return null; } + catch (OverflowException) + { + } + + return null; } private void readCustomSampleBanks(string str, SampleBankInfo bankInfo) From 0241148385c3687e28c717bdc13b772e787e2223 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Mar 2019 11:30:38 +0900 Subject: [PATCH 15/27] Add extensive tests --- .../Beatmaps/Formats/ParsingTest.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 osu.Game.Tests/Beatmaps/Formats/ParsingTest.cs diff --git a/osu.Game.Tests/Beatmaps/Formats/ParsingTest.cs b/osu.Game.Tests/Beatmaps/Formats/ParsingTest.cs new file mode 100644 index 0000000000..b3863bcf44 --- /dev/null +++ b/osu.Game.Tests/Beatmaps/Formats/ParsingTest.cs @@ -0,0 +1,72 @@ +// 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.Globalization; +using NUnit.Framework; +using osu.Game.Beatmaps.Formats; + +namespace osu.Game.Tests.Beatmaps.Formats +{ + [TestFixture] + public class ParsingTest + { + [Test] + public void TestNaNHandling() => allThrow("NaN"); + + [Test] + public void TestBadStringHandling() => allThrow("Random string 123"); + + [TestCase(Parsing.MAX_PARSE_VALUE)] + [TestCase(-1)] + [TestCase(0)] + [TestCase(1)] + [TestCase(-Parsing.MAX_PARSE_VALUE)] + [TestCase(10, 10)] + [TestCase(-10, 10)] + public void TestValidRanges(double input, double limit = Parsing.MAX_PARSE_VALUE) + { + Assert.AreEqual(Parsing.ParseInt((input).ToString(CultureInfo.InvariantCulture), (int)limit), (int)input); + Assert.AreEqual(Parsing.ParseFloat((input).ToString(CultureInfo.InvariantCulture), (float)limit), (float)input); + Assert.AreEqual(Parsing.ParseDouble((input).ToString(CultureInfo.InvariantCulture), limit), input); + } + + [TestCase(double.PositiveInfinity)] + [TestCase(double.NegativeInfinity)] + [TestCase(999999999999)] + [TestCase(Parsing.MAX_PARSE_VALUE * 1.1)] + [TestCase(-Parsing.MAX_PARSE_VALUE * 1.1)] + [TestCase(11, 10)] + [TestCase(-11, 10)] + public void TestOutOfRangeHandling(double input, double limit = Parsing.MAX_PARSE_VALUE) + => allThrow(input.ToString(CultureInfo.InvariantCulture), limit); + + private void allThrow(string input, double limit = Parsing.MAX_PARSE_VALUE) + where T : Exception + { + Assert.Throws(getIntParseException(input) ?? typeof(T), () => Parsing.ParseInt(input, (int)limit)); + Assert.Throws(() => Parsing.ParseFloat(input, (float)limit)); + Assert.Throws(() => Parsing.ParseDouble(input, limit)); + } + + /// + /// may not be able to parse some inputs. + /// In this case we expect to receive the raw parsing exception. + /// + /// The input attempting to be parsed. + /// The type of exception thrown by . Null if no exception is thrown. + private Type getIntParseException(string input) + { + try + { + var _ = int.Parse(input); + } + catch (Exception e) + { + return e.GetType(); + } + + return null; + } + } +} From a35d9ccc091c215e92087273eaf1ae498cc529c1 Mon Sep 17 00:00:00 2001 From: Dan Balasescu <1329837+smoogipoo@users.noreply.github.com> Date: Wed, 13 Mar 2019 12:35:05 +0900 Subject: [PATCH 16/27] Apply clamping suggestion Co-Authored-By: peppy --- osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index d738b1028e..791de05bf7 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -257,7 +257,7 @@ namespace osu.Game.Rulesets.Objects.Legacy bankInfo.CustomSampleBank = Parsing.ParseInt(split[2]); if (split.Length > 3) - bankInfo.Volume = Parsing.ParseInt(split[3]); + bankInfo.Volume = Math.Max(0, Parsing.ParseInt(split[3])); bankInfo.Filename = split.Length > 4 ? split[4] : null; } From f8b5cf8c8e51741a2feb4d4b56f44b43183dcca4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Mar 2019 13:46:41 +0900 Subject: [PATCH 17/27] Add extra clamping of endTime to startTime --- osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 791de05bf7..73b3e823fd 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -196,12 +196,12 @@ namespace osu.Game.Rulesets.Objects.Legacy { // Note: Hold is generated by BMS converts - double endTime = Math.Max(0, Parsing.ParseDouble(split[2])); + double endTime = Math.Max(startTime, Parsing.ParseDouble(split[2])); if (split.Length > 5 && !string.IsNullOrEmpty(split[5])) { string[] ss = split[5].Split(':'); - endTime = Parsing.ParseDouble(ss[0]); + endTime = Math.Max(startTime, Parsing.ParseDouble(ss[0])); readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo); } From f4c505709ab2a9403ce88a5254e2104a9a9c823f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Mar 2019 13:56:31 +0900 Subject: [PATCH 18/27] Use parsing methods in more places Limiting scope to beatmap decoding for this pass. Can expand to skin/storyboard in the future. --- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 61 ++++++++++--------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 84bd953068..9b3607029f 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Globalization; using System.IO; using System.Linq; using osu.Framework.IO.File; @@ -25,7 +24,7 @@ namespace osu.Game.Beatmaps.Formats public static void Register() { - AddDecoder(@"osu file format v", m => new LegacyBeatmapDecoder(int.Parse(m.Split('v').Last()))); + AddDecoder(@"osu file format v", m => new LegacyBeatmapDecoder(Parsing.ParseInt(m.Split('v').Last()))); } /// @@ -104,25 +103,25 @@ namespace osu.Game.Beatmaps.Formats metadata.AudioFile = FileSafety.PathStandardise(pair.Value); break; case @"AudioLeadIn": - beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value); + beatmap.BeatmapInfo.AudioLeadIn = Parsing.ParseInt(pair.Value); break; case @"PreviewTime": - metadata.PreviewTime = getOffsetTime(int.Parse(pair.Value)); + metadata.PreviewTime = getOffsetTime(Parsing.ParseInt(pair.Value)); break; case @"Countdown": - beatmap.BeatmapInfo.Countdown = int.Parse(pair.Value) == 1; + beatmap.BeatmapInfo.Countdown = Parsing.ParseInt(pair.Value) == 1; break; case @"SampleSet": defaultSampleBank = (LegacySampleBank)Enum.Parse(typeof(LegacySampleBank), pair.Value); break; case @"SampleVolume": - defaultSampleVolume = int.Parse(pair.Value); + defaultSampleVolume = Parsing.ParseInt(pair.Value); break; case @"StackLeniency": - beatmap.BeatmapInfo.StackLeniency = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + beatmap.BeatmapInfo.StackLeniency = Parsing.ParseFloat(pair.Value); break; case @"Mode": - beatmap.BeatmapInfo.RulesetID = int.Parse(pair.Value); + beatmap.BeatmapInfo.RulesetID = Parsing.ParseInt(pair.Value); switch (beatmap.BeatmapInfo.RulesetID) { @@ -142,13 +141,13 @@ namespace osu.Game.Beatmaps.Formats break; case @"LetterboxInBreaks": - beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(pair.Value) == 1; + beatmap.BeatmapInfo.LetterboxInBreaks = Parsing.ParseInt(pair.Value) == 1; break; case @"SpecialStyle": - beatmap.BeatmapInfo.SpecialStyle = int.Parse(pair.Value) == 1; + beatmap.BeatmapInfo.SpecialStyle = Parsing.ParseInt(pair.Value) == 1; break; case @"WidescreenStoryboard": - beatmap.BeatmapInfo.WidescreenStoryboard = int.Parse(pair.Value) == 1; + beatmap.BeatmapInfo.WidescreenStoryboard = Parsing.ParseInt(pair.Value) == 1; break; } } @@ -163,16 +162,16 @@ namespace osu.Game.Beatmaps.Formats beatmap.BeatmapInfo.StoredBookmarks = pair.Value; break; case @"DistanceSpacing": - beatmap.BeatmapInfo.DistanceSpacing = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + beatmap.BeatmapInfo.DistanceSpacing = Math.Max(0, Parsing.ParseDouble(pair.Value)); break; case @"BeatDivisor": - beatmap.BeatmapInfo.BeatDivisor = int.Parse(pair.Value); + beatmap.BeatmapInfo.BeatDivisor = Parsing.ParseInt(pair.Value); break; case @"GridSize": - beatmap.BeatmapInfo.GridSize = int.Parse(pair.Value); + beatmap.BeatmapInfo.GridSize = Parsing.ParseInt(pair.Value); break; case @"TimelineZoom": - beatmap.BeatmapInfo.TimelineZoom = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + beatmap.BeatmapInfo.TimelineZoom = Math.Max(0, Parsing.ParseDouble(pair.Value)); break; } } @@ -209,10 +208,10 @@ namespace osu.Game.Beatmaps.Formats beatmap.BeatmapInfo.Metadata.Tags = pair.Value; break; case @"BeatmapID": - beatmap.BeatmapInfo.OnlineBeatmapID = int.Parse(pair.Value); + beatmap.BeatmapInfo.OnlineBeatmapID = Parsing.ParseInt(pair.Value); break; case @"BeatmapSetID": - beatmap.BeatmapInfo.BeatmapSet = new BeatmapSetInfo { OnlineBeatmapSetID = int.Parse(pair.Value) }; + beatmap.BeatmapInfo.BeatmapSet = new BeatmapSetInfo { OnlineBeatmapSetID = Parsing.ParseInt(pair.Value) }; break; } } @@ -225,22 +224,22 @@ namespace osu.Game.Beatmaps.Formats switch (pair.Key) { case @"HPDrainRate": - difficulty.DrainRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + difficulty.DrainRate = Parsing.ParseFloat(pair.Value); break; case @"CircleSize": - difficulty.CircleSize = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + difficulty.CircleSize = Parsing.ParseFloat(pair.Value); break; case @"OverallDifficulty": - difficulty.OverallDifficulty = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + difficulty.OverallDifficulty = Parsing.ParseFloat(pair.Value); break; case @"ApproachRate": - difficulty.ApproachRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + difficulty.ApproachRate = Parsing.ParseFloat(pair.Value); break; case @"SliderMultiplier": - difficulty.SliderMultiplier = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + difficulty.SliderMultiplier = Parsing.ParseDouble(pair.Value); break; case @"SliderTickRate": - difficulty.SliderTickRate = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + difficulty.SliderTickRate = Parsing.ParseDouble(pair.Value); break; } } @@ -260,10 +259,12 @@ namespace osu.Game.Beatmaps.Formats beatmap.BeatmapInfo.Metadata.BackgroundFile = FileSafety.PathStandardise(filename); break; case EventType.Break: + double start = getOffsetTime(Parsing.ParseDouble(split[1])); + var breakEvent = new BreakPeriod { - StartTime = getOffsetTime(double.Parse(split[1], NumberFormatInfo.InvariantInfo)), - EndTime = getOffsetTime(double.Parse(split[2], NumberFormatInfo.InvariantInfo)) + StartTime = start, + EndTime = Math.Max(start, getOffsetTime(Parsing.ParseDouble(split[1]))) }; if (!breakEvent.HasEffect) @@ -286,19 +287,19 @@ namespace osu.Game.Beatmaps.Formats TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple; if (split.Length >= 3) - timeSignature = split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)int.Parse(split[2]); + timeSignature = split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)Parsing.ParseInt(split[2]); LegacySampleBank sampleSet = defaultSampleBank; if (split.Length >= 4) - sampleSet = (LegacySampleBank)int.Parse(split[3]); + sampleSet = (LegacySampleBank)Parsing.ParseInt(split[3]); int customSampleBank = 0; if (split.Length >= 5) - customSampleBank = int.Parse(split[4]); + customSampleBank = Parsing.ParseInt(split[4]); int sampleVolume = defaultSampleVolume; if (split.Length >= 6) - sampleVolume = int.Parse(split[5]); + sampleVolume = Parsing.ParseInt(split[5]); bool timingChange = true; if (split.Length >= 7) @@ -308,7 +309,7 @@ namespace osu.Game.Beatmaps.Formats bool omitFirstBarSignature = false; if (split.Length >= 8) { - EffectFlags effectFlags = (EffectFlags)int.Parse(split[7]); + EffectFlags effectFlags = (EffectFlags)Parsing.ParseInt(split[7]); kiaiMode = effectFlags.HasFlag(EffectFlags.Kiai); omitFirstBarSignature = effectFlags.HasFlag(EffectFlags.OmitFirstBarLine); } From 241e336c212ae097511b4bb93f2c62cd82fb691a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Mar 2019 14:22:16 +0900 Subject: [PATCH 19/27] Fix break parsing --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 9b3607029f..d51bb8d6ca 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -264,7 +264,7 @@ namespace osu.Game.Beatmaps.Formats var breakEvent = new BreakPeriod { StartTime = start, - EndTime = Math.Max(start, getOffsetTime(Parsing.ParseDouble(split[1]))) + EndTime = Math.Max(start, getOffsetTime(Parsing.ParseDouble(split[2]))) }; if (!breakEvent.HasEffect) From 8f00f2290a1ffb9591521affbc076dc9d958d15f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Mar 2019 14:56:41 +0900 Subject: [PATCH 20/27] Log issues --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 3 +++ osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs | 2 ++ 2 files changed, 5 insertions(+) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index d51bb8d6ca..a27126ad9c 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -5,6 +5,7 @@ using System; using System.IO; using System.Linq; using osu.Framework.IO.File; +using osu.Framework.Logging; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Beatmaps.ControlPoints; @@ -351,9 +352,11 @@ namespace osu.Game.Beatmaps.Formats } catch (FormatException) { + Logger.Log("A timing point could not be parsed correctly and will be ignored", LoggingTarget.Runtime, LogLevel.Important); } catch (OverflowException) { + Logger.Log("A timing point could not be parsed correctly and will be ignored", LoggingTarget.Runtime, LogLevel.Important); } } diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 73b3e823fd..8d6bb8bd3f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -225,9 +225,11 @@ namespace osu.Game.Rulesets.Objects.Legacy } catch (FormatException) { + Logger.Log("A hitobject could not be parsed correctly and will be ignored", LoggingTarget.Runtime, LogLevel.Important); } catch (OverflowException) { + Logger.Log("A hitobject could not be parsed correctly and will be ignored", LoggingTarget.Runtime, LogLevel.Important); } return null; From 3d4a80957fc52198c1225ad64acb8b5ba1fd6d00 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Mar 2019 18:27:54 +0900 Subject: [PATCH 21/27] Improve comment --- osu.Game/Rulesets/Judgements/DrawableJudgement.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index b273faec8f..89db954c36 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -33,9 +33,9 @@ namespace osu.Game.Rulesets.Judgements protected SpriteText JudgementText; /// - /// The amount of time for this judgement to initially fade in. + /// Duration of initial fade in. + /// Default fade out will start immediately after this duration. /// - /// Override this to change fade in times of mode-specific judgements protected virtual double FadeInDuration => 100; /// From 5d23a966d71984f170fc20fad3932829020e4e52 Mon Sep 17 00:00:00 2001 From: Dan Balasescu <1329837+smoogipoo@users.noreply.github.com> Date: Wed, 13 Mar 2019 20:55:33 +0900 Subject: [PATCH 22/27] Fix incorrect application logic for rate fallback Co-Authored-By: peppy --- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index 1573418d95..0fadbd1d39 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Mods if (clock is IHasPitchAdjust tempo) tempo.PitchAdjust = baseAdjust * localAdjust; else - clock.Rate *= baseAdjust * localAdjust; + clock.Rate = baseAdjust * localAdjust; } } } From c40e24739b2040a62035021786226e26b9cdfaf0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Mar 2019 21:37:33 +0900 Subject: [PATCH 23/27] Fix weird application logic --- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index 0fadbd1d39..082f0091c6 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -62,12 +62,14 @@ namespace osu.Game.Rulesets.Mods public virtual void Update(Playfield playfield) { var absRate = Math.Abs(FinalRateAdjustment); - applyAdjustment(MathHelper.Clamp(absRate * ((clock.CurrentTime - beginRampTime) / finalRateTime), 0, absRate)); + applyAdjustment((clock.CurrentTime - beginRampTime) / finalRateTime); } - private void applyAdjustment(double adjustment) + private void applyAdjustment(double adjustAmount) { - var localAdjust = 1 + Math.Sign(FinalRateAdjustment) * adjustment; + adjustAmount = MathHelper.Clamp(adjustAmount, 0, 1); + + var localAdjust = 1 + Math.Sign(FinalRateAdjustment) * adjustAmount * Math.Abs(FinalRateAdjustment); if (clock is IHasPitchAdjust tempo) tempo.PitchAdjust = baseAdjust * localAdjust; From 108dcd0a92e61a281e4bd7308e7b4c3e55e0c66f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Mar 2019 21:58:50 +0900 Subject: [PATCH 24/27] Update framework --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 2e945c212d..e2c96effb6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -16,7 +16,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index b25e2a8bb2..288630d1ec 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From 85c518f146f4b8ff1c8bbd7971d0023545825701 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Mar 2019 12:29:16 +0900 Subject: [PATCH 25/27] Change logic to better handle external adjustments --- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 43 +++++++++++++++++---------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index 082f0091c6..8cd4b28ce3 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -34,18 +34,11 @@ namespace osu.Game.Rulesets.Mods /// private const double final_rate_progress = 0.75f; - /// - /// The adjustment applied on entering this mod's application method. - /// - private double baseAdjust; - public virtual void ApplyToClock(IAdjustableClock clock) { this.clock = clock; - // we capture the adjustment applied before entering our application method. - // this will cover external changes, which should re-fire this method. - baseAdjust = (clock as IHasPitchAdjust)?.PitchAdjust ?? clock.Rate; + lastAdjust = 1; // for preview purposes. during gameplay, Update will overwrite this setting. applyAdjustment(1); @@ -61,20 +54,38 @@ namespace osu.Game.Rulesets.Mods public virtual void Update(Playfield playfield) { - var absRate = Math.Abs(FinalRateAdjustment); applyAdjustment((clock.CurrentTime - beginRampTime) / finalRateTime); } - private void applyAdjustment(double adjustAmount) + private double lastAdjust = 1; + + private bool reported; + + /// + /// Adjust the rate along the specified ramp + /// + /// The amount of adjustment to apply (from 0..1). + private void applyAdjustment(double amount) { - adjustAmount = MathHelper.Clamp(adjustAmount, 0, 1); + double adjust = 1 + (Math.Sign(FinalRateAdjustment) * MathHelper.Clamp(amount, 0, 1) * Math.Abs(FinalRateAdjustment)); - var localAdjust = 1 + Math.Sign(FinalRateAdjustment) * adjustAmount * Math.Abs(FinalRateAdjustment); + switch (clock) + { + case IHasPitchAdjust pitch: + pitch.PitchAdjust /= lastAdjust; + pitch.PitchAdjust *= adjust; + break; + case IHasTempoAdjust tempo: + tempo.TempoAdjust /= lastAdjust; + tempo.TempoAdjust *= adjust; + break; + default: + clock.Rate /= lastAdjust; + clock.Rate *= adjust; + break; + } - if (clock is IHasPitchAdjust tempo) - tempo.PitchAdjust = baseAdjust * localAdjust; - else - clock.Rate = baseAdjust * localAdjust; + lastAdjust = adjust; } } } From 7d56ce63d38433541f12932b2b015d9f6f059615 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Mar 2019 12:31:17 +0900 Subject: [PATCH 26/27] Fix test case failures --- osu.Game/Tests/Visual/RateAdjustedBeatmapTestCase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/RateAdjustedBeatmapTestCase.cs b/osu.Game/Tests/Visual/RateAdjustedBeatmapTestCase.cs index 14b4af1e7d..3b3adb4d76 100644 --- a/osu.Game/Tests/Visual/RateAdjustedBeatmapTestCase.cs +++ b/osu.Game/Tests/Visual/RateAdjustedBeatmapTestCase.cs @@ -13,7 +13,7 @@ namespace osu.Game.Tests.Visual base.Update(); // note that this will override any mod rate application - Beatmap.Value.Track.Rate = Clock.Rate; + Beatmap.Value.Track.TempoAdjust = Clock.Rate; } } } From 2815c3b07cc296b8c13c9d8c0006f4e1ae75aee7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 14 Mar 2019 15:12:26 +0900 Subject: [PATCH 27/27] Remove unused variable --- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index 8cd4b28ce3..62407907c1 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -59,8 +59,6 @@ namespace osu.Game.Rulesets.Mods private double lastAdjust = 1; - private bool reported; - /// /// Adjust the rate along the specified ramp ///