From e5e454ddcda5dda65221723e75116cd56dcc08e8 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 7 Mar 2019 16:17:12 +0900 Subject: [PATCH 01/50] Don't perform lookup of beatmap stats unless an online id is present --- osu.Game/Screens/Select/BeatmapDetails.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 604d7a132b..d9334d65ac 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -175,7 +175,7 @@ namespace osu.Game.Screens.Select private void updateStatistics() { - if (Beatmap == null) + if (Beatmap?.OnlineBeatmapID == null) { clearStats(); return; From 054db480897a6fbea1a41f6ec90c5a7c9f3435c9 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 7 Mar 2019 16:59:43 +0900 Subject: [PATCH 02/50] Move online id null check to only bypass metrics lookup --- osu.Game/Screens/Select/BeatmapDetails.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index d9334d65ac..07db073edb 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -175,7 +175,7 @@ namespace osu.Game.Screens.Select private void updateStatistics() { - if (Beatmap?.OnlineBeatmapID == null) + if (Beatmap == null) { clearStats(); return; @@ -188,7 +188,7 @@ namespace osu.Game.Screens.Select tags.Text = Beatmap.Metadata.Tags; var requestedBeatmap = Beatmap; - if (requestedBeatmap.Metrics == null) + if (requestedBeatmap.Metrics == null && requestedBeatmap.OnlineBeatmapID != null) { var lookup = new GetBeatmapDetailsRequest(requestedBeatmap); lookup.Success += res => From 800007c3782b11ebce37a0ac695e763345b9a84e Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 8 Mar 2019 18:17:50 +0900 Subject: [PATCH 03/50] Set DummyWorkingBeatmap difficulties to 0 for better fallback display --- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 0aa1697bf8..f9df025be8 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -26,7 +26,12 @@ namespace osu.Game.Beatmaps Title = "no beatmaps available!" }, BeatmapSet = new BeatmapSetInfo(), - BaseDifficulty = new BeatmapDifficulty(), + BaseDifficulty = new BeatmapDifficulty + { + DrainRate = 0, + CircleSize = 0, + OverallDifficulty = 0, + }, Ruleset = new DummyRulesetInfo() }) { From 192c257aac669402ac70bd34f341badaac07ad36 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 8 Mar 2019 18:32:39 +0900 Subject: [PATCH 04/50] Add test steps for BeatmapDetailArea --- .../Visual/TestCaseBeatmapDetailArea.cs | 133 +++++++++++++++++- 1 file changed, 132 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapDetailArea.cs b/osu.Game.Tests/Visual/TestCaseBeatmapDetailArea.cs index c23075a127..ff39ad5c0d 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapDetailArea.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapDetailArea.cs @@ -1,8 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; +using osu.Game.Beatmaps; using osu.Game.Screens.Select; using osuTK; @@ -14,12 +16,141 @@ namespace osu.Game.Tests.Visual { public TestCaseBeatmapDetailArea() { - Add(new BeatmapDetailArea + BeatmapDetailArea detailsArea; + Add(detailsArea = new BeatmapDetailArea { Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(550f, 450f), }); + + AddStep("all metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap + { + BeatmapInfo = + { + Version = "All Metrics", + Metadata = new BeatmapMetadata + { + Source = "osu!lazer", + Tags = "this beatmap has all the metrics", + }, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 7, + DrainRate = 1, + OverallDifficulty = 5.7f, + ApproachRate = 3.5f, + }, + StarDifficulty = 5.3f, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + } + } + ); + + AddStep("all except source", () => detailsArea.Beatmap = new DummyWorkingBeatmap + { + BeatmapInfo = + { + Version = "All Metrics", + Metadata = new BeatmapMetadata + { + Tags = "this beatmap has all the metrics", + }, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 7, + DrainRate = 1, + OverallDifficulty = 5.7f, + ApproachRate = 3.5f, + }, + StarDifficulty = 5.3f, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + } + }); + + AddStep("ratings", () => detailsArea.Beatmap = new DummyWorkingBeatmap + { + BeatmapInfo = + { + Version = "Only Ratings", + Metadata = new BeatmapMetadata + { + Source = "osu!lazer", + Tags = "this beatmap has ratings metrics but not retries or fails", + }, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 6, + DrainRate = 9, + OverallDifficulty = 6, + ApproachRate = 6, + }, + StarDifficulty = 4.8f, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + }, + } + }); + + AddStep("fails+retries", () => detailsArea.Beatmap = new DummyWorkingBeatmap + { + BeatmapInfo = + { + Version = "Only Retries and Fails", + Metadata = new BeatmapMetadata + { + Source = "osu!lazer", + Tags = "this beatmap has retries and fails but no ratings", + }, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 3.7f, + DrainRate = 6, + OverallDifficulty = 6, + ApproachRate = 7, + }, + StarDifficulty = 2.91f, + Metrics = new BeatmapMetrics + { + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + } + }); + + AddStep("null metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap + { + BeatmapInfo = + { + Version = "No Metrics", + Metadata = new BeatmapMetadata + { + Source = "osu!lazer", + Tags = "this beatmap has no metrics", + }, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 5, + DrainRate = 5, + OverallDifficulty = 5.5f, + ApproachRate = 6.5f, + }, + StarDifficulty = 1.97f, + } + }); + + AddStep("null beatmap", () => detailsArea.Beatmap = null); } } } From 8e5816805c11c67a3546952bfbef51a1dcdddfa0 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 8 Mar 2019 18:44:35 +0900 Subject: [PATCH 05/50] Fix showing outdated data for non-online beatmaps --- osu.Game/Screens/Select/BeatmapDetails.cs | 70 +++++++---------------- 1 file changed, 21 insertions(+), 49 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 07db073edb..6057ba382b 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -175,21 +175,21 @@ namespace osu.Game.Screens.Select private void updateStatistics() { + advanced.Beatmap = Beatmap; + description.Text = Beatmap?.Version; + source.Text = Beatmap?.Metadata?.Source; + tags.Text = Beatmap?.Metadata?.Tags; + if (Beatmap == null) { - clearStats(); + ratingsContainer.FadeOut(transition_duration); + failRetryContainer.FadeOut(transition_duration); return; } - ratingsContainer.FadeIn(transition_duration); - advanced.Beatmap = Beatmap; - description.Text = Beatmap.Version; - source.Text = Beatmap.Metadata.Source; - tags.Text = Beatmap.Metadata.Tags; - - var requestedBeatmap = Beatmap; - if (requestedBeatmap.Metrics == null && requestedBeatmap.OnlineBeatmapID != null) + if (Beatmap.Metrics == null && Beatmap.OnlineBeatmapID != null) { + var requestedBeatmap = Beatmap; var lookup = new GetBeatmapDetailsRequest(requestedBeatmap); lookup.Success += res => { @@ -198,39 +198,35 @@ namespace osu.Game.Screens.Select return; requestedBeatmap.Metrics = res; - Schedule(() => displayMetrics(res)); + Schedule(() => updateMetrics(res)); }; - lookup.Failure += e => Schedule(() => displayMetrics(null)); - + lookup.Failure += e => Schedule(() => updateMetrics(null)); api.Queue(lookup); loading.Show(); } - - displayMetrics(requestedBeatmap.Metrics, false); + else + { + updateMetrics(Beatmap.Metrics); + } } - private void displayMetrics(BeatmapMetrics metrics, bool failOnMissing = true) + private void updateMetrics(BeatmapMetrics metrics) { var hasRatings = metrics?.Ratings?.Any() ?? false; var hasRetriesFails = (metrics?.Retries?.Any() ?? false) && (metrics.Fails?.Any() ?? false); - if (failOnMissing) loading.Hide(); - if (hasRatings) { ratings.Metrics = metrics; - ratings.FadeIn(transition_duration); + ratingsContainer.FadeIn(transition_duration); } - else if (failOnMissing) + else { ratings.Metrics = new BeatmapMetrics { Ratings = new int[10], }; - } - else - { - ratings.FadeTo(0.25f, transition_duration); + ratingsContainer.FadeTo(0.25f, transition_duration); } if (hasRetriesFails) @@ -238,41 +234,17 @@ namespace osu.Game.Screens.Select failRetryGraph.Metrics = metrics; failRetryContainer.FadeIn(transition_duration); } - else if (failOnMissing) + else { failRetryGraph.Metrics = new BeatmapMetrics { Fails = new int[100], Retries = new int[100], }; + failRetryContainer.FadeOut(transition_duration); } - else - { - failRetryContainer.FadeTo(0.25f, transition_duration); - } - } - - private void clearStats() - { - description.Text = null; - source.Text = null; - tags.Text = null; - - advanced.Beatmap = new BeatmapInfo - { - StarDifficulty = 0, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 0, - DrainRate = 0, - OverallDifficulty = 0, - ApproachRate = 0, - }, - }; loading.Hide(); - ratingsContainer.FadeOut(transition_duration); - failRetryContainer.FadeOut(transition_duration); } private class DetailBox : Container From 13b3036ec6c6d21b5b2278010ac184efe09f4e8f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Mar 2019 02:45:45 +0900 Subject: [PATCH 06/50] 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 07/50] 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 08/50] 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 09/50] 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 10/50] 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 11/50] 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 12/50] 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 13/50] 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 14/50] 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 15/50] 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 16/50] 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 17/50] 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 18/50] 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 19/50] 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 20/50] 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 f0114d776db14f2c71e89eedefc710e9956921e6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Mar 2019 12:56:47 +0900 Subject: [PATCH 21/50] Use interface to access API Allows for better testability. --- .../Visual/TestCaseMatchLeaderboard.cs | 2 +- ...stCaseUpdateableBeatmapBackgroundSprite.cs | 2 +- osu.Game.Tests/Visual/TestCaseUserProfile.cs | 4 +- osu.Game/Beatmaps/BeatmapManager.cs | 4 +- osu.Game/Online/API/APIAccess.cs | 2 +- osu.Game/Online/API/APIRequest.cs | 11 +++-- osu.Game/Online/API/DummyAPIAccess.cs | 32 +++++++++++++-- osu.Game/Online/API/IAPIProvider.cs | 40 +++++++++++++++++++ osu.Game/Online/API/IOnlineComponent.cs | 2 +- osu.Game/Online/Leaderboards/Leaderboard.cs | 6 +-- osu.Game/OsuGameBase.cs | 1 - .../Overlays/AccountCreation/ScreenEntry.cs | 4 +- .../Overlays/AccountCreation/ScreenWarning.cs | 4 +- osu.Game/Overlays/AccountCreationOverlay.cs | 4 +- .../BeatmapSet/Buttons/DownloadButton.cs | 2 +- .../BeatmapSet/Scores/ScoresContainer.cs | 4 +- osu.Game/Overlays/BeatmapSetOverlay.cs | 4 +- osu.Game/Overlays/DirectOverlay.cs | 4 +- .../Profile/Sections/PaginatedContainer.cs | 4 +- .../Sections/Recent/DrawableRecentActivity.cs | 4 +- .../Sections/General/LoginSettings.cs | 8 ++-- osu.Game/Overlays/SocialOverlay.cs | 6 +-- .../Overlays/Toolbar/ToolbarUserButton.cs | 4 +- osu.Game/Overlays/UserProfileOverlay.cs | 4 +- osu.Game/Screens/Menu/ButtonSystem.cs | 2 +- osu.Game/Screens/Menu/Disclaimer.cs | 4 +- .../Multi/Lounge/Components/RoomInspector.cs | 2 +- osu.Game/Screens/Multi/Multiplayer.cs | 4 +- .../Screens/Multi/Play/TimeshiftPlayer.cs | 2 +- osu.Game/Screens/Multi/RoomManager.cs | 2 +- osu.Game/Screens/Play/Player.cs | 4 +- osu.Game/Screens/Select/BeatmapDetails.cs | 4 +- .../Select/Leaderboards/BeatmapLeaderboard.cs | 2 +- 33 files changed, 127 insertions(+), 61 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseMatchLeaderboard.cs b/osu.Game.Tests/Visual/TestCaseMatchLeaderboard.cs index 42a886a5a3..484a212a38 100644 --- a/osu.Game.Tests/Visual/TestCaseMatchLeaderboard.cs +++ b/osu.Game.Tests/Visual/TestCaseMatchLeaderboard.cs @@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual } [Resolved] - private APIAccess api { get; set; } + private IAPIProvider api { get; set; } [BackgroundDependencyLoader] private void load() diff --git a/osu.Game.Tests/Visual/TestCaseUpdateableBeatmapBackgroundSprite.cs b/osu.Game.Tests/Visual/TestCaseUpdateableBeatmapBackgroundSprite.cs index 506121efd7..ac90c264c4 100644 --- a/osu.Game.Tests/Visual/TestCaseUpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game.Tests/Visual/TestCaseUpdateableBeatmapBackgroundSprite.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual private BeatmapManager beatmaps { get; set; } [BackgroundDependencyLoader] - private void load(OsuGameBase osu, APIAccess api, RulesetStore rulesets) + private void load(OsuGameBase osu, IAPIProvider api, RulesetStore rulesets) { Bindable beatmapBindable = new Bindable(); diff --git a/osu.Game.Tests/Visual/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/TestCaseUserProfile.cs index 726134294e..46ee74b69f 100644 --- a/osu.Game.Tests/Visual/TestCaseUserProfile.cs +++ b/osu.Game.Tests/Visual/TestCaseUserProfile.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual public class TestCaseUserProfile : OsuTestCase { private readonly TestUserProfileOverlay profile; - private APIAccess api; + private IAPIProvider api; public override IReadOnlyList RequiredTypes => new[] { @@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual } [BackgroundDependencyLoader] - private void load(APIAccess api) + private void load(IAPIProvider api) { this.api = api; } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 711aa0b79b..0ef8098a76 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -64,7 +64,7 @@ namespace osu.Game.Beatmaps private readonly BeatmapStore beatmaps; - private readonly APIAccess api; + private readonly IAPIProvider api; private readonly AudioManager audioManager; @@ -72,7 +72,7 @@ namespace osu.Game.Beatmaps private readonly List currentDownloads = new List(); - public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, APIAccess api, AudioManager audioManager, GameHost host = null, + public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, AudioManager audioManager, GameHost host = null, WorkingBeatmap defaultBeatmap = null) : base(storage, contextFactory, new BeatmapStore(contextFactory), host) { diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 5593abf348..3d861e44bf 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -22,7 +22,7 @@ namespace osu.Game.Online.API private readonly OsuConfigManager config; private readonly OAuth authentication; - public string Endpoint = @"https://osu.ppy.sh"; + public string Endpoint => @"https://osu.ppy.sh"; private const string client_id = @"5"; private const string client_secret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk"; diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 2781a5709b..96f3b85272 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -61,9 +61,12 @@ namespace osu.Game.Online.API private Action pendingFailure; - public void Perform(APIAccess api) + public void Perform(IAPIProvider api) { - API = api; + if (!(api is APIAccess apiAccess)) + throw new NotSupportedException($"A {nameof(APIAccess)} is required to perform requests."); + + API = apiAccess; if (checkAndScheduleFailure()) return; @@ -71,7 +74,7 @@ namespace osu.Game.Online.API WebRequest = CreateWebRequest(); WebRequest.Failed += Fail; WebRequest.AllowRetryOnTimeout = false; - WebRequest.AddHeader("Authorization", $"Bearer {api.AccessToken}"); + WebRequest.AddHeader("Authorization", $"Bearer {API.AccessToken}"); if (checkAndScheduleFailure()) return; @@ -85,7 +88,7 @@ namespace osu.Game.Online.API if (checkAndScheduleFailure()) return; - api.Schedule(delegate { Success?.Invoke(); }); + API.Schedule(delegate { Success?.Invoke(); }); } public void Cancel() => Fail(new OperationCanceledException(@"Request cancelled")); diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index 096ab5d8c8..af2fe51b38 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -11,14 +11,16 @@ namespace osu.Game.Online.API public Bindable LocalUser { get; } = new Bindable(new User { Username = @"Dummy", - Id = 1, + Id = 1001, }); public bool IsLoggedIn => true; - public void Update() - { - } + public string ProvidedUsername => LocalUser.Value.Username; + + public string Endpoint => "https://test.com"; + + public APIState State => LocalUser.Value.Id == 1 ? APIState.Offline : APIState.Online; public virtual void Queue(APIRequest request) { @@ -26,6 +28,28 @@ namespace osu.Game.Online.API public void Register(IOnlineComponent component) { + // todo: add support } + + public void Unregister(IOnlineComponent component) + { + // todo: add support + } + + public void Login(string username, string password) + { + LocalUser.Value = new User + { + Username = @"Dummy", + Id = 1, + }; + } + + public void Logout() + { + LocalUser.Value = new GuestUser(); + } + + public RegistrationRequest.RegistrationRequestErrors CreateAccount(string email, string username, string password) => null; } } diff --git a/osu.Game/Online/API/IAPIProvider.cs b/osu.Game/Online/API/IAPIProvider.cs index e4533ecb3d..7c1f850943 100644 --- a/osu.Game/Online/API/IAPIProvider.cs +++ b/osu.Game/Online/API/IAPIProvider.cs @@ -18,6 +18,19 @@ namespace osu.Game.Online.API /// bool IsLoggedIn { get; } + /// + /// The last username provided by the end-user. + /// May not be authenticated. + /// + string ProvidedUsername { get; } + + /// + /// The URL endpoint for this API. Does not include a trailing slash. + /// + string Endpoint { get; } + + APIState State { get; } + /// /// Queue a new request. /// @@ -29,5 +42,32 @@ namespace osu.Game.Online.API /// /// The component to register. void Register(IOnlineComponent component); + + /// + /// Unregisters a component to receive state changes. + /// + /// The component to unregister. + void Unregister(IOnlineComponent component); + + /// + /// Attempt to login using the provided credentials. This is a non-blocking operation. + /// + /// The user's username. + /// The user's password. + void Login(string username, string password); + + /// + /// Log out the current user. + /// + void Logout(); + + /// + /// Create a new user account. This is a blocking operation. + /// + /// The email to create the account with. + /// The username to create the account with. + /// The password to create the account with. + /// Any errors encoutnered during account creation. + RegistrationRequest.RegistrationRequestErrors CreateAccount(string email, string username, string password); } } diff --git a/osu.Game/Online/API/IOnlineComponent.cs b/osu.Game/Online/API/IOnlineComponent.cs index fb40bfd1e6..da6b784759 100644 --- a/osu.Game/Online/API/IOnlineComponent.cs +++ b/osu.Game/Online/API/IOnlineComponent.cs @@ -5,6 +5,6 @@ namespace osu.Game.Online.API { public interface IOnlineComponent { - void APIStateChanged(APIAccess api, APIState state); + void APIStateChanged(IAPIProvider api, APIState state); } } diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index f5b6e185c7..ac1666f8ed 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -174,12 +174,12 @@ namespace osu.Game.Online.Leaderboards }; } - private APIAccess api; + private IAPIProvider api; private ScheduledDelegate pendingUpdateScores; [BackgroundDependencyLoader(true)] - private void load(APIAccess api) + private void load(IAPIProvider api) { this.api = api; api?.Register(this); @@ -195,7 +195,7 @@ namespace osu.Game.Online.Leaderboards private APIRequest getScoresRequest; - public void APIStateChanged(APIAccess api, APIState state) + public void APIStateChanged(IAPIProvider api, APIState state) { if (state == APIState.Online) UpdateScores(); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 643a673faf..7d55d19e50 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -152,7 +152,6 @@ namespace osu.Game API = new APIAccess(LocalConfig); - dependencies.Cache(API); dependencies.CacheAs(API); var defaultBeatmap = new DummyWorkingBeatmap(this); diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs index 2fc00edbc1..13d8df098f 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.AccountCreation private OsuTextBox emailTextBox; private OsuPasswordTextBox passwordTextBox; - private APIAccess api; + private IAPIProvider api; private ShakeContainer registerShake; private IEnumerable characterCheckText; @@ -42,7 +42,7 @@ namespace osu.Game.Overlays.AccountCreation private GameHost host; [BackgroundDependencyLoader] - private void load(OsuColour colours, APIAccess api, GameHost host) + private void load(OsuColour colours, IAPIProvider api, GameHost host) { this.api = api; this.host = host; diff --git a/osu.Game/Overlays/AccountCreation/ScreenWarning.cs b/osu.Game/Overlays/AccountCreation/ScreenWarning.cs index 4e2cc1ea00..be417f4aac 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenWarning.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenWarning.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.AccountCreation { private OsuTextFlowContainer multiAccountExplanationText; private LinkFlowContainer furtherAssistance; - private APIAccess api; + private IAPIProvider api; private const string help_centre_url = "/help/wiki/Help_Centre#login"; @@ -39,7 +39,7 @@ namespace osu.Game.Overlays.AccountCreation } [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, APIAccess api, OsuGame game, TextureStore textures) + private void load(OsuColour colours, IAPIProvider api, OsuGame game, TextureStore textures) { this.api = api; diff --git a/osu.Game/Overlays/AccountCreationOverlay.cs b/osu.Game/Overlays/AccountCreationOverlay.cs index bc780538d5..e8e44c206e 100644 --- a/osu.Game/Overlays/AccountCreationOverlay.cs +++ b/osu.Game/Overlays/AccountCreationOverlay.cs @@ -30,7 +30,7 @@ namespace osu.Game.Overlays } [BackgroundDependencyLoader] - private void load(OsuColour colours, APIAccess api) + private void load(OsuColour colours, IAPIProvider api) { api.Register(this); @@ -96,7 +96,7 @@ namespace osu.Game.Overlays this.FadeOut(100); } - public void APIStateChanged(APIAccess api, APIState state) + public void APIStateChanged(IAPIProvider api, APIState state) { switch (state) { diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs index edd886f0f2..bbbcff0558 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs @@ -39,7 +39,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons } [BackgroundDependencyLoader] - private void load(APIAccess api, BeatmapManager beatmaps) + private void load(IAPIProvider api, BeatmapManager beatmaps) { FillFlowContainer textSprites; diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 6c65d491af..3dd03fcea6 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -45,7 +45,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores } private GetScoresRequest getScoresRequest; - private APIAccess api; + private IAPIProvider api; public BeatmapInfo Beatmap { @@ -129,7 +129,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores } [BackgroundDependencyLoader] - private void load(APIAccess api) + private void load(IAPIProvider api) { this.api = api; updateDisplay(); diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 55c21b7fc9..c49268bc16 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -31,7 +31,7 @@ namespace osu.Game.Overlays private readonly Header header; - private APIAccess api; + private IAPIProvider api; private RulesetStore rulesets; private readonly ScrollContainer scroll; @@ -101,7 +101,7 @@ namespace osu.Game.Overlays } [BackgroundDependencyLoader] - private void load(APIAccess api, RulesetStore rulesets) + private void load(IAPIProvider api, RulesetStore rulesets) { this.api = api; this.rulesets = rulesets; diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index a3d25a7a1c..34edbbcc8b 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -28,7 +28,7 @@ namespace osu.Game.Overlays { private const float panel_padding = 10f; - private APIAccess api; + private IAPIProvider api; private RulesetStore rulesets; private readonly FillFlowContainer resultCountsContainer; @@ -164,7 +164,7 @@ namespace osu.Game.Overlays } [BackgroundDependencyLoader] - private void load(OsuColour colours, APIAccess api, RulesetStore rulesets, PreviewTrackManager previewTrackManager) + private void load(OsuColour colours, IAPIProvider api, RulesetStore rulesets, PreviewTrackManager previewTrackManager) { this.api = api; this.rulesets = rulesets; diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index 497d6c3fc4..46c65b9db7 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -28,7 +28,7 @@ namespace osu.Game.Overlays.Profile.Sections protected readonly Bindable User = new Bindable(); - protected APIAccess Api; + protected IAPIProvider Api; protected APIRequest RetrievalRequest; protected RulesetStore Rulesets; @@ -84,7 +84,7 @@ namespace osu.Game.Overlays.Profile.Sections } [BackgroundDependencyLoader] - private void load(APIAccess api, RulesetStore rulesets) + private void load(IAPIProvider api, RulesetStore rulesets) { Api = api; Rulesets = rulesets; diff --git a/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs index 7e721ac807..8fab29e42c 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Profile.Sections.Recent { public class DrawableRecentActivity : DrawableProfileRow { - private APIAccess api; + private IAPIProvider api; private readonly APIRecentActivity activity; @@ -28,7 +28,7 @@ namespace osu.Game.Overlays.Profile.Sections.Recent } [BackgroundDependencyLoader] - private void load(APIAccess api) + private void load(IAPIProvider api) { this.api = api; diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs index 026424e1ff..d6738250f9 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs @@ -58,14 +58,14 @@ namespace osu.Game.Overlays.Settings.Sections.General } [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuColour colours, APIAccess api) + private void load(OsuColour colours, IAPIProvider api) { this.colours = colours; api?.Register(this); } - public void APIStateChanged(APIAccess api, APIState state) + public void APIStateChanged(IAPIProvider api, APIState state) { form = null; @@ -194,7 +194,7 @@ namespace osu.Game.Overlays.Settings.Sections.General { private TextBox username; private TextBox password; - private APIAccess api; + private IAPIProvider api; public Action RequestHide; @@ -205,7 +205,7 @@ namespace osu.Game.Overlays.Settings.Sections.General } [BackgroundDependencyLoader(permitNulls: true)] - private void load(APIAccess api, OsuConfigManager config, AccountCreationOverlay accountCreation) + private void load(IAPIProvider api, OsuConfigManager config, AccountCreationOverlay accountCreation) { this.api = api; Direction = FillDirection.Vertical; diff --git a/osu.Game/Overlays/SocialOverlay.cs b/osu.Game/Overlays/SocialOverlay.cs index 3464058abb..daf3d1c576 100644 --- a/osu.Game/Overlays/SocialOverlay.cs +++ b/osu.Game/Overlays/SocialOverlay.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays { public class SocialOverlay : SearchableListOverlay, IOnlineComponent { - private APIAccess api; + private IAPIProvider api; private readonly LoadingAnimation loading; private FillFlowContainer panels; @@ -89,7 +89,7 @@ namespace osu.Game.Overlays } [BackgroundDependencyLoader] - private void load(APIAccess api) + private void load(IAPIProvider api) { this.api = api; api.Register(this); @@ -193,7 +193,7 @@ namespace osu.Game.Overlays } } - public void APIStateChanged(APIAccess api, APIState state) + public void APIStateChanged(IAPIProvider api, APIState state) { switch (state) { diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs index d47f3a7b16..8d1910fc19 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs @@ -43,12 +43,12 @@ namespace osu.Game.Overlays.Toolbar } [BackgroundDependencyLoader] - private void load(APIAccess api) + private void load(IAPIProvider api) { api.Register(this); } - public void APIStateChanged(APIAccess api, APIState state) + public void APIStateChanged(IAPIProvider api, APIState state) { switch (state) { diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 1ff1c2ce91..48ce055975 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -26,7 +26,7 @@ namespace osu.Game.Overlays private ProfileSection lastSection; private ProfileSection[] sections; private GetUserRequest userReq; - private APIAccess api; + private IAPIProvider api; protected ProfileHeader Header; private SectionsContainer sectionsContainer; private ProfileTabControl tabs; @@ -56,7 +56,7 @@ namespace osu.Game.Overlays } [BackgroundDependencyLoader] - private void load(APIAccess api) + private void load(IAPIProvider api) { this.api = api; } diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 1f68818669..3df4ef9059 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -97,7 +97,7 @@ namespace osu.Game.Screens.Menu private OsuGame game { get; set; } [Resolved] - private APIAccess api { get; set; } + private IAPIProvider api { get; set; } [Resolved(CanBeNull = true)] private NotificationOverlay notifications { get; set; } diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index 89f4f92092..7a80a686b1 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.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; @@ -44,7 +44,7 @@ namespace osu.Game.Screens.Menu } [BackgroundDependencyLoader] - private void load(OsuColour colours, APIAccess api) + private void load(OsuColour colours, IAPIProvider api) { InternalChildren = new Drawable[] { diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs index fd9c8d7b35..5798fce457 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs @@ -246,7 +246,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components } [Resolved] - private APIAccess api { get; set; } + private IAPIProvider api { get; set; } private GetRoomScoresRequest request; diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index dd01ae4160..f38fc4e3ff 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -54,7 +54,7 @@ namespace osu.Game.Screens.Multi private OsuGameBase game { get; set; } [Resolved] - private APIAccess api { get; set; } + private IAPIProvider api { get; set; } [Resolved(CanBeNull = true)] private OsuLogo logo { get; set; } @@ -163,7 +163,7 @@ namespace osu.Game.Screens.Multi this.Push(new PlayerLoader(player)); } - public void APIStateChanged(APIAccess api, APIState state) + public void APIStateChanged(IAPIProvider api, APIState state) { if (state != APIState.Online) forcefullyExit(); diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index 6b88403b9e..d5b8f1f0c8 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Multi.Play private readonly PlaylistItem playlistItem; [Resolved] - private APIAccess api { get; set; } + private IAPIProvider api { get; set; } [Resolved] private IBindable ruleset { get; set; } diff --git a/osu.Game/Screens/Multi/RoomManager.cs b/osu.Game/Screens/Multi/RoomManager.cs index c15a8471a1..385cbe20e5 100644 --- a/osu.Game/Screens/Multi/RoomManager.cs +++ b/osu.Game/Screens/Multi/RoomManager.cs @@ -30,7 +30,7 @@ namespace osu.Game.Screens.Multi private Bindable currentFilter { get; set; } [Resolved] - private APIAccess api { get; set; } + private IAPIProvider api { get; set; } [Resolved] private RulesetStore rulesets { get; set; } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ced0a43679..b11c5e51c9 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -60,7 +60,7 @@ namespace osu.Game.Screens.Play private RulesetInfo ruleset; - private APIAccess api; + private IAPIProvider api; private SampleChannel sampleRestart; @@ -85,7 +85,7 @@ namespace osu.Game.Screens.Play private GameplayClockContainer gameplayClockContainer; [BackgroundDependencyLoader] - private void load(AudioManager audio, APIAccess api, OsuConfigManager config) + private void load(AudioManager audio, IAPIProvider api, OsuConfigManager config) { this.api = api; diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 604d7a132b..751ff164e2 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.Select private readonly FailRetryGraph failRetryGraph; private readonly DimmedLoadingAnimation loading; - private APIAccess api; + private IAPIProvider api; private ScheduledDelegate pendingBeatmapSwitch; @@ -160,7 +160,7 @@ namespace osu.Game.Screens.Select } [BackgroundDependencyLoader] - private void load(APIAccess api) + private void load(IAPIProvider api) { this.api = api; } diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 61370f7ce3..ebb1d78ba0 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -43,7 +43,7 @@ namespace osu.Game.Screens.Select.Leaderboards private IBindable ruleset { get; set; } [Resolved] - private APIAccess api { get; set; } + private IAPIProvider api { get; set; } [BackgroundDependencyLoader] private void load() From f8b5cf8c8e51741a2feb4d4b56f44b43183dcca4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Mar 2019 13:46:41 +0900 Subject: [PATCH 22/50] 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 23/50] 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 04b88cdd11889d309777a93937066ab943d968c6 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Wed, 13 Mar 2019 14:12:05 +0900 Subject: [PATCH 24/50] Add required types for BeatmapDetailArea test case --- osu.Game.Tests/Visual/TestCaseBeatmapDetailArea.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapDetailArea.cs b/osu.Game.Tests/Visual/TestCaseBeatmapDetailArea.cs index ff39ad5c0d..6cc3982f9c 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapDetailArea.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapDetailArea.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.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; @@ -14,6 +16,8 @@ namespace osu.Game.Tests.Visual [System.ComponentModel.Description("PlaySongSelect leaderboard/details area")] public class TestCaseBeatmapDetailArea : OsuTestCase { + public override IReadOnlyList RequiredTypes => new[] { typeof(BeatmapDetails) }; + public TestCaseBeatmapDetailArea() { BeatmapDetailArea detailsArea; From e6449db8e374f33779e1d9326e43c5f6270c7603 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Wed, 13 Mar 2019 14:12:36 +0900 Subject: [PATCH 25/50] Null metrics on null beatmap for transition animation --- osu.Game/Screens/Select/BeatmapDetails.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 6057ba382b..46979d0000 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -182,6 +182,7 @@ namespace osu.Game.Screens.Select if (Beatmap == null) { + updateMetrics(null); ratingsContainer.FadeOut(transition_duration); failRetryContainer.FadeOut(transition_duration); return; From 241e336c212ae097511b4bb93f2c62cd82fb691a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Mar 2019 14:22:16 +0900 Subject: [PATCH 26/50] 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 27/50] 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 28/50] 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 6aa3dc9f554e0d41dd42fc01c4115597574b05b4 Mon Sep 17 00:00:00 2001 From: Dan Balasescu <1329837+smoogipoo@users.noreply.github.com> Date: Wed, 13 Mar 2019 18:56:48 +0900 Subject: [PATCH 29/50] Use non-real domain Co-Authored-By: peppy --- osu.Game/Online/API/DummyAPIAccess.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index af2fe51b38..be3859b1eb 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -18,7 +18,7 @@ namespace osu.Game.Online.API public string ProvidedUsername => LocalUser.Value.Username; - public string Endpoint => "https://test.com"; + public string Endpoint => "http://localhost"; public APIState State => LocalUser.Value.Id == 1 ? APIState.Offline : APIState.Online; From 7d637691d77ccd7d490beff037ee1166daf022f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Mar 2019 19:01:42 +0900 Subject: [PATCH 30/50] Use non-guest user ID for non-guest user --- osu.Game/Online/API/DummyAPIAccess.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index be3859b1eb..0cb49951f7 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -41,7 +41,7 @@ namespace osu.Game.Online.API LocalUser.Value = new User { Username = @"Dummy", - Id = 1, + Id = 1001, }; } From dd6fbccb5614a62ae943ab0ef53584729221038b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Mar 2019 19:57:45 +0900 Subject: [PATCH 31/50] Slight refactoring of order for readability --- osu.Game/Screens/Select/BeatmapDetails.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 46979d0000..2784e4fc3b 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using System.Linq; +using JetBrains.Annotations; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Framework.Threading; @@ -180,15 +181,15 @@ namespace osu.Game.Screens.Select source.Text = Beatmap?.Metadata?.Source; tags.Text = Beatmap?.Metadata?.Tags; - if (Beatmap == null) + // metrics may have been previously fetched + if (Beatmap?.Metrics != null) { - updateMetrics(null); - ratingsContainer.FadeOut(transition_duration); - failRetryContainer.FadeOut(transition_duration); + updateMetrics(Beatmap.Metrics); return; } - if (Beatmap.Metrics == null && Beatmap.OnlineBeatmapID != null) + // metrics may not be fetched but can be + if (Beatmap?.OnlineBeatmapID != null) { var requestedBeatmap = Beatmap; var lookup = new GetBeatmapDetailsRequest(requestedBeatmap); @@ -204,14 +205,13 @@ namespace osu.Game.Screens.Select lookup.Failure += e => Schedule(() => updateMetrics(null)); api.Queue(lookup); loading.Show(); + return; } - else - { - updateMetrics(Beatmap.Metrics); - } + + updateMetrics(null); } - private void updateMetrics(BeatmapMetrics metrics) + private void updateMetrics([CanBeNull] BeatmapMetrics metrics) { var hasRatings = metrics?.Ratings?.Any() ?? false; var hasRetriesFails = (metrics?.Retries?.Any() ?? false) && (metrics.Fails?.Any() ?? false); 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 32/50] 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 33/50] 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 34/50] 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 35/50] 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 36/50] 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 69eb4ef9830f41c425c08ac307f5083cd78934b5 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 14 Mar 2019 13:23:31 +0900 Subject: [PATCH 37/50] Change updateMetrics parameter to be optional --- osu.Game/Screens/Select/BeatmapDetails.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 2784e4fc3b..e8ef94c101 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using System.Linq; -using JetBrains.Annotations; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Framework.Threading; @@ -202,16 +201,16 @@ namespace osu.Game.Screens.Select requestedBeatmap.Metrics = res; Schedule(() => updateMetrics(res)); }; - lookup.Failure += e => Schedule(() => updateMetrics(null)); + lookup.Failure += e => Schedule(() => updateMetrics()); api.Queue(lookup); loading.Show(); return; } - updateMetrics(null); + updateMetrics(); } - private void updateMetrics([CanBeNull] BeatmapMetrics metrics) + private void updateMetrics(BeatmapMetrics metrics = null) { var hasRatings = metrics?.Ratings?.Any() ?? false; var hasRetriesFails = (metrics?.Retries?.Any() ?? false) && (metrics.Fails?.Any() ?? false); From 2815c3b07cc296b8c13c9d8c0006f4e1ae75aee7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 14 Mar 2019 15:12:26 +0900 Subject: [PATCH 38/50] 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 /// From 71d79f0a3914a2f858514e9b173e4f4db93a36aa Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 14 Mar 2019 15:20:09 +0900 Subject: [PATCH 39/50] Fix ticks not being generated at the minimum tick distance --- osu.Game/Rulesets/Objects/SliderEventGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/SliderEventGenerator.cs b/osu.Game/Rulesets/Objects/SliderEventGenerator.cs index a0f9d0a481..7cda7485f9 100644 --- a/osu.Game/Rulesets/Objects/SliderEventGenerator.cs +++ b/osu.Game/Rulesets/Objects/SliderEventGenerator.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Objects for (var d = tickDistance; d <= length; d += tickDistance) { - if (d > length - minDistanceFromEnd) + if (d >= length - minDistanceFromEnd) break; var pathProgress = d / length; From 5cc670cd19012a0a6b26d83404e7221c5faf9826 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 14 Mar 2019 18:51:28 +0900 Subject: [PATCH 40/50] Prevent ApplyTransformsAt from propogating to SliderBall children --- .../Objects/Drawables/Pieces/SliderBall.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 1b2e2c1f47..e41c568403 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -132,6 +132,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces base.ClearTransformsAfter(time, false, targetMember); } + public override void ApplyTransformsAt(double time, bool propagateChildren = false) + { + // For the same reasons as above w.r.t rewinding, we shouldn't propagate to children here either. + base.ApplyTransformsAt(time, false); + } + private bool tracking; public bool Tracking From 144b8761d4e01fcb9c6aa1bf7f5a9e10c2b8275e Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 14 Mar 2019 19:15:47 +0900 Subject: [PATCH 41/50] Change "Redundant argument with default value" inspection from warning to hint. --- osu.sln.DotSettings | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index b1ac31f2ce..71cbd83e3e 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -111,6 +111,7 @@ HINT WARNING WARNING + HINT WARNING WARNING WARNING From d13423fcdbcf7a723c0c7afaa0ea66f4c9b5bb00 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 14 Mar 2019 23:06:23 +0900 Subject: [PATCH 42/50] Fix catch difficulty calculator not providing adjustment mods --- .../Difficulty/CatchDifficultyCalculator.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 8cfda5d532..f3b88bd928 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Difficulty.Preprocessing; using osu.Game.Rulesets.Catch.Difficulty.Skills; +using osu.Game.Rulesets.Catch.Mods; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Difficulty; @@ -88,5 +89,13 @@ namespace osu.Game.Rulesets.Catch.Difficulty { new Movement(), }; + + protected override Mod[] DifficultyAdjustmentMods => new Mod[] + { + new CatchModDoubleTime(), + new CatchModHalfTime(), + new CatchModHardRock(), + new CatchModEasy(), + }; } } From e31680e3730a04a27dacd019476a41b002e12bac Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 14 Mar 2019 16:05:54 -0700 Subject: [PATCH 43/50] Address styling issues on CodeFactor --- osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.cs | 1 - osu.iOS/Application.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.cs b/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.cs index c38cd19b42..026442f328 100644 --- a/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.cs +++ b/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.cs @@ -13,7 +13,6 @@ namespace osu.Game.Migrations protected override void Down(MigrationBuilder migrationBuilder) { - } } } diff --git a/osu.iOS/Application.cs b/osu.iOS/Application.cs index 8a5cfcdbe8..cb75e5c159 100644 --- a/osu.iOS/Application.cs +++ b/osu.iOS/Application.cs @@ -13,4 +13,3 @@ namespace osu.iOS } } } - From 8c6caf0b18383b015a79f14976b668270c00214e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 16 Mar 2019 01:03:13 +0900 Subject: [PATCH 44/50] 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 e2c96effb6..c56d50ae15 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 288630d1ec..fedc20397d 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From 771d676ba1a8baed1661985929598598893b5a33 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 16 Mar 2019 13:47:11 +0900 Subject: [PATCH 45/50] Split RulesetInputManager out to FrameStabilityContainer --- .../Rulesets/UI/FrameStabilityContainer.cs | 161 ++++++++++++++++++ osu.Game/Rulesets/UI/RulesetContainer.cs | 14 +- osu.Game/Rulesets/UI/RulesetInputManager.cs | 140 +-------------- 3 files changed, 178 insertions(+), 137 deletions(-) create mode 100644 osu.Game/Rulesets/UI/FrameStabilityContainer.cs diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs new file mode 100644 index 0000000000..921ed46d0e --- /dev/null +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -0,0 +1,161 @@ +// 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.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Timing; +using osu.Game.Input.Handlers; +using osu.Game.Screens.Play; + +namespace osu.Game.Rulesets.UI +{ + /// + /// A container which consumes a parent gameplay clock and standardises frame counts for children. + /// Will ensure a minimum of 40 frames per clock second is maintained, regardless of any system lag or seeks. + /// + public class FrameStabilityContainer : Container, IHasReplayHandler + { + public FrameStabilityContainer() + { + RelativeSizeAxes = Axes.Both; + gameplayClock = new GameplayClock(framedClock = new FramedClock(manualClock = new ManualClock())); + } + + #region Clock control + + private readonly ManualClock manualClock; + + private readonly FramedClock framedClock; + + [Cached] + private GameplayClock gameplayClock; + + private IFrameBasedClock parentGameplayClock; + + [BackgroundDependencyLoader(true)] + private void load(GameplayClock clock) + { + if (clock != null) + parentGameplayClock = clock; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + setClock(); + } + + /// + /// Whether we are running up-to-date with our parent clock. + /// If not, we will need to keep processing children until we catch up. + /// + private bool requireMoreUpdateLoops; + + /// + /// Whether we are in a valid state (ie. should we keep processing children frames). + /// This should be set to false when the replay is, for instance, waiting for future frames to arrive. + /// + private bool validState; + + protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && validState; + + private bool isAttached => ReplayInputHandler != null; + + private const int max_catch_up_updates_per_frame = 50; + + private const double sixty_frame_time = 1000.0 / 60; + + public override bool UpdateSubTree() + { + requireMoreUpdateLoops = true; + validState = true; + + int loops = 0; + + while (validState && requireMoreUpdateLoops && loops++ < max_catch_up_updates_per_frame) + { + updateClock(); + + if (validState) + { + base.UpdateSubTree(); + UpdateSubTreeMasking(this, ScreenSpaceDrawQuad.AABBFloat); + } + } + + return true; + } + + private void updateClock() + { + if (parentGameplayClock == null) + setClock(); // LoadComplete may not be run yet, but we still want the clock. + + validState = true; + + manualClock.Rate = parentGameplayClock.Rate; + manualClock.IsRunning = parentGameplayClock.IsRunning; + + var newProposedTime = parentGameplayClock.CurrentTime; + + try + { + if (Math.Abs(manualClock.CurrentTime - newProposedTime) > sixty_frame_time * 1.2f) + { + newProposedTime = manualClock.Rate > 0 + ? Math.Min(newProposedTime, manualClock.CurrentTime + sixty_frame_time) + : Math.Max(newProposedTime, manualClock.CurrentTime - sixty_frame_time); + } + + if (!isAttached) + { + manualClock.CurrentTime = newProposedTime; + } + else + { + double? newTime = ReplayInputHandler.SetFrameFromTime(newProposedTime); + + if (newTime == null) + { + // we shouldn't execute for this time value. probably waiting on more replay data. + validState = false; + + requireMoreUpdateLoops = true; + manualClock.CurrentTime = newProposedTime; + return; + } + + manualClock.CurrentTime = newTime.Value; + } + + requireMoreUpdateLoops = manualClock.CurrentTime != parentGameplayClock.CurrentTime; + } + finally + { + // The manual clock time has changed in the above code. The framed clock now needs to be updated + // to ensure that the its time is valid for our children before input is processed + framedClock.ProcessFrame(); + } + } + + private void setClock() + { + // in case a parent gameplay clock isn't available, just use the parent clock. + if (parentGameplayClock == null) + parentGameplayClock = Clock; + + Clock = gameplayClock; + ProcessCustomClock = false; + } + + #endregion + + #region IHasReplayHandler + + public ReplayInputHandler ReplayInputHandler { get; set; } + + #endregion + } +} diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index ed5f23dc7f..d8813631dc 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -132,6 +132,8 @@ namespace osu.Game.Rulesets.UI protected virtual ReplayInputHandler CreateReplayInputHandler(Replay replay) => null; + protected FrameStabilityContainer FrameStabilityContainer; + public Score ReplayScore { get; private set; } /// @@ -149,7 +151,11 @@ namespace osu.Game.Rulesets.UI throw new InvalidOperationException($"A {nameof(KeyBindingInputManager)} which supports replay loading is not available"); ReplayScore = replayScore; - ReplayInputManager.ReplayInputHandler = replayScore != null ? CreateReplayInputHandler(replayScore.Replay) : null; + + var handler = replayScore != null ? CreateReplayInputHandler(replayScore.Replay) : null; + + ReplayInputManager.ReplayInputHandler = handler; + FrameStabilityContainer.ReplayInputHandler = handler; HasReplayLoaded.Value = ReplayInputManager.ReplayInputHandler != null; } @@ -243,7 +249,6 @@ namespace osu.Game.Rulesets.UI Beatmap = (Beatmap)workingBeatmap.GetPlayableBeatmap(ruleset.RulesetInfo); KeyBindingInputManager = CreateInputManager(); - KeyBindingInputManager.RelativeSizeAxes = Axes.Both; applyBeatmapMods(Mods); } @@ -262,7 +267,10 @@ namespace osu.Game.Rulesets.UI InternalChildren = new Drawable[] { - KeyBindingInputManager, + FrameStabilityContainer = new FrameStabilityContainer + { + Child = KeyBindingInputManager, + }, Overlays = new Container { RelativeSizeAxes = Axes.Both } }; diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 87220a37e8..e303166774 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.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 System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -12,7 +11,6 @@ using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Input.StateChanges.Events; using osu.Framework.Input.States; -using osu.Framework.Timing; using osu.Game.Configuration; using osu.Game.Input.Bindings; using osu.Game.Input.Handlers; @@ -41,7 +39,12 @@ namespace osu.Game.Rulesets.UI protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) { InternalChild = KeyBindingContainer = CreateKeyBindingContainer(ruleset, variant, unique); - gameplayClock = new GameplayClock(framedClock = new FramedClock(manualClock = new ManualClock())); + } + + [BackgroundDependencyLoader(true)] + private void load(OsuConfigManager config) + { + mouseDisabled = config.GetBindable(OsuSetting.MouseDisableButtons); } #region Action mapping (for replays) @@ -85,137 +88,6 @@ namespace osu.Game.Rulesets.UI #endregion - #region Clock control - - private readonly ManualClock manualClock; - - private readonly FramedClock framedClock; - - [Cached] - private GameplayClock gameplayClock; - - private IFrameBasedClock parentGameplayClock; - - [BackgroundDependencyLoader(true)] - private void load(OsuConfigManager config, GameplayClock clock) - { - mouseDisabled = config.GetBindable(OsuSetting.MouseDisableButtons); - - if (clock != null) - parentGameplayClock = clock; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - setClock(); - } - - /// - /// Whether we are running up-to-date with our parent clock. - /// If not, we will need to keep processing children until we catch up. - /// - private bool requireMoreUpdateLoops; - - /// - /// Whether we are in a valid state (ie. should we keep processing children frames). - /// This should be set to false when the replay is, for instance, waiting for future frames to arrive. - /// - private bool validState; - - protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && validState; - - private bool isAttached => replayInputHandler != null && !UseParentInput; - - private const int max_catch_up_updates_per_frame = 50; - - private const double sixty_frame_time = 1000.0 / 60; - - public override bool UpdateSubTree() - { - requireMoreUpdateLoops = true; - validState = true; - - int loops = 0; - - while (validState && requireMoreUpdateLoops && loops++ < max_catch_up_updates_per_frame) - { - updateClock(); - - if (validState) - { - base.UpdateSubTree(); - UpdateSubTreeMasking(this, ScreenSpaceDrawQuad.AABBFloat); - } - } - - return true; - } - - private void updateClock() - { - if (parentGameplayClock == null) - setClock(); // LoadComplete may not be run yet, but we still want the clock. - - validState = true; - - manualClock.Rate = parentGameplayClock.Rate; - manualClock.IsRunning = parentGameplayClock.IsRunning; - - var newProposedTime = parentGameplayClock.CurrentTime; - - try - { - if (Math.Abs(manualClock.CurrentTime - newProposedTime) > sixty_frame_time * 1.2f) - { - newProposedTime = manualClock.Rate > 0 - ? Math.Min(newProposedTime, manualClock.CurrentTime + sixty_frame_time) - : Math.Max(newProposedTime, manualClock.CurrentTime - sixty_frame_time); - } - - if (!isAttached) - { - manualClock.CurrentTime = newProposedTime; - } - else - { - double? newTime = replayInputHandler.SetFrameFromTime(newProposedTime); - - if (newTime == null) - { - // we shouldn't execute for this time value. probably waiting on more replay data. - validState = false; - - requireMoreUpdateLoops = true; - manualClock.CurrentTime = newProposedTime; - return; - } - - manualClock.CurrentTime = newTime.Value; - } - - requireMoreUpdateLoops = manualClock.CurrentTime != parentGameplayClock.CurrentTime; - } - finally - { - // The manual clock time has changed in the above code. The framed clock now needs to be updated - // to ensure that the its time is valid for our children before input is processed - framedClock.ProcessFrame(); - } - } - - private void setClock() - { - // in case a parent gameplay clock isn't available, just use the parent clock. - if (parentGameplayClock == null) - parentGameplayClock = Clock; - - Clock = gameplayClock; - ProcessCustomClock = false; - } - - #endregion - #region Setting application (disables etc.) private Bindable mouseDisabled; From c496f6e56bc6440ba91f12a52971f4e8f4b4c65e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 17 Mar 2019 13:43:23 +0900 Subject: [PATCH 46/50] Fix usages of OnLoadComplete --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 2 +- .../UpdateableBeatmapBackgroundSprite.cs | 2 +- .../Drawables/UpdateableBeatmapSetCover.cs | 7 +++++-- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 7 +++++-- .../Overlays/Chat/Tabs/PrivateChannelTabItem.cs | 7 +++++-- osu.Game/Overlays/MedalSplash/DrawableMedal.cs | 2 +- osu.Game/Overlays/Profile/ProfileHeader.cs | 7 +++++-- .../Multi/Match/Components/Participants.cs | 16 +++++++++++----- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 +- .../Carousel/DrawableCarouselBeatmapSet.cs | 11 ++++++++--- osu.Game/Users/UpdateableAvatar.cs | 2 +- osu.Game/Users/UserPanel.cs | 7 +++++-- 12 files changed, 49 insertions(+), 23 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index d0f50c6af2..c6dd0a86a0 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Catch.UI if (lastPlateableFruit.IsLoaded) action(); else - lastPlateableFruit.OnLoadComplete = _ => action(); + lastPlateableFruit.OnLoadComplete += _ => action(); } if (result.IsHit && fruit.CanBePlated) diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs index ec75c1a1fb..ce7811fc0d 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs @@ -51,7 +51,7 @@ namespace osu.Game.Beatmaps.Drawables drawable.Anchor = Anchor.Centre; drawable.Origin = Anchor.Centre; drawable.FillMode = FillMode.Fill; - drawable.OnLoadComplete = d => d.FadeInFromZero(400); + drawable.OnLoadComplete += d => d.FadeInFromZero(400); return drawable; } diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs index 367b63d6d1..c7c4c1fb1e 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs @@ -67,16 +67,19 @@ namespace osu.Game.Beatmaps.Drawables if (beatmapSet != null) { + BeatmapSetCover cover; + Add(displayedCover = new DelayedLoadWrapper( - new BeatmapSetCover(beatmapSet, coverType) + cover = new BeatmapSetCover(beatmapSet, coverType) { Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fill, - OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out), }) ); + + cover.OnLoadComplete += d => d.FadeInFromZero(400, Easing.Out); } } } diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 34981bf849..c5602fc4ad 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -64,6 +64,8 @@ namespace osu.Game.Online.Leaderboards statisticsLabels = GetStatistics(score).Select(s => new ScoreComponentLabel(s)).ToList(); + Avatar innerAvatar; + Children = new Drawable[] { new Container @@ -109,12 +111,11 @@ namespace osu.Game.Online.Leaderboards Children = new[] { avatar = new DelayedLoadWrapper( - new Avatar(user) + innerAvatar = new Avatar(user) { RelativeSizeAxes = Axes.Both, CornerRadius = corner_radius, Masking = true, - OnLoadComplete = d => d.FadeInFromZero(200), EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, @@ -214,6 +215,8 @@ namespace osu.Game.Online.Leaderboards }, }, }; + + innerAvatar.OnLoadComplete += d => d.FadeInFromZero(200); } public override void Show() diff --git a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs index 8cedde82ff..8111ac7394 100644 --- a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs @@ -28,6 +28,8 @@ namespace osu.Game.Overlays.Chat.Tabs if (value.Type != ChannelType.PM) throw new ArgumentException("Argument value needs to have the targettype user!"); + Avatar avatar; + AddRange(new Drawable[] { new Container @@ -49,11 +51,10 @@ namespace osu.Game.Overlays.Chat.Tabs Anchor = Anchor.Centre, Origin = Anchor.Centre, Masking = true, - Child = new DelayedLoadWrapper(new Avatar(value.Users.First()) + Child = new DelayedLoadWrapper(avatar = new Avatar(value.Users.First()) { RelativeSizeAxes = Axes.Both, OpenOnClick = { Value = false }, - OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint), }) { RelativeSizeAxes = Axes.Both, @@ -63,6 +64,8 @@ namespace osu.Game.Overlays.Chat.Tabs }, }); + avatar.OnLoadComplete += d => d.FadeInFromZero(300, Easing.OutQuint); + Text.X = ChatOverlay.TAB_AREA_HEIGHT; TextBold.X = ChatOverlay.TAB_AREA_HEIGHT; } diff --git a/osu.Game/Overlays/MedalSplash/DrawableMedal.cs b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs index eeb42ec991..431ae98c2c 100644 --- a/osu.Game/Overlays/MedalSplash/DrawableMedal.cs +++ b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs @@ -109,7 +109,7 @@ namespace osu.Game.Overlays.MedalSplash s.Font = s.Font.With(size: 16); }); - medalContainer.OnLoadComplete = d => + medalContainer.OnLoadComplete += d => { unlocked.Position = new Vector2(0f, medalContainer.DrawSize.Y / 2 + 10); infoFlow.Position = new Vector2(0f, unlocked.Position.Y + 90); diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 2641a0551d..c41d977701 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -335,9 +335,12 @@ namespace osu.Game.Overlays.Profile Anchor = Anchor.Centre, Origin = Anchor.Centre, FillMode = FillMode.Fill, - OnLoadComplete = d => d.FadeInFromZero(200), Depth = float.MaxValue, - }, coverContainer.Add); + }, background => + { + coverContainer.Add(background); + background.FadeInFromZero(200); + }); if (user.IsSupporter) SupporterTag.Show(); diff --git a/osu.Game/Screens/Multi/Match/Components/Participants.cs b/osu.Game/Screens/Multi/Match/Components/Participants.cs index 2d6099c65d..09d25572ec 100644 --- a/osu.Game/Screens/Multi/Match/Components/Participants.cs +++ b/osu.Game/Screens/Multi/Match/Components/Participants.cs @@ -52,12 +52,18 @@ namespace osu.Game.Screens.Multi.Match.Components Participants.BindValueChanged(participants => { - usersFlow.Children = participants.NewValue.Select(u => new UserPanel(u) + usersFlow.Children = participants.NewValue.Select(u => { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Width = 300, - OnLoadComplete = d => d.FadeInFromZero(60), + var panel = new UserPanel(u) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Width = 300, + }; + + panel.OnLoadComplete += d => d.FadeInFromZero(60); + + return panel; }).ToList(); }, true); } diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 389f614ef2..bfd1d3d236 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -577,7 +577,7 @@ namespace osu.Game.Screens.Select else { float y = currentY; - d.OnLoadComplete = _ => performMove(y, setY); + d.OnLoadComplete += _ => performMove(y, setY); } break; diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index e01149ebc8..51ca9902d2 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -49,11 +49,16 @@ namespace osu.Game.Screens.Select.Carousel Children = new Drawable[] { new DelayedLoadUnloadWrapper(() => - new PanelBackground(manager.GetWorkingBeatmap(beatmapSet.Beatmaps.FirstOrDefault())) + { + var background = new PanelBackground(manager.GetWorkingBeatmap(beatmapSet.Beatmaps.FirstOrDefault())) { RelativeSizeAxes = Axes.Both, - OnLoadComplete = d => d.FadeInFromZero(1000, Easing.OutQuint), - }, 300, 5000 + }; + + background.OnLoadComplete += d => d.FadeInFromZero(1000, Easing.OutQuint); + + return background; + }, 300, 5000 ), new FillFlowContainer { diff --git a/osu.Game/Users/UpdateableAvatar.cs b/osu.Game/Users/UpdateableAvatar.cs index cefb91797b..7259468674 100644 --- a/osu.Game/Users/UpdateableAvatar.cs +++ b/osu.Game/Users/UpdateableAvatar.cs @@ -57,9 +57,9 @@ namespace osu.Game.Users var avatar = new Avatar(user) { RelativeSizeAxes = Axes.Both, - OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint), }; + avatar.OnLoadComplete += d => d.FadeInFromZero(300, Easing.OutQuint); avatar.OpenOnClick.BindTo(OpenOnClick); Add(displayedAvatar = new DelayedLoadWrapper(avatar)); diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index 4dfde04e07..65062dc58e 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -59,6 +59,8 @@ namespace osu.Game.Users FillFlowContainer infoContainer; + UserCoverBackground coverBackground; + AddInternal(content = new Container { RelativeSizeAxes = Axes.Both, @@ -73,13 +75,12 @@ namespace osu.Game.Users Children = new Drawable[] { - new DelayedLoadWrapper(new UserCoverBackground(user) + new DelayedLoadWrapper(coverBackground = new UserCoverBackground(user) { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, FillMode = FillMode.Fill, - OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out) }, 300) { RelativeSizeAxes = Axes.Both }, new Box { @@ -181,6 +182,8 @@ namespace osu.Game.Users } }); + coverBackground.OnLoadComplete += d => d.FadeInFromZero(400, Easing.Out); + if (user.IsSupporter) { infoContainer.Add(new SupporterIcon From 9ca4d9d4d1acff69ae673621b7fdcec0b9b92178 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Mar 2019 10:48:44 +0900 Subject: [PATCH 47/50] Remove regions --- osu.Game/Rulesets/UI/FrameStabilityContainer.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index 921ed46d0e..161e7aecb4 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -23,8 +23,6 @@ namespace osu.Game.Rulesets.UI gameplayClock = new GameplayClock(framedClock = new FramedClock(manualClock = new ManualClock())); } - #region Clock control - private readonly ManualClock manualClock; private readonly FramedClock framedClock; @@ -150,12 +148,6 @@ namespace osu.Game.Rulesets.UI ProcessCustomClock = false; } - #endregion - - #region IHasReplayHandler - public ReplayInputHandler ReplayInputHandler { get; set; } - - #endregion } } From 6a26972284d9cc6d89cea1263331fedb28510c0d Mon Sep 17 00:00:00 2001 From: David Zhao Date: Mon, 18 Mar 2019 15:25:54 +0900 Subject: [PATCH 48/50] DI gameplay clock for Storyboards --- osu.Game/Storyboards/Drawables/DrawableStoryboard.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index f5e1f612ca..12507b215e 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; using osu.Game.IO; +using osu.Game.Screens.Play; namespace osu.Game.Storyboards.Drawables { @@ -56,8 +57,11 @@ namespace osu.Game.Storyboards.Drawables } [BackgroundDependencyLoader] - private void load(FileStore fileStore) + private void load(FileStore fileStore, GameplayClock clock) { + if (Clock != null) + Clock = clock; + dependencies.Cache(new TextureStore(new TextureLoaderStore(fileStore.Store), false, scaleAdjust: 1)); foreach (var layer in Storyboard.Layers) From 05147768d3100d3e429df27b62acaf5ee4ee1128 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Mon, 18 Mar 2019 15:50:34 +0900 Subject: [PATCH 49/50] Permit nulls --- osu.Game/Storyboards/Drawables/DrawableStoryboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index 12507b215e..ffd9b71254 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -56,7 +56,7 @@ namespace osu.Game.Storyboards.Drawables }); } - [BackgroundDependencyLoader] + [BackgroundDependencyLoader(true)] private void load(FileStore fileStore, GameplayClock clock) { if (Clock != null) From acc133896ba4acfb29badc842d4f7531e2733825 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Mon, 18 Mar 2019 18:19:59 +0900 Subject: [PATCH 50/50] Correct null check --- osu.Game/Storyboards/Drawables/DrawableStoryboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index ffd9b71254..1182cacfc1 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -59,7 +59,7 @@ namespace osu.Game.Storyboards.Drawables [BackgroundDependencyLoader(true)] private void load(FileStore fileStore, GameplayClock clock) { - if (Clock != null) + if (clock != null) Clock = clock; dependencies.Cache(new TextureStore(new TextureLoaderStore(fileStore.Store), false, scaleAdjust: 1));