From b51a457e5a7231795708e1060c1557cc3809233a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 6 Mar 2019 14:36:30 +0900 Subject: [PATCH 001/121] Implement sorcerer's diffcalc changes --- .../Difficulty/CatchDifficultyCalculator.cs | 2 +- .../Preprocessing/CatchDifficultyHitObject.cs | 6 +-- .../Difficulty/Skills/Movement.cs | 46 ++++++++++++++----- 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 8cfda5d532..2cb71428b9 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty { public class CatchDifficultyCalculator : DifficultyCalculator { - private const double star_scaling_factor = 0.145; + private const double star_scaling_factor = 0.15; protected override int SectionLength => 750; diff --git a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs index 24e526ed19..f0c68e4392 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing public readonly float LastNormalizedPosition; /// - /// Milliseconds elapsed since the start time of the previous , with a minimum of 25ms. + /// Milliseconds elapsed since the start time of the previous , with a minimum of 40ms. /// public readonly double StrainTime; @@ -34,8 +34,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing NormalizedPosition = BaseObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor; LastNormalizedPosition = LastObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor; - // Every strain interval is hard capped at the equivalent of 600 BPM streaming speed as a safety measure - StrainTime = Math.Max(25, DeltaTime); + // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure + StrainTime = Math.Max(40, DeltaTime); } } } diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index d146153294..b1b5ba0312 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -14,7 +14,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills { private const float absolute_player_positioning_error = 16f; private const float normalized_hitobject_radius = 41.0f; - private const double direction_change_bonus = 12.5; + private const double direction_change_bonus = 9.5; + private const double antiflow_bonus = 25.0; protected override double SkillMultiplier => 850; protected override double StrainDecayBase => 0.2; @@ -23,6 +24,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills private float? lastPlayerPosition; private float lastDistanceMoved; + private double lastStrainTime; protected override double StrainValueOf(DifficultyHitObject current) { @@ -39,8 +41,11 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills float distanceMoved = playerPosition - lastPlayerPosition.Value; - double distanceAddition = Math.Pow(Math.Abs(distanceMoved), 1.3) / 500; - double sqrtStrain = Math.Sqrt(catchCurrent.StrainTime); + // Reduce speed scaling + double weightedStrainTime = catchCurrent.StrainTime + 20; + + double distanceAddition = Math.Pow(Math.Abs(distanceMoved), 1.3) / 600; + double sqrtStrain = Math.Sqrt(weightedStrainTime); double bonus = 0; @@ -53,33 +58,50 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills distanceAddition += direction_change_bonus / sqrtStrain * bonusFactor; - // Bonus for tougher direction switches and "almost" hyperdashes at this point - if (catchCurrent.LastObject.DistanceToHyperDash <= 10 / CatchPlayfield.BASE_WIDTH) - bonus = 0.3 * bonusFactor; + // Direction changes after jumps (antiflow) are harder + double antiflowBonusFactor = Math.Min(Math.Sqrt(Math.Abs(distanceMoved)) / 10, 1); + + distanceAddition += (antiflow_bonus / sqrtStrain) * (Math.Sqrt(Math.Abs(lastDistanceMoved)) / (lastStrainTime / 40 + 10.0)) * antiflowBonusFactor; } // Base bonus for every movement, giving some weight to streams. - distanceAddition += 7.5 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain; + distanceAddition += 10.0 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain; } - // Bonus for "almost" hyperdashes at corner points - if (catchCurrent.LastObject.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH) + // Big bonus for edge hyperdashes + if (catchCurrent.LastObject.DistanceToHyperDash <= 14.0f / CatchPlayfield.BASE_WIDTH) { if (!catchCurrent.LastObject.HyperDash) - bonus += 1.0; + bonus += 5.0; else { // After a hyperdash we ARE in the correct position. Always! playerPosition = catchCurrent.NormalizedPosition; } - distanceAddition *= 1.0 + bonus * ((10 - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 10); + distanceAddition *= 1.0 + bonus * (14 - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 14 * (Math.Min(catchCurrent.StrainTime, 180) / 180); // Edge dashes are easier at lower ms values + } + + // Prevent wide, dense stacks of notes which fit on the catcher from greatly increasing SR + if (Math.Abs(distanceMoved) > 0.1) + { + if (Math.Abs(lastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(lastDistanceMoved)) + { + if (Math.Abs(distanceMoved) <= (CatcherArea.CATCHER_SIZE) && Math.Abs(lastDistanceMoved) == Math.Abs(distanceMoved)) + { + if (catchCurrent.StrainTime <= 80 && lastStrainTime == catchCurrent.StrainTime) + { + distanceAddition *= Math.Max(((catchCurrent.StrainTime / 80) - 0.75) * 4, 0); + } + } + } } lastPlayerPosition = playerPosition; lastDistanceMoved = distanceMoved; + lastStrainTime = catchCurrent.StrainTime; - return distanceAddition / catchCurrent.StrainTime; + return distanceAddition / weightedStrainTime; } } } From 24fb25f1cdc770ecb7c016a8fba472c00507484d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 14 Mar 2019 23:39:45 +0900 Subject: [PATCH 002/121] Use fresh mods for each difficulty calculation --- .../Difficulty/CatchDifficultyCalculator.cs | 10 +++--- .../Difficulty/ManiaDifficultyCalculator.cs | 33 ++++++++++--------- .../Difficulty/OsuDifficultyCalculator.cs | 10 +++--- .../Difficulty/TaikoDifficultyCalculator.cs | 11 ++++--- ...DifficultyAdjustmentModCombinationsTest.cs | 14 ++++---- .../Difficulty/DifficultyCalculator.cs | 10 +++--- 6 files changed, 46 insertions(+), 42 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index f3b88bd928..d73ee19d41 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -90,12 +90,12 @@ namespace osu.Game.Rulesets.Catch.Difficulty new Movement(), }; - protected override Mod[] DifficultyAdjustmentMods => new Mod[] + protected override Type[] DifficultyAdjustmentMods => new[] { - new CatchModDoubleTime(), - new CatchModHalfTime(), - new CatchModHardRock(), - new CatchModEasy(), + typeof(CatchModDoubleTime), + typeof(CatchModHalfTime), + typeof(CatchModHardRock), + typeof(CatchModEasy), }; } } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index 59fed1031f..bff3bfdb23 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; @@ -91,33 +92,33 @@ namespace osu.Game.Rulesets.Mania.Difficulty return skills.ToArray(); } - protected override Mod[] DifficultyAdjustmentMods + protected override Type[] DifficultyAdjustmentMods { get { - var mods = new Mod[] + var mods = new[] { - new ManiaModDoubleTime(), - new ManiaModHalfTime(), - new ManiaModEasy(), - new ManiaModHardRock(), + typeof(ManiaModDoubleTime), + typeof(ManiaModHalfTime), + typeof(ManiaModEasy), + typeof(ManiaModHardRock) }; if (isForCurrentRuleset) return mods; // if we are a convert, we can be played in any key mod. - return mods.Concat(new Mod[] + return mods.Concat(new[] { - new ManiaModKey1(), - new ManiaModKey2(), - new ManiaModKey3(), - new ManiaModKey4(), - new ManiaModKey5(), - new ManiaModKey6(), - new ManiaModKey7(), - new ManiaModKey8(), - new ManiaModKey9(), + typeof(ManiaModKey1), + typeof(ManiaModKey2), + typeof(ManiaModKey3), + typeof(ManiaModKey4), + typeof(ManiaModKey5), + typeof(ManiaModKey6), + typeof(ManiaModKey7), + typeof(ManiaModKey8), + typeof(ManiaModKey9), }).ToArray(); } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index e2a1542574..9c44eb6f97 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -74,12 +74,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty new Speed() }; - protected override Mod[] DifficultyAdjustmentMods => new Mod[] + protected override Type[] DifficultyAdjustmentMods => new[] { - new OsuModDoubleTime(), - new OsuModHalfTime(), - new OsuModEasy(), - new OsuModHardRock(), + typeof(OsuModDoubleTime), + typeof(OsuModHalfTime), + typeof(OsuModEasy), + typeof(OsuModHardRock), }; } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 685ad9949b..ad1fb4c2e5 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; @@ -47,12 +48,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty protected override Skill[] CreateSkills(IBeatmap beatmap) => new Skill[] { new Strain() }; - protected override Mod[] DifficultyAdjustmentMods => new Mod[] + protected override Type[] DifficultyAdjustmentMods => new[] { - new TaikoModDoubleTime(), - new TaikoModHalfTime(), - new TaikoModEasy(), - new TaikoModHardRock(), + typeof(TaikoModDoubleTime), + typeof(TaikoModHalfTime), + typeof(TaikoModEasy), + typeof(TaikoModHardRock), }; } } diff --git a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs index 760a033aff..3bce6fedbb 100644 --- a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs +++ b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestSingleMod() { - var combinations = new TestLegacyDifficultyCalculator(new ModA()).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(typeof(ModA)).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(2, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -37,7 +37,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestDoubleMod() { - var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModB()).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(typeof(ModA), typeof(ModB)).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(4, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -52,7 +52,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestIncompatibleMods() { - var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModIncompatibleWithA()).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(typeof(ModA), typeof(ModIncompatibleWithA)).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(3, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -63,7 +63,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestDoubleIncompatibleMods() { - var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModB(), new ModIncompatibleWithA(), new ModIncompatibleWithAAndB()).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(typeof(ModA), typeof(ModB), typeof(ModIncompatibleWithA), typeof(ModIncompatibleWithAAndB)).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(8, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -86,7 +86,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestIncompatibleThroughBaseType() { - var combinations = new TestLegacyDifficultyCalculator(new ModAofA(), new ModIncompatibleWithAofA()).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(typeof(ModAofA), typeof(ModIncompatibleWithAofA)).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(3, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -141,13 +141,13 @@ namespace osu.Game.Tests.NonVisual private class TestLegacyDifficultyCalculator : DifficultyCalculator { - public TestLegacyDifficultyCalculator(params Mod[] mods) + public TestLegacyDifficultyCalculator(params Type[] mods) : base(null, null) { DifficultyAdjustmentMods = mods; } - protected override Mod[] DifficultyAdjustmentMods { get; } + protected override Type[] DifficultyAdjustmentMods { get; } protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index db8bdde6bb..47ffa48b91 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -106,7 +106,7 @@ namespace osu.Game.Rulesets.Difficulty { return createDifficultyAdjustmentModCombinations(Enumerable.Empty(), DifficultyAdjustmentMods).ToArray(); - IEnumerable createDifficultyAdjustmentModCombinations(IEnumerable currentSet, Mod[] adjustmentSet, int currentSetCount = 0, int adjustmentSetStart = 0) + IEnumerable createDifficultyAdjustmentModCombinations(IEnumerable currentSet, Type[] adjustmentSet, int currentSetCount = 0, int adjustmentSetStart = 0) { switch (currentSetCount) { @@ -129,12 +129,14 @@ namespace osu.Game.Rulesets.Difficulty // combinations in further recursions, so a moving subset is used to eliminate this effect for (int i = adjustmentSetStart; i < adjustmentSet.Length; i++) { - var adjustmentMod = adjustmentSet[i]; + var adjustmentMod = createMod(); if (currentSet.Any(c => c.IncompatibleMods.Any(m => m.IsInstanceOfType(adjustmentMod)))) continue; - foreach (var combo in createDifficultyAdjustmentModCombinations(currentSet.Append(adjustmentMod), adjustmentSet, currentSetCount + 1, i + 1)) + foreach (var combo in createDifficultyAdjustmentModCombinations(currentSet.Append(createMod()), adjustmentSet, currentSetCount + 1, i + 1)) yield return combo; + + Mod createMod() => (Mod)Activator.CreateInstance(adjustmentSet[i]); } } } @@ -142,7 +144,7 @@ namespace osu.Game.Rulesets.Difficulty /// /// Retrieves all s which adjust the difficulty. /// - protected virtual Mod[] DifficultyAdjustmentMods => Array.Empty(); + protected virtual Type[] DifficultyAdjustmentMods => Array.Empty(); /// /// Creates to describe beatmap's calculated difficulty. From f959a2ee373f13a998bb8e752bc42d614ae12cd3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 16 Mar 2019 10:12:05 +0900 Subject: [PATCH 003/121] Update antiflow bonus --- osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index b1b5ba0312..d06813f160 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -59,9 +59,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills distanceAddition += direction_change_bonus / sqrtStrain * bonusFactor; // Direction changes after jumps (antiflow) are harder - double antiflowBonusFactor = Math.Min(Math.Sqrt(Math.Abs(distanceMoved)) / 10, 1); + double antiflowBonusFactor = Math.Min(Math.Abs(distanceMoved) / 70, 1); - distanceAddition += (antiflow_bonus / sqrtStrain) * (Math.Sqrt(Math.Abs(lastDistanceMoved)) / (lastStrainTime / 40 + 10.0)) * antiflowBonusFactor; + distanceAddition += (antiflow_bonus / (catchCurrent.StrainTime / 40 + 10)) * (Math.Sqrt(Math.Abs(lastDistanceMoved)) / Math.Sqrt(lastStrainTime + 20)) * antiflowBonusFactor; } // Base bonus for every movement, giving some weight to streams. From 9ae6cde837470d8e5708788bd224631f82af8243 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 20 Mar 2019 12:14:26 +0900 Subject: [PATCH 004/121] Nerf back-and-forth hyperdash chains --- .../Difficulty/Skills/Movement.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index d06813f160..d8e359bb48 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -15,9 +15,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills private const float absolute_player_positioning_error = 16f; private const float normalized_hitobject_radius = 41.0f; private const double direction_change_bonus = 9.5; - private const double antiflow_bonus = 25.0; + private const double antiflow_bonus = 26.0; - protected override double SkillMultiplier => 850; + protected override double SkillMultiplier => 860; protected override double StrainDecayBase => 0.2; protected override double DecayWeight => 0.94; @@ -25,6 +25,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills private float? lastPlayerPosition; private float lastDistanceMoved; private double lastStrainTime; + private bool lastHyperdash; protected override double StrainValueOf(DifficultyHitObject current) { @@ -44,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills // Reduce speed scaling double weightedStrainTime = catchCurrent.StrainTime + 20; - double distanceAddition = Math.Pow(Math.Abs(distanceMoved), 1.3) / 600; + double distanceAddition = Math.Pow(Math.Abs(distanceMoved), 1.2) / 340; double sqrtStrain = Math.Sqrt(weightedStrainTime); double bonus = 0; @@ -61,7 +62,14 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills // Direction changes after jumps (antiflow) are harder double antiflowBonusFactor = Math.Min(Math.Abs(distanceMoved) / 70, 1); - distanceAddition += (antiflow_bonus / (catchCurrent.StrainTime / 40 + 10)) * (Math.Sqrt(Math.Abs(lastDistanceMoved)) / Math.Sqrt(lastStrainTime + 20)) * antiflowBonusFactor; + distanceAddition += (antiflow_bonus / (catchCurrent.StrainTime / 17.5 + 10)) * (Math.Sqrt(Math.Abs(lastDistanceMoved)) / Math.Sqrt(lastStrainTime + 25)) * antiflowBonusFactor; + + // Reduce strain slightly for Hyperdash chains + if (catchCurrent.LastObject.HyperDash && lastHyperdash) + distanceAddition *= 0.95; + + if (catchCurrent.LastObject.DistanceToHyperDash <= 14.0f / CatchPlayfield.BASE_WIDTH && !catchCurrent.LastObject.HyperDash) + bonus += 3.0; } // Base bonus for every movement, giving some weight to streams. @@ -100,6 +108,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills lastPlayerPosition = playerPosition; lastDistanceMoved = distanceMoved; lastStrainTime = catchCurrent.StrainTime; + lastHyperdash = catchCurrent.LastObject.HyperDash; return distanceAddition / weightedStrainTime; } From 9f12a36598f504539644674a89a33f370f9ef3c5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 20 Mar 2019 12:14:53 +0900 Subject: [PATCH 005/121] Buff slower edge dashes, nerf faster ones --- osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index d8e359bb48..d06ab2f928 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills { private const float absolute_player_positioning_error = 16f; private const float normalized_hitobject_radius = 41.0f; - private const double direction_change_bonus = 9.5; + private const double direction_change_bonus = 9.8; private const double antiflow_bonus = 26.0; protected override double SkillMultiplier => 860; @@ -76,18 +76,18 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills distanceAddition += 10.0 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain; } - // Big bonus for edge hyperdashes + // Big bonus for edge dashes if (catchCurrent.LastObject.DistanceToHyperDash <= 14.0f / CatchPlayfield.BASE_WIDTH) { if (!catchCurrent.LastObject.HyperDash) - bonus += 5.0; + bonus += 4.5; else { // After a hyperdash we ARE in the correct position. Always! playerPosition = catchCurrent.NormalizedPosition; } - distanceAddition *= 1.0 + bonus * (14 - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 14 * (Math.Min(catchCurrent.StrainTime, 180) / 180); // Edge dashes are easier at lower ms values + distanceAddition *= 1.0 + bonus * (14.0f - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 14.0f * (Math.Min(catchCurrent.StrainTime, 250) / 250); // Edge dashes are easier at lower ms values } // Prevent wide, dense stacks of notes which fit on the catcher from greatly increasing SR From 839dd7343f1cc6411077616ce0b2965e29130584 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 23 Mar 2019 15:57:22 +0900 Subject: [PATCH 006/121] Revert "Use fresh mods for each difficulty calculation" This reverts commit 24fb25f1cdc770ecb7c016a8fba472c00507484d. --- .../Difficulty/CatchDifficultyCalculator.cs | 10 +++--- .../Difficulty/ManiaDifficultyCalculator.cs | 33 +++++++++---------- .../Difficulty/OsuDifficultyCalculator.cs | 10 +++--- .../Difficulty/TaikoDifficultyCalculator.cs | 11 +++---- ...DifficultyAdjustmentModCombinationsTest.cs | 14 ++++---- .../Difficulty/DifficultyCalculator.cs | 10 +++--- 6 files changed, 42 insertions(+), 46 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 289deab8cd..47e2bc5259 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -91,12 +91,12 @@ namespace osu.Game.Rulesets.Catch.Difficulty new Movement(), }; - protected override Type[] DifficultyAdjustmentMods => new[] + protected override Mod[] DifficultyAdjustmentMods => new Mod[] { - typeof(CatchModDoubleTime), - typeof(CatchModHalfTime), - typeof(CatchModHardRock), - typeof(CatchModEasy), + new CatchModDoubleTime(), + new CatchModHalfTime(), + new CatchModHardRock(), + new CatchModEasy(), }; } } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index bff3bfdb23..59fed1031f 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.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.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; @@ -92,33 +91,33 @@ namespace osu.Game.Rulesets.Mania.Difficulty return skills.ToArray(); } - protected override Type[] DifficultyAdjustmentMods + protected override Mod[] DifficultyAdjustmentMods { get { - var mods = new[] + var mods = new Mod[] { - typeof(ManiaModDoubleTime), - typeof(ManiaModHalfTime), - typeof(ManiaModEasy), - typeof(ManiaModHardRock) + new ManiaModDoubleTime(), + new ManiaModHalfTime(), + new ManiaModEasy(), + new ManiaModHardRock(), }; if (isForCurrentRuleset) return mods; // if we are a convert, we can be played in any key mod. - return mods.Concat(new[] + return mods.Concat(new Mod[] { - typeof(ManiaModKey1), - typeof(ManiaModKey2), - typeof(ManiaModKey3), - typeof(ManiaModKey4), - typeof(ManiaModKey5), - typeof(ManiaModKey6), - typeof(ManiaModKey7), - typeof(ManiaModKey8), - typeof(ManiaModKey9), + new ManiaModKey1(), + new ManiaModKey2(), + new ManiaModKey3(), + new ManiaModKey4(), + new ManiaModKey5(), + new ManiaModKey6(), + new ManiaModKey7(), + new ManiaModKey8(), + new ManiaModKey9(), }).ToArray(); } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 9c44eb6f97..e2a1542574 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -74,12 +74,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty new Speed() }; - protected override Type[] DifficultyAdjustmentMods => new[] + protected override Mod[] DifficultyAdjustmentMods => new Mod[] { - typeof(OsuModDoubleTime), - typeof(OsuModHalfTime), - typeof(OsuModEasy), - typeof(OsuModHardRock), + new OsuModDoubleTime(), + new OsuModHalfTime(), + new OsuModEasy(), + new OsuModHardRock(), }; } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index ad1fb4c2e5..685ad9949b 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.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.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; @@ -48,12 +47,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty protected override Skill[] CreateSkills(IBeatmap beatmap) => new Skill[] { new Strain() }; - protected override Type[] DifficultyAdjustmentMods => new[] + protected override Mod[] DifficultyAdjustmentMods => new Mod[] { - typeof(TaikoModDoubleTime), - typeof(TaikoModHalfTime), - typeof(TaikoModEasy), - typeof(TaikoModHardRock), + new TaikoModDoubleTime(), + new TaikoModHalfTime(), + new TaikoModEasy(), + new TaikoModHardRock(), }; } } diff --git a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs index 3bce6fedbb..760a033aff 100644 --- a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs +++ b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestSingleMod() { - var combinations = new TestLegacyDifficultyCalculator(typeof(ModA)).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(new ModA()).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(2, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -37,7 +37,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestDoubleMod() { - var combinations = new TestLegacyDifficultyCalculator(typeof(ModA), typeof(ModB)).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModB()).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(4, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -52,7 +52,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestIncompatibleMods() { - var combinations = new TestLegacyDifficultyCalculator(typeof(ModA), typeof(ModIncompatibleWithA)).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModIncompatibleWithA()).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(3, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -63,7 +63,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestDoubleIncompatibleMods() { - var combinations = new TestLegacyDifficultyCalculator(typeof(ModA), typeof(ModB), typeof(ModIncompatibleWithA), typeof(ModIncompatibleWithAAndB)).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModB(), new ModIncompatibleWithA(), new ModIncompatibleWithAAndB()).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(8, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -86,7 +86,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestIncompatibleThroughBaseType() { - var combinations = new TestLegacyDifficultyCalculator(typeof(ModAofA), typeof(ModIncompatibleWithAofA)).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(new ModAofA(), new ModIncompatibleWithAofA()).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(3, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -141,13 +141,13 @@ namespace osu.Game.Tests.NonVisual private class TestLegacyDifficultyCalculator : DifficultyCalculator { - public TestLegacyDifficultyCalculator(params Type[] mods) + public TestLegacyDifficultyCalculator(params Mod[] mods) : base(null, null) { DifficultyAdjustmentMods = mods; } - protected override Type[] DifficultyAdjustmentMods { get; } + protected override Mod[] DifficultyAdjustmentMods { get; } protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 9b630865c2..aad55f8a38 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -108,7 +108,7 @@ namespace osu.Game.Rulesets.Difficulty { return createDifficultyAdjustmentModCombinations(Enumerable.Empty(), DifficultyAdjustmentMods).ToArray(); - IEnumerable createDifficultyAdjustmentModCombinations(IEnumerable currentSet, Type[] adjustmentSet, int currentSetCount = 0, int adjustmentSetStart = 0) + IEnumerable createDifficultyAdjustmentModCombinations(IEnumerable currentSet, Mod[] adjustmentSet, int currentSetCount = 0, int adjustmentSetStart = 0) { switch (currentSetCount) { @@ -131,14 +131,12 @@ namespace osu.Game.Rulesets.Difficulty // combinations in further recursions, so a moving subset is used to eliminate this effect for (int i = adjustmentSetStart; i < adjustmentSet.Length; i++) { - var adjustmentMod = createMod(); + var adjustmentMod = adjustmentSet[i]; if (currentSet.Any(c => c.IncompatibleMods.Any(m => m.IsInstanceOfType(adjustmentMod)))) continue; - foreach (var combo in createDifficultyAdjustmentModCombinations(currentSet.Append(createMod()), adjustmentSet, currentSetCount + 1, i + 1)) + foreach (var combo in createDifficultyAdjustmentModCombinations(currentSet.Append(adjustmentMod), adjustmentSet, currentSetCount + 1, i + 1)) yield return combo; - - Mod createMod() => (Mod)Activator.CreateInstance(adjustmentSet[i]); } } } @@ -146,7 +144,7 @@ namespace osu.Game.Rulesets.Difficulty /// /// Retrieves all s which adjust the difficulty. /// - protected virtual Type[] DifficultyAdjustmentMods => Array.Empty(); + protected virtual Mod[] DifficultyAdjustmentMods => Array.Empty(); /// /// Creates to describe beatmap's calculated difficulty. From be5ffdbf22cd5a8dc5fca3c87306042741e2dafa Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 23 Mar 2019 16:01:14 +0900 Subject: [PATCH 007/121] Adjust edge bonuses to consider clock rate --- .../Preprocessing/CatchDifficultyHitObject.cs | 2 ++ .../Difficulty/Skills/Movement.cs | 21 +++++++------------ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs index f0c68e4392..b2b4129c8a 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs @@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing /// Milliseconds elapsed since the start time of the previous , with a minimum of 40ms. /// public readonly double StrainTime; + public readonly double ClockRate; public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth) : base(hitObject, lastObject, clockRate) @@ -36,6 +37,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure StrainTime = Math.Max(40, DeltaTime); + ClockRate = clockRate; } } } diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index d06ab2f928..227fb2820c 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills private const double direction_change_bonus = 9.8; private const double antiflow_bonus = 26.0; - protected override double SkillMultiplier => 860; + protected override double SkillMultiplier => 850; protected override double StrainDecayBase => 0.2; protected override double DecayWeight => 0.94; @@ -25,7 +25,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills private float? lastPlayerPosition; private float lastDistanceMoved; private double lastStrainTime; - private bool lastHyperdash; protected override double StrainValueOf(DifficultyHitObject current) { @@ -62,35 +61,32 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills // Direction changes after jumps (antiflow) are harder double antiflowBonusFactor = Math.Min(Math.Abs(distanceMoved) / 70, 1); - distanceAddition += (antiflow_bonus / (catchCurrent.StrainTime / 17.5 + 10)) * (Math.Sqrt(Math.Abs(lastDistanceMoved)) / Math.Sqrt(lastStrainTime + 25)) * antiflowBonusFactor; - - // Reduce strain slightly for Hyperdash chains - if (catchCurrent.LastObject.HyperDash && lastHyperdash) - distanceAddition *= 0.95; + distanceAddition += (antiflow_bonus / (catchCurrent.StrainTime / 17.5 + 10)) * (Math.Sqrt(Math.Abs(lastDistanceMoved)) / Math.Sqrt(lastStrainTime + 20)) * antiflowBonusFactor; + // Bonus for edge dashes on direction change if (catchCurrent.LastObject.DistanceToHyperDash <= 14.0f / CatchPlayfield.BASE_WIDTH && !catchCurrent.LastObject.HyperDash) - bonus += 3.0; + bonus += 1.0; } // Base bonus for every movement, giving some weight to streams. distanceAddition += 10.0 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain; } - // Big bonus for edge dashes + // Bonus for edge dashes regardless of direction change if (catchCurrent.LastObject.DistanceToHyperDash <= 14.0f / CatchPlayfield.BASE_WIDTH) { if (!catchCurrent.LastObject.HyperDash) - bonus += 4.5; + bonus += 0.9; else { // After a hyperdash we ARE in the correct position. Always! playerPosition = catchCurrent.NormalizedPosition; } - distanceAddition *= 1.0 + bonus * (14.0f - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 14.0f * (Math.Min(catchCurrent.StrainTime, 250) / 250); // Edge dashes are easier at lower ms values + distanceAddition *= 1.0 + bonus * Math.Pow(14.0f - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH, 1.6f) / 14.0f * (Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 250) / 250); // Edge dashes are easier at lower ms values } - // Prevent wide, dense stacks of notes which fit on the catcher from greatly increasing SR + // Prevent wide dense stacks of notes which fit on the catcher from greatly increasing SR if (Math.Abs(distanceMoved) > 0.1) { if (Math.Abs(lastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(lastDistanceMoved)) @@ -108,7 +104,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills lastPlayerPosition = playerPosition; lastDistanceMoved = distanceMoved; lastStrainTime = catchCurrent.StrainTime; - lastHyperdash = catchCurrent.LastObject.HyperDash; return distanceAddition / weightedStrainTime; } From 2705263145b1dcae03bc5b58304eebe9aebfa7f3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 26 Mar 2019 13:25:52 +0900 Subject: [PATCH 008/121] Scale edge dash threshold with clock rate --- .../Difficulty/Skills/Movement.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 227fb2820c..27558838f4 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -62,28 +62,26 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills double antiflowBonusFactor = Math.Min(Math.Abs(distanceMoved) / 70, 1); distanceAddition += (antiflow_bonus / (catchCurrent.StrainTime / 17.5 + 10)) * (Math.Sqrt(Math.Abs(lastDistanceMoved)) / Math.Sqrt(lastStrainTime + 20)) * antiflowBonusFactor; - - // Bonus for edge dashes on direction change - if (catchCurrent.LastObject.DistanceToHyperDash <= 14.0f / CatchPlayfield.BASE_WIDTH && !catchCurrent.LastObject.HyperDash) - bonus += 1.0; } // Base bonus for every movement, giving some weight to streams. distanceAddition += 10.0 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain; } - // Bonus for edge dashes regardless of direction change - if (catchCurrent.LastObject.DistanceToHyperDash <= 14.0f / CatchPlayfield.BASE_WIDTH) + // Bonus for edge dashes + double edgeDashThreshold = 15.5f * ((Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 250) * 0.9 + 25) / 250); + + if (catchCurrent.LastObject.DistanceToHyperDash <= edgeDashThreshold / CatchPlayfield.BASE_WIDTH) { if (!catchCurrent.LastObject.HyperDash) - bonus += 0.9; + bonus += 2.3; else { // After a hyperdash we ARE in the correct position. Always! playerPosition = catchCurrent.NormalizedPosition; } - distanceAddition *= 1.0 + bonus * Math.Pow(14.0f - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH, 1.6f) / 14.0f * (Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 250) / 250); // Edge dashes are easier at lower ms values + distanceAddition *= 1.0 + bonus * Math.Pow(edgeDashThreshold - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH, 1.6f) / edgeDashThreshold * (Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 265) / 265); // Edge dashes are easier at lower ms values } // Prevent wide dense stacks of notes which fit on the catcher from greatly increasing SR From 9d0d402336e6b7123ee715105d953979797cc44c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Mar 2019 17:21:57 +0900 Subject: [PATCH 009/121] Apply pp calculator changes (Backported from https://github.com/ppy/osu-performance/compare/master...smoogipoo:sorcerer-catch-changes) --- .../Difficulty/CatchPerformanceCalculator.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs index 5a640f6d1a..28da047187 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs @@ -55,8 +55,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty // Longer maps are worth more float lengthBonus = - 0.95f + 0.4f * Math.Min(1.0f, numTotalHits / 3000.0f) + - (numTotalHits > 3000 ? (float)Math.Log10(numTotalHits / 3000.0f) * 0.5f : 0.0f); + 0.95f + 0.3f * Math.Min(1.0f, numTotalHits / 2500.0f) + + (numTotalHits > 2500 ? (float)Math.Log10(numTotalHits / 2500.0f) * 0.475f : 0.0f); // Longer maps are worth more value *= lengthBonus; @@ -73,14 +73,22 @@ namespace osu.Game.Rulesets.Catch.Difficulty float approachRateFactor = 1.0f; if (approachRate > 9.0f) approachRateFactor += 0.1f * (approachRate - 9.0f); // 10% for each AR above 9 + if (approachRate > 10.0f) + approachRateFactor += 0.2f * (float)Math.Pow(approachRate - 10.0f, 1.5f); // Additional 20% at AR 11, 40% total else if (approachRate < 8.0f) approachRateFactor += 0.025f * (8.0f - approachRate); // 2.5% for each AR below 8 value *= approachRateFactor; if (mods.Any(m => m is ModHidden)) - // Hiddens gives nothing on max approach rate, and more the lower it is + { value *= 1.05f + 0.075f * (10.0f - Math.Min(10.0f, approachRate)); // 7.5% for each AR below 10 + // Hiddens gives almost nothing on max approach rate, and more the lower it is + if (approachRate <= 10.0f) + value *= 1.05f + 0.075f * (10.0f - approachRate); // 7.5% for each AR below 10 + else if (approachRate > 10.0f) + value *= 1.01f + 0.04f * (11.0f - Math.Min(11.0f, approachRate)); // 5% at AR 10, 1% at AR 11 + } if (mods.Any(m => m is ModFlashlight)) // Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps. From b402981fc644c85422d52449c90cd3120a3bd44d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Apr 2019 10:57:01 +0900 Subject: [PATCH 010/121] Buff CS > 5 --- .../Difficulty/CatchDifficultyCalculator.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 47e2bc5259..a475aefd71 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -52,7 +52,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty using (var catcher = new CatcherArea.Catcher(beatmap.BeatmapInfo.BaseDifficulty)) { halfCatchWidth = catcher.CatchWidth * 0.5f; - halfCatchWidth *= 0.8f; // We're only using 80% of the catcher's width to simulate imperfect gameplay. + // We're only using 80% of the catcher's width to simulate imperfect gameplay. + halfCatchWidth *= Math.Min(1.05f - (0.05f * beatmap.BeatmapInfo.BaseDifficulty.CircleSize), 0.8f); // Reduce the catcher's width further at circle sizes above 5. } CatchHitObject lastObject = null; From b2396b82a5bf218ac61786f9e38ae8a6976d91c8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Apr 2019 10:58:26 +0900 Subject: [PATCH 011/121] Change edge dashes to scale linearly once again --- .../Preprocessing/CatchDifficultyHitObject.cs | 3 +++ osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs | 10 ++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs index b2b4129c8a..9f6de35605 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs @@ -25,6 +25,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing /// public readonly double StrainTime; public readonly double ClockRate; + public readonly double HalfCatcherWidth; public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth) : base(hitObject, lastObject, clockRate) @@ -38,6 +39,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure StrainTime = Math.Max(40, DeltaTime); ClockRate = clockRate; + HalfCatcherWidth = halfCatcherWidth; } } } + diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 27558838f4..473b254d5b 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -68,20 +68,18 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills distanceAddition += 10.0 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain; } - // Bonus for edge dashes - double edgeDashThreshold = 15.5f * ((Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 250) * 0.9 + 25) / 250); - - if (catchCurrent.LastObject.DistanceToHyperDash <= edgeDashThreshold / CatchPlayfield.BASE_WIDTH) + // Bonus for "almost" hyperdashes at corner points + if (catchCurrent.LastObject.DistanceToHyperDash <= 20.0f / CatchPlayfield.BASE_WIDTH) { if (!catchCurrent.LastObject.HyperDash) - bonus += 2.3; + bonus += 5.7; else { // After a hyperdash we ARE in the correct position. Always! playerPosition = catchCurrent.NormalizedPosition; } - distanceAddition *= 1.0 + bonus * Math.Pow(edgeDashThreshold - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH, 1.6f) / edgeDashThreshold * (Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 265) / 265); // Edge dashes are easier at lower ms values + distanceAddition *= 1.0 + bonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime, 265) / 265), 1.5); } // Prevent wide dense stacks of notes which fit on the catcher from greatly increasing SR From efee2fb283903f00b4039330117836c271016eb0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Apr 2019 11:00:26 +0900 Subject: [PATCH 012/121] Adjust antiflow calculations --- .../Difficulty/Skills/Movement.cs | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 473b254d5b..e78e5c0a58 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -14,10 +14,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills { private const float absolute_player_positioning_error = 16f; private const float normalized_hitobject_radius = 41.0f; - private const double direction_change_bonus = 9.8; - private const double antiflow_bonus = 26.0; + private const double direction_change_bonus = 21.0; - protected override double SkillMultiplier => 850; + protected override double SkillMultiplier => 900; protected override double StrainDecayBase => 0.2; protected override double DecayWeight => 0.94; @@ -29,6 +28,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills protected override double StrainValueOf(DifficultyHitObject current) { var catchCurrent = (CatchDifficultyHitObject)current; + double halfCatcherWidth = catchCurrent.HalfCatcherWidth; if (lastPlayerPosition == null) lastPlayerPosition = catchCurrent.LastNormalizedPosition; @@ -41,10 +41,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills float distanceMoved = playerPosition - lastPlayerPosition.Value; - // Reduce speed scaling - double weightedStrainTime = catchCurrent.StrainTime + 20; + double weightedStrainTime = catchCurrent.StrainTime + 18; - double distanceAddition = Math.Pow(Math.Abs(distanceMoved), 1.2) / 340; + double distanceAddition = (Math.Pow(Math.Abs(distanceMoved), 1.3) / 510); double sqrtStrain = Math.Sqrt(weightedStrainTime); double bonus = 0; @@ -54,18 +53,14 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills { if (Math.Abs(lastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(lastDistanceMoved)) { - double bonusFactor = Math.Min(absolute_player_positioning_error, Math.Abs(distanceMoved)) / absolute_player_positioning_error; + double bonusFactor = Math.Min(halfCatcherWidth, Math.Abs(distanceMoved)) / halfCatcherWidth; + double antiflowFactor = Math.Max(Math.Min(halfCatcherWidth, Math.Abs(lastDistanceMoved)) / halfCatcherWidth, 0.3); - distanceAddition += direction_change_bonus / sqrtStrain * bonusFactor; - - // Direction changes after jumps (antiflow) are harder - double antiflowBonusFactor = Math.Min(Math.Abs(distanceMoved) / 70, 1); - - distanceAddition += (antiflow_bonus / (catchCurrent.StrainTime / 17.5 + 10)) * (Math.Sqrt(Math.Abs(lastDistanceMoved)) / Math.Sqrt(lastStrainTime + 20)) * antiflowBonusFactor; + distanceAddition += direction_change_bonus / Math.Sqrt(lastStrainTime + 18) * bonusFactor * antiflowFactor * Math.Max(1 - Math.Pow(weightedStrainTime / 1000, 2), 0); } // Base bonus for every movement, giving some weight to streams. - distanceAddition += 10.0 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain; + distanceAddition += 12.5 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain; } // Bonus for "almost" hyperdashes at corner points From 21e62c37d8d2fcacb49a1f475ae65d73f04b4abf Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Apr 2019 07:28:04 +0900 Subject: [PATCH 013/121] General fixes --- .../Difficulty/CatchDifficultyCalculator.cs | 2 +- .../Preprocessing/CatchDifficultyHitObject.cs | 3 --- .../Difficulty/Skills/Movement.cs | 13 ++++++------- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index a475aefd71..4cd297478a 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty { public class CatchDifficultyCalculator : DifficultyCalculator { - private const double star_scaling_factor = 0.15; + private const double star_scaling_factor = 0.153; protected override int SectionLength => 750; diff --git a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs index 9f6de35605..b2b4129c8a 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs @@ -25,7 +25,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing /// public readonly double StrainTime; public readonly double ClockRate; - public readonly double HalfCatcherWidth; public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth) : base(hitObject, lastObject, clockRate) @@ -39,8 +38,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure StrainTime = Math.Max(40, DeltaTime); ClockRate = clockRate; - HalfCatcherWidth = halfCatcherWidth; } } } - diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index e78e5c0a58..4ea5135c4f 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -28,7 +28,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills protected override double StrainValueOf(DifficultyHitObject current) { var catchCurrent = (CatchDifficultyHitObject)current; - double halfCatcherWidth = catchCurrent.HalfCatcherWidth; if (lastPlayerPosition == null) lastPlayerPosition = catchCurrent.LastNormalizedPosition; @@ -46,17 +45,17 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills double distanceAddition = (Math.Pow(Math.Abs(distanceMoved), 1.3) / 510); double sqrtStrain = Math.Sqrt(weightedStrainTime); - double bonus = 0; + double edgeDashBonus = 0; // Direction changes give an extra point! if (Math.Abs(distanceMoved) > 0.1) { if (Math.Abs(lastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(lastDistanceMoved)) { - double bonusFactor = Math.Min(halfCatcherWidth, Math.Abs(distanceMoved)) / halfCatcherWidth; - double antiflowFactor = Math.Max(Math.Min(halfCatcherWidth, Math.Abs(lastDistanceMoved)) / halfCatcherWidth, 0.3); + double bonusFactor = Math.Min(50, Math.Abs(distanceMoved)) / 50; + double antiflowFactor = Math.Max(Math.Min(70, Math.Abs(lastDistanceMoved)) / 70, 0.3); - distanceAddition += direction_change_bonus / Math.Sqrt(lastStrainTime + 18) * bonusFactor * antiflowFactor * Math.Max(1 - Math.Pow(weightedStrainTime / 1000, 2), 0); + distanceAddition += direction_change_bonus / Math.Sqrt(lastStrainTime + 18) * bonusFactor * antiflowFactor * Math.Max(1 - Math.Pow(weightedStrainTime / 1000, 3), 0); } // Base bonus for every movement, giving some weight to streams. @@ -67,14 +66,14 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills if (catchCurrent.LastObject.DistanceToHyperDash <= 20.0f / CatchPlayfield.BASE_WIDTH) { if (!catchCurrent.LastObject.HyperDash) - bonus += 5.7; + edgeDashBonus += 5.7; else { // After a hyperdash we ARE in the correct position. Always! playerPosition = catchCurrent.NormalizedPosition; } - distanceAddition *= 1.0 + bonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime, 265) / 265), 1.5); + distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values } // Prevent wide dense stacks of notes which fit on the catcher from greatly increasing SR From 5566c4881a25af18e2b34407260966d148f51ff0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Apr 2019 11:38:48 +0900 Subject: [PATCH 014/121] Buff DT --- osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 4ea5135c4f..7d5a7b4007 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills float distanceMoved = playerPosition - lastPlayerPosition.Value; - double weightedStrainTime = catchCurrent.StrainTime + 18; + double weightedStrainTime = catchCurrent.StrainTime + 10 + (8 / catchCurrent.ClockRate); double distanceAddition = (Math.Pow(Math.Abs(distanceMoved), 1.3) / 510); double sqrtStrain = Math.Sqrt(weightedStrainTime); From 2824a32db60b99222ac2f862e1f54c980af0aab7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Apr 2019 11:39:13 +0900 Subject: [PATCH 015/121] Adjust circle-size bonus point --- .../Difficulty/CatchDifficultyCalculator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 4cd297478a..c56881ba51 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -52,8 +52,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty using (var catcher = new CatcherArea.Catcher(beatmap.BeatmapInfo.BaseDifficulty)) { halfCatchWidth = catcher.CatchWidth * 0.5f; - // We're only using 80% of the catcher's width to simulate imperfect gameplay. - halfCatchWidth *= Math.Min(1.05f - (0.05f * beatmap.BeatmapInfo.BaseDifficulty.CircleSize), 0.8f); // Reduce the catcher's width further at circle sizes above 5. + // We're only using 80% of the catcher's width to simulate imperfect gameplay, reduced further at circle sizes above 5.5 + halfCatchWidth *= Math.Min(1.075f - (0.05f * beatmap.BeatmapInfo.BaseDifficulty.CircleSize), 0.8f); } CatchHitObject lastObject = null; From 9d116efdbd8731443e46195f666aa1c21752aec5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 10 Apr 2019 10:57:27 +0900 Subject: [PATCH 016/121] Limit to 10000 tiny ticks per slider --- osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 2adc156efd..9baa6a8531 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -55,6 +55,8 @@ namespace osu.Game.Rulesets.Catch.Objects SliderEventDescriptor? lastEvent = null; + int ticksGenerated = 0; + foreach (var e in SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset)) { // generate tiny droplets since the last point @@ -70,6 +72,9 @@ namespace osu.Game.Rulesets.Catch.Objects for (double t = timeBetweenTiny; t < sinceLastTick; t += timeBetweenTiny) { + if (ticksGenerated++ >= 10000) + break; + AddNested(new TinyDroplet { Samples = tickSamples, From 2fec8b7b8555101200250846e9ea0958f77b6b1c Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Sun, 5 Apr 2020 13:01:10 +0930 Subject: [PATCH 017/121] Use DisplayModes rather than AvailableResolutions --- .../Settings/Sections/Graphics/LayoutSettings.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index b73c8f7622..00b7643332 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -209,15 +209,16 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private IReadOnlyList getResolutions() { var resolutions = new List { new Size(9999, 9999) }; + var currentDisplay = game.Window?.CurrentDisplay.Value; - if (game.Window != null) + if (currentDisplay != null) { - resolutions.AddRange(game.Window.AvailableResolutions - .Where(r => r.Width >= 800 && r.Height >= 600) - .OrderByDescending(r => r.Width) - .ThenByDescending(r => r.Height) - .Select(res => new Size(res.Width, res.Height)) - .Distinct()); + resolutions.AddRange(currentDisplay.DisplayModes + .Where(m => m.Size.Width >= 800 && m.Size.Height >= 600) + .OrderByDescending(m => m.Size.Width) + .ThenByDescending(m => m.Size.Height) + .Select(m => m.Size) + .Distinct()); } return resolutions; From c5aae9b757ef7c726b513a7b0dfd2e1b55d2cda2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 8 Apr 2020 12:19:09 +0900 Subject: [PATCH 018/121] Fix post-merge errors --- .../Difficulty/CatchDifficultyCalculator.cs | 14 +++++--------- .../Preprocessing/CatchDifficultyHitObject.cs | 1 + 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 7d763f1792..ee3b410780 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -50,15 +50,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) { - float halfCatchWidth; - - using (var catcher = new CatcherArea.Catcher(beatmap.BeatmapInfo.BaseDifficulty)) - { - halfCatchWidth = catcher.CatchWidth * 0.5f; - // We're only using 80% of the catcher's width to simulate imperfect gameplay, reduced further at circle sizes above 5.5 - halfCatchWidth *= Math.Min(1.075f - (0.05f * beatmap.BeatmapInfo.BaseDifficulty.CircleSize), 0.8f); - } - CatchHitObject lastObject = null; // In 2B beatmaps, it is possible that a normal Fruit is placed in the middle of a JuiceStream. @@ -81,8 +72,13 @@ namespace osu.Game.Rulesets.Catch.Difficulty protected override Skill[] CreateSkills(IBeatmap beatmap) { using (var catcher = new Catcher(beatmap.BeatmapInfo.BaseDifficulty)) + { halfCatcherWidth = catcher.CatchWidth * 0.5f; + // For circle sizes above 5.5, reduce the catcher width further to simulate imperfect gameplay. + halfCatcherWidth *= 1 - (Math.Max(0, beatmap.BeatmapInfo.BaseDifficulty.CircleSize - 5.5f) * 0.0625f); + } + return new Skill[] { new Movement(halfCatcherWidth), diff --git a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs index b2b4129c8a..360af1a8c9 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs @@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing /// Milliseconds elapsed since the start time of the previous , with a minimum of 40ms. /// public readonly double StrainTime; + public readonly double ClockRate; public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth) From fd51bbb9ecd1e7e381c481d50c85ba392a63bc75 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 8 Apr 2020 12:20:46 +0900 Subject: [PATCH 019/121] Apply latest changes --- .../Difficulty/Skills/Movement.cs | 25 ++++--------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 3ab4ae63a1..5cd2f1f581 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -46,29 +46,29 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills float distanceMoved = playerPosition - lastPlayerPosition.Value; - double weightedStrainTime = catchCurrent.StrainTime + 10 + (8 / catchCurrent.ClockRate); + double weightedStrainTime = catchCurrent.StrainTime + 13 + (3 / catchCurrent.ClockRate); double distanceAddition = (Math.Pow(Math.Abs(distanceMoved), 1.3) / 510); double sqrtStrain = Math.Sqrt(weightedStrainTime); double edgeDashBonus = 0; - // Direction changes give an extra point! + // Direction change bonus. if (Math.Abs(distanceMoved) > 0.1) { if (Math.Abs(lastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(lastDistanceMoved)) { double bonusFactor = Math.Min(50, Math.Abs(distanceMoved)) / 50; - double antiflowFactor = Math.Max(Math.Min(70, Math.Abs(lastDistanceMoved)) / 70, 0.3); + double antiflowFactor = Math.Max(Math.Min(70, Math.Abs(lastDistanceMoved)) / 70, 0.38); - distanceAddition += direction_change_bonus / Math.Sqrt(lastStrainTime + 18) * bonusFactor * antiflowFactor * Math.Max(1 - Math.Pow(weightedStrainTime / 1000, 3), 0); + distanceAddition += direction_change_bonus / Math.Sqrt(lastStrainTime + 16) * bonusFactor * antiflowFactor * Math.Max(1 - Math.Pow(weightedStrainTime / 1000, 3), 0); } // Base bonus for every movement, giving some weight to streams. distanceAddition += 12.5 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain; } - // Bonus for "almost" hyperdashes at corner points + // Bonus for edge dashes. if (catchCurrent.LastObject.DistanceToHyperDash <= 20.0f / CatchPlayfield.BASE_WIDTH) { if (!catchCurrent.LastObject.HyperDash) @@ -82,21 +82,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values } - // Prevent wide dense stacks of notes which fit on the catcher from greatly increasing SR - if (Math.Abs(distanceMoved) > 0.1) - { - if (Math.Abs(lastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(lastDistanceMoved)) - { - if (Math.Abs(distanceMoved) <= (CatcherArea.CATCHER_SIZE) && Math.Abs(lastDistanceMoved) == Math.Abs(distanceMoved)) - { - if (catchCurrent.StrainTime <= 80 && lastStrainTime == catchCurrent.StrainTime) - { - distanceAddition *= Math.Max(((catchCurrent.StrainTime / 80) - 0.75) * 4, 0); - } - } - } - } - lastPlayerPosition = playerPosition; lastDistanceMoved = distanceMoved; lastStrainTime = catchCurrent.StrainTime; From 91b3aa2914427cfa0af3cd1dbc58ea4b25ad9919 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 9 Apr 2020 22:56:10 +0300 Subject: [PATCH 020/121] Implement interval list --- osu.Game/Lists/IntervalList.cs | 128 +++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 osu.Game/Lists/IntervalList.cs diff --git a/osu.Game/Lists/IntervalList.cs b/osu.Game/Lists/IntervalList.cs new file mode 100644 index 0000000000..493b6b6e72 --- /dev/null +++ b/osu.Game/Lists/IntervalList.cs @@ -0,0 +1,128 @@ +// 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; +using System.Collections.Generic; +using osu.Framework.Lists; + +namespace osu.Game.Lists +{ + /// + /// Represents a list of intervals that can be used for whether a specific value falls into one of them. + /// + /// The type of interval values. + public class IntervalList : ICollection>, IReadOnlyList> + { + private static readonly IComparer type_comparer = Comparer.Default; + + private readonly SortedList> intervals = new SortedList>((x, y) => type_comparer.Compare(x.Start, y.Start)); + + /// + /// The index of the nearest interval from last call. + /// + protected int NearestIntervalIndex; + + /// + /// Whether the provided value is in any interval added to this list. + /// + /// The value to check for. + public bool IsInAnyInterval(T value) + { + if (intervals.Count == 0) + return false; + + // Clamp the nearest index in case there were intervals + // removed from the list causing the index to go out of range. + NearestIntervalIndex = Math.Clamp(NearestIntervalIndex, 0, Count - 1); + + if (type_comparer.Compare(value, this[NearestIntervalIndex].End) > 0) + { + while (type_comparer.Compare(value, this[NearestIntervalIndex].End) > 0 && NearestIntervalIndex < Count - 1) + NearestIntervalIndex++; + } + else + { + while (type_comparer.Compare(value, this[NearestIntervalIndex].Start) < 0 && NearestIntervalIndex > 0) + NearestIntervalIndex--; + } + + var nearestInterval = this[NearestIntervalIndex]; + + return type_comparer.Compare(value, nearestInterval.Start) >= 0 && + type_comparer.Compare(value, nearestInterval.End) <= 0; + } + + /// + /// Adds a new interval to the list. + /// + /// The start value of the interval. + /// The end value of the interval. + public void Add(T start, T end) => Add(new Interval(start, end)); + + #region ICollection> + + public int Count => intervals.Count; + + bool ICollection>.IsReadOnly => false; + + /// + /// Adds a new interval to the list + /// + /// The interval to add. + public void Add(Interval interval) => intervals.Add(interval); + + /// + /// Removes an existing interval from the list. + /// + /// The interval to remove. + /// Whether the provided interval exists in the list and has been removed. + public bool Remove(Interval interval) => intervals.Remove(interval); + + /// + /// Removes all intervals from the list. + /// + public void Clear() => intervals.Clear(); + + public void CopyTo(Interval[] array, int arrayIndex) => intervals.CopyTo(array, arrayIndex); + + public bool Contains(Interval item) => intervals.Contains(item); + + public IEnumerator> GetEnumerator() => intervals.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + #endregion + + #region IReadOnlyList> + + public Interval this[int index] + { + get => intervals[index]; + set => intervals[index] = value; + } + + #endregion + } + + public readonly struct Interval + { + /// + /// The start value of this interval. + /// + public readonly T Start; + + /// + /// The end value of this interval. + /// + public readonly T End; + + public Interval(T start, T end) + { + bool startLessThanEnd = Comparer.Default.Compare(start, end) < 0; + + Start = startLessThanEnd ? start : end; + End = startLessThanEnd ? end : start; + } + } +} From 38ee5f310302e3d05b240c4d55e1bb4e1bd5f8e7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 9 Apr 2020 22:57:54 +0300 Subject: [PATCH 021/121] Add tests covering most cases of interval list checking --- osu.Game.Tests/Lists/IntervalListTest.cs | 141 +++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 osu.Game.Tests/Lists/IntervalListTest.cs diff --git a/osu.Game.Tests/Lists/IntervalListTest.cs b/osu.Game.Tests/Lists/IntervalListTest.cs new file mode 100644 index 0000000000..1bc1483e15 --- /dev/null +++ b/osu.Game.Tests/Lists/IntervalListTest.cs @@ -0,0 +1,141 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Lists; + +namespace osu.Game.Tests.Lists +{ + [TestFixture] + public class IntervalListTest + { + // this is intended to be unordered to test adding intervals in unordered way. + private static readonly (double, double)[] test_intervals = + { + (-9.1d, -8.3d), + (-3.4d, 2.1d), + (50.0d, 9.0d), // intentionally reversing interval. + (5.25d, 10.50d), + }; + + [Test] + public void TestCheckValueInsideSingleInterval() + { + var list = new IntervalList { { 1.0d, 2.0d } }; + + Assert.IsTrue(list.IsInAnyInterval(1.0d)); + Assert.IsTrue(list.IsInAnyInterval(1.5d)); + Assert.IsTrue(list.IsInAnyInterval(2.0d)); + } + + [Test] + public void TestCheckValuesInsideIntervals() + { + var list = new IntervalList(); + + foreach (var (start, end) in test_intervals) + list.Add(start, end); + + Assert.IsTrue(list.IsInAnyInterval(-8.75d)); + Assert.IsTrue(list.IsInAnyInterval(1.0d)); + Assert.IsTrue(list.IsInAnyInterval(7.89d)); + Assert.IsTrue(list.IsInAnyInterval(9.8d)); + Assert.IsTrue(list.IsInAnyInterval(15.83d)); + } + + [Test] + public void TestCheckValuesInRandomOrder() + { + var list = new IntervalList(); + + foreach (var (start, end) in test_intervals) + list.Add(start, end); + + Assert.IsTrue(list.IsInAnyInterval(9.8d)); + Assert.IsTrue(list.IsInAnyInterval(7.89d)); + Assert.IsTrue(list.IsInAnyInterval(1.0d)); + Assert.IsTrue(list.IsInAnyInterval(15.83d)); + Assert.IsTrue(list.IsInAnyInterval(-8.75d)); + } + + [Test] + public void TestCheckValuesOutOfIntervals() + { + var list = new IntervalList(); + + foreach (var (start, end) in test_intervals) + list.Add(start, end); + + Assert.IsFalse(list.IsInAnyInterval(-9.2d)); + Assert.IsFalse(list.IsInAnyInterval(2.2d)); + Assert.IsFalse(list.IsInAnyInterval(5.15d)); + Assert.IsFalse(list.IsInAnyInterval(51.2d)); + } + + [Test] + public void TestCheckValueAfterRemovedInterval() + { + var list = new IntervalList { { 50, 100 }, { 150, 200 }, { 250, 300 } }; + + Assert.IsTrue(list.IsInAnyInterval(75)); + Assert.IsTrue(list.IsInAnyInterval(175)); + Assert.IsTrue(list.IsInAnyInterval(275)); + + list.Remove(list[1]); + + Assert.IsFalse(list.IsInAnyInterval(175)); + Assert.IsTrue(list.IsInAnyInterval(75)); + Assert.IsTrue(list.IsInAnyInterval(275)); + } + + [Test] + public void TestCheckValueAfterAddedInterval() + { + var list = new IntervalList { { 50, 100 }, { 250, 300 } }; + + Assert.IsFalse(list.IsInAnyInterval(175)); + Assert.IsTrue(list.IsInAnyInterval(75)); + Assert.IsTrue(list.IsInAnyInterval(275)); + + list.Add(150, 200); + + Assert.IsTrue(list.IsInAnyInterval(175)); + } + + [Test] + public void TestCheckIntervalIndexOnChecks() + { + var list = new TestIntervalList { { 1.0d, 2.0d }, { 3.0d, 4.0d }, { 5.0d, 6.0d }, { 7.0d, 8.0d } }; + + Assert.IsTrue(list.IsInAnyInterval(1.5d)); + Assert.IsTrue(list.NearestIntervalIndex == 0); + + Assert.IsTrue(list.IsInAnyInterval(5.5d)); + Assert.IsTrue(list.NearestIntervalIndex == 2); + + Assert.IsTrue(list.IsInAnyInterval(7.5d)); + Assert.IsTrue(list.NearestIntervalIndex == 3); + } + + [Test] + public void TestCheckIntervalIndexOnOutOfIntervalsChecks() + { + var list = new TestIntervalList { { 1.0d, 2.0d }, { 3.0d, 4.0d }, { 5.0d, 6.0d }, { 7.0d, 8.0d } }; + + Assert.IsFalse(list.IsInAnyInterval(4.5d)); + Assert.IsTrue(list.NearestIntervalIndex == 1 || + list.NearestIntervalIndex == 2); // 4.5 in between 3.0-4.0 and 5.0-6.0 + + Assert.IsFalse(list.IsInAnyInterval(9.0d)); + Assert.IsTrue(list.NearestIntervalIndex == 3); // 9.0 goes above 7.0-8.0 + + Assert.IsFalse(list.IsInAnyInterval(0.0d)); + Assert.IsTrue(list.NearestIntervalIndex == 0); // 0.0 goes below 1.0-2.0 + } + + private class TestIntervalList : IntervalList + { + public new int NearestIntervalIndex => base.NearestIntervalIndex; + } + } +} From 9a29797a5bee661a767d43584af541d6619d5ea5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 9 Apr 2020 23:00:16 +0300 Subject: [PATCH 022/121] Use IntervalList for tracking break periods --- .../Visual/Gameplay/TestSceneAutoplay.cs | 2 +- .../Visual/Gameplay/TestSceneBreakTracker.cs | 4 -- osu.Game/Screens/Play/BreakTracker.cs | 50 ++++++------------- 3 files changed, 16 insertions(+), 40 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs index 4b1c2ec256..0be949650e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs @@ -52,7 +52,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep($"seek to break {breakIndex}", () => Player.GameplayClockContainer.Seek(destBreak().StartTime)); AddUntilStep("wait for seek to complete", () => Player.HUDOverlay.Progress.ReferenceClock.CurrentTime >= destBreak().StartTime); - BreakPeriod destBreak() => Player.ChildrenOfType().First().Breaks.ElementAt(breakIndex); + BreakPeriod destBreak() => Beatmap.Value.Beatmap.Breaks.ElementAt(breakIndex); } } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakTracker.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakTracker.cs index 91d6f2f143..a6f996c30d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakTracker.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakTracker.cs @@ -97,8 +97,6 @@ namespace osu.Game.Tests.Visual.Gameplay loadBreaksStep("multiple breaks", testBreaks); seekAndAssertBreak("seek to break start", testBreaks[1].StartTime, true); - AddAssert("is skipped to break #2", () => breakTracker.CurrentBreakIndex == 1); - seekAndAssertBreak("seek to break middle", testBreaks[1].StartTime + testBreaks[1].Duration / 2, true); seekAndAssertBreak("seek to break end", testBreaks[1].EndTime, false); seekAndAssertBreak("seek to break after end", testBreaks[1].EndTime + 500, false); @@ -174,8 +172,6 @@ namespace osu.Game.Tests.Visual.Gameplay private readonly ManualClock manualClock; private IFrameBasedClock originalClock; - public new int CurrentBreakIndex => base.CurrentBreakIndex; - public double ManualClockTime { get => manualClock.CurrentTime; diff --git a/osu.Game/Screens/Play/BreakTracker.cs b/osu.Game/Screens/Play/BreakTracker.cs index 64262d52b5..c2eb069ee6 100644 --- a/osu.Game/Screens/Play/BreakTracker.cs +++ b/osu.Game/Screens/Play/BreakTracker.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps.Timing; +using osu.Game.Lists; using osu.Game.Rulesets.Scoring; namespace osu.Game.Screens.Play @@ -20,22 +21,24 @@ namespace osu.Game.Screens.Play /// public IBindable IsBreakTime => isBreakTime; - protected int CurrentBreakIndex; - private readonly BindableBool isBreakTime = new BindableBool(); - private IReadOnlyList breaks; + private readonly IntervalList breakIntervals = new IntervalList(); public IReadOnlyList Breaks { - get => breaks; set { - breaks = value; - - // reset index in case the new breaks list is smaller than last one isBreakTime.Value = false; - CurrentBreakIndex = 0; + breakIntervals.Clear(); + + foreach (var b in value) + { + if (!b.HasEffect) + continue; + + breakIntervals.Add(b.StartTime, b.EndTime - BreakOverlay.BREAK_FADE_DURATION); + } } } @@ -49,34 +52,11 @@ namespace osu.Game.Screens.Play { base.Update(); - isBreakTime.Value = getCurrentBreak()?.HasEffect == true - || Clock.CurrentTime < gameplayStartTime + var time = Clock.CurrentTime; + + isBreakTime.Value = breakIntervals.IsInAnyInterval(time) + || time < gameplayStartTime || scoreProcessor?.HasCompleted == true; } - - private BreakPeriod getCurrentBreak() - { - if (breaks?.Count > 0) - { - var time = Clock.CurrentTime; - - if (time > breaks[CurrentBreakIndex].EndTime) - { - while (time > breaks[CurrentBreakIndex].EndTime && CurrentBreakIndex < breaks.Count - 1) - CurrentBreakIndex++; - } - else - { - while (time < breaks[CurrentBreakIndex].StartTime && CurrentBreakIndex > 0) - CurrentBreakIndex--; - } - - var closest = breaks[CurrentBreakIndex]; - - return closest.Contains(time) ? closest : null; - } - - return null; - } } } From 1001fcfb94d28787d62fa9e185499188620b4522 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 10 Apr 2020 15:48:00 +0300 Subject: [PATCH 023/121] Revert nearest index accessibility back to private Implementation details should not be tested. --- osu.Game/Lists/IntervalList.cs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/osu.Game/Lists/IntervalList.cs b/osu.Game/Lists/IntervalList.cs index 493b6b6e72..6a71d94ea8 100644 --- a/osu.Game/Lists/IntervalList.cs +++ b/osu.Game/Lists/IntervalList.cs @@ -17,11 +17,8 @@ namespace osu.Game.Lists private static readonly IComparer type_comparer = Comparer.Default; private readonly SortedList> intervals = new SortedList>((x, y) => type_comparer.Compare(x.Start, y.Start)); + private int nearestIndex; - /// - /// The index of the nearest interval from last call. - /// - protected int NearestIntervalIndex; /// /// Whether the provided value is in any interval added to this list. @@ -34,20 +31,20 @@ namespace osu.Game.Lists // Clamp the nearest index in case there were intervals // removed from the list causing the index to go out of range. - NearestIntervalIndex = Math.Clamp(NearestIntervalIndex, 0, Count - 1); + nearestIndex = Math.Clamp(nearestIndex, 0, intervals.Count - 1); - if (type_comparer.Compare(value, this[NearestIntervalIndex].End) > 0) + if (type_comparer.Compare(value, this[nearestIndex].End) > 0) { - while (type_comparer.Compare(value, this[NearestIntervalIndex].End) > 0 && NearestIntervalIndex < Count - 1) - NearestIntervalIndex++; + while (type_comparer.Compare(value, this[nearestIndex].End) > 0 && nearestIndex < intervals.Count - 1) + nearestIndex++; } else { - while (type_comparer.Compare(value, this[NearestIntervalIndex].Start) < 0 && NearestIntervalIndex > 0) - NearestIntervalIndex--; + while (type_comparer.Compare(value, this[nearestIndex].Start) < 0 && nearestIndex > 0) + nearestIndex--; } - var nearestInterval = this[NearestIntervalIndex]; + var nearestInterval = this[nearestIndex]; return type_comparer.Compare(value, nearestInterval.Start) >= 0 && type_comparer.Compare(value, nearestInterval.End) <= 0; From b7ed17dfbd16a78561da25516757bc19170e9af2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 10 Apr 2020 15:49:06 +0300 Subject: [PATCH 024/121] Throw if interval with reversed values added May reduce in subtle bugs. --- osu.Game.Tests/Lists/IntervalListTest.cs | 38 ++++-------------------- osu.Game/Lists/IntervalList.cs | 7 +++-- 2 files changed, 10 insertions(+), 35 deletions(-) diff --git a/osu.Game.Tests/Lists/IntervalListTest.cs b/osu.Game.Tests/Lists/IntervalListTest.cs index 1bc1483e15..0958f0fa7c 100644 --- a/osu.Game.Tests/Lists/IntervalListTest.cs +++ b/osu.Game.Tests/Lists/IntervalListTest.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using NUnit.Framework; using osu.Game.Lists; @@ -14,7 +15,7 @@ namespace osu.Game.Tests.Lists { (-9.1d, -8.3d), (-3.4d, 2.1d), - (50.0d, 9.0d), // intentionally reversing interval. + (9.0d, 50.0d), (5.25d, 10.50d), }; @@ -103,39 +104,12 @@ namespace osu.Game.Tests.Lists } [Test] - public void TestCheckIntervalIndexOnChecks() + public void TestReversedIntervalThrows() { - var list = new TestIntervalList { { 1.0d, 2.0d }, { 3.0d, 4.0d }, { 5.0d, 6.0d }, { 7.0d, 8.0d } }; + var list = new IntervalList(); - Assert.IsTrue(list.IsInAnyInterval(1.5d)); - Assert.IsTrue(list.NearestIntervalIndex == 0); - - Assert.IsTrue(list.IsInAnyInterval(5.5d)); - Assert.IsTrue(list.NearestIntervalIndex == 2); - - Assert.IsTrue(list.IsInAnyInterval(7.5d)); - Assert.IsTrue(list.NearestIntervalIndex == 3); - } - - [Test] - public void TestCheckIntervalIndexOnOutOfIntervalsChecks() - { - var list = new TestIntervalList { { 1.0d, 2.0d }, { 3.0d, 4.0d }, { 5.0d, 6.0d }, { 7.0d, 8.0d } }; - - Assert.IsFalse(list.IsInAnyInterval(4.5d)); - Assert.IsTrue(list.NearestIntervalIndex == 1 || - list.NearestIntervalIndex == 2); // 4.5 in between 3.0-4.0 and 5.0-6.0 - - Assert.IsFalse(list.IsInAnyInterval(9.0d)); - Assert.IsTrue(list.NearestIntervalIndex == 3); // 9.0 goes above 7.0-8.0 - - Assert.IsFalse(list.IsInAnyInterval(0.0d)); - Assert.IsTrue(list.NearestIntervalIndex == 0); // 0.0 goes below 1.0-2.0 - } - - private class TestIntervalList : IntervalList - { - public new int NearestIntervalIndex => base.NearestIntervalIndex; + Assert.Throws(() => list.Add(50, 25)); + Assert.Throws(() => list.Add(new Interval(50, 25))); } } } diff --git a/osu.Game/Lists/IntervalList.cs b/osu.Game/Lists/IntervalList.cs index 6a71d94ea8..15d9a349dc 100644 --- a/osu.Game/Lists/IntervalList.cs +++ b/osu.Game/Lists/IntervalList.cs @@ -116,10 +116,11 @@ namespace osu.Game.Lists public Interval(T start, T end) { - bool startLessThanEnd = Comparer.Default.Compare(start, end) < 0; + if (Comparer.Default.Compare(start, end) >= 0) + throw new ArgumentException($"Invalid interval, {nameof(start)} must be less than {nameof(end)}", nameof(start)); - Start = startLessThanEnd ? start : end; - End = startLessThanEnd ? end : start; + Start = start; + End = end; } } } From 5966c9af9ccdd05d84a92921eae77b3cff787712 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 10 Apr 2020 15:50:07 +0300 Subject: [PATCH 025/121] Limit generic type to IConvertible structures (common number types) --- osu.Game/Lists/IntervalList.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Lists/IntervalList.cs b/osu.Game/Lists/IntervalList.cs index 15d9a349dc..5a5866ca91 100644 --- a/osu.Game/Lists/IntervalList.cs +++ b/osu.Game/Lists/IntervalList.cs @@ -13,6 +13,7 @@ namespace osu.Game.Lists /// /// The type of interval values. public class IntervalList : ICollection>, IReadOnlyList> + where T : struct, IConvertible { private static readonly IComparer type_comparer = Comparer.Default; @@ -103,6 +104,7 @@ namespace osu.Game.Lists } public readonly struct Interval + where T : struct, IConvertible { /// /// The start value of this interval. From bf124a5cc6d9d99566f099e405f2c03bcdc6ac3f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 10 Apr 2020 15:50:40 +0300 Subject: [PATCH 026/121] Simplify IntervalList implementation enough to work for its usages --- osu.Game/Lists/IntervalList.cs | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/osu.Game/Lists/IntervalList.cs b/osu.Game/Lists/IntervalList.cs index 5a5866ca91..580015bb96 100644 --- a/osu.Game/Lists/IntervalList.cs +++ b/osu.Game/Lists/IntervalList.cs @@ -12,7 +12,7 @@ namespace osu.Game.Lists /// Represents a list of intervals that can be used for whether a specific value falls into one of them. /// /// The type of interval values. - public class IntervalList : ICollection>, IReadOnlyList> + public class IntervalList : IEnumerable> where T : struct, IConvertible { private static readonly IComparer type_comparer = Comparer.Default; @@ -20,6 +20,11 @@ namespace osu.Game.Lists private readonly SortedList> intervals = new SortedList>((x, y) => type_comparer.Compare(x.Start, y.Start)); private int nearestIndex; + public Interval this[int i] + { + get => intervals[i]; + set => intervals[i] = value; + } /// /// Whether the provided value is in any interval added to this list. @@ -58,12 +63,6 @@ namespace osu.Game.Lists /// The end value of the interval. public void Add(T start, T end) => Add(new Interval(start, end)); - #region ICollection> - - public int Count => intervals.Count; - - bool ICollection>.IsReadOnly => false; - /// /// Adds a new interval to the list /// @@ -82,25 +81,9 @@ namespace osu.Game.Lists /// public void Clear() => intervals.Clear(); - public void CopyTo(Interval[] array, int arrayIndex) => intervals.CopyTo(array, arrayIndex); - - public bool Contains(Interval item) => intervals.Contains(item); - public IEnumerator> GetEnumerator() => intervals.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - #endregion - - #region IReadOnlyList> - - public Interval this[int index] - { - get => intervals[index]; - set => intervals[index] = value; - } - - #endregion } public readonly struct Interval From c6aa6acc1b2f46c6c39a0d241325b1d2d8f154a5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 16 Apr 2020 17:28:06 +0900 Subject: [PATCH 027/121] Apply performance calculator changes --- .../Difficulty/CatchPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs index 21d4642c22..bc52f0b812 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty if (approachRate > 9.0f) approachRateFactor += 0.1f * (approachRate - 9.0f); // 10% for each AR above 9 if (approachRate > 10.0f) - approachRateFactor += 0.2f * (float)Math.Pow(approachRate - 10.0f, 1.5f); // Additional 20% at AR 11, 40% total + approachRateFactor += 0.1f * (approachRate - 10.0f); // Additional 10% at AR 11, 30% total else if (approachRate < 8.0f) approachRateFactor += 0.025f * (8.0f - approachRate); // 2.5% for each AR below 8 From d7ea5432a8b1eda9d85e29de2261b0d4cfa3ccbc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 16 Apr 2020 18:15:52 +0900 Subject: [PATCH 028/121] Fix incorrect combo calculation --- .../Difficulty/CatchPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs index bc52f0b812..e7ce680365 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty // Combo scaling if (Attributes.MaxCombo > 0) - value *= Math.Min(Math.Pow(Attributes.MaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); + value *= Math.Min(Math.Pow(Score.MaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); float approachRate = (float)Attributes.ApproachRate; float approachRateFactor = 1.0f; From ae210d567d794f5b16587b8684bb9bdb9edfeeb2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 16 Apr 2020 18:16:08 +0900 Subject: [PATCH 029/121] Add temporary solution for tick hit/miss count --- osu.Game/Rulesets/Scoring/HitResult.cs | 5 +++++ .../Scoring/Legacy/ScoreInfoExtensions.cs | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/osu.Game/Rulesets/Scoring/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs index 7ba88d3df8..0c895bd086 100644 --- a/osu.Game/Rulesets/Scoring/HitResult.cs +++ b/osu.Game/Rulesets/Scoring/HitResult.cs @@ -43,5 +43,10 @@ namespace osu.Game.Rulesets.Scoring /// [Description(@"Perfect")] Perfect, + + SmallTickHit, + SmallTickMiss, + LargeTickHit, + LargeTickMiss, } } diff --git a/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs b/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs index 66b1acf591..9745d1abef 100644 --- a/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs +++ b/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs @@ -66,6 +66,9 @@ namespace osu.Game.Scoring.Legacy { case 3: return scoreInfo.Statistics[HitResult.Good]; + + case 2: + return scoreInfo.Statistics[HitResult.SmallTickMiss]; } return null; @@ -78,6 +81,10 @@ namespace osu.Game.Scoring.Legacy case 3: scoreInfo.Statistics[HitResult.Good] = value; break; + + case 2: + scoreInfo.Statistics[HitResult.SmallTickMiss] = value; + break; } } @@ -91,6 +98,9 @@ namespace osu.Game.Scoring.Legacy case 3: return scoreInfo.Statistics[HitResult.Ok]; + + case 2: + return scoreInfo.Statistics[HitResult.LargeTickHit]; } return null; @@ -108,6 +118,10 @@ namespace osu.Game.Scoring.Legacy case 3: scoreInfo.Statistics[HitResult.Ok] = value; break; + + case 2: + scoreInfo.Statistics[HitResult.LargeTickHit] = value; + break; } } @@ -118,6 +132,9 @@ namespace osu.Game.Scoring.Legacy case 0: case 3: return scoreInfo.Statistics[HitResult.Meh]; + + case 2: + return scoreInfo.Statistics[HitResult.SmallTickHit]; } return null; @@ -131,6 +148,10 @@ namespace osu.Game.Scoring.Legacy case 3: scoreInfo.Statistics[HitResult.Meh] = value; break; + + case 2: + scoreInfo.Statistics[HitResult.SmallTickHit] = value; + break; } } From 12c235027df835b189669ade2313f8128b4d31ea Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 23 Apr 2020 13:28:27 +0900 Subject: [PATCH 030/121] Remove stale file --- .../Edit/Masks/ManiaSelectionBlueprint.cs | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 osu.Game.Rulesets.Mania/Edit/Masks/ManiaSelectionBlueprint.cs diff --git a/osu.Game.Rulesets.Mania/Edit/Masks/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Masks/ManiaSelectionBlueprint.cs deleted file mode 100644 index 433db79ae0..0000000000 --- a/osu.Game.Rulesets.Mania/Edit/Masks/ManiaSelectionBlueprint.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Objects.Drawables; - -namespace osu.Game.Rulesets.Mania.Edit.Masks -{ - public abstract class ManiaSelectionBlueprint : OverlaySelectionBlueprint - { - protected ManiaSelectionBlueprint(DrawableHitObject drawableObject) - : base(drawableObject) - { - RelativeSizeAxes = Axes.None; - } - } -} From 0a840a26133b76a1bbd80b9d77782843460e28d9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 23 Apr 2020 17:41:33 +0900 Subject: [PATCH 031/121] Fix mania not getting its own selection handler --- osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs index d744036b4c..cea27498c3 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs @@ -30,5 +30,7 @@ namespace osu.Game.Rulesets.Mania.Edit return base.CreateBlueprintFor(hitObject); } + + protected override SelectionHandler CreateSelectionHandler() => new ManiaSelectionHandler(); } } From 4ebb28d3e7fdc0a412643f5e4a3effce353e9ebe Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 23 Apr 2020 17:52:54 +0900 Subject: [PATCH 032/121] wip --- .../Edit/Blueprints/HoldNoteSelectionBlueprint.cs | 2 ++ osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs | 4 ++-- .../Screens/Edit/Compose/Components/BlueprintContainer.cs | 7 +++++-- osu.Game/Tests/Beatmaps/TestBeatmap.cs | 1 + 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs index d569d68b59..43d43ef252 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs @@ -76,5 +76,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints } public override Quad SelectionQuad => ScreenSpaceDrawQuad; + + public override Vector2 SelectionPoint => DrawableObject.Head.ScreenSpaceDrawQuad.Centre; } } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs index 9069a636a8..78f159b733 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Edit var maniaBlueprint = (ManiaSelectionBlueprint)moveEvent.Blueprint; int lastColumn = maniaBlueprint.DrawableObject.HitObject.Column; - adjustOrigins(maniaBlueprint); + // adjustOrigins(maniaBlueprint); performDragMovement(moveEvent); performColumnMovement(lastColumn, moveEvent); @@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Mania.Edit // When scrolling downwards the anchor position is at the bottom of the screen, however the movement event assumes the anchor is at the top of the screen. // This causes the delta to assume a positive hitobject position, and which can be corrected for by subtracting the parent height. if (scrollingInfo.Direction.Value == ScrollingDirection.Down) - delta -= moveEvent.Blueprint.Parent.DrawHeight; // todo: probably wrong + delta -= moveEvent.Blueprint.Parent.DrawHeight; // todo: definitely wrong foreach (var selectionBlueprint in SelectedBlueprints) { diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index ad16e22e5e..0823be01f8 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -401,15 +401,18 @@ namespace osu.Game.Screens.Edit.Compose.Components HitObject draggedObject = movementBlueprint.HitObject; - // The final movement position, relative to screenSpaceMovementStartPosition + // The final movement position, relative to movementBlueprintOriginalPosition Vector2 movePosition = movementBlueprintOriginalPosition.Value + e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition; - (Vector2 snappedPosition, double snappedTime) = snapProvider.GetSnappedPosition(ToLocalSpace(movePosition), draggedObject.StartTime); + (Vector2 snappedPosition, _) = snapProvider.GetSnappedPosition(ToLocalSpace(movePosition), draggedObject.StartTime); // Move the hitobjects if (!selectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, ToScreenSpace(snappedPosition)))) return true; + // Todo: Temp + (_, double snappedTime) = snapProvider.GetSnappedPosition(ToLocalSpace(snappedPosition), draggedObject.StartTime); + // Apply the start time at the newly snapped-to position double offset = snappedTime - draggedObject.StartTime; foreach (HitObject obj in selectionHandler.SelectedHitObjects) diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs index 96e3c037a3..a7c84bf692 100644 --- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs @@ -23,6 +23,7 @@ namespace osu.Game.Tests.Beatmaps HitObjects = baseBeatmap.HitObjects; BeatmapInfo.Ruleset = ruleset; + BeatmapInfo.RulesetID = ruleset.ID ?? 0; BeatmapInfo.BeatmapSet.Metadata = BeatmapInfo.Metadata; BeatmapInfo.BeatmapSet.Beatmaps = new List { BeatmapInfo }; BeatmapInfo.BeatmapSet.OnlineInfo = new BeatmapSetOnlineInfo From acf95fca9cdd8b12027d907838738fa0216845b4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 27 Apr 2020 17:14:00 +0900 Subject: [PATCH 033/121] Remove old, now unnecessary method --- .../Edit/ManiaSelectionHandler.cs | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs index 78f159b733..d290e4ec24 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs @@ -35,35 +35,12 @@ namespace osu.Game.Rulesets.Mania.Edit var maniaBlueprint = (ManiaSelectionBlueprint)moveEvent.Blueprint; int lastColumn = maniaBlueprint.DrawableObject.HitObject.Column; - // adjustOrigins(maniaBlueprint); performDragMovement(moveEvent); performColumnMovement(lastColumn, moveEvent); return true; } - /// - /// Ensures that the position of hitobjects remains centred to the mouse position. - /// E.g. The hitobject position will change if the editor scrolls while a hitobject is dragged. - /// - /// The that received the drag event. - private void adjustOrigins(ManiaSelectionBlueprint reference) - { - var referenceParent = (HitObjectContainer)reference.DrawableObject.Parent; - - float offsetFromReferenceOrigin = reference.DragPosition.Y - reference.DrawableObject.OriginPosition.Y; - float targetPosition = referenceParent.ToLocalSpace(reference.ScreenSpaceDragPosition).Y - offsetFromReferenceOrigin; - - // Flip the vertical coordinate space when scrolling downwards - if (scrollingInfo.Direction.Value == ScrollingDirection.Down) - targetPosition -= referenceParent.DrawHeight; - - float movementDelta = targetPosition - reference.DrawableObject.Position.Y; - - foreach (var b in SelectedBlueprints.OfType()) - b.DrawableObject.Y += movementDelta; - } - private void performDragMovement(MoveSelectionEvent moveEvent) { float delta = moveEvent.InstantDelta.Y; From b88dd442526c8a58a9bd992101b5d7b5da33e3a6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 27 Apr 2020 19:47:25 +0900 Subject: [PATCH 034/121] Fix movement not working correctly in down-scroll --- .../Blueprints/ManiaSelectionBlueprint.cs | 20 +++++++++++++++++++ .../Edit/ManiaSelectionHandler.cs | 19 +++++++----------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs index 9f57160f99..14d52dd08e 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs @@ -74,5 +74,25 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints DrawableObject.AlwaysAlive = false; base.Hide(); } + + public override Vector2 GetInstantDelta(Vector2 screenSpacePosition) + { + var baseDelta = base.GetInstantDelta(screenSpacePosition); + + if (scrollingInfo.Direction.Value == ScrollingDirection.Down) + { + // The parent of DrawableObject is the scrolling hitobject container (SHOC). + // In the coordinate-space of the SHOC, the screen-space position at the hit target is equal to the height of the SHOC, + // but this is not what we want as it means a slight movement downwards results in a delta greater than the height of the SHOC. + // To get around this issue, the height of the SHOC is subtracted from the delta. + // + // Ideally this should be a _negative_ value in the case described above, however this code gives a _positive_ delta. + // This is intentional as the delta is added to the hitobject's position (see: ManiaSelectionHandler) and a negative delta would move them towards the top of the screen instead, + // which would cause the delta to get increasingly larger as additional movements are performed. + return new Vector2(baseDelta.X, baseDelta.Y - DrawableObject.Parent.DrawHeight); + } + + return baseDelta; + } } } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs index d290e4ec24..8dfc97f87a 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs @@ -45,11 +45,6 @@ namespace osu.Game.Rulesets.Mania.Edit { float delta = moveEvent.InstantDelta.Y; - // When scrolling downwards the anchor position is at the bottom of the screen, however the movement event assumes the anchor is at the top of the screen. - // This causes the delta to assume a positive hitobject position, and which can be corrected for by subtracting the parent height. - if (scrollingInfo.Direction.Value == ScrollingDirection.Down) - delta -= moveEvent.Blueprint.Parent.DrawHeight; // todo: definitely wrong - foreach (var selectionBlueprint in SelectedBlueprints) { var b = (OverlaySelectionBlueprint)selectionBlueprint; @@ -57,24 +52,24 @@ namespace osu.Game.Rulesets.Mania.Edit var hitObject = b.DrawableObject; var objectParent = (HitObjectContainer)hitObject.Parent; - // StartTime could be used to adjust the position if only one movement event was received per frame. - // However this is not the case and ScrollingHitObjectContainer performs movement in UpdateAfterChildren() so the position must also be updated to be valid for further movement events + // We receive multiple movement events per frame such that we can't rely on updating the start time + // since the scrolling hitobject container requires at least one update frame to update the position. + // However the position needs to be valid for future movement events to calculate the correct deltas. hitObject.Y += delta; float targetPosition = hitObject.Position.Y; - // The scrolling algorithm always assumes an anchor at the top of the screen, so the position must be flipped when scrolling downwards to reflect a top anchor if (scrollingInfo.Direction.Value == ScrollingDirection.Down) + { + // When scrolling downwards, the position is _negative_ when the object's start time is after the current time (e.g. in the middle of the stage). + // However all scrolling algorithms upwards scrolling, meaning that a positive (inverse) position is expected in the same scenario. targetPosition = -targetPosition; - - objectParent.Remove(hitObject); + } hitObject.HitObject.StartTime = scrollingInfo.Algorithm.TimeAt(targetPosition, editorClock.CurrentTime, scrollingInfo.TimeRange.Value, objectParent.DrawHeight); - - objectParent.Add(hitObject); } } From cebc0fc0466d61db6f22c19358c8d22ce5f12d09 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 27 Apr 2020 20:35:24 +0900 Subject: [PATCH 035/121] Attempt to fix multiple selection movements --- .../Edit/ManiaHitObjectComposer.cs | 28 ++++++++++++++++++- .../Edit/ManiaSelectionHandler.cs | 14 ---------- osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 16 +++++++++++ .../Compose/Components/BlueprintContainer.cs | 10 +++---- 4 files changed, 47 insertions(+), 21 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 62b609610f..8d012c63ac 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -10,6 +10,7 @@ using osu.Framework.Allocation; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; +using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Screens.Edit.Compose.Components; using osuTK; @@ -37,7 +38,32 @@ namespace osu.Game.Rulesets.Mania.Edit protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - public int TotalColumns => ((ManiaPlayfield)drawableRuleset.Playfield).TotalColumns; + public ManiaPlayfield Playfield => ((ManiaPlayfield)drawableRuleset.Playfield); + + public int TotalColumns => Playfield.TotalColumns; + + public override (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time) + { + var hoc = Playfield.GetColumn(0).HitObjectContainer; + + position.Y -= ToLocalSpace(hoc.ScreenSpaceDrawQuad.TopLeft).Y; + + float targetPosition = position.Y; + + if (drawableRuleset.ScrollingInfo.Direction.Value == ScrollingDirection.Down) + { + // When scrolling downwards, the position is _negative_ when the object's start time is after the current time (e.g. in the middle of the stage). + // However all scrolling algorithms upwards scrolling, meaning that a positive (inverse) position is expected in the same scenario. + targetPosition = -targetPosition; + } + + double targetTime = drawableRuleset.ScrollingInfo.Algorithm.TimeAt(targetPosition, + EditorClock.CurrentTime, + drawableRuleset.ScrollingInfo.TimeRange.Value, + hoc.DrawHeight); + + return base.GetSnappedPosition(position, targetTime); + } protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) { diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs index 8dfc97f87a..989e0f5b01 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs @@ -56,20 +56,6 @@ namespace osu.Game.Rulesets.Mania.Edit // since the scrolling hitobject container requires at least one update frame to update the position. // However the position needs to be valid for future movement events to calculate the correct deltas. hitObject.Y += delta; - - float targetPosition = hitObject.Position.Y; - - if (scrollingInfo.Direction.Value == ScrollingDirection.Down) - { - // When scrolling downwards, the position is _negative_ when the object's start time is after the current time (e.g. in the middle of the stage). - // However all scrolling algorithms upwards scrolling, meaning that a positive (inverse) position is expected in the same scenario. - targetPosition = -targetPosition; - } - - hitObject.HitObject.StartTime = scrollingInfo.Algorithm.TimeAt(targetPosition, - editorClock.CurrentTime, - scrollingInfo.TimeRange.Value, - objectParent.DrawHeight); } } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index 2dec468654..f9faa262ed 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -87,6 +87,22 @@ namespace osu.Game.Rulesets.Mania.UI return found; } + public Column GetColumn(int index) + { + foreach (var stage in stages) + { + if (index >= stage.Columns.Count) + { + index -= stage.Columns.Count; + continue; + } + + return stage.Columns[index]; + } + + throw new ArgumentOutOfRangeException(nameof(index)); + } + /// /// Retrieves the total amount of columns across all stages in this playfield. /// diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 0823be01f8..8910684463 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -401,18 +401,16 @@ namespace osu.Game.Screens.Edit.Compose.Components HitObject draggedObject = movementBlueprint.HitObject; - // The final movement position, relative to movementBlueprintOriginalPosition + // The final movement position, relative to movementBlueprintOriginalPosition. Vector2 movePosition = movementBlueprintOriginalPosition.Value + e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition; - (Vector2 snappedPosition, _) = snapProvider.GetSnappedPosition(ToLocalSpace(movePosition), draggedObject.StartTime); + // Retrieve a snapped position. + (Vector2 snappedPosition, double snappedTime) = snapProvider.GetSnappedPosition(ToLocalSpace(movePosition), draggedObject.StartTime); - // Move the hitobjects + // Move the hitobjects. if (!selectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, ToScreenSpace(snappedPosition)))) return true; - // Todo: Temp - (_, double snappedTime) = snapProvider.GetSnappedPosition(ToLocalSpace(snappedPosition), draggedObject.StartTime); - // Apply the start time at the newly snapped-to position double offset = snappedTime - draggedObject.StartTime; foreach (HitObject obj in selectionHandler.SelectedHitObjects) From 7342e0015161a7b7e339370ac4aa6aadcee6f3ce Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 14:00:14 +0900 Subject: [PATCH 036/121] Convert positions to local HOC coordinate space --- osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 8d012c63ac..b415c9f0c9 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -46,9 +46,7 @@ namespace osu.Game.Rulesets.Mania.Edit { var hoc = Playfield.GetColumn(0).HitObjectContainer; - position.Y -= ToLocalSpace(hoc.ScreenSpaceDrawQuad.TopLeft).Y; - - float targetPosition = position.Y; + float targetPosition = hoc.ToLocalSpace(ToScreenSpace(position)).Y; if (drawableRuleset.ScrollingInfo.Direction.Value == ScrollingDirection.Down) { From da30eafa3020d3c1479e32ca4e37f43e114098ad Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 14:47:45 +0900 Subject: [PATCH 037/121] Prevent potential exception --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 5062c92afe..7170d425e2 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.UI /// public PassThroughInputManager KeyBindingInputManager; - public override double GameplayStartTime => Objects.First().StartTime - 2000; + public override double GameplayStartTime => Objects.FirstOrDefault()?.StartTime - 2000 ?? 0; private readonly Lazy playfield; From d905ef53b37ac287c2072fb8bcb26c8704211c27 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 14:47:52 +0900 Subject: [PATCH 038/121] Add test scene for mania composer --- .../TestSceneManiaHitObjectComposer.cs | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs new file mode 100644 index 0000000000..3cd2530f36 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs @@ -0,0 +1,69 @@ +// 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 NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Edit; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Screens.Edit; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Mania.Tests +{ + public class TestSceneManiaHitObjectComposer : EditorClockTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(ManiaBlueprintContainer) + }; + + [Cached(typeof(EditorBeatmap))] + [Cached(typeof(IBeatSnapProvider))] + private readonly EditorBeatmap editorBeatmap; + + protected override Container Content { get; } + + private ManiaHitObjectComposer composer; + + public TestSceneManiaHitObjectComposer() + { + base.Content.Add(new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + editorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition { Columns = 4 })) + { + BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo } + }, + Content = new Container + { + RelativeSizeAxes = Axes.Both, + } + }, + }); + + for (int i = 0; i < 10; i++) + { + editorBeatmap.Add(new Note { StartTime = 100 * i }); + } + } + + [SetUp] + public void Setup() => Schedule(() => + { + Children = new Drawable[] + { + composer = new ManiaHitObjectComposer(new ManiaRuleset()) + }; + + BeatDivisor.Value = 8; + }); + } +} From 330521a2ae7f268b1645c03fc7feab010c48ce74 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 15:34:10 +0900 Subject: [PATCH 039/121] Fix lifetime override not working --- .../Drawables/DrawableManiaHitObject.cs | 62 +++++++++++++++++-- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index 88888001b4..a44d8b09aa 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -13,11 +13,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables { public abstract class DrawableManiaHitObject : DrawableHitObject { - /// - /// Whether this should always remain alive. - /// - internal bool AlwaysAlive; - /// /// The which causes this to be hit. /// @@ -54,7 +49,62 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables Direction.BindValueChanged(OnDirectionChanged, true); } - protected override bool ShouldBeAlive => AlwaysAlive || base.ShouldBeAlive; + private double computedLifetimeStart; + + public override double LifetimeStart + { + get => base.LifetimeStart; + set + { + computedLifetimeStart = value; + + if (!AlwaysAlive) + base.LifetimeStart = value; + } + } + + private double computedLifetimeEnd; + + public override double LifetimeEnd + { + get => base.LifetimeEnd; + set + { + computedLifetimeEnd = value; + + if (!AlwaysAlive) + base.LifetimeEnd = value; + } + } + + private bool alwaysAlive; + + /// + /// Whether this should always remain alive. + /// + internal bool AlwaysAlive + { + get => alwaysAlive; + set + { + if (alwaysAlive == value) + return; + + alwaysAlive = value; + + if (value) + { + // Set the base lifetimes directly, to avoid mangling the computed lifetimes + base.LifetimeStart = double.MinValue; + base.LifetimeEnd = double.MaxValue; + } + else + { + LifetimeStart = computedLifetimeStart; + LifetimeEnd = computedLifetimeEnd; + } + } + } protected virtual void OnDirectionChanged(ValueChangedEvent e) { From 3eb7c8755c1ed9e21a36ab0cee16047aab20f6e1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 15:34:41 +0900 Subject: [PATCH 040/121] Cleanup --- .../TestSceneManiaHitObjectComposer.cs | 4 +-- .../Blueprints/ManiaSelectionBlueprint.cs | 29 ------------------- .../Edit/ManiaSelectionHandler.cs | 11 ------- 3 files changed, 1 insertion(+), 43 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs index 3cd2530f36..180ceb94f4 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs @@ -29,8 +29,6 @@ namespace osu.Game.Rulesets.Mania.Tests protected override Container Content { get; } - private ManiaHitObjectComposer composer; - public TestSceneManiaHitObjectComposer() { base.Content.Add(new Container @@ -60,7 +58,7 @@ namespace osu.Game.Rulesets.Mania.Tests { Children = new Drawable[] { - composer = new ManiaHitObjectComposer(new ManiaRuleset()) + new ManiaHitObjectComposer(new ManiaRuleset()) }; BeatDivisor.Value = 8; diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs index 14d52dd08e..b03bf7c078 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs @@ -3,8 +3,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Input.Events; -using osu.Framework.Timing; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables; @@ -15,13 +13,8 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints { public class ManiaSelectionBlueprint : OverlaySelectionBlueprint { - public Vector2 ScreenSpaceDragPosition { get; private set; } - public Vector2 DragPosition { get; private set; } - public new DrawableManiaHitObject DrawableObject => (DrawableManiaHitObject)base.DrawableObject; - protected IClock EditorClock { get; private set; } - [Resolved] private IScrollingInfo scrollingInfo { get; set; } @@ -34,12 +27,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints RelativeSizeAxes = Axes.None; } - [BackgroundDependencyLoader] - private void load(IAdjustableClock clock) - { - EditorClock = clock; - } - protected override void Update() { base.Update(); @@ -47,22 +34,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints Position = Parent.ToLocalSpace(DrawableObject.ToScreenSpace(Vector2.Zero)); } - protected override bool OnMouseDown(MouseDownEvent e) - { - ScreenSpaceDragPosition = e.ScreenSpaceMousePosition; - DragPosition = DrawableObject.ToLocalSpace(e.ScreenSpaceMousePosition); - - return base.OnMouseDown(e); - } - - protected override void OnDrag(DragEvent e) - { - base.OnDrag(e); - - ScreenSpaceDragPosition = e.ScreenSpaceMousePosition; - DragPosition = DrawableObject.ToLocalSpace(e.ScreenSpaceMousePosition); - } - public override void Show() { DrawableObject.AlwaysAlive = true; diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs index 989e0f5b01..11e9b56a53 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs @@ -4,11 +4,9 @@ using System; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Timing; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Edit.Blueprints; using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Screens.Edit.Compose.Components; @@ -22,14 +20,6 @@ namespace osu.Game.Rulesets.Mania.Edit [Resolved] private IManiaHitObjectComposer composer { get; set; } - private IClock editorClock; - - [BackgroundDependencyLoader] - private void load(IAdjustableClock clock) - { - editorClock = clock; - } - public override bool HandleMovement(MoveSelectionEvent moveEvent) { var maniaBlueprint = (ManiaSelectionBlueprint)moveEvent.Blueprint; @@ -50,7 +40,6 @@ namespace osu.Game.Rulesets.Mania.Edit var b = (OverlaySelectionBlueprint)selectionBlueprint; var hitObject = b.DrawableObject; - var objectParent = (HitObjectContainer)hitObject.Parent; // We receive multiple movement events per frame such that we can't rely on updating the start time // since the scrolling hitobject container requires at least one update frame to update the position. From 31c3fd86b9ffc1117cff3628a4441309255c504b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Apr 2020 16:22:00 +0900 Subject: [PATCH 041/121] Avoid using internal EF methods in tests --- osu.Game.Tests/Beatmaps/TestSceneEditorBeatmap.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/TestSceneEditorBeatmap.cs b/osu.Game.Tests/Beatmaps/TestSceneEditorBeatmap.cs index 2d4587341d..b7b48ec06a 100644 --- a/osu.Game.Tests/Beatmaps/TestSceneEditorBeatmap.cs +++ b/osu.Game.Tests/Beatmaps/TestSceneEditorBeatmap.cs @@ -1,9 +1,9 @@ // 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 Microsoft.EntityFrameworkCore.Internal; using NUnit.Framework; using osu.Framework.Testing; using osu.Game.Rulesets.Objects; @@ -137,7 +137,7 @@ namespace osu.Game.Tests.Beatmaps var hitCircle = new HitCircle { StartTime = 1000 }; editorBeatmap.Add(hitCircle); Assert.That(editorBeatmap.HitObjects.Count(h => h == hitCircle), Is.EqualTo(1)); - Assert.That(editorBeatmap.HitObjects.IndexOf(hitCircle), Is.EqualTo(3)); + Assert.That(Array.IndexOf(editorBeatmap.HitObjects.ToArray(), hitCircle), Is.EqualTo(3)); } /// @@ -161,7 +161,7 @@ namespace osu.Game.Tests.Beatmaps hitCircle.StartTime = 0; Assert.That(editorBeatmap.HitObjects.Count(h => h == hitCircle), Is.EqualTo(1)); - Assert.That(editorBeatmap.HitObjects.IndexOf(hitCircle), Is.EqualTo(1)); + Assert.That(Array.IndexOf(editorBeatmap.HitObjects.ToArray(), hitCircle), Is.EqualTo(1)); } /// From f3fbb3cdc6595bda5e66382b01027bcdf4498ce1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Apr 2020 16:49:43 +0900 Subject: [PATCH 042/121] Add to banned symbols --- CodeAnalysis/BannedSymbols.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CodeAnalysis/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt index a92191a439..4d7135a195 100644 --- a/CodeAnalysis/BannedSymbols.txt +++ b/CodeAnalysis/BannedSymbols.txt @@ -4,3 +4,4 @@ M:System.ValueType.Equals(System.Object)~System.Boolean;Don't use object.Equals( M:System.Nullable`1.Equals(System.Object)~System.Boolean;Use == instead. T:System.IComparable;Don't use non-generic IComparable. Use generic version instead. M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText. +T:Microsoft.EntityFrameworkCore.Internal.EnumerableExtensions;Don't use internal extension methods. \ No newline at end of file From c3a41c8476d97a5710e90b6da19e72787b026449 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Apr 2020 16:56:36 +0900 Subject: [PATCH 043/121] Also avoid using internal TypeExtensions --- CodeAnalysis/BannedSymbols.txt | 3 ++- osu.Game/Screens/OsuScreen.cs | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CodeAnalysis/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt index 4d7135a195..e34626a59e 100644 --- a/CodeAnalysis/BannedSymbols.txt +++ b/CodeAnalysis/BannedSymbols.txt @@ -4,4 +4,5 @@ M:System.ValueType.Equals(System.Object)~System.Boolean;Don't use object.Equals( M:System.Nullable`1.Equals(System.Object)~System.Boolean;Use == instead. T:System.IComparable;Don't use non-generic IComparable. Use generic version instead. M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText. -T:Microsoft.EntityFrameworkCore.Internal.EnumerableExtensions;Don't use internal extension methods. \ No newline at end of file +T:Microsoft.EntityFrameworkCore.Internal.EnumerableExtensions;Don't use internal extension methods. +T:Microsoft.EntityFrameworkCore.Internal.TypeExtensions;Don't use internal extension methods. diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 61e94ae969..2124a66a75 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using Microsoft.EntityFrameworkCore.Internal; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -31,7 +30,7 @@ namespace osu.Game.Screens /// /// A user-facing title for this screen. /// - public virtual string Title => GetType().ShortDisplayName(); + public virtual string Title => GetType().Name; public string Description => Title; From e5131400e77d7ba9c213259ad883fc715206b9f7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 18:34:39 +0900 Subject: [PATCH 044/121] Remove now unnecessary position manipulation --- .../Edit/ManiaSelectionHandler.cs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs index 11e9b56a53..55245198c8 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs @@ -4,7 +4,6 @@ using System; using System.Linq; using osu.Framework.Allocation; -using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Edit.Blueprints; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.UI.Scrolling; @@ -25,29 +24,11 @@ namespace osu.Game.Rulesets.Mania.Edit var maniaBlueprint = (ManiaSelectionBlueprint)moveEvent.Blueprint; int lastColumn = maniaBlueprint.DrawableObject.HitObject.Column; - performDragMovement(moveEvent); performColumnMovement(lastColumn, moveEvent); return true; } - private void performDragMovement(MoveSelectionEvent moveEvent) - { - float delta = moveEvent.InstantDelta.Y; - - foreach (var selectionBlueprint in SelectedBlueprints) - { - var b = (OverlaySelectionBlueprint)selectionBlueprint; - - var hitObject = b.DrawableObject; - - // We receive multiple movement events per frame such that we can't rely on updating the start time - // since the scrolling hitobject container requires at least one update frame to update the position. - // However the position needs to be valid for future movement events to calculate the correct deltas. - hitObject.Y += delta; - } - } - private void performColumnMovement(int lastColumn, MoveSelectionEvent moveEvent) { var currentColumn = composer.ColumnAt(moveEvent.ScreenSpacePosition); From a7a680b4862e5adf6d0579b81534f07b169488c4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 18:34:55 +0900 Subject: [PATCH 045/121] Fix horizontal drag not working --- osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index f9faa262ed..6960d15f31 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Mania.UI { foreach (var column in stage.Columns) { - if (column.ReceivePositionalInputAt(screenSpacePosition)) + if (column.ReceivePositionalInputAt(new Vector2(screenSpacePosition.X, column.ScreenSpaceDrawQuad.Centre.Y))) { found = column; break; From f93291e25b4967edd658d3e309d33905684d5343 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 18:35:22 +0900 Subject: [PATCH 046/121] Remove unused override --- .../Blueprints/ManiaSelectionBlueprint.cs | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs index b03bf7c078..b8574b804e 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs @@ -45,25 +45,5 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints DrawableObject.AlwaysAlive = false; base.Hide(); } - - public override Vector2 GetInstantDelta(Vector2 screenSpacePosition) - { - var baseDelta = base.GetInstantDelta(screenSpacePosition); - - if (scrollingInfo.Direction.Value == ScrollingDirection.Down) - { - // The parent of DrawableObject is the scrolling hitobject container (SHOC). - // In the coordinate-space of the SHOC, the screen-space position at the hit target is equal to the height of the SHOC, - // but this is not what we want as it means a slight movement downwards results in a delta greater than the height of the SHOC. - // To get around this issue, the height of the SHOC is subtracted from the delta. - // - // Ideally this should be a _negative_ value in the case described above, however this code gives a _positive_ delta. - // This is intentional as the delta is added to the hitobject's position (see: ManiaSelectionHandler) and a negative delta would move them towards the top of the screen instead, - // which would cause the delta to get increasingly larger as additional movements are performed. - return new Vector2(baseDelta.X, baseDelta.Y - DrawableObject.Parent.DrawHeight); - } - - return baseDelta; - } } } From 7d54d4b800234bb017602dc49e8b2c4a5134193e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 18:35:37 +0900 Subject: [PATCH 047/121] Improve test scene --- .../TestSceneManiaHitObjectComposer.cs | 171 ++++++++++++++---- .../Edit/ManiaHitObjectComposer.cs | 2 + osu.Game/Tests/Visual/EditorClockTestScene.cs | 2 +- 3 files changed, 140 insertions(+), 35 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs index 180ceb94f4..4ce2424ffa 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs @@ -3,16 +3,23 @@ using System; using System.Collections.Generic; +using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Edit; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Screens.Edit; using osu.Game.Tests.Visual; +using osuTK; +using osuTK.Input; namespace osu.Game.Rulesets.Mania.Tests { @@ -23,45 +30,141 @@ namespace osu.Game.Rulesets.Mania.Tests typeof(ManiaBlueprintContainer) }; - [Cached(typeof(EditorBeatmap))] - [Cached(typeof(IBeatSnapProvider))] - private readonly EditorBeatmap editorBeatmap; - - protected override Container Content { get; } - - public TestSceneManiaHitObjectComposer() - { - base.Content.Add(new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - editorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition { Columns = 4 })) - { - BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo } - }, - Content = new Container - { - RelativeSizeAxes = Axes.Both, - } - }, - }); - - for (int i = 0; i < 10; i++) - { - editorBeatmap.Add(new Note { StartTime = 100 * i }); - } - } + private TestComposer composer; [SetUp] public void Setup() => Schedule(() => { - Children = new Drawable[] - { - new ManiaHitObjectComposer(new ManiaRuleset()) - }; - BeatDivisor.Value = 8; + Clock.Seek(0); + + Child = composer = new TestComposer { RelativeSizeAxes = Axes.Both }; }); + + [Test] + public void TestDragOffscreenSelectionVerticallyUpScroll() + { + DrawableHitObject lastObject = null; + Vector2 originalPosition = Vector2.Zero; + + AddStep("seek to last object", () => + { + lastObject = this.ChildrenOfType().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last()); + Clock.Seek(composer.EditorBeatmap.HitObjects.Last().StartTime); + }); + + AddStep("select all objects", () => composer.EditorBeatmap.SelectedHitObjects.AddRange(composer.EditorBeatmap.HitObjects)); + + AddStep("click last object", () => + { + originalPosition = lastObject.DrawPosition; + + InputManager.MoveMouseTo(lastObject); + InputManager.PressButton(MouseButton.Left); + }); + + AddStep("move mouse downwards", () => + { + InputManager.MoveMouseTo(lastObject, new Vector2(0, 20)); + InputManager.ReleaseButton(MouseButton.Left); + }); + + AddAssert("hitobjects not moved columns", () => composer.EditorBeatmap.HitObjects.All(h => ((ManiaHitObject)h).Column == 0)); + AddAssert("hitobjects moved downwards", () => lastObject.DrawPosition.Y - originalPosition.Y > 0); + AddAssert("hitobjects not moved too far", () => lastObject.DrawPosition.Y - originalPosition.Y < 50); + } + + [Test] + public void TestDragOffscreenSelectionVerticallyDownScroll() + { + DrawableHitObject lastObject = null; + Vector2 originalPosition = Vector2.Zero; + + AddStep("set down scroll", () => ((Bindable)composer.Composer.ScrollingInfo.Direction).Value = ScrollingDirection.Down); + + AddStep("seek to last object", () => + { + lastObject = this.ChildrenOfType().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last()); + Clock.Seek(composer.EditorBeatmap.HitObjects.Last().StartTime); + }); + + AddStep("select all objects", () => composer.EditorBeatmap.SelectedHitObjects.AddRange(composer.EditorBeatmap.HitObjects)); + + AddStep("click last object", () => + { + originalPosition = lastObject.DrawPosition; + + InputManager.MoveMouseTo(lastObject); + InputManager.PressButton(MouseButton.Left); + }); + + AddStep("move mouse upwards", () => + { + InputManager.MoveMouseTo(lastObject, new Vector2(0, -20)); + InputManager.ReleaseButton(MouseButton.Left); + }); + + AddAssert("hitobjects not moved columns", () => composer.EditorBeatmap.HitObjects.All(h => ((ManiaHitObject)h).Column == 0)); + AddAssert("hitobjects moved upwards", () => originalPosition.Y - lastObject.DrawPosition.Y > 0); + AddAssert("hitobjects not moved too far", () => originalPosition.Y - lastObject.DrawPosition.Y < 50); + } + + [Test] + public void TestDragOffscreenSelectionHorizontally() + { + DrawableHitObject lastObject = null; + Vector2 originalPosition = Vector2.Zero; + + AddStep("seek to last object", () => + { + lastObject = this.ChildrenOfType().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last()); + originalPosition = lastObject.DrawPosition; + + Clock.Seek(composer.EditorBeatmap.HitObjects.Last().StartTime); + }); + + AddStep("select all objects", () => composer.EditorBeatmap.SelectedHitObjects.AddRange(composer.EditorBeatmap.HitObjects)); + + AddStep("click last object", () => + { + InputManager.MoveMouseTo(lastObject); + InputManager.PressButton(MouseButton.Left); + }); + + AddStep("move mouse right", () => + { + InputManager.MoveMouseTo(lastObject, new Vector2(40, 0)); + InputManager.ReleaseButton(MouseButton.Left); + }); + + AddAssert("hitobjects moved columns", () => composer.EditorBeatmap.HitObjects.All(h => ((ManiaHitObject)h).Column == 1)); + + // Todo: They'll have moved vertically by half the height of a note. Probably a problem. + AddAssert("hitobjects not moved vertically", () => lastObject.DrawPosition.Y - originalPosition.Y < 10); + } + + private class TestComposer : CompositeDrawable + { + [Cached(typeof(EditorBeatmap))] + [Cached(typeof(IBeatSnapProvider))] + public readonly EditorBeatmap EditorBeatmap; + + public readonly ManiaHitObjectComposer Composer; + + public TestComposer() + { + InternalChildren = new Drawable[] + { + EditorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition { Columns = 4 })) + { + BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo } + }, + Composer = new ManiaHitObjectComposer(new ManiaRuleset()) + }; + + for (int i = 0; i < 10; i++) + EditorBeatmap.Add(new Note { StartTime = 100 * i }); + } + } } } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index b415c9f0c9..fba80f92d2 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -40,6 +40,8 @@ namespace osu.Game.Rulesets.Mania.Edit public ManiaPlayfield Playfield => ((ManiaPlayfield)drawableRuleset.Playfield); + public IScrollingInfo ScrollingInfo => drawableRuleset.ScrollingInfo; + public int TotalColumns => Playfield.TotalColumns; public override (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time) diff --git a/osu.Game/Tests/Visual/EditorClockTestScene.cs b/osu.Game/Tests/Visual/EditorClockTestScene.cs index 58a443ed3d..830e6ed363 100644 --- a/osu.Game/Tests/Visual/EditorClockTestScene.cs +++ b/osu.Game/Tests/Visual/EditorClockTestScene.cs @@ -15,7 +15,7 @@ namespace osu.Game.Tests.Visual /// Provides a clock, beat-divisor, and scrolling capability for test cases of editor components that /// are preferrably tested within the presence of a clock and seek controls. /// - public abstract class EditorClockTestScene : OsuTestScene + public abstract class EditorClockTestScene : OsuManualInputManagerTestScene { protected readonly BindableBeatDivisor BeatDivisor = new BindableBeatDivisor(); protected new readonly EditorClock Clock; From ff24a15760167b1db112239b6c3418870654f5a1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 18:36:24 +0900 Subject: [PATCH 048/121] Fix vertical drag in down-scroll scenarios --- osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index fba80f92d2..af465af16a 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -52,9 +52,9 @@ namespace osu.Game.Rulesets.Mania.Edit if (drawableRuleset.ScrollingInfo.Direction.Value == ScrollingDirection.Down) { - // When scrolling downwards, the position is _negative_ when the object's start time is after the current time (e.g. in the middle of the stage). - // However all scrolling algorithms upwards scrolling, meaning that a positive (inverse) position is expected in the same scenario. - targetPosition = -targetPosition; + // We're dealing with screen coordinates in which the position decreases towards the centre of the screen resulting in an increase in start time. + // The scrolling algorithm assumes a top anchor meaning an increase in time corresponds to an increase in position, so when scrolling downwards the coordinates need to be flipped. + targetPosition = hoc.DrawHeight - targetPosition; } double targetTime = drawableRuleset.ScrollingInfo.Algorithm.TimeAt(targetPosition, From db12fafc2c3187220488e6235acc4f93cfa97687 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 18:53:30 +0900 Subject: [PATCH 049/121] Update comment --- .../TestSceneManiaHitObjectComposer.cs | 5 +++-- osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs index 4ce2424ffa..a84fe83245 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs @@ -14,6 +14,7 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Edit; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Screens.Edit; @@ -139,8 +140,8 @@ namespace osu.Game.Rulesets.Mania.Tests AddAssert("hitobjects moved columns", () => composer.EditorBeatmap.HitObjects.All(h => ((ManiaHitObject)h).Column == 1)); - // Todo: They'll have moved vertically by half the height of a note. Probably a problem. - AddAssert("hitobjects not moved vertically", () => lastObject.DrawPosition.Y - originalPosition.Y < 10); + // Todo: They'll move vertically by the height of a note since there's no snapping and the selection point is the middle of the note. + AddAssert("hitobjects not moved vertically", () => lastObject.DrawPosition.Y - originalPosition.Y <= DefaultNotePiece.NOTE_HEIGHT); } private class TestComposer : CompositeDrawable diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index af465af16a..dfa933baad 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -53,7 +53,8 @@ namespace osu.Game.Rulesets.Mania.Edit if (drawableRuleset.ScrollingInfo.Direction.Value == ScrollingDirection.Down) { // We're dealing with screen coordinates in which the position decreases towards the centre of the screen resulting in an increase in start time. - // The scrolling algorithm assumes a top anchor meaning an increase in time corresponds to an increase in position, so when scrolling downwards the coordinates need to be flipped. + // The scrolling algorithm instead assumes a top anchor meaning an increase in time corresponds to an increase in position, + // so when scrolling downwards the coordinates need to be flipped. targetPosition = hoc.DrawHeight - targetPosition; } From ff3928465c06a7e578afe1088a9b0a045ea2eca7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 18:55:58 +0900 Subject: [PATCH 050/121] Add xmldoc --- osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index 6960d15f31..1af7d06998 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -87,8 +87,17 @@ namespace osu.Game.Rulesets.Mania.UI return found; } + /// + /// Retrieves a by index. + /// + /// The index of the column. + /// The corresponding to the given index. + /// If is less than 0 or greater than . public Column GetColumn(int index) { + if (index < 0 || index > TotalColumns - 1) + throw new ArgumentOutOfRangeException(nameof(index)); + foreach (var stage in stages) { if (index >= stage.Columns.Count) From 1aaab40228baa33ccb6cadadf07e84805fa48f66 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 19:23:33 +0900 Subject: [PATCH 051/121] Fix mods affecting mania scroll speed --- .../UI/DrawableManiaRuleset.cs | 25 ++++++++++++++++--- osu.Game/Rulesets/UI/DrawableRuleset.cs | 8 +++--- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 14cad39b04..39f3331fbb 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; @@ -48,6 +50,10 @@ namespace osu.Game.Rulesets.Mania.UI protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config; private readonly Bindable configDirection = new Bindable(); + private readonly Bindable configTimeRange = new Bindable(); + + // Stores the current speed adjustment active in gameplay. + private readonly Track speedAdjustmentTrack = new TrackVirtual(1000); public DrawableManiaRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) : base(ruleset, beatmap, mods) @@ -58,6 +64,9 @@ namespace osu.Game.Rulesets.Mania.UI [BackgroundDependencyLoader] private void load() { + foreach (var mod in Mods.OfType()) + mod.ApplyToTrack(speedAdjustmentTrack); + bool isForCurrentRuleset = Beatmap.BeatmapInfo.Ruleset.Equals(Ruleset.RulesetInfo); foreach (var p in ControlPoints) @@ -76,7 +85,8 @@ namespace osu.Game.Rulesets.Mania.UI Config.BindWith(ManiaRulesetSetting.ScrollDirection, configDirection); configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true); - Config.BindWith(ManiaRulesetSetting.ScrollTime, TimeRange); + Config.BindWith(ManiaRulesetSetting.ScrollTime, configTimeRange); + configTimeRange.BindValueChanged(_ => updateTimeRange()); } protected override void AdjustScrollSpeed(int amount) @@ -86,10 +96,19 @@ namespace osu.Game.Rulesets.Mania.UI private double relativeTimeRange { - get => MAX_TIME_RANGE / TimeRange.Value; - set => TimeRange.Value = MAX_TIME_RANGE / value; + get => MAX_TIME_RANGE / configTimeRange.Value; + set => configTimeRange.Value = MAX_TIME_RANGE / value; } + protected override void Update() + { + base.Update(); + + updateTimeRange(); + } + + private void updateTimeRange() => TimeRange.Value = configTimeRange.Value * speedAdjustmentTrack.AggregateTempo.Value * speedAdjustmentTrack.AggregateFrequency.Value; + /// /// Retrieves the column that intersects a screen-space position. /// diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 5062c92afe..0a1c35c7c6 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.UI /// The mods which are to be applied. /// [Cached(typeof(IReadOnlyList))] - private readonly IReadOnlyList mods; + protected readonly IReadOnlyList Mods; private FrameStabilityContainer frameStabilityContainer; @@ -129,7 +129,7 @@ namespace osu.Game.Rulesets.UI throw new ArgumentException($"{GetType()} expected the beatmap to contain hitobjects of type {typeof(TObject)}.", nameof(beatmap)); Beatmap = tBeatmap; - this.mods = mods?.ToArray() ?? Array.Empty(); + Mods = mods?.ToArray() ?? Array.Empty(); RelativeSizeAxes = Axes.Both; @@ -204,7 +204,7 @@ namespace osu.Game.Rulesets.UI .WithChild(ResumeOverlay))); } - applyRulesetMods(mods, config); + applyRulesetMods(Mods, config); loadObjects(cancellationToken); } @@ -224,7 +224,7 @@ namespace osu.Game.Rulesets.UI Playfield.PostProcess(); - foreach (var mod in mods.OfType()) + foreach (var mod in Mods.OfType()) mod.ApplyToDrawableHitObjects(Playfield.AllHitObjects); } From 7868c0dad5cff824fe5bf7c90c0a8d4dea71d5eb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 20:15:56 +0900 Subject: [PATCH 052/121] Fix test case failures --- .../TestSceneManiaHitObjectComposer.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs index a84fe83245..286e3f6e50 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs @@ -119,8 +119,6 @@ namespace osu.Game.Rulesets.Mania.Tests AddStep("seek to last object", () => { lastObject = this.ChildrenOfType().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last()); - originalPosition = lastObject.DrawPosition; - Clock.Seek(composer.EditorBeatmap.HitObjects.Last().StartTime); }); @@ -128,13 +126,18 @@ namespace osu.Game.Rulesets.Mania.Tests AddStep("click last object", () => { + originalPosition = lastObject.DrawPosition; + InputManager.MoveMouseTo(lastObject); InputManager.PressButton(MouseButton.Left); }); AddStep("move mouse right", () => { - InputManager.MoveMouseTo(lastObject, new Vector2(40, 0)); + var firstColumn = composer.Composer.Playfield.GetColumn(0); + var secondColumn = composer.Composer.Playfield.GetColumn(1); + + InputManager.MoveMouseTo(lastObject, new Vector2(secondColumn.ScreenSpaceDrawQuad.Centre.X - firstColumn.ScreenSpaceDrawQuad.Centre.X + 1, 0)); InputManager.ReleaseButton(MouseButton.Left); }); From 00918ecb6d6c47f6c131fca8991448825e9297e3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 29 Apr 2020 04:43:49 +0300 Subject: [PATCH 053/121] Replace interval collection with a more-specific immutable component Covers all small changes into one commit: - Remove generics and use `double` type instead. - Make the component immutable and not enumerable for simplicity of it's worth. - Make the component more-specific (to time period tracking) - Apply small adjustments to the component --- osu.Game/Lists/IntervalList.cs | 111 -------------------------------- osu.Game/Utils/PeriodTracker.cs | 90 ++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 111 deletions(-) delete mode 100644 osu.Game/Lists/IntervalList.cs create mode 100644 osu.Game/Utils/PeriodTracker.cs diff --git a/osu.Game/Lists/IntervalList.cs b/osu.Game/Lists/IntervalList.cs deleted file mode 100644 index 580015bb96..0000000000 --- a/osu.Game/Lists/IntervalList.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections; -using System.Collections.Generic; -using osu.Framework.Lists; - -namespace osu.Game.Lists -{ - /// - /// Represents a list of intervals that can be used for whether a specific value falls into one of them. - /// - /// The type of interval values. - public class IntervalList : IEnumerable> - where T : struct, IConvertible - { - private static readonly IComparer type_comparer = Comparer.Default; - - private readonly SortedList> intervals = new SortedList>((x, y) => type_comparer.Compare(x.Start, y.Start)); - private int nearestIndex; - - public Interval this[int i] - { - get => intervals[i]; - set => intervals[i] = value; - } - - /// - /// Whether the provided value is in any interval added to this list. - /// - /// The value to check for. - public bool IsInAnyInterval(T value) - { - if (intervals.Count == 0) - return false; - - // Clamp the nearest index in case there were intervals - // removed from the list causing the index to go out of range. - nearestIndex = Math.Clamp(nearestIndex, 0, intervals.Count - 1); - - if (type_comparer.Compare(value, this[nearestIndex].End) > 0) - { - while (type_comparer.Compare(value, this[nearestIndex].End) > 0 && nearestIndex < intervals.Count - 1) - nearestIndex++; - } - else - { - while (type_comparer.Compare(value, this[nearestIndex].Start) < 0 && nearestIndex > 0) - nearestIndex--; - } - - var nearestInterval = this[nearestIndex]; - - return type_comparer.Compare(value, nearestInterval.Start) >= 0 && - type_comparer.Compare(value, nearestInterval.End) <= 0; - } - - /// - /// Adds a new interval to the list. - /// - /// The start value of the interval. - /// The end value of the interval. - public void Add(T start, T end) => Add(new Interval(start, end)); - - /// - /// Adds a new interval to the list - /// - /// The interval to add. - public void Add(Interval interval) => intervals.Add(interval); - - /// - /// Removes an existing interval from the list. - /// - /// The interval to remove. - /// Whether the provided interval exists in the list and has been removed. - public bool Remove(Interval interval) => intervals.Remove(interval); - - /// - /// Removes all intervals from the list. - /// - public void Clear() => intervals.Clear(); - - public IEnumerator> GetEnumerator() => intervals.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } - - public readonly struct Interval - where T : struct, IConvertible - { - /// - /// The start value of this interval. - /// - public readonly T Start; - - /// - /// The end value of this interval. - /// - public readonly T End; - - public Interval(T start, T end) - { - if (Comparer.Default.Compare(start, end) >= 0) - throw new ArgumentException($"Invalid interval, {nameof(start)} must be less than {nameof(end)}", nameof(start)); - - Start = start; - End = end; - } - } -} diff --git a/osu.Game/Utils/PeriodTracker.cs b/osu.Game/Utils/PeriodTracker.cs new file mode 100644 index 0000000000..589f061c1d --- /dev/null +++ b/osu.Game/Utils/PeriodTracker.cs @@ -0,0 +1,90 @@ +// 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; + +namespace osu.Game.Utils +{ + /// + /// Represents a tracking component used for whether a + /// specific time falls into any of the provided periods. + /// + public class PeriodTracker + { + private readonly List periods = new List(); + private int nearestIndex; + + /// + /// The list of periods to add to the tracker for using the required check methods. + /// + public IEnumerable Periods + { + set + { + var sortedValue = value?.ToList(); + sortedValue?.Sort(); + + if (sortedValue != null && periods.SequenceEqual(sortedValue)) + return; + + periods.Clear(); + nearestIndex = 0; + + if (value?.Any() != true) + return; + + periods.AddRange(sortedValue); + } + } + + /// + /// Whether the provided time is in any of the added periods. + /// + /// The time value to check for. + public bool Contains(double time) + { + if (periods.Count == 0) + return false; + + if (time > periods[nearestIndex].End) + { + while (time > periods[nearestIndex].End && nearestIndex < periods.Count - 1) + nearestIndex++; + } + else + { + while (time < periods[nearestIndex].Start && nearestIndex > 0) + nearestIndex--; + } + + var nearest = periods[nearestIndex]; + return time >= nearest.Start && time <= nearest.End; + } + } + + public readonly struct Period : IComparable + { + /// + /// The start time of this period. + /// + public readonly double Start; + + /// + /// The end time of this period. + /// + public readonly double End; + + public Period(double start, double end) + { + if (start >= end) + throw new ArgumentException($"Invalid period provided, {nameof(start)} must be less than {nameof(end)}", nameof(start)); + + Start = start; + End = end; + } + + public int CompareTo(Period other) => Start.CompareTo(other.Start); + } +} From 587f946dc63ba8315b8f90b8b1cd9b5d42a9fc06 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 29 Apr 2020 04:58:08 +0300 Subject: [PATCH 054/121] Adjust, simplify and make the test code more flexible to some degree --- osu.Game.Tests/Lists/IntervalListTest.cs | 115 ------------------ osu.Game.Tests/NonVisual/PeriodTrackerTest.cs | 103 ++++++++++++++++ 2 files changed, 103 insertions(+), 115 deletions(-) delete mode 100644 osu.Game.Tests/Lists/IntervalListTest.cs create mode 100644 osu.Game.Tests/NonVisual/PeriodTrackerTest.cs diff --git a/osu.Game.Tests/Lists/IntervalListTest.cs b/osu.Game.Tests/Lists/IntervalListTest.cs deleted file mode 100644 index 0958f0fa7c..0000000000 --- a/osu.Game.Tests/Lists/IntervalListTest.cs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using NUnit.Framework; -using osu.Game.Lists; - -namespace osu.Game.Tests.Lists -{ - [TestFixture] - public class IntervalListTest - { - // this is intended to be unordered to test adding intervals in unordered way. - private static readonly (double, double)[] test_intervals = - { - (-9.1d, -8.3d), - (-3.4d, 2.1d), - (9.0d, 50.0d), - (5.25d, 10.50d), - }; - - [Test] - public void TestCheckValueInsideSingleInterval() - { - var list = new IntervalList { { 1.0d, 2.0d } }; - - Assert.IsTrue(list.IsInAnyInterval(1.0d)); - Assert.IsTrue(list.IsInAnyInterval(1.5d)); - Assert.IsTrue(list.IsInAnyInterval(2.0d)); - } - - [Test] - public void TestCheckValuesInsideIntervals() - { - var list = new IntervalList(); - - foreach (var (start, end) in test_intervals) - list.Add(start, end); - - Assert.IsTrue(list.IsInAnyInterval(-8.75d)); - Assert.IsTrue(list.IsInAnyInterval(1.0d)); - Assert.IsTrue(list.IsInAnyInterval(7.89d)); - Assert.IsTrue(list.IsInAnyInterval(9.8d)); - Assert.IsTrue(list.IsInAnyInterval(15.83d)); - } - - [Test] - public void TestCheckValuesInRandomOrder() - { - var list = new IntervalList(); - - foreach (var (start, end) in test_intervals) - list.Add(start, end); - - Assert.IsTrue(list.IsInAnyInterval(9.8d)); - Assert.IsTrue(list.IsInAnyInterval(7.89d)); - Assert.IsTrue(list.IsInAnyInterval(1.0d)); - Assert.IsTrue(list.IsInAnyInterval(15.83d)); - Assert.IsTrue(list.IsInAnyInterval(-8.75d)); - } - - [Test] - public void TestCheckValuesOutOfIntervals() - { - var list = new IntervalList(); - - foreach (var (start, end) in test_intervals) - list.Add(start, end); - - Assert.IsFalse(list.IsInAnyInterval(-9.2d)); - Assert.IsFalse(list.IsInAnyInterval(2.2d)); - Assert.IsFalse(list.IsInAnyInterval(5.15d)); - Assert.IsFalse(list.IsInAnyInterval(51.2d)); - } - - [Test] - public void TestCheckValueAfterRemovedInterval() - { - var list = new IntervalList { { 50, 100 }, { 150, 200 }, { 250, 300 } }; - - Assert.IsTrue(list.IsInAnyInterval(75)); - Assert.IsTrue(list.IsInAnyInterval(175)); - Assert.IsTrue(list.IsInAnyInterval(275)); - - list.Remove(list[1]); - - Assert.IsFalse(list.IsInAnyInterval(175)); - Assert.IsTrue(list.IsInAnyInterval(75)); - Assert.IsTrue(list.IsInAnyInterval(275)); - } - - [Test] - public void TestCheckValueAfterAddedInterval() - { - var list = new IntervalList { { 50, 100 }, { 250, 300 } }; - - Assert.IsFalse(list.IsInAnyInterval(175)); - Assert.IsTrue(list.IsInAnyInterval(75)); - Assert.IsTrue(list.IsInAnyInterval(275)); - - list.Add(150, 200); - - Assert.IsTrue(list.IsInAnyInterval(175)); - } - - [Test] - public void TestReversedIntervalThrows() - { - var list = new IntervalList(); - - Assert.Throws(() => list.Add(50, 25)); - Assert.Throws(() => list.Add(new Interval(50, 25))); - } - } -} diff --git a/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs b/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs new file mode 100644 index 0000000000..39eea2b386 --- /dev/null +++ b/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs @@ -0,0 +1,103 @@ +// 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 NUnit.Framework; +using osu.Framework.Utils; +using osu.Game.Utils; + +namespace osu.Game.Tests.NonVisual +{ + [TestFixture] + public class PeriodTrackerTest + { + private static readonly Period[] test_single_period = { new Period(1.0, 2.0) }; + + // this is intended to be unordered to test adding periods in unordered way. + private static readonly Period[] test_periods = + { + new Period(-9.1, -8.3), + new Period(-3.4, 2.1), + new Period(9.0, 50.0), + new Period(5.25, 10.50) + }; + + [Test] + public void TestCheckValueInsideSinglePeriod() + { + var tracker = new PeriodTracker { Periods = test_single_period }; + + var period = test_single_period.Single(); + Assert.IsTrue(tracker.Contains(period.Start)); + Assert.IsTrue(tracker.Contains(getMidTime(period))); + Assert.IsTrue(tracker.Contains(period.End)); + } + + [Test] + public void TestCheckValuesInsidePeriods() + { + var tracker = new PeriodTracker { Periods = test_periods }; + + foreach (var period in test_periods) + Assert.IsTrue(tracker.Contains(getMidTime(period))); + } + + [Test] + public void TestCheckValuesInRandomOrder() + { + var tracker = new PeriodTracker { Periods = test_periods }; + + foreach (var period in test_periods.OrderBy(_ => RNG.Next())) + Assert.IsTrue(tracker.Contains(getMidTime(period))); + } + + [Test] + public void TestCheckValuesOutOfPeriods() + { + var tracker = new PeriodTracker + { + Periods = new[] + { + new Period(1.0, 2.0), + new Period(3.0, 4.0) + } + }; + + Assert.IsFalse(tracker.Contains(0.9), "Time before first period is being considered inside"); + + Assert.IsFalse(tracker.Contains(2.1), "Time right after first period is being considered inside"); + Assert.IsFalse(tracker.Contains(2.9), "Time right before second period is being considered inside"); + + Assert.IsFalse(tracker.Contains(4.1), "Time after last period is being considered inside"); + } + + [Test] + public void TestNullRemovesExistingPeriods() + { + var tracker = new PeriodTracker { Periods = test_single_period }; + + var period = test_single_period.Single(); + Assert.IsTrue(tracker.Contains(getMidTime(period))); + + tracker.Periods = null; + Assert.IsFalse(tracker.Contains(getMidTime(period))); + } + + [Test] + public void TestReversedPeriodHandling() + { + var tracker = new PeriodTracker(); + + Assert.Throws(() => + { + tracker.Periods = new[] + { + new Period(2.0, 1.0) + }; + }); + } + + private double getMidTime(Period period) => period.Start + (period.End - period.Start) / 2; + } +} From 8d899f4e7742e93b8afa0432e865685e3b92e71f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 29 Apr 2020 05:07:58 +0300 Subject: [PATCH 055/121] Apply changes to the BreakTracker and more adjustment --- osu.Game/Screens/Play/BreakTracker.cs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Play/BreakTracker.cs b/osu.Game/Screens/Play/BreakTracker.cs index c2eb069ee6..e30c0c6dec 100644 --- a/osu.Game/Screens/Play/BreakTracker.cs +++ b/osu.Game/Screens/Play/BreakTracker.cs @@ -2,20 +2,22 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps.Timing; -using osu.Game.Lists; using osu.Game.Rulesets.Scoring; +using osu.Game.Utils; namespace osu.Game.Screens.Play { public class BreakTracker : Component { private readonly ScoreProcessor scoreProcessor; - private readonly double gameplayStartTime; + private readonly PeriodTracker tracker = new PeriodTracker(); + /// /// Whether the gameplay is currently in a break. /// @@ -23,22 +25,14 @@ namespace osu.Game.Screens.Play private readonly BindableBool isBreakTime = new BindableBool(); - private readonly IntervalList breakIntervals = new IntervalList(); - public IReadOnlyList Breaks { set { isBreakTime.Value = false; - breakIntervals.Clear(); - foreach (var b in value) - { - if (!b.HasEffect) - continue; - - breakIntervals.Add(b.StartTime, b.EndTime - BreakOverlay.BREAK_FADE_DURATION); - } + tracker.Periods = value?.Where(b => b.HasEffect) + .Select(b => new Period(b.StartTime, b.EndTime - BreakOverlay.BREAK_FADE_DURATION)); } } @@ -54,7 +48,7 @@ namespace osu.Game.Screens.Play var time = Clock.CurrentTime; - isBreakTime.Value = breakIntervals.IsInAnyInterval(time) + isBreakTime.Value = tracker.Contains(time) || time < gameplayStartTime || scoreProcessor?.HasCompleted == true; } From 6e76e5900a4d965eddecb7dc2223d47cf8d2c38b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 29 Apr 2020 05:08:38 +0300 Subject: [PATCH 056/121] Rename is-in-any check method to a more legible name --- osu.Game.Tests/NonVisual/PeriodTrackerTest.cs | 22 +++++++++---------- osu.Game/Screens/Play/BreakTracker.cs | 2 +- osu.Game/Utils/PeriodTracker.cs | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs b/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs index 39eea2b386..f033672576 100644 --- a/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs +++ b/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs @@ -29,9 +29,9 @@ namespace osu.Game.Tests.NonVisual var tracker = new PeriodTracker { Periods = test_single_period }; var period = test_single_period.Single(); - Assert.IsTrue(tracker.Contains(period.Start)); - Assert.IsTrue(tracker.Contains(getMidTime(period))); - Assert.IsTrue(tracker.Contains(period.End)); + Assert.IsTrue(tracker.IsInAny(period.Start)); + Assert.IsTrue(tracker.IsInAny(getMidTime(period))); + Assert.IsTrue(tracker.IsInAny(period.End)); } [Test] @@ -40,7 +40,7 @@ namespace osu.Game.Tests.NonVisual var tracker = new PeriodTracker { Periods = test_periods }; foreach (var period in test_periods) - Assert.IsTrue(tracker.Contains(getMidTime(period))); + Assert.IsTrue(tracker.IsInAny(getMidTime(period))); } [Test] @@ -49,7 +49,7 @@ namespace osu.Game.Tests.NonVisual var tracker = new PeriodTracker { Periods = test_periods }; foreach (var period in test_periods.OrderBy(_ => RNG.Next())) - Assert.IsTrue(tracker.Contains(getMidTime(period))); + Assert.IsTrue(tracker.IsInAny(getMidTime(period))); } [Test] @@ -64,12 +64,12 @@ namespace osu.Game.Tests.NonVisual } }; - Assert.IsFalse(tracker.Contains(0.9), "Time before first period is being considered inside"); + Assert.IsFalse(tracker.IsInAny(0.9), "Time before first period is being considered inside"); - Assert.IsFalse(tracker.Contains(2.1), "Time right after first period is being considered inside"); - Assert.IsFalse(tracker.Contains(2.9), "Time right before second period is being considered inside"); + Assert.IsFalse(tracker.IsInAny(2.1), "Time right after first period is being considered inside"); + Assert.IsFalse(tracker.IsInAny(2.9), "Time right before second period is being considered inside"); - Assert.IsFalse(tracker.Contains(4.1), "Time after last period is being considered inside"); + Assert.IsFalse(tracker.IsInAny(4.1), "Time after last period is being considered inside"); } [Test] @@ -78,10 +78,10 @@ namespace osu.Game.Tests.NonVisual var tracker = new PeriodTracker { Periods = test_single_period }; var period = test_single_period.Single(); - Assert.IsTrue(tracker.Contains(getMidTime(period))); + Assert.IsTrue(tracker.IsInAny(getMidTime(period))); tracker.Periods = null; - Assert.IsFalse(tracker.Contains(getMidTime(period))); + Assert.IsFalse(tracker.IsInAny(getMidTime(period))); } [Test] diff --git a/osu.Game/Screens/Play/BreakTracker.cs b/osu.Game/Screens/Play/BreakTracker.cs index e30c0c6dec..eb77cb9369 100644 --- a/osu.Game/Screens/Play/BreakTracker.cs +++ b/osu.Game/Screens/Play/BreakTracker.cs @@ -48,7 +48,7 @@ namespace osu.Game.Screens.Play var time = Clock.CurrentTime; - isBreakTime.Value = tracker.Contains(time) + isBreakTime.Value = tracker.IsInAny(time) || time < gameplayStartTime || scoreProcessor?.HasCompleted == true; } diff --git a/osu.Game/Utils/PeriodTracker.cs b/osu.Game/Utils/PeriodTracker.cs index 589f061c1d..49b372bb27 100644 --- a/osu.Game/Utils/PeriodTracker.cs +++ b/osu.Game/Utils/PeriodTracker.cs @@ -43,7 +43,7 @@ namespace osu.Game.Utils /// Whether the provided time is in any of the added periods. /// /// The time value to check for. - public bool Contains(double time) + public bool IsInAny(double time) { if (periods.Count == 0) return false; From 024f10a494f124e632652cae331bbcce1dbf3c39 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 29 Apr 2020 13:24:31 +0900 Subject: [PATCH 057/121] Use non-generic bindable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Bartłomiej Dach --- osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 39f3331fbb..fa26e6d713 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Mania.UI protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config; private readonly Bindable configDirection = new Bindable(); - private readonly Bindable configTimeRange = new Bindable(); + private readonly Bindable configTimeRange = new BindableDouble(); // Stores the current speed adjustment active in gameplay. private readonly Track speedAdjustmentTrack = new TrackVirtual(1000); From 0c95d11fdb0db19fc1610774961ffe376241d3e5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Apr 2020 13:27:33 +0900 Subject: [PATCH 058/121] Remove unnecessary value change binding --- osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index fa26e6d713..00fa68d088 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -86,7 +86,6 @@ namespace osu.Game.Rulesets.Mania.UI configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true); Config.BindWith(ManiaRulesetSetting.ScrollTime, configTimeRange); - configTimeRange.BindValueChanged(_ => updateTimeRange()); } protected override void AdjustScrollSpeed(int amount) From 4f332ace1426b1c6840d0b4af3aad2e4a8e58e14 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Apr 2020 14:27:21 +0900 Subject: [PATCH 059/121] Use 0 length --- osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 00fa68d088..f3f843f366 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Mania.UI private readonly Bindable configTimeRange = new BindableDouble(); // Stores the current speed adjustment active in gameplay. - private readonly Track speedAdjustmentTrack = new TrackVirtual(1000); + private readonly Track speedAdjustmentTrack = new TrackVirtual(0); public DrawableManiaRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) : base(ruleset, beatmap, mods) From c73d45bc01de6d2d2175c028400bb4c6d727c2f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Apr 2020 15:23:28 +0900 Subject: [PATCH 060/121] Reduce initial channel load overhead by only loading history on active channel --- osu.Game/Online/Chat/ChannelManager.cs | 12 ++++++------ osu.Game/Overlays/Chat/DrawableChannel.cs | 8 ++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 822f628dd2..53872ddcba 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -93,6 +93,12 @@ namespace osu.Game.Online.Chat { if (!(e.NewValue is ChannelSelectorTabItem.ChannelSelectorTabChannel)) JoinChannel(e.NewValue); + + if (e.NewValue?.MessagesLoaded == false) + { + // let's fetch a small number of messages to bring us up-to-date with the backlog. + fetchInitalMessages(e.NewValue); + } } /// @@ -375,12 +381,6 @@ namespace osu.Game.Online.Chat if (CurrentChannel.Value == null) CurrentChannel.Value = channel; - if (!channel.MessagesLoaded) - { - // let's fetch a small number of messages to bring us up-to-date with the backlog. - fetchInitalMessages(channel); - } - return channel; } diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 6019657cf0..d63faebae4 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -105,6 +105,14 @@ namespace osu.Game.Overlays.Chat private void newMessagesArrived(IEnumerable newMessages) { + if (newMessages.Min(m => m.Id) < chatLines.Max(c => c.Message.Id)) + { + // there is a case (on initial population) that we may receive past messages and need to reorder. + // easiest way is to just combine messages and recreate drawables (less worrying about day separators etc.) + newMessages = newMessages.Concat(chatLines.Select(c => c.Message)).OrderBy(m => m.Id).ToList(); + ChatLineFlow.Clear(); + } + bool shouldScrollToEnd = scroll.IsScrolledToEnd(10) || !chatLines.Any() || newMessages.Any(m => m is LocalMessage); // Add up to last Channel.MAX_HISTORY messages From d1ec99ffd9c790bf57f10a9592bd9e06b7b4df53 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Apr 2020 16:51:22 +0900 Subject: [PATCH 061/121] Further improve beatmap carousel load performance by avoiding incorrect query construction --- osu.Game/Beatmaps/BeatmapManager.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 5651d07566..b8dfac0342 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -300,7 +300,7 @@ namespace osu.Game.Beatmaps /// /// The level of detail to include in the returned objects. /// A list of available . - public IQueryable GetAllUsableBeatmapSetsEnumerable(IncludedDetails includes) + public IEnumerable GetAllUsableBeatmapSetsEnumerable(IncludedDetails includes) { IQueryable queryable; @@ -319,7 +319,10 @@ namespace osu.Game.Beatmaps break; } - return queryable.Where(s => !s.DeletePending && !s.Protected); + // AsEnumerable used here to avoid applying the WHERE in sql. When done so, ef core 2.x uses an incorrect ORDER BY + // clause which causes queries to take 5-10x longer. + // TODO: remove if upgrading to EF core 3.x. + return queryable.AsEnumerable().Where(s => !s.DeletePending && !s.Protected); } /// From 48733a7e2f5c4d0d02666950e5d61fe04d435f25 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Apr 2020 17:53:25 +0900 Subject: [PATCH 062/121] Change taiko hit explosion animation to match stable for skins --- .../Skinning/LegacyHitExplosion.cs | 10 ++++++++-- .../UI/DefaultHitExplosion.cs | 3 +++ osu.Game.Rulesets.Taiko/UI/HitExplosion.cs | 16 +++++++--------- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs index d29b574866..42d4a34b9d 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs @@ -22,8 +22,14 @@ namespace osu.Game.Rulesets.Taiko.Skinning { base.LoadComplete(); - this.FadeIn(120); - this.ScaleTo(0.6f).Then().ScaleTo(1, 240, Easing.OutElastic); + const double animation_time = 120; + + this.FadeInFromZero(animation_time).Then().FadeOut(animation_time * 1.5); + + this.ScaleTo(0.6f) + .Then().ScaleTo(1.1f, animation_time * 0.8) + .Then().ScaleTo(0.9f, animation_time * 0.4) + .Then().ScaleTo(1f, animation_time * 0.2); } } } diff --git a/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs index aa444d0494..a0ca5f1c39 100644 --- a/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs @@ -49,6 +49,9 @@ namespace osu.Game.Rulesets.Taiko.UI base.LoadComplete(); this.ScaleTo(3f, 1000, Easing.OutQuint); + this.FadeOut(500); + + Expire(true); } } } diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs index 35a54d6ea7..f0585b9c50 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs @@ -23,6 +23,12 @@ namespace osu.Game.Rulesets.Taiko.UI [Cached(typeof(DrawableHitObject))] public readonly DrawableHitObject JudgedObject; + private SkinnableDrawable skinnable; + + public override double LifetimeStart => skinnable.Drawable.LifetimeStart; + + public override double LifetimeEnd => skinnable.Drawable.LifetimeEnd; + public HitExplosion(DrawableHitObject judgedObject) { JudgedObject = judgedObject; @@ -39,7 +45,7 @@ namespace osu.Game.Rulesets.Taiko.UI [BackgroundDependencyLoader] private void load() { - Child = new SkinnableDrawable(new TaikoSkinComponent(getComponentName(JudgedObject.Result?.Type ?? HitResult.Great)), _ => new DefaultHitExplosion()); + Child = skinnable = new SkinnableDrawable(new TaikoSkinComponent(getComponentName(JudgedObject.Result?.Type ?? HitResult.Great)), _ => new DefaultHitExplosion()); } private TaikoSkinComponents getComponentName(HitResult resultType) @@ -59,14 +65,6 @@ namespace osu.Game.Rulesets.Taiko.UI throw new ArgumentOutOfRangeException(nameof(resultType), "Invalid result type"); } - protected override void LoadComplete() - { - base.LoadComplete(); - - this.FadeOut(500); - Expire(true); - } - /// /// Transforms this hit explosion to visualise a secondary hit. /// From 783dc58ef0f600073ef115f4e4868902f96473ea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Apr 2020 09:41:56 +0900 Subject: [PATCH 063/121] Move taiko additive blending locally to avoid applying to legacy skins --- osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs | 2 ++ osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs | 2 ++ osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 3 --- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs index 42d4a34b9d..c44da9ce1e 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs @@ -16,6 +16,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning Origin = Anchor.Centre; AutoSizeAxes = Axes.Both; + + Blending = BlendingParameters.Additive; } protected override void LoadComplete() diff --git a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs index 3a307bb3bb..067d390894 100644 --- a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs @@ -31,6 +31,8 @@ namespace osu.Game.Rulesets.Taiko.UI RelativeSizeAxes = Axes.Both; Size = new Vector2(TaikoHitObject.DEFAULT_SIZE, 1); + Blending = BlendingParameters.Additive; + Masking = true; Alpha = 0.25f; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 6a78c0a1fb..5c763cb332 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -68,7 +68,6 @@ namespace osu.Game.Rulesets.Taiko.UI hitExplosionContainer = new Container { RelativeSizeAxes = Axes.Both, - Blending = BlendingParameters.Additive, }, HitTarget = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.HitTarget), _ => new TaikoHitTarget()) { @@ -100,13 +99,11 @@ namespace osu.Game.Rulesets.Taiko.UI Name = "Kiai hit explosions", RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fit, - Blending = BlendingParameters.Additive }, judgementContainer = new JudgementContainer { Name = "Judgements", RelativeSizeAxes = Axes.Y, - Blending = BlendingParameters.Additive }, } }, From 49a98fde7374055a8b2aa5286b733ec3673f55ad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Apr 2020 09:57:14 +0900 Subject: [PATCH 064/121] Move to non-legacy class --- osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs | 2 -- osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs index c44da9ce1e..42d4a34b9d 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs @@ -16,8 +16,6 @@ namespace osu.Game.Rulesets.Taiko.Skinning Origin = Anchor.Centre; AutoSizeAxes = Axes.Both; - - Blending = BlendingParameters.Additive; } protected override void LoadComplete() diff --git a/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs index a0ca5f1c39..9943a58e3e 100644 --- a/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs @@ -26,6 +26,8 @@ namespace osu.Game.Rulesets.Taiko.UI BorderColour = Color4.White; BorderThickness = 1; + Blending = BlendingParameters.Additive; + Alpha = 0.15f; Masking = true; From cf4e79cf38490ecb85f213b154dc7eef3ffb14ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Apr 2020 11:51:06 +0900 Subject: [PATCH 065/121] Show loading spinner when carousel is not ready to be displayed --- .../SongSelect/TestScenePlaySongSelect.cs | 1 + osu.Game/Screens/Select/SongSelect.cs | 33 +++++++++++-------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 39e04ed39a..aed8e19fb2 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -797,6 +797,7 @@ namespace osu.Game.Tests.Visual.SongSelect { AddStep("create song select", () => LoadScreen(songSelect = new TestSongSelect())); AddUntilStep("wait for present", () => songSelect.IsCurrentScreen()); + AddUntilStep("wait for carousel loaded", () => songSelect.Carousel.IsAlive); } private void addManyTestMaps() diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index c07465ca44..6b896694ea 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -34,6 +34,7 @@ using System.Linq; using System.Threading.Tasks; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; +using osu.Game.Graphics.UserInterface; using osu.Game.Scoring; namespace osu.Game.Screens.Select @@ -92,6 +93,8 @@ namespace osu.Game.Screens.Select private SampleChannel sampleChangeDifficulty; private SampleChannel sampleChangeBeatmap; + private Container carouselContainer; + protected BeatmapDetailArea BeatmapDetails { get; private set; } private readonly Bindable decoupledRuleset = new Bindable(); @@ -105,9 +108,22 @@ namespace osu.Game.Screens.Select // initial value transfer is required for FilterControl (it uses our re-cached bindables in its async load for the initial filter). transferRulesetValue(); + LoadComponentAsync(Carousel = new BeatmapCarousel + { + AllowSelection = false, // delay any selection until our bindables are ready to make a good choice. + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Both, + BleedTop = FilterControl.HEIGHT, + BleedBottom = Footer.HEIGHT, + SelectionChanged = updateSelectedBeatmap, + BeatmapSetsChanged = carouselBeatmapsLoaded, + GetRecommendedBeatmap = (recommender = new DifficultyRecommender()).GetRecommendedBeatmap, + }, c => carouselContainer.Child = c); + AddRangeInternal(new Drawable[] { - recommender = new DifficultyRecommender(), + recommender, new ResetScrollContainer(() => Carousel.ScrollToSelected()) { RelativeSizeAxes = Axes.Y, @@ -139,7 +155,7 @@ namespace osu.Game.Screens.Select Padding = new MarginPadding { Right = -150 }, }, }, - new Container + carouselContainer = new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding @@ -147,18 +163,7 @@ namespace osu.Game.Screens.Select Top = FilterControl.HEIGHT, Bottom = Footer.HEIGHT }, - Child = Carousel = new BeatmapCarousel - { - AllowSelection = false, // delay any selection until our bindables are ready to make a good choice. - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.Both, - BleedTop = FilterControl.HEIGHT, - BleedBottom = Footer.HEIGHT, - SelectionChanged = updateSelectedBeatmap, - BeatmapSetsChanged = carouselBeatmapsLoaded, - GetRecommendedBeatmap = recommender.GetRecommendedBeatmap, - }, + Child = new LoadingSpinner(true) { State = { Value = Visibility.Visible } } } }, } From 21c6ac8c43725f8739dcaea3a2b6105e7feab142 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Apr 2020 12:12:28 +0900 Subject: [PATCH 066/121] Allow closing the game during intro --- osu.Game/OsuGame.cs | 5 +---- osu.Game/Screens/Menu/IntroScreen.cs | 2 -- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index f5f7d0cef4..8e62819c95 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -914,10 +914,7 @@ namespace osu.Game if (ScreenStack.CurrentScreen is Loader) return false; - if (introScreen == null) - return true; - - if (!introScreen.DidLoadMenu || !(ScreenStack.CurrentScreen is IntroScreen)) + if (introScreen.DidLoadMenu && !(ScreenStack.CurrentScreen is IntroScreen)) { Scheduler.Add(introScreen.MakeCurrent); return true; diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index d2296573a6..736202ee52 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -96,8 +96,6 @@ namespace osu.Game.Screens.Menu Track = introBeatmap.Track; } - public override bool OnExiting(IScreen next) => !DidLoadMenu; - public override void OnResuming(IScreen last) { this.FadeIn(300); From 48af4d4eb4fc3ef8abf757ab512211defddee949 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Apr 2020 16:18:15 +0900 Subject: [PATCH 067/121] Fix skinned taiko hit explosions not being removed on rewind --- osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs index 42d4a34b9d..b5ec2e8def 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs @@ -30,6 +30,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning .Then().ScaleTo(1.1f, animation_time * 0.8) .Then().ScaleTo(0.9f, animation_time * 0.4) .Then().ScaleTo(1f, animation_time * 0.2); + + Expire(true); } } } From d0a8c0fa71bfe372a535380d258eb259b7f60dd4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Apr 2020 16:42:38 +0900 Subject: [PATCH 068/121] Add kiai support to osu!taiko skinned playfields --- .../Skinning/TestSceneTaikoPlayfield.cs | 18 ++++++ .../TaikoLegacyPlayfieldBackgroundRight.cs | 57 +++++++++++++++++++ .../Skinning/TaikoLegacySkinTransformer.cs | 9 +-- osu.Game/Beatmaps/WorkingBeatmap.cs | 4 +- osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs | 4 ++ 5 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyPlayfieldBackgroundRight.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs index 16b3c036a3..ae5dd1e622 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs @@ -9,6 +9,7 @@ using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Taiko.Beatmaps; using osu.Game.Rulesets.Taiko.Skinning; using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.UI.Scrolling; @@ -34,6 +35,18 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning public TestSceneTaikoPlayfield() { + TaikoBeatmap beatmap; + bool kiai = false; + + AddStep("set beatmap", () => + { + Beatmap.Value = CreateWorkingBeatmap(beatmap = new TaikoBeatmap()); + + beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 1000 }); + + Beatmap.Value.Track.Start(); + }); + AddStep("Load playfield", () => SetContents(() => new TaikoPlayfield(new ControlPointInfo()) { Anchor = Anchor.CentreLeft, @@ -41,6 +54,11 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning })); AddRepeatStep("change height", () => this.ChildrenOfType().ForEach(p => p.Height = Math.Max(0.2f, (p.Height + 0.2f) % 1f)), 50); + + AddStep("Toggle kiai", () => + { + Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new EffectControlPoint { KiaiMode = (kiai = !kiai) }); + }); } } } diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyPlayfieldBackgroundRight.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyPlayfieldBackgroundRight.cs new file mode 100644 index 0000000000..7508c75231 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyPlayfieldBackgroundRight.cs @@ -0,0 +1,57 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.Containers; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Rulesets.Taiko.Skinning +{ + public class TaikoLegacyPlayfieldBackgroundRight : BeatSyncedContainer + { + private Sprite kiai; + + private bool kiaiDisplayed; + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + RelativeSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new Sprite + { + Texture = skin.GetTexture("taiko-bar-right"), + RelativeSizeAxes = Axes.Both, + Size = Vector2.One, + }, + kiai = new Sprite + { + Texture = skin.GetTexture("taiko-bar-right-glow"), + RelativeSizeAxes = Axes.Both, + Size = Vector2.One, + Alpha = 0, + } + }; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) + { + base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + + if (effectPoint.KiaiMode != kiaiDisplayed) + { + kiaiDisplayed = effectPoint.KiaiMode; + + kiai.ClearTransforms(); + kiai.FadeTo(kiaiDisplayed ? 1 : 0, 200); + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index f0df612e18..5dfc7ec0df 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Game.Audio; using osu.Game.Skinning; -using osuTK; namespace osu.Game.Rulesets.Taiko.Skinning { @@ -60,13 +59,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning case TaikoSkinComponents.PlayfieldBackgroundRight: if (GetTexture("taiko-bar-right") != null) - { - return this.GetAnimation("taiko-bar-right", false, false).With(d => - { - d.RelativeSizeAxes = Axes.Both; - d.Size = Vector2.One; - }); - } + return new TaikoLegacyPlayfieldBackgroundRight(); return null; diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index f30340956a..d2804bdc05 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -197,7 +197,7 @@ namespace osu.Game.Beatmaps public override string ToString() => BeatmapInfo.ToString(); - public bool BeatmapLoaded => beatmapLoadTask?.IsCompleted ?? false; + public virtual bool BeatmapLoaded => beatmapLoadTask?.IsCompleted ?? false; public IBeatmap Beatmap { @@ -233,7 +233,7 @@ namespace osu.Game.Beatmaps protected abstract Texture GetBackground(); private readonly RecyclableLazy background; - public bool TrackLoaded => track.IsResultAvailable; + public virtual bool TrackLoaded => track.IsResultAvailable; public Track Track => track.Value; protected abstract Track GetTrack(); private RecyclableLazy track; diff --git a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs index 8f8afb87d4..cdf9170701 100644 --- a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs @@ -27,6 +27,10 @@ namespace osu.Game.Tests.Beatmaps this.storyboard = storyboard; } + public override bool TrackLoaded => true; + + public override bool BeatmapLoaded => true; + protected override IBeatmap GetBeatmap() => beatmap; protected override Storyboard GetStoryboard() => storyboard ?? base.GetStoryboard(); From 9bec42bc7ee8e59a1e9faebdc9388350259a7212 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Apr 2020 20:03:46 +0900 Subject: [PATCH 069/121] Fix mania crashing on undo/redo --- osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs index 17eba87076..45a0a5c485 100644 --- a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs +++ b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs @@ -84,7 +84,11 @@ namespace osu.Game.Screens.Edit { using (var stream = new MemoryStream(state)) using (var reader = new LineBufferedReader(stream, true)) - return new PassThroughWorkingBeatmap(Decoder.GetDecoder(reader).Decode(reader)).GetPlayableBeatmap(editorBeatmap.BeatmapInfo.Ruleset); + { + var decoded = Decoder.GetDecoder(reader).Decode(reader); + decoded.BeatmapInfo.Ruleset = editorBeatmap.BeatmapInfo.Ruleset; + return new PassThroughWorkingBeatmap(decoded).GetPlayableBeatmap(editorBeatmap.BeatmapInfo.Ruleset); + } } private class PassThroughWorkingBeatmap : WorkingBeatmap From c96bc5c51cdfe0f607ebd9925ab160358fb10a81 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Apr 2020 20:39:41 +0900 Subject: [PATCH 070/121] Fix undo/redo behaving poorly with simultaneous objects --- .../Editing/LegacyEditorBeatmapPatcherTest.cs | 25 +++++++++++++++++++ osu.Game/Screens/Edit/EditorBeatmap.cs | 20 ++++++++++++--- .../Edit/LegacyEditorBeatmapPatcher.cs | 6 +++-- 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs b/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs index a3ab677d96..ff17f23d50 100644 --- a/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs +++ b/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs @@ -304,6 +304,31 @@ namespace osu.Game.Tests.Editing runTest(patch); } + [Test] + public void TestChangeHitObjectAtSameTime() + { + current.AddRange(new[] + { + new HitCircle { StartTime = 500, Position = new Vector2(50) }, + new HitCircle { StartTime = 500, Position = new Vector2(100) }, + new HitCircle { StartTime = 500, Position = new Vector2(150) }, + new HitCircle { StartTime = 500, Position = new Vector2(200) }, + }); + + var patch = new OsuBeatmap + { + HitObjects = + { + new HitCircle { StartTime = 500, Position = new Vector2(150) }, + new HitCircle { StartTime = 500, Position = new Vector2(100) }, + new HitCircle { StartTime = 500, Position = new Vector2(50) }, + new HitCircle { StartTime = 500, Position = new Vector2(200) }, + } + }; + + runTest(patch); + } + private void runTest(IBeatmap patch) { // Due to the method of testing, "patch" comes in without having been decoded via a beatmap decoder. diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index a2d2f08ce9..2e8e03bc73 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -136,14 +136,26 @@ namespace osu.Game.Screens.Edit /// The to add. public void Add(HitObject hitObject) { - trackStartTime(hitObject); - // Preserve existing sorting order in the beatmap var insertionIndex = findInsertionIndex(PlayableBeatmap.HitObjects, hitObject.StartTime); - mutableHitObjects.Insert(insertionIndex + 1, hitObject); + Insert(insertionIndex + 1, hitObject); + } + + /// + /// Inserts a into this . + /// + /// + /// It is the invoker's responsibility to make sure that sorting order is maintained. + /// + /// The index to insert the at. + /// The to insert. + public void Insert(int index, HitObject hitObject) + { + trackStartTime(hitObject); + + mutableHitObjects.Insert(index, hitObject); HitObjectAdded?.Invoke(hitObject); - updateHitObject(hitObject, true); } diff --git a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs index 17eba87076..04faba6478 100644 --- a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs +++ b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs @@ -63,8 +63,10 @@ namespace osu.Game.Screens.Edit } } - // Make the removal indices are sorted so that iteration order doesn't get messed up post-removal. + // Sort the indices to ensure that removal + insertion indices don't get jumbled up post-removal or post-insertion. + // This isn't strictly required, but the differ makes no guarantees about order. toRemove.Sort(); + toAdd.Sort(); // Apply the changes. for (int i = toRemove.Count - 1; i >= 0; i--) @@ -74,7 +76,7 @@ namespace osu.Game.Screens.Edit { IBeatmap newBeatmap = readBeatmap(newState); foreach (var i in toAdd) - editorBeatmap.Add(newBeatmap.HitObjects[i]); + editorBeatmap.Insert(i, newBeatmap.HitObjects[i]); } } From 000c34dc26ea9d815f0f76c32510f1a0b5f600a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Apr 2020 21:01:53 +0900 Subject: [PATCH 071/121] Move recommender to field construction --- osu.Game/Screens/Select/SongSelect.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 6b896694ea..a7e27c27ba 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -78,7 +78,7 @@ namespace osu.Game.Screens.Select protected BeatmapCarousel Carousel { get; private set; } - private DifficultyRecommender recommender; + private readonly DifficultyRecommender recommender = new DifficultyRecommender(); private BeatmapInfoWedge beatmapInfoWedge; private DialogOverlay dialogOverlay; @@ -118,7 +118,7 @@ namespace osu.Game.Screens.Select BleedBottom = Footer.HEIGHT, SelectionChanged = updateSelectedBeatmap, BeatmapSetsChanged = carouselBeatmapsLoaded, - GetRecommendedBeatmap = (recommender = new DifficultyRecommender()).GetRecommendedBeatmap, + GetRecommendedBeatmap = recommender.GetRecommendedBeatmap, }, c => carouselContainer.Child = c); AddRangeInternal(new Drawable[] From e0ae9f791c357a040f0ff06180451bf20dca2428 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 1 May 2020 11:56:40 +0900 Subject: [PATCH 072/121] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 73fbe3ab2e..336479c40a 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3b05eb82d7..acb7fe5fbe 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index f3202693f3..6662e57dcd 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From 700214d249d2938977c6161e4dcea22b712b1025 Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 30 Apr 2020 22:13:38 -0700 Subject: [PATCH 073/121] Truncate beatmap title and artist on score panel --- .../Ranking/TestSceneExpandedPanelMiddleContent.cs | 5 ++++- .../Ranking/Expanded/ExpandedPanelMiddleContent.cs | 10 ++++++++-- osu.Game/Screens/Ranking/ScorePanel.cs | 4 ++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs index 52d8ea0480..328a0e0c27 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs @@ -20,6 +20,7 @@ using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; +using osu.Game.Screens.Ranking; using osu.Game.Screens.Ranking.Expanded; using osu.Game.Screens.Ranking.Expanded.Accuracy; using osu.Game.Screens.Ranking.Expanded.Statistics; @@ -74,6 +75,8 @@ namespace osu.Game.Tests.Visual.Ranking { var beatmap = new TestBeatmap(rulesetStore.GetRuleset(0)); beatmap.Metadata.Author = author; + beatmap.Metadata.Title = "Verrrrrrrrrrrrrrrrrrry looooooooooooooooooooooooong beatmap title"; + beatmap.Metadata.Artist = "Verrrrrrrrrrrrrrrrrrry looooooooooooooooooooooooong beatmap artist"; return new TestWorkingBeatmap(beatmap); } @@ -114,7 +117,7 @@ namespace osu.Game.Tests.Visual.Ranking Anchor = Anchor.Centre; Origin = Anchor.Centre; - Size = new Vector2(500, 700); + Size = new Vector2(ScorePanel.EXPANDED_WIDTH, 700); Children = new Drawable[] { new Box diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index b058cc142b..fd8ac33aef 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -35,6 +35,8 @@ namespace osu.Game.Screens.Ranking.Expanded private RollingCounter scoreCounter; + private const float padding = 10; + /// /// Creates a new . /// @@ -46,7 +48,7 @@ namespace osu.Game.Screens.Ranking.Expanded RelativeSizeAxes = Axes.Both; Masking = true; - Padding = new MarginPadding { Vertical = 10, Horizontal = 10 }; + Padding = new MarginPadding(padding); } [BackgroundDependencyLoader] @@ -92,13 +94,17 @@ namespace osu.Game.Screens.Ranking.Expanded Origin = Anchor.TopCentre, Text = new LocalisedString((metadata.TitleUnicode, metadata.Title)), Font = OsuFont.Torus.With(size: 20, weight: FontWeight.SemiBold), + MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, + Truncate = true, }, new OsuSpriteText { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist)), - Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold) + Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), + MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, + Truncate = true, }, new Container { diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index a1adfcc500..c055df7ccc 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -31,7 +31,7 @@ namespace osu.Game.Screens.Ranking /// /// Width of the panel when expanded. /// - private const float expanded_width = 360; + public const float EXPANDED_WIDTH = 360; /// /// Height of the panel when expanded. @@ -183,7 +183,7 @@ namespace osu.Game.Screens.Ranking switch (state) { case PanelState.Expanded: - this.ResizeTo(new Vector2(expanded_width, expanded_height), resize_duration, Easing.OutQuint); + this.ResizeTo(new Vector2(EXPANDED_WIDTH, expanded_height), resize_duration, Easing.OutQuint); topLayerBackground.FadeColour(expanded_top_layer_colour, resize_duration, Easing.OutQuint); middleLayerBackground.FadeColour(expanded_middle_layer_colour, resize_duration, Easing.OutQuint); From 811874773288186d4e93620e44024331ebb59615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 2 May 2020 01:33:33 +0200 Subject: [PATCH 074/121] Make PeriodTracker actually immutable --- osu.Game.Tests/NonVisual/PeriodTrackerTest.cs | 54 +++++++------------ osu.Game/Screens/Play/BreakTracker.cs | 8 +-- osu.Game/Utils/PeriodTracker.cs | 35 +++--------- 3 files changed, 29 insertions(+), 68 deletions(-) diff --git a/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs b/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs index f033672576..62c7732b66 100644 --- a/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs +++ b/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs @@ -12,10 +12,9 @@ namespace osu.Game.Tests.NonVisual [TestFixture] public class PeriodTrackerTest { - private static readonly Period[] test_single_period = { new Period(1.0, 2.0) }; + private static readonly Period[] single_period = { new Period(1.0, 2.0) }; - // this is intended to be unordered to test adding periods in unordered way. - private static readonly Period[] test_periods = + private static readonly Period[] unordered_periods = { new Period(-9.1, -8.3), new Period(-3.4, 2.1), @@ -26,43 +25,40 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestCheckValueInsideSinglePeriod() { - var tracker = new PeriodTracker { Periods = test_single_period }; + var tracker = new PeriodTracker(single_period); - var period = test_single_period.Single(); + var period = single_period.Single(); Assert.IsTrue(tracker.IsInAny(period.Start)); - Assert.IsTrue(tracker.IsInAny(getMidTime(period))); + Assert.IsTrue(tracker.IsInAny(getMidpoint(period))); Assert.IsTrue(tracker.IsInAny(period.End)); } [Test] public void TestCheckValuesInsidePeriods() { - var tracker = new PeriodTracker { Periods = test_periods }; + var tracker = new PeriodTracker(unordered_periods); - foreach (var period in test_periods) - Assert.IsTrue(tracker.IsInAny(getMidTime(period))); + foreach (var period in unordered_periods) + Assert.IsTrue(tracker.IsInAny(getMidpoint(period))); } [Test] public void TestCheckValuesInRandomOrder() { - var tracker = new PeriodTracker { Periods = test_periods }; + var tracker = new PeriodTracker(unordered_periods); - foreach (var period in test_periods.OrderBy(_ => RNG.Next())) - Assert.IsTrue(tracker.IsInAny(getMidTime(period))); + foreach (var period in unordered_periods.OrderBy(_ => RNG.Next())) + Assert.IsTrue(tracker.IsInAny(getMidpoint(period))); } [Test] public void TestCheckValuesOutOfPeriods() { - var tracker = new PeriodTracker + var tracker = new PeriodTracker(new[] { - Periods = new[] - { - new Period(1.0, 2.0), - new Period(3.0, 4.0) - } - }; + new Period(1.0, 2.0), + new Period(3.0, 4.0) + }); Assert.IsFalse(tracker.IsInAny(0.9), "Time before first period is being considered inside"); @@ -72,32 +68,18 @@ namespace osu.Game.Tests.NonVisual Assert.IsFalse(tracker.IsInAny(4.1), "Time after last period is being considered inside"); } - [Test] - public void TestNullRemovesExistingPeriods() - { - var tracker = new PeriodTracker { Periods = test_single_period }; - - var period = test_single_period.Single(); - Assert.IsTrue(tracker.IsInAny(getMidTime(period))); - - tracker.Periods = null; - Assert.IsFalse(tracker.IsInAny(getMidTime(period))); - } - [Test] public void TestReversedPeriodHandling() { - var tracker = new PeriodTracker(); - Assert.Throws(() => { - tracker.Periods = new[] + _ = new PeriodTracker(new[] { new Period(2.0, 1.0) - }; + }); }); } - private double getMidTime(Period period) => period.Start + (period.End - period.Start) / 2; + private double getMidpoint(Period period) => period.Start + (period.End - period.Start) / 2; } } diff --git a/osu.Game/Screens/Play/BreakTracker.cs b/osu.Game/Screens/Play/BreakTracker.cs index 79da548336..51e21656e1 100644 --- a/osu.Game/Screens/Play/BreakTracker.cs +++ b/osu.Game/Screens/Play/BreakTracker.cs @@ -16,7 +16,7 @@ namespace osu.Game.Screens.Play private readonly ScoreProcessor scoreProcessor; private readonly double gameplayStartTime; - private readonly PeriodTracker tracker = new PeriodTracker(); + private PeriodTracker breaks; /// /// Whether the gameplay is currently in a break. @@ -31,8 +31,8 @@ namespace osu.Game.Screens.Play { isBreakTime.Value = false; - tracker.Periods = value?.Where(b => b.HasEffect) - .Select(b => new Period(b.StartTime, b.EndTime - BreakOverlay.BREAK_FADE_DURATION)); + breaks = new PeriodTracker(value.Where(b => b.HasEffect) + .Select(b => new Period(b.StartTime, b.EndTime - BreakOverlay.BREAK_FADE_DURATION))); } } @@ -48,7 +48,7 @@ namespace osu.Game.Screens.Play var time = Clock.CurrentTime; - isBreakTime.Value = tracker.IsInAny(time) + isBreakTime.Value = breaks?.IsInAny(time) == true || time < gameplayStartTime || scoreProcessor?.HasCompleted.Value == true; } diff --git a/osu.Game/Utils/PeriodTracker.cs b/osu.Game/Utils/PeriodTracker.cs index 49b372bb27..ba77702247 100644 --- a/osu.Game/Utils/PeriodTracker.cs +++ b/osu.Game/Utils/PeriodTracker.cs @@ -8,41 +8,22 @@ using System.Linq; namespace osu.Game.Utils { /// - /// Represents a tracking component used for whether a - /// specific time falls into any of the provided periods. + /// Represents a tracking component used for whether a specific time instant falls into any of the provided periods. /// public class PeriodTracker { - private readonly List periods = new List(); + private readonly List periods; private int nearestIndex; - /// - /// The list of periods to add to the tracker for using the required check methods. - /// - public IEnumerable Periods + public PeriodTracker(IEnumerable periods) { - set - { - var sortedValue = value?.ToList(); - sortedValue?.Sort(); - - if (sortedValue != null && periods.SequenceEqual(sortedValue)) - return; - - periods.Clear(); - nearestIndex = 0; - - if (value?.Any() != true) - return; - - periods.AddRange(sortedValue); - } + this.periods = periods.OrderBy(period => period.Start).ToList(); } /// /// Whether the provided time is in any of the added periods. /// - /// The time value to check for. + /// The time value to check. public bool IsInAny(double time) { if (periods.Count == 0) @@ -64,7 +45,7 @@ namespace osu.Game.Utils } } - public readonly struct Period : IComparable + public readonly struct Period { /// /// The start time of this period. @@ -79,12 +60,10 @@ namespace osu.Game.Utils public Period(double start, double end) { if (start >= end) - throw new ArgumentException($"Invalid period provided, {nameof(start)} must be less than {nameof(end)}", nameof(start)); + throw new ArgumentException($"Invalid period provided, {nameof(start)} must be less than {nameof(end)}"); Start = start; End = end; } - - public int CompareTo(Period other) => Start.CompareTo(other.Start); } } From deb87517d01019bca4b9a121118d52172600e74f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 2 May 2020 14:35:12 +0900 Subject: [PATCH 075/121] Add local beatmap lookup cache --- osu.Game/Beatmaps/BeatmapManager.cs | 68 +------ .../Beatmaps/BeatmapManager_UpdateQueue.cs | 180 ++++++++++++++++++ osu.Game/Database/ArchiveModelManager.cs | 2 +- osu.Game/osu.Game.csproj | 1 + 4 files changed, 183 insertions(+), 68 deletions(-) create mode 100644 osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index b8dfac0342..22451382a3 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -17,7 +17,6 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Lists; using osu.Framework.Logging; using osu.Framework.Platform; -using osu.Framework.Threading; using osu.Game.Beatmaps.Formats; using osu.Game.Database; using osu.Game.IO; @@ -78,7 +77,7 @@ namespace osu.Game.Beatmaps beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b); beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b); - updateQueue = new BeatmapUpdateQueue(api); + updateQueue = new BeatmapUpdateQueue(api, storage); exportStorage = storage.GetStorageForDirectory("exports"); } @@ -446,71 +445,6 @@ namespace osu.Game.Beatmaps protected override Texture GetBackground() => null; protected override Track GetTrack() => null; } - - private class BeatmapUpdateQueue - { - private readonly IAPIProvider api; - - private const int update_queue_request_concurrency = 4; - - private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(update_queue_request_concurrency, nameof(BeatmapUpdateQueue)); - - public BeatmapUpdateQueue(IAPIProvider api) - { - this.api = api; - } - - public Task UpdateAsync(BeatmapSetInfo beatmapSet, CancellationToken cancellationToken) - { - if (api?.State != APIState.Online) - return Task.CompletedTask; - - LogForModel(beatmapSet, "Performing online lookups..."); - return Task.WhenAll(beatmapSet.Beatmaps.Select(b => UpdateAsync(beatmapSet, b, cancellationToken)).ToArray()); - } - - // todo: expose this when we need to do individual difficulty lookups. - protected Task UpdateAsync(BeatmapSetInfo beatmapSet, BeatmapInfo beatmap, CancellationToken cancellationToken) - => Task.Factory.StartNew(() => update(beatmapSet, beatmap), cancellationToken, TaskCreationOptions.HideScheduler, updateScheduler); - - private void update(BeatmapSetInfo set, BeatmapInfo beatmap) - { - if (api?.State != APIState.Online) - return; - - var req = new GetBeatmapRequest(beatmap); - - req.Failure += fail; - - try - { - // intentionally blocking to limit web request concurrency - api.Perform(req); - - var res = req.Result; - - if (res != null) - { - beatmap.Status = res.Status; - beatmap.BeatmapSet.Status = res.BeatmapSet.Status; - beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID; - beatmap.OnlineBeatmapID = res.OnlineBeatmapID; - - LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}."); - } - } - catch (Exception e) - { - fail(e); - } - - void fail(Exception e) - { - beatmap.OnlineBeatmapID = null; - LogForModel(set, $"Online retrieval failed for {beatmap} ({e.Message})"); - } - } - } } /// diff --git a/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs b/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs new file mode 100644 index 0000000000..aa8be823f7 --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs @@ -0,0 +1,180 @@ +// 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.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Dapper; +using Microsoft.Data.Sqlite; +using osu.Framework.IO.Network; +using osu.Framework.Logging; +using osu.Framework.Platform; +using osu.Framework.Threading; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using SharpCompress.Compressors; +using SharpCompress.Compressors.BZip2; + +namespace osu.Game.Beatmaps +{ + public partial class BeatmapManager + { + private class BeatmapUpdateQueue + { + private readonly IAPIProvider api; + private readonly Storage storage; + + private const int update_queue_request_concurrency = 4; + + private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(update_queue_request_concurrency, nameof(BeatmapUpdateQueue)); + + private FileWebRequest cacheDownloadRequest; + + private const string cache_database_name = "online.db"; + + public BeatmapUpdateQueue(IAPIProvider api, Storage storage) + { + this.api = api; + this.storage = storage; + + if (!storage.Exists(cache_database_name)) + prepareLocalCache(); + } + + public Task UpdateAsync(BeatmapSetInfo beatmapSet, CancellationToken cancellationToken) + { + if (api?.State != APIState.Online) + return Task.CompletedTask; + + LogForModel(beatmapSet, "Performing online lookups..."); + return Task.WhenAll(beatmapSet.Beatmaps.Select(b => UpdateAsync(beatmapSet, b, cancellationToken)).ToArray()); + } + + // todo: expose this when we need to do individual difficulty lookups. + protected Task UpdateAsync(BeatmapSetInfo beatmapSet, BeatmapInfo beatmap, CancellationToken cancellationToken) + => Task.Factory.StartNew(() => update(beatmapSet, beatmap), cancellationToken, TaskCreationOptions.HideScheduler, updateScheduler); + + private void update(BeatmapSetInfo set, BeatmapInfo beatmap) + { + if (cacheDownloadRequest == null && storage.Exists(cache_database_name)) + { + try + { + using (var db = new SqliteConnection(storage.GetDatabaseConnectionString("online"))) + { + var found = db.QueryFirstOrDefault( + "SELECT * FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path", beatmap); + + if (found != null) + { + var status = (BeatmapSetOnlineStatus)found.approved; + + beatmap.Status = status; + beatmap.BeatmapSet.Status = status; + beatmap.BeatmapSet.OnlineBeatmapSetID = found.beatmapset_id; + beatmap.OnlineBeatmapID = found.beatmap_id; + + LogForModel(set, $"Cached local retrieval for {beatmap}."); + return; + } + } + } + catch (Exception ex) + { + LogForModel(set, $"Cached local retrieval for {beatmap} failed with {ex}."); + } + } + + if (api?.State != APIState.Online) + return; + + var req = new GetBeatmapRequest(beatmap); + + req.Failure += fail; + + try + { + // intentionally blocking to limit web request concurrency + api.Perform(req); + + var res = req.Result; + + if (res != null) + { + beatmap.Status = res.Status; + beatmap.BeatmapSet.Status = res.BeatmapSet.Status; + beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID; + beatmap.OnlineBeatmapID = res.OnlineBeatmapID; + + LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}."); + } + } + catch (Exception e) + { + fail(e); + } + + void fail(Exception e) + { + beatmap.OnlineBeatmapID = null; + LogForModel(set, $"Online retrieval failed for {beatmap} ({e.Message})"); + } + } + + private void prepareLocalCache() + { + string cacheFilePath = storage.GetFullPath(cache_database_name); + string compressedCacheFilePath = $"{cacheFilePath}.bz2"; + + cacheDownloadRequest = new FileWebRequest(compressedCacheFilePath, $"https://assets.ppy.sh/client-resources/{cache_database_name}.bz2"); + + cacheDownloadRequest.Failed += ex => + { + File.Delete(compressedCacheFilePath); + File.Delete(cacheFilePath); + + Logger.Log($"{nameof(BeatmapUpdateQueue)}'s online cache download failed: {ex}", LoggingTarget.Database); + }; + + cacheDownloadRequest.Finished += () => + { + try + { + using (var stream = File.OpenRead(cacheDownloadRequest.Filename)) + using (var outStream = File.OpenWrite(cacheFilePath)) + using (var bz2 = new BZip2Stream(stream, CompressionMode.Decompress, false)) + bz2.CopyTo(outStream); + + // set to null on completion to allow lookups to begin using the new source + cacheDownloadRequest = null; + } + catch (Exception ex) + { + Logger.Log($"{nameof(BeatmapUpdateQueue)}'s online cache extraction failed: {ex}", LoggingTarget.Database); + } + finally + { + File.Delete(compressedCacheFilePath); + File.Delete(cacheFilePath); + } + }; + + cacheDownloadRequest.PerformAsync(); + } + + [Serializable] + [SuppressMessage("ReSharper", "InconsistentNaming")] + private class CachedOnlineBeatmapLookup + { + public int approved { get; set; } + + public int? beatmapset_id { get; set; } + + public int? beatmap_id { get; set; } + } + } + } +} diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 5e237d2ecb..839f9075e5 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -245,7 +245,7 @@ namespace osu.Game.Database /// protected abstract string[] HashableFileTypes { get; } - protected static void LogForModel(TModel model, string message, Exception e = null) + internal static void LogForModel(TModel model, string message, Exception e = null) { string prefix = $"[{(model?.Hash ?? "?????").Substring(0, 5)}]"; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index acb7fe5fbe..81818360a4 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,6 +18,7 @@ + From 917393697cf36c61c3356719bd39fbaac2208947 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 2 May 2020 14:38:46 +0900 Subject: [PATCH 076/121] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 336479c40a..8214fa2f2c 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index acb7fe5fbe..eae763c412 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 6662e57dcd..9ff7e3fc02 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From b9b57792514d43a92dc5d2de7b84b248a487ad28 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 May 2020 09:31:56 +0900 Subject: [PATCH 077/121] Move deletion to catch instead of finally --- osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs b/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs index aa8be823f7..be4bd0d30d 100644 --- a/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs +++ b/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs @@ -154,11 +154,11 @@ namespace osu.Game.Beatmaps catch (Exception ex) { Logger.Log($"{nameof(BeatmapUpdateQueue)}'s online cache extraction failed: {ex}", LoggingTarget.Database); + File.Delete(cacheFilePath); } finally { File.Delete(compressedCacheFilePath); - File.Delete(cacheFilePath); } }; From 035b513b68347b6144fa06c9727a2bb404c46b29 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 May 2020 09:32:33 +0900 Subject: [PATCH 078/121] Use QuerySingle instead of QueryFirst --- osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs b/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs index be4bd0d30d..84901f9b50 100644 --- a/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs +++ b/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs @@ -65,7 +65,7 @@ namespace osu.Game.Beatmaps { using (var db = new SqliteConnection(storage.GetDatabaseConnectionString("online"))) { - var found = db.QueryFirstOrDefault( + var found = db.QuerySingleOrDefault( "SELECT * FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path", beatmap); if (found != null) From 6fef4eeb8f9fd1e46e3b8553b03496c201d911b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 May 2020 09:35:48 +0900 Subject: [PATCH 079/121] Rename class and extract out lookup method --- osu.Game/Beatmaps/BeatmapManager.cs | 6 +- ...eatmapManager_BeatmapOnlineLookupQueue.cs} | 83 +++++++++++-------- 2 files changed, 51 insertions(+), 38 deletions(-) rename osu.Game/Beatmaps/{BeatmapManager_UpdateQueue.cs => BeatmapManager_BeatmapOnlineLookupQueue.cs} (71%) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 22451382a3..19d1162d23 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -60,7 +60,7 @@ namespace osu.Game.Beatmaps private readonly BeatmapStore beatmaps; private readonly AudioManager audioManager; private readonly GameHost host; - private readonly BeatmapUpdateQueue updateQueue; + private readonly BeatmapOnlineLookupQueue onlineLookupQueue; private readonly Storage exportStorage; public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, AudioManager audioManager, GameHost host = null, @@ -77,7 +77,7 @@ namespace osu.Game.Beatmaps beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b); beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b); - updateQueue = new BeatmapUpdateQueue(api, storage); + onlineLookupQueue = new BeatmapOnlineLookupQueue(api, storage); exportStorage = storage.GetStorageForDirectory("exports"); } @@ -104,7 +104,7 @@ namespace osu.Game.Beatmaps bool hadOnlineBeatmapIDs = beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0); - await updateQueue.UpdateAsync(beatmapSet, cancellationToken); + await onlineLookupQueue.UpdateAsync(beatmapSet, cancellationToken); // ensure at least one beatmap was able to retrieve or keep an online ID, else drop the set ID. if (hadOnlineBeatmapIDs && !beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0)) diff --git a/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs similarity index 71% rename from osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs rename to osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs index 84901f9b50..2bd7529ab0 100644 --- a/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs +++ b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs @@ -22,20 +22,20 @@ namespace osu.Game.Beatmaps { public partial class BeatmapManager { - private class BeatmapUpdateQueue + private class BeatmapOnlineLookupQueue { private readonly IAPIProvider api; private readonly Storage storage; private const int update_queue_request_concurrency = 4; - private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(update_queue_request_concurrency, nameof(BeatmapUpdateQueue)); + private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(update_queue_request_concurrency, nameof(BeatmapOnlineLookupQueue)); private FileWebRequest cacheDownloadRequest; private const string cache_database_name = "online.db"; - public BeatmapUpdateQueue(IAPIProvider api, Storage storage) + public BeatmapOnlineLookupQueue(IAPIProvider api, Storage storage) { this.api = api; this.storage = storage; @@ -55,38 +55,12 @@ namespace osu.Game.Beatmaps // todo: expose this when we need to do individual difficulty lookups. protected Task UpdateAsync(BeatmapSetInfo beatmapSet, BeatmapInfo beatmap, CancellationToken cancellationToken) - => Task.Factory.StartNew(() => update(beatmapSet, beatmap), cancellationToken, TaskCreationOptions.HideScheduler, updateScheduler); + => Task.Factory.StartNew(() => lookup(beatmapSet, beatmap), cancellationToken, TaskCreationOptions.HideScheduler, updateScheduler); - private void update(BeatmapSetInfo set, BeatmapInfo beatmap) + private void lookup(BeatmapSetInfo set, BeatmapInfo beatmap) { - if (cacheDownloadRequest == null && storage.Exists(cache_database_name)) - { - try - { - using (var db = new SqliteConnection(storage.GetDatabaseConnectionString("online"))) - { - var found = db.QuerySingleOrDefault( - "SELECT * FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path", beatmap); - - if (found != null) - { - var status = (BeatmapSetOnlineStatus)found.approved; - - beatmap.Status = status; - beatmap.BeatmapSet.Status = status; - beatmap.BeatmapSet.OnlineBeatmapSetID = found.beatmapset_id; - beatmap.OnlineBeatmapID = found.beatmap_id; - - LogForModel(set, $"Cached local retrieval for {beatmap}."); - return; - } - } - } - catch (Exception ex) - { - LogForModel(set, $"Cached local retrieval for {beatmap} failed with {ex}."); - } - } + if (checkLocalCache(set, beatmap)) + return; if (api?.State != APIState.Online) return; @@ -136,7 +110,7 @@ namespace osu.Game.Beatmaps File.Delete(compressedCacheFilePath); File.Delete(cacheFilePath); - Logger.Log($"{nameof(BeatmapUpdateQueue)}'s online cache download failed: {ex}", LoggingTarget.Database); + Logger.Log($"{nameof(BeatmapOnlineLookupQueue)}'s online cache download failed: {ex}", LoggingTarget.Database); }; cacheDownloadRequest.Finished += () => @@ -153,7 +127,7 @@ namespace osu.Game.Beatmaps } catch (Exception ex) { - Logger.Log($"{nameof(BeatmapUpdateQueue)}'s online cache extraction failed: {ex}", LoggingTarget.Database); + Logger.Log($"{nameof(BeatmapOnlineLookupQueue)}'s online cache extraction failed: {ex}", LoggingTarget.Database); File.Delete(cacheFilePath); } finally @@ -165,6 +139,45 @@ namespace osu.Game.Beatmaps cacheDownloadRequest.PerformAsync(); } + private bool checkLocalCache(BeatmapSetInfo set, BeatmapInfo beatmap) + { + // download is in progress (or was, and failed). + if (cacheDownloadRequest != null) + return false; + + // database is unavailable. + if (!storage.Exists(cache_database_name)) + return false; + + try + { + using (var db = new SqliteConnection(storage.GetDatabaseConnectionString("online"))) + { + var found = db.QuerySingleOrDefault( + "SELECT * FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path", beatmap); + + if (found != null) + { + var status = (BeatmapSetOnlineStatus)found.approved; + + beatmap.Status = status; + beatmap.BeatmapSet.Status = status; + beatmap.BeatmapSet.OnlineBeatmapSetID = found.beatmapset_id; + beatmap.OnlineBeatmapID = found.beatmap_id; + + LogForModel(set, $"Cached local retrieval for {beatmap}."); + return true; + } + } + } + catch (Exception ex) + { + LogForModel(set, $"Cached local retrieval for {beatmap} failed with {ex}."); + } + + return false; + } + [Serializable] [SuppressMessage("ReSharper", "InconsistentNaming")] private class CachedOnlineBeatmapLookup From 68d40cf79064efc39f89631a80873f0886df99f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 May 2020 13:25:57 +0900 Subject: [PATCH 080/121] Fix test failures due to online cache download --- osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs index 2bd7529ab0..2c79a664c5 100644 --- a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs +++ b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Dapper; using Microsoft.Data.Sqlite; +using osu.Framework.Development; using osu.Framework.IO.Network; using osu.Framework.Logging; using osu.Framework.Platform; @@ -40,7 +41,8 @@ namespace osu.Game.Beatmaps this.api = api; this.storage = storage; - if (!storage.Exists(cache_database_name)) + // avoid downloading / using cache for unit tests. + if (!DebugUtils.IsNUnitRunning && !storage.Exists(cache_database_name)) prepareLocalCache(); } From a1cd007cadfdb120f3a977e258676fedad854081 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 May 2020 14:43:47 +0900 Subject: [PATCH 081/121] Fix song select tests potentially failing due to difficulty panels not yet displayed --- .../Visual/SongSelect/TestScenePlaySongSelect.cs | 12 +++++++++--- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index aed8e19fb2..802c324c90 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -110,7 +110,7 @@ namespace osu.Game.Tests.Visual.SongSelect createSongSelect(); - AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault); + waitForInitialSelection(); WorkingBeatmap selected = null; @@ -135,7 +135,7 @@ namespace osu.Game.Tests.Visual.SongSelect createSongSelect(); - AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault); + waitForInitialSelection(); WorkingBeatmap selected = null; @@ -189,7 +189,7 @@ namespace osu.Game.Tests.Visual.SongSelect createSongSelect(); - AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault); + waitForInitialSelection(); WorkingBeatmap selected = null; @@ -769,6 +769,12 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("Check first item in group selected", () => Beatmap.Value.BeatmapInfo == groupIcon.Items.First().Beatmap); } + private void waitForInitialSelection() + { + AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault); + AddUntilStep("wait for difficulty panels visible", () => songSelect.Carousel.ChildrenOfType().Any()); + } + private int getBeatmapIndex(BeatmapSetInfo set, BeatmapInfo info) => set.Beatmaps.FindIndex(b => b == info); private int getCurrentBeatmapIndex() => getBeatmapIndex(songSelect.Carousel.SelectedBeatmapSet, songSelect.Carousel.SelectedBeatmap); diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 19d1162d23..1b29f14c9b 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -140,7 +140,7 @@ namespace osu.Game.Beatmaps { var beatmapIds = beatmapSet.Beatmaps.Where(b => b.OnlineBeatmapID.HasValue).Select(b => b.OnlineBeatmapID).ToList(); - LogForModel(beatmapSet, "Validating online IDs..."); + LogForModel(beatmapSet, $"Validating online IDs for {beatmapSet.Beatmaps.Count} beatmaps..."); // ensure all IDs are unique if (beatmapIds.GroupBy(b => b).Any(g => g.Count() > 1)) From 02b9f51bdd4c2e0c2b07cefea40b54b4f0bbc0b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 May 2020 13:31:49 +0900 Subject: [PATCH 082/121] Add failing test --- .../SongSelect/TestScenePlaySongSelect.cs | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 802c324c90..851801d38a 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -24,10 +24,12 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Taiko; +using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Carousel; using osu.Game.Screens.Select.Filter; +using osu.Game.Users; using osuTK.Input; namespace osu.Game.Tests.Visual.SongSelect @@ -769,6 +771,70 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("Check first item in group selected", () => Beatmap.Value.BeatmapInfo == groupIcon.Items.First().Beatmap); } + [Test] + public void TestChangeRulesetWhilePresentingScore() + { + changeRuleset(0); + + createSongSelect(); + + addRulesetImportStep(0); + addRulesetImportStep(1); + + AddStep("present score", () => + { + var presentBeatmap = manager.QueryBeatmap(b => b.RulesetID == 0); + var switchBeatmap = manager.QueryBeatmap(b => b.RulesetID == 1); + + // this ruleset change should be overridden by the present. + Ruleset.Value = switchBeatmap.Ruleset; + + songSelect.PresentScore(new ScoreInfo + { + User = new User { Username = "woo" }, + Beatmap = presentBeatmap, + Ruleset = presentBeatmap.Ruleset + }); + }); + + AddUntilStep("wait for results screen presented", () => !songSelect.IsCurrentScreen()); + + AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.Equals(manager.QueryBeatmap(b => b.RulesetID == 0))); + AddAssert("check ruleset is correct for score", () => Ruleset.Value.ID == 0); + } + + [Test] + public void TestChangeBeatmapWhilePresentingScore() + { + changeRuleset(0); + + createSongSelect(); + + addRulesetImportStep(0); + addRulesetImportStep(1); + + AddStep("present score", () => + { + var presentBeatmap = manager.QueryBeatmap(b => b.RulesetID == 0); + var switchBeatmap = manager.QueryBeatmap(b => b.RulesetID == 1); + + // this beatmap change should be overridden by the present. + Beatmap.Value = manager.GetWorkingBeatmap(switchBeatmap); + + songSelect.PresentScore(new ScoreInfo + { + User = new User { Username = "woo" }, + Beatmap = presentBeatmap, + Ruleset = presentBeatmap.Ruleset + }); + }); + + AddUntilStep("wait for results screen presented", () => !songSelect.IsCurrentScreen()); + + AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.Equals(manager.QueryBeatmap(b => b.RulesetID == 0))); + AddAssert("check ruleset is correct for score", () => Ruleset.Value.ID == 0); + } + private void waitForInitialSelection() { AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault); @@ -882,6 +948,8 @@ namespace osu.Game.Tests.Visual.SongSelect public WorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap; public new BeatmapCarousel Carousel => base.Carousel; + public new void PresentScore(ScoreInfo score) => base.PresentScore(score); + protected override bool OnStart() { StartRequested?.Invoke(); From 06f58dd3e35a6d233845dbd73482e2638d22156f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 May 2020 14:04:30 +0900 Subject: [PATCH 083/121] Ensure correct beatmap and ruleset when presenting a score from song select --- osu.Game/Screens/Select/PlaySongSelect.cs | 6 +++++- osu.Game/Screens/Select/SongSelect.cs | 15 ++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 179aab54a3..21ddc5685d 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Screens; using osu.Game.Graphics; +using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; using osu.Game.Users; @@ -32,9 +33,12 @@ namespace osu.Game.Screens.Select Edit(); }, Key.Number4); - ((PlayBeatmapDetailArea)BeatmapDetails).Leaderboard.ScoreSelected += score => this.Push(new ResultsScreen(score)); + ((PlayBeatmapDetailArea)BeatmapDetails).Leaderboard.ScoreSelected += PresentScore; } + protected void PresentScore(ScoreInfo score) => + FinaliseSelection(score.Beatmap, score.Ruleset, () => this.Push(new ResultsScreen(score))); + protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); public override void OnResuming(IScreen last) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index a7e27c27ba..2b373ab7e0 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -342,13 +342,17 @@ namespace osu.Game.Screens.Select /// Call to make a selection and perform the default action for this SongSelect. /// /// An optional beatmap to override the current carousel selection. - /// Whether to trigger . - public void FinaliseSelection(BeatmapInfo beatmap = null, bool performStartAction = true) + /// An optional ruleset to override the current carousel selection. + /// An optional custom action to perform instead of . + public void FinaliseSelection(BeatmapInfo beatmap = null, RulesetInfo ruleset = null, Action customStartAction = null) { // This is very important as we have not yet bound to screen-level bindables before the carousel load is completed. if (!Carousel.BeatmapSetsLoaded) return; + if (ruleset != null) + Ruleset.Value = ruleset; + transferRulesetValue(); // while transferRulesetValue will flush, it only does so if the ruleset changes. @@ -369,7 +373,12 @@ namespace osu.Game.Screens.Select selectionChangedDebounce = null; } - if (performStartAction && OnStart()) + if (customStartAction != null) + { + customStartAction(); + Carousel.AllowSelection = false; + } + else if (OnStart()) Carousel.AllowSelection = false; } From 81889e0034115e5a22f4882b1f6e98703d9acf50 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 May 2020 15:19:36 +0900 Subject: [PATCH 084/121] Fix tests potentially selecting a deleted beatmap --- .../SongSelect/TestScenePlaySongSelect.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 851801d38a..81fd1b66e5 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -774,6 +774,9 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestChangeRulesetWhilePresentingScore() { + BeatmapInfo getPresentBeatmap() => manager.QueryBeatmap(b => !b.BeatmapSet.DeletePending && b.RulesetID == 0); + BeatmapInfo getSwitchBeatmap() => manager.QueryBeatmap(b => !b.BeatmapSet.DeletePending && b.RulesetID == 1); + changeRuleset(0); createSongSelect(); @@ -783,55 +786,52 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("present score", () => { - var presentBeatmap = manager.QueryBeatmap(b => b.RulesetID == 0); - var switchBeatmap = manager.QueryBeatmap(b => b.RulesetID == 1); - // this ruleset change should be overridden by the present. - Ruleset.Value = switchBeatmap.Ruleset; + Ruleset.Value = getSwitchBeatmap().Ruleset; songSelect.PresentScore(new ScoreInfo { User = new User { Username = "woo" }, - Beatmap = presentBeatmap, - Ruleset = presentBeatmap.Ruleset + Beatmap = getPresentBeatmap(), + Ruleset = getPresentBeatmap().Ruleset }); }); AddUntilStep("wait for results screen presented", () => !songSelect.IsCurrentScreen()); - AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.Equals(manager.QueryBeatmap(b => b.RulesetID == 0))); + AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.Equals(getPresentBeatmap())); AddAssert("check ruleset is correct for score", () => Ruleset.Value.ID == 0); } [Test] public void TestChangeBeatmapWhilePresentingScore() { - changeRuleset(0); + BeatmapInfo getPresentBeatmap() => manager.QueryBeatmap(b => !b.BeatmapSet.DeletePending && b.RulesetID == 0); + BeatmapInfo getSwitchBeatmap() => manager.QueryBeatmap(b => !b.BeatmapSet.DeletePending && b.RulesetID == 1); - createSongSelect(); + changeRuleset(0); addRulesetImportStep(0); addRulesetImportStep(1); + createSongSelect(); + AddStep("present score", () => { - var presentBeatmap = manager.QueryBeatmap(b => b.RulesetID == 0); - var switchBeatmap = manager.QueryBeatmap(b => b.RulesetID == 1); - // this beatmap change should be overridden by the present. - Beatmap.Value = manager.GetWorkingBeatmap(switchBeatmap); + Beatmap.Value = manager.GetWorkingBeatmap(getSwitchBeatmap()); songSelect.PresentScore(new ScoreInfo { User = new User { Username = "woo" }, - Beatmap = presentBeatmap, - Ruleset = presentBeatmap.Ruleset + Beatmap = getPresentBeatmap(), + Ruleset = getPresentBeatmap().Ruleset }); }); AddUntilStep("wait for results screen presented", () => !songSelect.IsCurrentScreen()); - AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.Equals(manager.QueryBeatmap(b => b.RulesetID == 0))); + AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.Equals(getPresentBeatmap())); AddAssert("check ruleset is correct for score", () => Ruleset.Value.ID == 0); } From 46b0526db7afd25e1a962420ee8235df1f109801 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 May 2020 15:27:04 +0900 Subject: [PATCH 085/121] Remove hack limiting max number of ticks --- osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 19219cc1ba..01011645bd 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -58,8 +58,6 @@ namespace osu.Game.Rulesets.Catch.Objects SliderEventDescriptor? lastEvent = null; - int ticksGenerated = 0; - foreach (var e in SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset)) { // generate tiny droplets since the last point @@ -75,9 +73,6 @@ namespace osu.Game.Rulesets.Catch.Objects for (double t = timeBetweenTiny; t < sinceLastTick; t += timeBetweenTiny) { - if (ticksGenerated++ >= 10000) - break; - AddNested(new TinyDroplet { StartTime = t + lastEvent.Value.Time, From 6d3a24ff01cdeea9bf52d164087279989b277b6d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 May 2020 15:55:42 +0900 Subject: [PATCH 086/121] Reorder tick hit results --- osu.Game/Rulesets/Scoring/HitResult.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs index 0c895bd086..b057af2a50 100644 --- a/osu.Game/Rulesets/Scoring/HitResult.cs +++ b/osu.Game/Rulesets/Scoring/HitResult.cs @@ -44,9 +44,24 @@ namespace osu.Game.Rulesets.Scoring [Description(@"Perfect")] Perfect, - SmallTickHit, + /// + /// Indicates small tick miss. + /// SmallTickMiss, - LargeTickHit, + + /// + /// Indicates a small tick hit. + /// + SmallTickHit, + + /// + /// Indicates a large tick miss. + /// LargeTickMiss, + + /// + /// Indicates a large tick hit. + /// + LargeTickHit } } From 62d433c9c57b2de866d579f4bfbad543eadc9649 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 May 2020 17:01:07 +0900 Subject: [PATCH 087/121] Adjust diffcalc test value --- osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs index 51fe0b035d..ee416e5a38 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Catch.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Catch"; - [TestCase(4.2058561036909863d, "diffcalc-test")] + [TestCase(4.050601681491468d, "diffcalc-test")] public void Test(double expected, string name) => base.Test(expected, name); From e2593ac3e77e82d14c776ef4cf6c19c60ac697b3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 4 May 2020 08:56:18 +0000 Subject: [PATCH 088/121] Bump Microsoft.CodeAnalysis.FxCopAnalyzers from 2.9.8 to 3.0.0 Bumps [Microsoft.CodeAnalysis.FxCopAnalyzers](https://github.com/dotnet/roslyn-analyzers) from 2.9.8 to 3.0.0. - [Release notes](https://github.com/dotnet/roslyn-analyzers/releases) - [Changelog](https://github.com/dotnet/roslyn-analyzers/blob/master/PostReleaseActivities.md) - [Commits](https://github.com/dotnet/roslyn-analyzers/compare/v2.9.8...v3.0.0) Signed-off-by: dependabot-preview[bot] --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 21b8b402e0..fbe300458e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -18,7 +18,7 @@ - + $(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset From 4ee2e6cd47add8c964d61445cca16a271b6236dc Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 4 May 2020 08:57:09 +0000 Subject: [PATCH 089/121] Bump Humanizer from 2.8.2 to 2.8.11 Bumps [Humanizer](https://github.com/Humanizr/Humanizer) from 2.8.2 to 2.8.11. - [Release notes](https://github.com/Humanizr/Humanizer/releases) - [Changelog](https://github.com/Humanizr/Humanizer/blob/master/release_notes.md) - [Commits](https://github.com/Humanizr/Humanizer/compare/v2.8.2...v2.8.11) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 91c89cbc20..9db5fe562c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -20,7 +20,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 9ff7e3fc02..82253a0418 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -76,7 +76,7 @@ - + From fe31bac505bb59891325fc59afb4ba597df3e052 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 May 2020 18:20:20 +0900 Subject: [PATCH 090/121] Fix build error --- osu.Game/Overlays/SearchableList/DisplayStyleControl.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs b/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs index a33f4eb30d..5ecb477a2f 100644 --- a/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs +++ b/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs @@ -91,6 +91,8 @@ namespace osu.Game.Overlays.SearchableList protected override void Dispose(bool isDisposing) { + base.Dispose(isDisposing); + bindable.ValueChanged -= Bindable_ValueChanged; } } From 969412a4265aa3c94f0a6112552b5ad9287f4908 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 4 May 2020 10:28:42 +0000 Subject: [PATCH 091/121] Bump Microsoft.CodeAnalysis.BannedApiAnalyzers from 2.9.8 to 3.0.0 Bumps [Microsoft.CodeAnalysis.BannedApiAnalyzers](https://github.com/dotnet/roslyn-analyzers) from 2.9.8 to 3.0.0. - [Release notes](https://github.com/dotnet/roslyn-analyzers/releases) - [Changelog](https://github.com/dotnet/roslyn-analyzers/blob/master/PostReleaseActivities.md) - [Commits](https://github.com/dotnet/roslyn-analyzers/compare/v2.9.8...v3.0.0) Signed-off-by: dependabot-preview[bot] --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 21b8b402e0..5d011dfdc5 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -16,7 +16,7 @@ - + From c987af988c98745a8039372563b87717a7691e46 Mon Sep 17 00:00:00 2001 From: alex Date: Mon, 4 May 2020 18:26:12 -0700 Subject: [PATCH 092/121] Fix typo --- osu.Game/Screens/OsuScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 2124a66a75..35bb4fa34f 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens { /// /// The amount of negative padding that should be applied to game background content which touches both the left and right sides of the screen. - /// This allows for the game content to be pushed byt he options/notification overlays without causing black areas to appear. + /// This allows for the game content to be pushed by the options/notification overlays without causing black areas to appear. /// public const float HORIZONTAL_OVERFLOW_PADDING = 50; From 0e2ccac33b916abc63e9aa4e8cb474e6761fb22e Mon Sep 17 00:00:00 2001 From: alex Date: Mon, 4 May 2020 18:31:11 -0700 Subject: [PATCH 093/121] Add spaces to comments --- osu.Desktop/Updater/SquirrelUpdateManager.cs | 10 ++++----- osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs | 2 +- .../Replays/CatchAutoGenerator.cs | 6 ++--- .../Beatmaps/OsuBeatmapProcessor.cs | 18 +++++++-------- osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs | 2 +- .../Drawables/Pieces/MainCirclePiece.cs | 2 +- .../Beatmaps/IO/ImportBeatmapTest.cs | 22 +++++++++---------- .../NonVisual/ControlPointInfoTest.cs | 2 +- .../NonVisual/FramedReplayInputHandlerTest.cs | 6 ++--- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- osu.Game/Graphics/Backgrounds/Triangles.cs | 4 ++-- .../Graphics/Containers/OsuScrollContainer.cs | 2 +- osu.Game/IPC/ArchiveImportIPCChannel.cs | 2 +- osu.Game/Online/API/APIAccess.cs | 8 +++---- osu.Game/Online/API/APIRequest.cs | 2 +- osu.Game/Online/Chat/Channel.cs | 2 +- osu.Game/Online/Chat/MessageFormatter.cs | 4 ++-- osu.Game/OsuGame.cs | 4 ++-- .../BeatmapListing/Panels/GridBeatmapPanel.cs | 2 +- osu.Game/Overlays/BeatmapSet/Header.cs | 2 +- osu.Game/Overlays/ChatOverlay.cs | 2 +- osu.Game/Overlays/DialogOverlay.cs | 2 +- osu.Game/Overlays/Music/PlaylistItem.cs | 2 +- osu.Game/Overlays/MusicController.cs | 2 +- osu.Game/Overlays/News/NewsArticleCover.cs | 2 +- .../Notifications/ProgressNotification.cs | 2 +- osu.Game/Overlays/NowPlayingOverlay.cs | 2 +- osu.Game/Overlays/OSD/Toast.cs | 2 +- .../Components/OverlinedInfoContainer.cs | 2 +- .../Profile/Header/MedalHeaderContainer.cs | 2 +- .../SearchableListFilterControl.cs | 2 +- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 2 +- osu.Game/Overlays/VolumeOverlay.cs | 2 +- .../Replays/FramedReplayInputHandler.cs | 6 ++--- osu.Game/Rulesets/RulesetStore.cs | 6 ++--- osu.Game/Screens/BackgroundScreen.cs | 2 +- osu.Game/Screens/Menu/IntroScreen.cs | 2 +- osu.Game/Screens/Menu/LogoVisualisation.cs | 10 ++++----- osu.Game/Screens/Menu/MainMenu.cs | 2 +- osu.Game/Screens/Play/BreakOverlay.cs | 2 +- .../Screens/Play/HUD/PlayerSettingsOverlay.cs | 2 +- osu.Game/Screens/Play/KeyCounter.cs | 4 ++-- osu.Game/Screens/Play/PlayerLoader.cs | 6 ++--- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 +- .../Select/BeatmapDetailAreaTabControl.cs | 2 +- osu.Game/Screens/Select/BeatmapDetails.cs | 4 ++-- .../Screens/Select/Details/AdvancedStats.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 2 +- 48 files changed, 92 insertions(+), 92 deletions(-) diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs index 60b47a8b3a..ade8460dd7 100644 --- a/osu.Desktop/Updater/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs @@ -43,7 +43,7 @@ namespace osu.Desktop.Updater private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null) { - //should we schedule a retry on completion of this check? + // should we schedule a retry on completion of this check? bool scheduleRecheck = true; try @@ -52,7 +52,7 @@ namespace osu.Desktop.Updater var info = await updateManager.CheckForUpdate(!useDeltaPatching); if (info.ReleasesToApply.Count == 0) - //no updates available. bail and retry later. + // no updates available. bail and retry later. return; if (notification == null) @@ -81,8 +81,8 @@ namespace osu.Desktop.Updater { logger.Add(@"delta patching failed; will attempt full download!"); - //could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959) - //try again without deltas. + // could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959) + // try again without deltas. checkForUpdateAsync(false, notification); scheduleRecheck = false; } @@ -101,7 +101,7 @@ namespace osu.Desktop.Updater { if (scheduleRecheck) { - //check again in 30 minutes. + // check again in 30 minutes. Scheduler.AddDelayed(() => checkForUpdateAsync(), 60000 * 30); } } diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs index 16414261a5..c1d24395e4 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Catch.Mods RelativeSizeAxes = Axes.Both; } - //disable keyboard controls + // disable keyboard controls public bool OnPressed(CatchAction action) => true; public void OnReleased(CatchAction action) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index b90b5812a6..7a33cb0577 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Catch.Replays if (lastPosition - catcher_width_half < h.X && lastPosition + catcher_width_half > h.X) { - //we are already in the correct range. + // we are already in the correct range. lastTime = h.StartTime; addFrame(h.StartTime, lastPosition); return; @@ -72,14 +72,14 @@ namespace osu.Game.Rulesets.Catch.Replays } else if (dashRequired) { - //we do a movement in two parts - the dash part then the normal part... + // we do a movement in two parts - the dash part then the normal part... double timeAtNormalSpeed = positionChange / movement_speed; double timeWeNeedToSave = timeAtNormalSpeed - timeAvailable; double timeAtDashSpeed = timeWeNeedToSave / 2; float midPosition = (float)Interpolation.Lerp(lastPosition, h.X, (float)timeAtDashSpeed / timeAvailable); - //dash movement + // dash movement addFrame(h.StartTime - timeAvailable + 1, lastPosition, true); addFrame(h.StartTime - timeAvailable + timeAtDashSpeed, midPosition); addFrame(h.StartTime, h.X); diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs index 3a829f72fa..f51f04bf87 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps double stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo.StackLeniency; if (objectN.StartTime - endTime > stackThreshold) - //We are no longer within stacking range of the next object. + // We are no longer within stacking range of the next object. break; if (Vector2Extensions.Distance(stackBaseObject.Position, objectN.Position) < stack_distance @@ -88,7 +88,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps } } - //Reverse pass for stack calculation. + // Reverse pass for stack calculation. int extendedStartIndex = startIndex; for (int i = extendedEndIndex; i > startIndex; i--) @@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps double endTime = objectN.GetEndTime(); if (objectI.StartTime - endTime > stackThreshold) - //We are no longer within stacking range of the previous object. + // We are no longer within stacking range of the previous object. break; // HitObjects before the specified update range haven't been reset yet @@ -145,20 +145,20 @@ namespace osu.Game.Rulesets.Osu.Beatmaps for (int j = n + 1; j <= i; j++) { - //For each object which was declared under this slider, we will offset it to appear *below* the slider end (rather than above). + // For each object which was declared under this slider, we will offset it to appear *below* the slider end (rather than above). OsuHitObject objectJ = beatmap.HitObjects[j]; if (Vector2Extensions.Distance(objectN.EndPosition, objectJ.Position) < stack_distance) objectJ.StackHeight -= offset; } - //We have hit a slider. We should restart calculation using this as the new base. - //Breaking here will mean that the slider still has StackCount of 0, so will be handled in the i-outer-loop. + // We have hit a slider. We should restart calculation using this as the new base. + // Breaking here will mean that the slider still has StackCount of 0, so will be handled in the i-outer-loop. break; } if (Vector2Extensions.Distance(objectN.Position, objectI.Position) < stack_distance) { - //Keep processing as if there are no sliders. If we come across a slider, this gets cancelled out. + // Keep processing as if there are no sliders. If we come across a slider, this gets cancelled out. //NOTE: Sliders with start positions stacking are a special case that is also handled here. objectN.StackHeight = objectI.StackHeight + 1; @@ -177,7 +177,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps if (objectN is Spinner) continue; if (objectI.StartTime - objectN.StartTime > stackThreshold) - //We are no longer within stacking range of the previous object. + // We are no longer within stacking range of the previous object. break; if (Vector2Extensions.Distance(objectN.EndPosition, objectI.Position) < stack_distance) @@ -221,7 +221,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps } else if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, position2) < stack_distance) { - //Case for sliders - bump notes down and right, rather than up and left. + // Case for sliders - bump notes down and right, rather than up and left. sliderStack++; beatmap.HitObjects[j].StackHeight -= sliderStack; startTime = beatmap.HitObjects[j].GetEndTime(); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs index 44dba7715a..5e80d08667 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.Mods Vector2 originalPosition = drawable.Position; Vector2 appearOffset = new Vector2(MathF.Cos(theta), MathF.Sin(theta)) * appearDistance; - //the - 1 and + 1 prevents the hit objects to appear in the wrong position. + // the - 1 and + 1 prevents the hit objects to appear in the wrong position. double appearTime = hitObject.StartTime - hitObject.TimePreempt - 1; double moveDuration = hitObject.TimePreempt + 1; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs index e364c96426..cb3787a493 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs @@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces using (BeginDelayedSequence(flash_in, true)) { - //after the flash, we can hide some elements that were behind it + // after the flash, we can hide some elements that were behind it ring.FadeOut(); circle.FadeOut(); number.FadeOut(); diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index c6095ae404..ba6f5fc85c 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportWhenClosed() { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportWhenClosed))) { try @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportThenDelete() { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenDelete))) { try @@ -69,7 +69,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportThenImport() { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImport))) { try @@ -96,7 +96,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportCorruptThenImport() { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportCorruptThenImport))) { try @@ -138,7 +138,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestRollbackOnFailure() { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestRollbackOnFailure))) { try @@ -215,7 +215,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportThenImportDifferentHash() { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportDifferentHash))) { try @@ -246,7 +246,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportThenDeleteThenImport() { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenDeleteThenImport))) { try @@ -274,7 +274,7 @@ namespace osu.Game.Tests.Beatmaps.IO [TestCase(false)] public async Task TestImportThenDeleteThenImportWithOnlineIDMismatch(bool set) { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost($"{nameof(TestImportThenDeleteThenImportWithOnlineIDMismatch)}-{set}")) { try @@ -308,7 +308,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportWithDuplicateBeatmapIDs() { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportWithDuplicateBeatmapIDs))) { try @@ -695,12 +695,12 @@ namespace osu.Game.Tests.Beatmaps.IO waitForOrAssert(() => (resultSets = store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526)).Any(), @"BeatmapSet did not import to the database in allocated time.", timeout); - //ensure we were stored to beatmap database backing... + // ensure we were stored to beatmap database backing... Assert.IsTrue(resultSets.Count() == 1, $@"Incorrect result count found ({resultSets.Count()} but should be 1)."); IEnumerable queryBeatmaps() => store.QueryBeatmaps(s => s.BeatmapSet.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0); IEnumerable queryBeatmapSets() => store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526); - //if we don't re-check here, the set will be inserted but the beatmaps won't be present yet. + // if we don't re-check here, the set will be inserted but the beatmaps won't be present yet. waitForOrAssert(() => queryBeatmaps().Count() == 12, @"Beatmaps did not import to the database in allocated time", timeout); waitForOrAssert(() => queryBeatmapSets().Count() == 1, diff --git a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs index 158954106d..830e4bc603 100644 --- a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs +++ b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs @@ -35,7 +35,7 @@ namespace osu.Game.Tests.NonVisual Assert.That(cpi.TimingPoints.Count, Is.EqualTo(2)); Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(2)); - cpi.Add(1000, new TimingControlPoint()); //is redundant + cpi.Add(1000, new TimingControlPoint()); // is redundant Assert.That(cpi.Groups.Count, Is.EqualTo(2)); Assert.That(cpi.TimingPoints.Count, Is.EqualTo(2)); diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index 7df7df22ea..92a60663de 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -46,12 +46,12 @@ namespace osu.Game.Tests.NonVisual confirmCurrentFrame(0); confirmNextFrame(1); - //if we hit the first frame perfectly, time should progress to it. + // if we hit the first frame perfectly, time should progress to it. setTime(1000, 1000); confirmCurrentFrame(1); confirmNextFrame(2); - //in between non-important frames should progress based on input. + // in between non-important frames should progress based on input. setTime(1200, 1200); confirmCurrentFrame(1); @@ -144,7 +144,7 @@ namespace osu.Game.Tests.NonVisual confirmCurrentFrame(2); confirmNextFrame(1); - //ensure each frame plays out until start + // ensure each frame plays out until start setTime(-500, 1000); confirmCurrentFrame(1); confirmNextFrame(0); diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 19d1162d23..d7c30dc9ff 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -381,7 +381,7 @@ namespace osu.Game.Beatmaps foreach (var file in files.Where(f => f.Filename.EndsWith(".osu"))) { using (var raw = Files.Store.GetStream(file.FileInfo.StoragePath)) - using (var ms = new MemoryStream()) //we need a memory stream so we can seek + using (var ms = new MemoryStream()) // we need a memory stream so we can seek using (var sr = new LineBufferedReader(ms)) { raw.CopyTo(ms); diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 590e4b2a5c..27027202ce 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -193,8 +193,8 @@ namespace osu.Game.Graphics.Backgrounds float u1 = 1 - RNG.NextSingle(); //uniform(0,1] random floats float u2 = 1 - RNG.NextSingle(); - float randStdNormal = (float)(Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2)); //random normal(0,1) - var scale = Math.Max(triangleScale * (mean + std_dev * randStdNormal), 0.1f); //random normal(mean,stdDev^2) + float randStdNormal = (float)(Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2)); // random normal(0,1) + var scale = Math.Max(triangleScale * (mean + std_dev * randStdNormal), 0.1f); // random normal(mean,stdDev^2) return new TriangleParticle { Scale = scale }; } diff --git a/osu.Game/Graphics/Containers/OsuScrollContainer.cs b/osu.Game/Graphics/Containers/OsuScrollContainer.cs index 1824fcd878..d504a11b22 100644 --- a/osu.Game/Graphics/Containers/OsuScrollContainer.cs +++ b/osu.Game/Graphics/Containers/OsuScrollContainer.cs @@ -158,7 +158,7 @@ namespace osu.Game.Graphics.Containers { if (!base.OnMouseDown(e)) return false; - //note that we are changing the colour of the box here as to not interfere with the hover effect. + // note that we are changing the colour of the box here as to not interfere with the hover effect. box.FadeColour(highlightColour, 100); return true; } diff --git a/osu.Game/IPC/ArchiveImportIPCChannel.cs b/osu.Game/IPC/ArchiveImportIPCChannel.cs index 484db932f8..029908ec9d 100644 --- a/osu.Game/IPC/ArchiveImportIPCChannel.cs +++ b/osu.Game/IPC/ArchiveImportIPCChannel.cs @@ -32,7 +32,7 @@ namespace osu.Game.IPC { if (importer == null) { - //we want to contact a remote osu! to handle the import. + // we want to contact a remote osu! to handle the import. await SendMessageAsync(new ArchiveImportMessage { Path = path }); return; } diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index adfef1d11f..4945f7f185 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -127,7 +127,7 @@ namespace osu.Game.Online.API case APIState.Offline: case APIState.Connecting: - //work to restore a connection... + // work to restore a connection... if (!HasLogin) { State = APIState.Offline; @@ -180,7 +180,7 @@ namespace osu.Game.Online.API break; } - //hard bail if we can't get a valid access token. + // hard bail if we can't get a valid access token. if (authentication.RequestAccessToken() == null) { Logout(); @@ -274,7 +274,7 @@ namespace osu.Game.Online.API { req.Perform(this); - //we could still be in initialisation, at which point we don't want to say we're Online yet. + // we could still be in initialisation, at which point we don't want to say we're Online yet. if (IsLoggedIn) State = APIState.Online; failureCount = 0; @@ -339,7 +339,7 @@ namespace osu.Game.Online.API log.Add($@"API failure count is now {failureCount}"); if (failureCount < 3) - //we might try again at an api level. + // we might try again at an api level. return false; if (State == APIState.Online) diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 0bba04cac3..0f8acbb7af 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -98,7 +98,7 @@ namespace osu.Game.Online.API if (checkAndScheduleFailure()) return; - if (!WebRequest.Aborted) //could have been aborted by a Cancel() call + if (!WebRequest.Aborted) // could have been aborted by a Cancel() call { Logger.Log($@"Performing request {this}", LoggingTarget.Network); WebRequest.Perform(); diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index 6f67a95f53..dbb2da5c03 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -61,7 +61,7 @@ namespace osu.Game.Online.Chat /// public event Action MessageRemoved; - public bool ReadOnly => false; //todo not yet used. + public bool ReadOnly => false; // todo: not yet used. public override string ToString() => Name; diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index 717de18c14..6af2561c89 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -78,13 +78,13 @@ namespace osu.Game.Online.Chat { result.Text = result.Text.Remove(index, m.Length).Insert(index, displayText); - //since we just changed the line display text, offset any already processed links. + // since we just changed the line display text, offset any already processed links. result.Links.ForEach(l => l.Index -= l.Index > index ? m.Length - displayText.Length : 0); var details = GetLinkDetails(linkText); result.Links.Add(new Link(linkText, index, displayText.Length, linkActionOverride ?? details.Action, details.Argument)); - //adjust the offset for processing the current matches group. + // adjust the offset for processing the current matches group. captureOffset += m.Length - displayText.Length; } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 8e62819c95..fdc8d94352 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -609,7 +609,7 @@ namespace osu.Game loadComponentSingleFile(screenshotManager, Add); - //overlay elements + // overlay elements loadComponentSingleFile(beatmapListing = new BeatmapListingOverlay(), overlayContent.Add, true); loadComponentSingleFile(dashboard = new DashboardOverlay(), overlayContent.Add, true); var rankingsOverlay = loadComponentSingleFile(new RankingsOverlay(), overlayContent.Add, true); @@ -781,7 +781,7 @@ namespace osu.Game { var previousLoadStream = asyncLoadStream; - //chain with existing load stream + // chain with existing load stream asyncLoadStream = Task.Run(async () => { if (previousLoadStream != null) diff --git a/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs index 84d35da096..28c36e6c56 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs @@ -35,7 +35,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels : base(beatmap) { Width = 380; - Height = 140 + vertical_padding; //full height of all the elements plus vertical padding (autosize uses the image) + Height = 140 + vertical_padding; // full height of all the elements plus vertical padding (autosize uses the image) } protected override void LoadComplete() diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 17fa689cd2..1ff08aab2c 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -140,7 +140,7 @@ namespace osu.Game.Overlays.BeatmapSet { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Left = 3, Bottom = 4 }, //To better lineup with the font + Margin = new MarginPadding { Left = 3, Bottom = 4 }, // To better lineup with the font }, } }, diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 34afc3c431..5ba55f6d45 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -358,7 +358,7 @@ namespace osu.Game.Overlays protected override void OnFocus(FocusEvent e) { - //this is necessary as textbox is masked away and therefore can't get focus :( + // this is necessary as textbox is masked away and therefore can't get focus :( textbox.TakeFocus(); base.OnFocus(e); } diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 59d748bc5d..9f9dbdbaf1 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -50,7 +50,7 @@ namespace osu.Game.Overlays { if (v != Visibility.Hidden) return; - //handle the dialog being dismissed. + // handle the dialog being dismissed. dialog.Delay(PopupDialog.EXIT_DURATION).Expire(); if (dialog == CurrentDialog) diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index de2f916946..840fa51b4f 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -78,7 +78,7 @@ namespace osu.Game.Overlays.Music { text.Clear(); - //space after the title to put a space between the title and artist + // space after the title to put a space between the title and artist titleSprites = text.AddText(title.Value + @" ", sprite => sprite.Font = OsuFont.GetFont(weight: FontWeight.Regular)).OfType(); text.AddText(artist.Value, sprite => diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index c872f82b32..ded641b262 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -250,7 +250,7 @@ namespace osu.Game.Overlays } else { - //figure out the best direction based on order in playlist. + // figure out the best direction based on order in playlist. var last = BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count(); var next = beatmap.NewValue == null ? -1 : BeatmapSets.TakeWhile(b => b.ID != beatmap.NewValue.BeatmapSetInfo?.ID).Count(); diff --git a/osu.Game/Overlays/News/NewsArticleCover.cs b/osu.Game/Overlays/News/NewsArticleCover.cs index cca0cfb4a0..e3f5a8cea3 100644 --- a/osu.Game/Overlays/News/NewsArticleCover.cs +++ b/osu.Game/Overlays/News/NewsArticleCover.cs @@ -162,7 +162,7 @@ namespace osu.Game.Overlays.News public string TooltipText => date.ToString("dddd dd MMMM yyyy hh:mm:ss UTCz").ToUpper(); } - //fake API data struct to use for now as a skeleton for data, as there is no API struct for news article info for now + // fake API data struct to use for now as a skeleton for data, as there is no API struct for news article info for now public class ArticleInfo { public string Title { get; set; } diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index 99836705c4..3105ecd742 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Notifications { base.LoadComplete(); - //we may have received changes before we were displayed. + // we may have received changes before we were displayed. updateState(); } diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index 118cb037cb..ebb4a96d14 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -261,7 +261,7 @@ namespace osu.Game.Overlays // todo: this can likely be replaced with WorkingBeatmap.GetBeatmapAsync() Task.Run(() => { - if (beatmap?.Beatmap == null) //this is not needed if a placeholder exists + if (beatmap?.Beatmap == null) // this is not needed if a placeholder exists { title.Text = @"Nothing to play"; artist.Text = @"Nothing to play"; diff --git a/osu.Game/Overlays/OSD/Toast.cs b/osu.Game/Overlays/OSD/Toast.cs index 5d36cac20e..1497ca8fa8 100644 --- a/osu.Game/Overlays/OSD/Toast.cs +++ b/osu.Game/Overlays/OSD/Toast.cs @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.OSD InternalChildren = new Drawable[] { - new Container //this container exists just to set a minimum width for the toast + new Container // this container exists just to set a minimum width for the toast { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs b/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs index b11e41f90f..9f56a34aa6 100644 --- a/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs +++ b/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs @@ -54,7 +54,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { Font = OsuFont.GetFont(size: big ? 40 : 18, weight: FontWeight.Light) }, - new Container //Add a minimum size to the FillFlowContainer + new Container // Add a minimum size to the FillFlowContainer { Width = minimumWidth, } diff --git a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs index a5938a3fe7..e7df4eb5eb 100644 --- a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs @@ -35,7 +35,7 @@ namespace osu.Game.Overlays.Profile.Header RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background5, }, - new Container //artificial shadow + new Container // artificial shadow { RelativeSizeAxes = Axes.X, Height = 3, diff --git a/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs b/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs index 117f905de4..d31470e685 100644 --- a/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs +++ b/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs @@ -94,7 +94,7 @@ namespace osu.Game.Overlays.SearchableList RelativeSizeAxes = Axes.X, }, }, - new Box //keep the tab strip part of autosize, but don't put it in the flow container + new Box // keep the tab strip part of autosize, but don't put it in the flow container { RelativeSizeAxes = Axes.X, Height = 1, diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index d6b810366d..3d66d3c28e 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -110,7 +110,7 @@ namespace osu.Game.Overlays.Toolbar tooltipContainer = new FillFlowContainer { Direction = FillDirection.Vertical, - RelativeSizeAxes = Axes.Both, //stops us being considered in parent's autosize + RelativeSizeAxes = Axes.Both, // stops us being considered in parent's autosize Anchor = TooltipAnchor.HasFlag(Anchor.x0) ? Anchor.BottomLeft : Anchor.BottomRight, Origin = TooltipAnchor, Position = new Vector2(TooltipAnchor.HasFlag(Anchor.x0) ? 5 : -5, 5), diff --git a/osu.Game/Overlays/VolumeOverlay.cs b/osu.Game/Overlays/VolumeOverlay.cs index b484921cce..676d2c941a 100644 --- a/osu.Game/Overlays/VolumeOverlay.cs +++ b/osu.Game/Overlays/VolumeOverlay.cs @@ -58,7 +58,7 @@ namespace osu.Game.Overlays { volumeMeterEffect = new VolumeMeter("EFFECTS", 125, colours.BlueDarker) { - Margin = new MarginPadding { Top = 100 + MuteButton.HEIGHT } //to counter the mute button and re-center the volume meters + Margin = new MarginPadding { Top = 100 + MuteButton.HEIGHT } // to counter the mute button and re-center the volume meters }, volumeMeterMaster = new VolumeMeter("MASTER", 150, colours.PinkDarker), volumeMeterMusic = new VolumeMeter("MUSIC", 125, colours.BlueDarker), diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index 7e17396fde..55d82c4083 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Replays { int newFrame = nextFrameIndex; - //ensure we aren't at an extent. + // ensure we aren't at an extent. if (newFrame == currentFrameIndex) return false; currentFrameIndex = newFrame; @@ -99,8 +99,8 @@ namespace osu.Game.Rulesets.Replays if (frame == null) return false; - return IsImportant(frame) && //a button is in a pressed state - Math.Abs(CurrentTime - NextFrame?.Time ?? 0) <= AllowedImportantTimeSpan; //the next frame is within an allowable time span + return IsImportant(frame) && // a button is in a pressed state + Math.Abs(CurrentTime - NextFrame?.Time ?? 0) <= AllowedImportantTimeSpan; // the next frame is within an allowable time span } } diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 543134cfb4..f302f8700f 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -81,7 +81,7 @@ namespace osu.Game.Rulesets var instances = loadedAssemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r)).ToList(); - //add all legacy rulesets first to ensure they have exclusive choice of primary key. + // add all legacy rulesets first to ensure they have exclusive choice of primary key. foreach (var r in instances.Where(r => r is ILegacyRuleset)) { if (context.RulesetInfo.SingleOrDefault(dbRuleset => dbRuleset.ID == r.RulesetInfo.ID) == null) @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets context.SaveChanges(); - //add any other modes + // add any other modes foreach (var r in instances.Where(r => !(r is ILegacyRuleset))) { if (context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo == r.RulesetInfo.InstantiationInfo) == null) @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets context.SaveChanges(); - //perform a consistency check + // perform a consistency check foreach (var r in context.RulesetInfo) { try diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs index 5dfaceccf5..0f3615b7a9 100644 --- a/osu.Game/Screens/BackgroundScreen.cs +++ b/osu.Game/Screens/BackgroundScreen.cs @@ -30,7 +30,7 @@ namespace osu.Game.Screens protected override bool OnKeyDown(KeyDownEvent e) { - //we don't want to handle escape key. + // we don't want to handle escape key. return false; } diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index 736202ee52..0d5f3d1142 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -101,7 +101,7 @@ namespace osu.Game.Screens.Menu this.FadeIn(300); double fadeOutTime = exit_delay; - //we also handle the exit transition. + // we also handle the exit transition. if (MenuVoice.Value) seeya.Play(); else diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index 67537fa9df..0db7f2a2dc 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -162,7 +162,7 @@ namespace osu.Game.Screens.Menu private IShader shader; private Texture texture; - //Assuming the logo is a circle, we don't need a second dimension. + // Assuming the logo is a circle, we don't need a second dimension. private float size; private Color4 colour; @@ -209,13 +209,13 @@ namespace osu.Game.Screens.Menu float rotation = MathUtils.DegreesToRadians(i / (float)bars_per_visualiser * 360 + j * 360 / visualiser_rounds); float rotationCos = MathF.Cos(rotation); float rotationSin = MathF.Sin(rotation); - //taking the cos and sin to the 0..1 range + // taking the cos and sin to the 0..1 range var barPosition = new Vector2(rotationCos / 2 + 0.5f, rotationSin / 2 + 0.5f) * size; var barSize = new Vector2(size * MathF.Sqrt(2 * (1 - MathF.Cos(MathUtils.DegreesToRadians(360f / bars_per_visualiser)))) / 2f, bar_length * audioData[i]); - //The distance between the position and the sides of the bar. + // The distance between the position and the sides of the bar. var bottomOffset = new Vector2(-rotationSin * barSize.X / 2, rotationCos * barSize.X / 2); - //The distance between the bottom side of the bar and the top side. + // The distance between the bottom side of the bar and the top side. var amplitudeOffset = new Vector2(rotationCos * barSize.Y, rotationSin * barSize.Y); var rectangle = new Quad( @@ -231,7 +231,7 @@ namespace osu.Game.Screens.Menu colourInfo, null, vertexBatch.AddAction, - //barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that. + // barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that. Vector2.Divide(inflation, barSize.Yx)); } } diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 0589e4d12b..f0da2482d6 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -250,7 +250,7 @@ namespace osu.Game.Screens.Menu (Background as BackgroundScreenDefault)?.Next(); - //we may have consumed our preloaded instance, so let's make another. + // we may have consumed our preloaded instance, so let's make another. preloadSongSelect(); if (Beatmap.Value.Track != null && music?.IsUserPaused != true) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index c978f4e96d..36f825b8f6 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -119,7 +119,7 @@ namespace osu.Game.Screens.Play FinishTransforms(true); Scheduler.CancelDelayedTasks(); - if (breaks == null) return; //we need breaks. + if (breaks == null) return; // we need breaks. foreach (var b in breaks) { diff --git a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs index d201b5d30e..fc80983834 100644 --- a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs +++ b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs @@ -50,7 +50,7 @@ namespace osu.Game.Screens.Play.HUD protected override void PopIn() => this.FadeIn(fade_duration); protected override void PopOut() => this.FadeOut(fade_duration); - //We want to handle keyboard inputs all the time in order to trigger ToggleVisibility() when not visible + // We want to handle keyboard inputs all the time in order to trigger ToggleVisibility() when not visible public override bool PropagateNonPositionalInputSubTree => true; protected override bool OnKeyDown(KeyDownEvent e) diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index f4109a63d0..98df73a5e6 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.cs @@ -124,8 +124,8 @@ namespace osu.Game.Screens.Play } } }; - //Set this manually because an element with Alpha=0 won't take it size to AutoSizeContainer, - //so the size can be changing between buttonSprite and glowSprite. + // Set this manually because an element with Alpha=0 won't take it size to AutoSizeContainer, + // so the size can be changing between buttonSprite and glowSprite. Height = buttonSprite.DrawHeight; Width = buttonSprite.DrawWidth; } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index c0d88feda2..93a734589c 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -314,8 +314,8 @@ namespace osu.Game.Screens.Play LoadTask = null; - //By default, we want to load the player and never be returned to. - //Note that this may change if the player we load requested a re-run. + // By default, we want to load the player and never be returned to. + // Note that this may change if the player we load requested a re-run. ValidForResume = false; if (player.LoadedBeatmapSuccessfully) @@ -360,7 +360,7 @@ namespace osu.Game.Screens.Play { if (!muteWarningShownOnce.Value) { - //Checks if the notification has not been shown yet and also if master volume is muted, track/music volume is muted or if the whole game is muted. + // Checks if the notification has not been shown yet and also if master volume is muted, track/music volume is muted or if the whole game is muted. if (volumeOverlay?.IsMuted.Value == true || audioManager.Volume.Value <= audioManager.Volume.MinValue || audioManager.VolumeTrack.Value <= audioManager.VolumeTrack.MinValue) { notificationOverlay?.Post(new MutedNotification()); diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 5a4a03662a..96b779cd20 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -208,7 +208,7 @@ namespace osu.Game.Screens.Select // without this, during a large beatmap import it is impossible to navigate the carousel. applyActiveCriteria(false, alwaysResetScrollPosition: false); - //check if we can/need to maintain our current selection. + // check if we can/need to maintain our current selection. if (previouslySelectedID != null) select((CarouselItem)newSet.Beatmaps.FirstOrDefault(b => b.Beatmap.ID == previouslySelectedID) ?? newSet); diff --git a/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs b/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs index f4bf1ab059..63711e3e50 100644 --- a/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs +++ b/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs @@ -26,7 +26,7 @@ namespace osu.Game.Screens.Select set => tabs.Current = value; } - public Action OnFilter; //passed the selected tab and if mods is checked + public Action OnFilter; // passed the selected tab and if mods is checked public IReadOnlyList TabItems { diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index aebb8e9d87..9669a1391c 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -201,7 +201,7 @@ namespace osu.Game.Screens.Select Schedule(() => { if (beatmap != requestedBeatmap) - //the beatmap has been changed since we started the lookup. + // the beatmap has been changed since we started the lookup. return; var b = res.ToBeatmap(rulesets); @@ -222,7 +222,7 @@ namespace osu.Game.Screens.Select Schedule(() => { if (beatmap != requestedBeatmap) - //the beatmap has been changed since we started the lookup. + // the beatmap has been changed since we started the lookup. return; updateMetrics(); diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index af0d36ea9a..02822ea608 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -52,7 +52,7 @@ namespace osu.Game.Screens.Select.Details AutoSizeAxes = Axes.Y, Children = new[] { - FirstValue = new StatisticRow(), //circle size/key amount + FirstValue = new StatisticRow(), // circle size/key amount HpDrain = new StatisticRow { Title = "HP Drain" }, Accuracy = new StatisticRow { Title = "Accuracy" }, ApproachRate = new StatisticRow { Title = "Approach Rate" }, diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index a7e27c27ba..5b7c9082f3 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -798,7 +798,7 @@ namespace osu.Game.Screens.Select Masking = true; Anchor = Anchor.Centre; Origin = Anchor.Centre; - Width = panel_overflow; //avoid horizontal masking so the panels don't clip when screen stack is pushed. + Width = panel_overflow; // avoid horizontal masking so the panels don't clip when screen stack is pushed. InternalChild = Content = new Container { RelativeSizeAxes = Axes.Both, From aff74db80da54a35a438a4e6443569107e5403a1 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 5 May 2020 10:40:10 +0200 Subject: [PATCH 094/121] Publicly expose HUDOverlay in Player. --- osu.Game/Screens/Play/Player.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ece4c6307e..af724d97a2 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -89,7 +89,7 @@ namespace osu.Game.Screens.Play protected DrawableRuleset DrawableRuleset { get; private set; } - protected HUDOverlay HUDOverlay { get; private set; } + public HUDOverlay HUDOverlay { get; private set; } public bool LoadedBeatmapSuccessfully => DrawableRuleset?.Objects.Any() == true; From 5186da8412ded39b4216cab60c0858fe05b42f71 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 May 2020 11:37:04 +0900 Subject: [PATCH 095/121] Fix potential song select nullref --- osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 6d760df065..1e4f6aeda1 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -61,7 +61,7 @@ namespace osu.Game.Screens.Select.Carousel terms.Add(Beatmap.Version); foreach (var criteriaTerm in criteria.SearchTerms) - match &= terms.Any(term => term.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0); + match &= terms.Any(term => term?.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0); } Filtered.Value = !match; From e91e4a73af89e719e05c1f349836433079a2523e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 May 2020 12:22:07 +0900 Subject: [PATCH 096/121] Fix catch crashing when finishing maps --- .../Scoring/Legacy/ScoreInfoExtensions.cs | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs b/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs index 9745d1abef..6f73a284a2 100644 --- a/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs +++ b/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs @@ -12,7 +12,7 @@ namespace osu.Game.Scoring.Legacy switch (scoreInfo.Ruleset?.ID ?? scoreInfo.RulesetID) { case 3: - return scoreInfo.Statistics[HitResult.Perfect]; + return getCount(scoreInfo, HitResult.Perfect); } return null; @@ -35,10 +35,10 @@ namespace osu.Game.Scoring.Legacy case 0: case 1: case 3: - return scoreInfo.Statistics[HitResult.Great]; + return getCount(scoreInfo, HitResult.Great); case 2: - return scoreInfo.Statistics[HitResult.Perfect]; + return getCount(scoreInfo, HitResult.Perfect); } return null; @@ -65,10 +65,10 @@ namespace osu.Game.Scoring.Legacy switch (scoreInfo.Ruleset?.ID ?? scoreInfo.RulesetID) { case 3: - return scoreInfo.Statistics[HitResult.Good]; + return getCount(scoreInfo, HitResult.Good); case 2: - return scoreInfo.Statistics[HitResult.SmallTickMiss]; + return getCount(scoreInfo, HitResult.SmallTickMiss); } return null; @@ -94,13 +94,13 @@ namespace osu.Game.Scoring.Legacy { case 0: case 1: - return scoreInfo.Statistics[HitResult.Good]; + return getCount(scoreInfo, HitResult.Good); case 3: - return scoreInfo.Statistics[HitResult.Ok]; + return getCount(scoreInfo, HitResult.Ok); case 2: - return scoreInfo.Statistics[HitResult.LargeTickHit]; + return getCount(scoreInfo, HitResult.LargeTickHit); } return null; @@ -131,10 +131,10 @@ namespace osu.Game.Scoring.Legacy { case 0: case 3: - return scoreInfo.Statistics[HitResult.Meh]; + return getCount(scoreInfo, HitResult.Meh); case 2: - return scoreInfo.Statistics[HitResult.SmallTickHit]; + return getCount(scoreInfo, HitResult.SmallTickHit); } return null; @@ -156,9 +156,17 @@ namespace osu.Game.Scoring.Legacy } public static int? GetCountMiss(this ScoreInfo scoreInfo) => - scoreInfo.Statistics[HitResult.Miss]; + getCount(scoreInfo, HitResult.Miss); public static void SetCountMiss(this ScoreInfo scoreInfo, int value) => scoreInfo.Statistics[HitResult.Miss] = value; + + private static int? getCount(ScoreInfo scoreInfo, HitResult result) + { + if (scoreInfo.Statistics.TryGetValue(result, out var existing)) + return existing; + + return null; + } } } From 401c516503239f341c73396e5539e7b78f2b1078 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 May 2020 13:04:08 +0900 Subject: [PATCH 097/121] Expose searchable terms from beatmap info instead --- osu.Game/Beatmaps/BeatmapInfo.cs | 5 +++++ osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs | 8 ++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 90c100db05..3860f12baa 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -149,6 +149,11 @@ namespace osu.Game.Beatmaps } } + public string[] SearchableTerms => new[] + { + Version + }.Concat(Metadata?.SearchableTerms ?? Enumerable.Empty()).Where(s => !string.IsNullOrEmpty(s)).ToArray(); + public override string ToString() { string version = string.IsNullOrEmpty(Version) ? string.Empty : $"[{Version}]"; diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 1e4f6aeda1..ed54c158db 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Screens.Select.Filter; @@ -55,13 +54,10 @@ namespace osu.Game.Screens.Select.Carousel if (match) { - var terms = new List(); - - terms.AddRange(Beatmap.Metadata.SearchableTerms); - terms.Add(Beatmap.Version); + var terms = Beatmap.SearchableTerms; foreach (var criteriaTerm in criteria.SearchTerms) - match &= terms.Any(term => term?.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0); + match &= terms.Any(term => term.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0); } Filtered.Value = !match; From 259ef688110bfbc6e9eed2f7b8779af8b65bbb3e Mon Sep 17 00:00:00 2001 From: Joehu Date: Wed, 6 May 2020 22:29:37 -0700 Subject: [PATCH 098/121] Fix date tooltip not showing in 24-hour format --- osu.Game/Graphics/DrawableDate.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/DrawableDate.cs b/osu.Game/Graphics/DrawableDate.cs index 8c520f4e10..8b6df4a834 100644 --- a/osu.Game/Graphics/DrawableDate.cs +++ b/osu.Game/Graphics/DrawableDate.cs @@ -139,7 +139,7 @@ namespace osu.Game.Graphics return false; dateText.Text = $"{date:d MMMM yyyy} "; - timeText.Text = $"{date:hh:mm:ss \"UTC\"z}"; + timeText.Text = $"{date:HH:mm:ss \"UTC\"z}"; return true; } From 09759565faee8f9f96fbc48aacaa7a8ad7842eb1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 May 2020 14:49:58 +0900 Subject: [PATCH 099/121] Add support for 3v3 tournament chroma key layout --- osu.Game.Tournament/Models/LadderInfo.cs | 6 ++ .../Screens/Gameplay/GameplayScreen.cs | 83 +++++++++++++++++-- 2 files changed, 82 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tournament/Models/LadderInfo.cs b/osu.Game.Tournament/Models/LadderInfo.cs index c2e6da9ca5..7794019437 100644 --- a/osu.Game.Tournament/Models/LadderInfo.cs +++ b/osu.Game.Tournament/Models/LadderInfo.cs @@ -32,5 +32,11 @@ namespace osu.Game.Tournament.Models MinValue = 640, MaxValue = 1366, }; + + public Bindable PlayersPerTeam = new BindableInt(4) + { + MinValue = 3, + MaxValue = 4, + }; } } diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 64a5cd6dec..e4e3842369 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -36,7 +36,7 @@ namespace osu.Game.Tournament.Screens.Gameplay [Resolved] private TournamentMatchChatDisplay chat { get; set; } - private Box chroma; + private Drawable chroma; [BackgroundDependencyLoader] private void load(LadderInfo ladder, MatchIPCInfo ipc, Storage storage) @@ -61,16 +61,30 @@ namespace osu.Game.Tournament.Screens.Gameplay Y = 110, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Children = new Drawable[] + Children = new[] { - chroma = new Box + chroma = new Container { - // chroma key area for stable gameplay - Name = "chroma", Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Height = 512, - Colour = new Color4(0, 255, 0, 255), + Children = new Drawable[] + { + new ChromaArea + { + Name = "Left chroma", + RelativeSizeAxes = Axes.Both, + Width = 0.5f, + }, + new ChromaArea + { + Name = "Right chroma", + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Width = 0.5f, + } + } }, } }, @@ -98,9 +112,15 @@ namespace osu.Game.Tournament.Screens.Gameplay }, new SettingsSlider { - LabelText = "Chroma Width", + LabelText = "Chroma width", Bindable = LadderInfo.ChromaKeyWidth, KeyboardStep = 1, + }, + new SettingsSlider + { + LabelText = "Players per team", + Bindable = LadderInfo.PlayersPerTeam, + KeyboardStep = 1, } } } @@ -201,5 +221,54 @@ namespace osu.Game.Tournament.Screens.Gameplay lastState = state.NewValue; } } + + private class ChromaArea : CompositeDrawable + { + [Resolved] + private LadderInfo ladder { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + // chroma key area for stable gameplay + Colour = new Color4(0, 255, 0, 255); + + ladder.PlayersPerTeam.BindValueChanged(performLayout, true); + } + + private void performLayout(ValueChangedEvent playerCount) + { + switch (playerCount.NewValue) + { + case 3: + InternalChildren = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Width = 0.5f, + Height = 0.5f, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Height = 0.5f, + }, + }; + break; + + default: + InternalChild = new Box + { + RelativeSizeAxes = Axes.Both, + }; + break; + } + } + } } } From 83be5455d3529707cbac2192e538984fc351eea4 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Thu, 7 May 2020 08:52:36 +0200 Subject: [PATCH 100/121] Disable the display of HUD through DisplayHud property. --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 9 +++++++++ osu.Game/Screens/Play/Player.cs | 9 ++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 5062c92afe..ff1f67783e 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -487,6 +487,15 @@ namespace osu.Game.Rulesets.UI protected virtual ResumeOverlay CreateResumeOverlay() => null; + /// + /// Whether to display the HUD with this ruleset. + /// Override to false to completely disable the display of the HUD with this ruleset. + /// + /// + /// HUD refers here to in player as well as . + /// + public virtual bool DisplayHud => true; + /// /// Sets a replay to be used, overriding local input. /// diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index af724d97a2..375976ea6c 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -89,7 +89,7 @@ namespace osu.Game.Screens.Play protected DrawableRuleset DrawableRuleset { get; private set; } - public HUDOverlay HUDOverlay { get; private set; } + protected HUDOverlay HUDOverlay { get; private set; } public bool LoadedBeatmapSuccessfully => DrawableRuleset?.Objects.Any() == true; @@ -184,6 +184,13 @@ namespace osu.Game.Screens.Play addGameplayComponents(GameplayClockContainer, Beatmap.Value, playableBeatmap); addOverlayComponents(GameplayClockContainer, Beatmap.Value); + if (!DrawableRuleset.DisplayHud) + { + HUDOverlay.ShowHud.Value = false; + HUDOverlay.ShowHud.Disabled = true; + BreakOverlay.Hide(); + } + DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true); // bind clock into components that require it From 83998d5ba5a661a284bb19b54d711f8bc583f6ad Mon Sep 17 00:00:00 2001 From: Lucas A Date: Thu, 7 May 2020 09:39:14 +0200 Subject: [PATCH 101/121] Trim whitespace. --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index ff1f67783e..57fbb7f1a5 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -489,7 +489,7 @@ namespace osu.Game.Rulesets.UI /// /// Whether to display the HUD with this ruleset. - /// Override to false to completely disable the display of the HUD with this ruleset. + /// Override to false to completely disable the display of the HUD with this ruleset. /// /// /// HUD refers here to in player as well as . From b6f232e39428ce2e056c821cf229c15717c1d134 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 May 2020 10:27:48 +0900 Subject: [PATCH 102/121] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 8214fa2f2c..af699af1ba 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9db5fe562c..47804ed06e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 82253a0418..86cffa9fba 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From d6840d880a0df25e310c7ee32f7427f0d4baf675 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 May 2020 10:38:31 +0900 Subject: [PATCH 103/121] Update StableStorage implementation in line with framework changes --- osu.Desktop/OsuGameDesktop.cs | 78 +++++++++++------------ osu.Game.Tournament/IPC/FileBasedIPC.cs | 84 +++++++++++-------------- 2 files changed, 72 insertions(+), 90 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index f05ee48914..9351e17419 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -6,15 +6,14 @@ using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; +using Microsoft.Win32; using osu.Desktop.Overlays; using osu.Framework.Platform; using osu.Game; using osuTK.Input; -using Microsoft.Win32; using osu.Desktop.Updater; using osu.Framework; using osu.Framework.Logging; -using osu.Framework.Platform.Windows; using osu.Framework.Screens; using osu.Game.Screens.Menu; using osu.Game.Updater; @@ -37,7 +36,11 @@ namespace osu.Desktop try { if (Host is DesktopGameHost desktopHost) - return new StableStorage(desktopHost); + { + string stablePath = getStableInstallPath(); + if (!string.IsNullOrEmpty(stablePath)) + return new DesktopStorage(stablePath, desktopHost); + } } catch (Exception) { @@ -47,6 +50,35 @@ namespace osu.Desktop return null; } + private string getStableInstallPath() + { + static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")); + + string stableInstallPath; + + try + { + using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) + stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); + + if (checkExists(stableInstallPath)) + return stableInstallPath; + } + catch + { + } + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + return null; + } + protected override UpdateManager CreateUpdateManager() { switch (RuntimeInfo.OS) @@ -111,45 +143,5 @@ namespace osu.Desktop Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning); } - - /// - /// A method of accessing an osu-stable install in a controlled fashion. - /// - private class StableStorage : WindowsStorage - { - protected override string LocateBasePath() - { - static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")); - - string stableInstallPath; - - try - { - using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) - stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); - - if (checkExists(stableInstallPath)) - return stableInstallPath; - } - catch - { - } - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); - if (checkExists(stableInstallPath)) - return stableInstallPath; - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); - if (checkExists(stableInstallPath)) - return stableInstallPath; - - return null; - } - - public StableStorage(DesktopGameHost host) - : base(string.Empty, host) - { - } - } } } diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index eefa9fcfe6..53ba597a7e 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -8,7 +8,6 @@ using Microsoft.Win32; using osu.Framework.Allocation; using osu.Framework.Logging; using osu.Framework.Platform; -using osu.Framework.Platform.Windows; using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; @@ -52,7 +51,12 @@ namespace osu.Game.Tournament.IPC try { - Storage = new StableStorage(host as DesktopGameHost); + var path = findStablePath(); + + if (string.IsNullOrEmpty(path)) + return null; + + Storage = new DesktopStorage(path, host as DesktopGameHost); const string file_ipc_filename = "ipc.txt"; const string file_ipc_state_filename = "ipc-state.txt"; @@ -145,64 +149,50 @@ namespace osu.Game.Tournament.IPC return Storage; } - /// - /// A method of accessing an osu-stable install in a controlled fashion. - /// - private class StableStorage : WindowsStorage + private string findStablePath() { - protected override string LocateBasePath() - { - static bool checkExists(string p) - { - return File.Exists(Path.Combine(p, "ipc.txt")); - } + static bool checkExists(string p) => File.Exists(Path.Combine(p, "ipc.txt")); - string stableInstallPath = string.Empty; + string stableInstallPath = string.Empty; + + try + { + try + { + stableInstallPath = Environment.GetEnvironmentVariable("OSU_STABLE_PATH"); + + if (checkExists(stableInstallPath)) + return stableInstallPath; + } + catch + { + } try { - try - { - stableInstallPath = Environment.GetEnvironmentVariable("OSU_STABLE_PATH"); + using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) + stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); - if (checkExists(stableInstallPath)) - return stableInstallPath; - } - catch - { - } - - try - { - using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) - stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); - - if (checkExists(stableInstallPath)) - return stableInstallPath; - } - catch - { - } - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); if (checkExists(stableInstallPath)) return stableInstallPath; - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); - if (checkExists(stableInstallPath)) - return stableInstallPath; - - return null; } - finally + catch { - Logger.Log($"Stable path for tourney usage: {stableInstallPath}"); } - } - public StableStorage(DesktopGameHost host) - : base(string.Empty, host) + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + return null; + } + finally { + Logger.Log($"Stable path for tourney usage: {stableInstallPath}"); } } } From af5e1f8298ad47fdbd783e3dedcb5ba0038aa898 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 May 2020 14:22:07 +0900 Subject: [PATCH 104/121] Commit autogenerated rider VCS settings update --- .idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml b/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml index 4bb9f4d2a0..7515e76054 100644 --- a/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml +++ b/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml @@ -1,6 +1,6 @@ - \ No newline at end of file From 9f64882f371aeae723e646871cbdfe5bb250f168 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 May 2020 14:48:38 +0900 Subject: [PATCH 105/121] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index af699af1ba..a406cdf08a 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 47804ed06e..5ccfaaac9e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 86cffa9fba..dc83d937f7 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From 8b5de7403f317cd87be9315976632d08873b021a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 May 2020 15:12:29 +0900 Subject: [PATCH 106/121] Fix android usage of obsoleted VersionCode --- osu.Android/OsuGameAndroid.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index 84f215f930..19ed7ffcf5 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -18,7 +18,8 @@ namespace osu.Android try { - string versionName = packageInfo.VersionCode.ToString(); + // todo: needs checking before play store redeploy. + string versionName = packageInfo.VersionName; // undo play store version garbling return new Version(int.Parse(versionName.Substring(0, 4)), int.Parse(versionName.Substring(4, 4)), int.Parse(versionName.Substring(8, 1))); } From c5589d278b17f30196272e26ac506dbfb2a3e46d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 May 2020 15:35:25 +0900 Subject: [PATCH 107/121] Revert "Commit autogenerated rider VCS settings update" This reverts commit af5e1f8298ad47fdbd783e3dedcb5ba0038aa898. --- .idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml b/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml index 7515e76054..4bb9f4d2a0 100644 --- a/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml +++ b/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml @@ -1,6 +1,6 @@ - \ No newline at end of file From 30dd158c33ef1c3968dd7432ffa3fc78f2aff460 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 8 May 2020 09:37:50 +0200 Subject: [PATCH 108/121] Rename property to AllowGameplayOverlays and update XMLDoc accordingly. --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 8 ++++---- osu.Game/Screens/Play/Player.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 57fbb7f1a5..e1c21209c2 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -488,13 +488,13 @@ namespace osu.Game.Rulesets.UI protected virtual ResumeOverlay CreateResumeOverlay() => null; /// - /// Whether to display the HUD with this ruleset. - /// Override to false to completely disable the display of the HUD with this ruleset. + /// Whether to display gameplay overlays with this ruleset. + /// Override to false to completely disable the display of gameplay overlays. /// /// - /// HUD refers here to in player as well as . + /// Gameplay overlays refer here to in player as well as . /// - public virtual bool DisplayHud => true; + public virtual bool AllowGameplayOverlays => true; /// /// Sets a replay to be used, overriding local input. diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 375976ea6c..acc8dc9c7c 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -184,7 +184,7 @@ namespace osu.Game.Screens.Play addGameplayComponents(GameplayClockContainer, Beatmap.Value, playableBeatmap); addOverlayComponents(GameplayClockContainer, Beatmap.Value); - if (!DrawableRuleset.DisplayHud) + if (!DrawableRuleset.AllowGameplayOverlays) { HUDOverlay.ShowHud.Value = false; HUDOverlay.ShowHud.Disabled = true; From a59db976d66fab80cb3035dd779c59db6070d68c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 May 2020 18:05:06 +0900 Subject: [PATCH 109/121] Fix loading a ruleset with a new version specification causing a crash --- osu.Game/Rulesets/RulesetInfo.cs | 16 +++++++++++++++- osu.Game/Rulesets/RulesetStore.cs | 12 ++++-------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs index afd499cb9e..2e32b96084 100644 --- a/osu.Game/Rulesets/RulesetInfo.cs +++ b/osu.Game/Rulesets/RulesetInfo.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Linq; using Newtonsoft.Json; namespace osu.Game.Rulesets @@ -15,7 +16,20 @@ namespace osu.Game.Rulesets public string ShortName { get; set; } - public string InstantiationInfo { get; set; } + private string instantiationInfo; + + public string InstantiationInfo + { + get => instantiationInfo; + set => instantiationInfo = abbreviateInstantiationInfo(value); + } + + private string abbreviateInstantiationInfo(string value) + { + // exclude version onwards, matching only on namespace and type. + // this is mainly to allow for new versions of already loaded rulesets to "upgrade" from old. + return string.Join(',', value.Split(',').Take(2)); + } [JsonIgnore] public bool Available { get; set; } diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index f302f8700f..b8f2abd766 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -93,7 +93,9 @@ namespace osu.Game.Rulesets // add any other modes foreach (var r in instances.Where(r => !(r is ILegacyRuleset))) { - if (context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo == r.RulesetInfo.InstantiationInfo) == null) + // todo: StartsWith can be changed to Equals on 2020-11-08 + // This is to give users enough time to have their database use new abbreviated info). + if (context.RulesetInfo.FirstOrDefault(ri => r.RulesetInfo.InstantiationInfo.StartsWith(ri.InstantiationInfo)) == null) context.RulesetInfo.Add(r.RulesetInfo); } @@ -104,13 +106,7 @@ namespace osu.Game.Rulesets { try { - var instanceInfo = ((Ruleset)Activator.CreateInstance(Type.GetType(r.InstantiationInfo, asm => - { - // for the time being, let's ignore the version being loaded. - // this allows for debug builds to successfully load rulesets (even though debug rulesets have a 0.0.0 version). - asm.Version = null; - return Assembly.Load(asm); - }, null))).RulesetInfo; + var instanceInfo = ((Ruleset)Activator.CreateInstance(Type.GetType(r.InstantiationInfo))).RulesetInfo; r.Name = instanceInfo.Name; r.ShortName = instanceInfo.ShortName; From dcfef6b44383c48246ab2a648b13855d13e8834c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 May 2020 18:46:37 +0900 Subject: [PATCH 110/121] Add clear method to EditorBeatmap --- osu.Game/Screens/Edit/EditorBeatmap.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index 2e8e03bc73..23c8c9f605 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -201,6 +202,25 @@ namespace osu.Game.Screens.Edit updateHitObject(null, true); } + /// + /// Clears all from this . + /// + public void Clear() + { + var removed = HitObjects.ToList(); + + mutableHitObjects.Clear(); + + foreach (var b in startTimeBindables) + b.Value.UnbindAll(); + startTimeBindables.Clear(); + + foreach (var h in removed) + HitObjectRemoved?.Invoke(h); + + updateHitObject(null, true); + } + private void trackStartTime(HitObject hitObject) { startTimeBindables[hitObject] = hitObject.StartTimeBindable.GetBoundCopy(); From efff2bf15df6d588bcb6335c6bcac54c572fe23a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 May 2020 18:49:19 +0900 Subject: [PATCH 111/121] Add HitObject to DefaultsApplied event --- osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs | 2 +- .../Objects/Drawables/Connections/FollowPointConnection.cs | 2 +- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 7 +++++-- osu.Game/Rulesets/Objects/HitObject.cs | 4 ++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs index a6c3be7e5a..c3b4d2625e 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs @@ -399,7 +399,7 @@ namespace osu.Game.Rulesets.Osu.Tests { public TestSlider() { - DefaultsApplied += () => + DefaultsApplied += _ => { HeadCircle.HitWindows = new TestHitWindows(); TailCircle.HitWindows = new TestHitWindows(); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs index 6f09bbcd57..8a0ef22c4a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs @@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections private void bindEvents(DrawableOsuHitObject drawableObject) { drawableObject.HitObject.PositionBindable.BindValueChanged(_ => scheduleRefresh()); - drawableObject.HitObject.DefaultsApplied += scheduleRefresh; + drawableObject.HitObject.DefaultsApplied += _ => scheduleRefresh(); } private void scheduleRefresh() diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 0047142cbd..3838e52f9b 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Objects.Drawables samplesBindable.CollectionChanged += (_, __) => loadSamples(); updateState(ArmedState.Idle, true); - onDefaultsApplied(); + apply(HitObject); } private void loadSamples() @@ -175,7 +175,10 @@ namespace osu.Game.Rulesets.Objects.Drawables AddInternal(Samples); } - private void onDefaultsApplied() => apply(HitObject); + private void onDefaultsApplied(HitObject hitObject) + { + apply(hitObject); + } private void apply(HitObject hitObject) { diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index 9a8efdde84..cffbdbae08 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Objects /// /// Invoked after has completed on this . /// - public event Action DefaultsApplied; + public event Action DefaultsApplied; public readonly Bindable StartTimeBindable = new BindableDouble(); @@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Objects foreach (var h in nestedHitObjects) h.ApplyDefaults(controlPointInfo, difficulty); - DefaultsApplied?.Invoke(); + DefaultsApplied?.Invoke(this); } protected virtual void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) From 22dda3fe0231dc01c66591724097aef72e3c7f1a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 May 2020 18:49:58 +0900 Subject: [PATCH 112/121] Make ScrollingHitObjectContainer respond to defaults applied events --- .../Objects/Drawables/DrawableHitObject.cs | 3 + .../Scrolling/ScrollingHitObjectContainer.cs | 69 +++++++++++++------ 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 3838e52f9b..ba6571fe1a 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Objects.Drawables [Cached(typeof(DrawableHitObject))] public abstract class DrawableHitObject : SkinReloadableDrawable { + public event Action DefaultsApplied; + public readonly HitObject HitObject; /// @@ -178,6 +180,7 @@ namespace osu.Game.Rulesets.Objects.Drawables private void onDefaultsApplied(HitObject hitObject) { apply(hitObject); + DefaultsApplied?.Invoke(this); } private void apply(HitObject hitObject) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 57f58be55a..3e01bb1d31 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -16,17 +16,23 @@ namespace osu.Game.Rulesets.UI.Scrolling { private readonly IBindable timeRange = new BindableDouble(); private readonly IBindable direction = new Bindable(); + private readonly Dictionary hitObjectInitialStateCache = new Dictionary(); [Resolved] private IScrollingInfo scrollingInfo { get; set; } - private readonly LayoutValue initialStateCache = new LayoutValue(Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo); + // Responds to changes in the layout. When the layout is changes, all hit object states must be recomputed. + private readonly LayoutValue layoutCache = new LayoutValue(Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo); + + // A combined cache across all hit object states to reduce per-update iterations. + // When invalidated, one or more (but not necessarily all) hitobject states must be re-validated. + private readonly Cached combinedObjCache = new Cached(); public ScrollingHitObjectContainer() { RelativeSizeAxes = Axes.Both; - AddLayout(initialStateCache); + AddLayout(layoutCache); } [BackgroundDependencyLoader] @@ -35,13 +41,14 @@ namespace osu.Game.Rulesets.UI.Scrolling direction.BindTo(scrollingInfo.Direction); timeRange.BindTo(scrollingInfo.TimeRange); - direction.ValueChanged += _ => initialStateCache.Invalidate(); - timeRange.ValueChanged += _ => initialStateCache.Invalidate(); + direction.ValueChanged += _ => layoutCache.Invalidate(); + timeRange.ValueChanged += _ => layoutCache.Invalidate(); } public override void Add(DrawableHitObject hitObject) { - initialStateCache.Invalidate(); + combinedObjCache.Invalidate(); + hitObject.DefaultsApplied += onDefaultsApplied; base.Add(hitObject); } @@ -51,8 +58,10 @@ namespace osu.Game.Rulesets.UI.Scrolling if (result) { - initialStateCache.Invalidate(); + combinedObjCache.Invalidate(); hitObjectInitialStateCache.Remove(hitObject); + + hitObject.DefaultsApplied -= onDefaultsApplied; } return result; @@ -60,23 +69,45 @@ namespace osu.Game.Rulesets.UI.Scrolling public override void Clear(bool disposeChildren = true) { + foreach (var h in Objects) + h.DefaultsApplied -= onDefaultsApplied; + base.Clear(disposeChildren); - initialStateCache.Invalidate(); + combinedObjCache.Invalidate(); hitObjectInitialStateCache.Clear(); } + private void onDefaultsApplied(DrawableHitObject drawableObject) + { + // The cache may not exist if the hitobject state hasn't been computed yet (e.g. if the hitobject was added + defaults applied in the same frame). + // In such a case, combinedObjCache will take care of updating the hitobject. + if (hitObjectInitialStateCache.TryGetValue(drawableObject, out var objCache)) + { + combinedObjCache.Invalidate(); + objCache.Invalidate(); + } + } + private float scrollLength; protected override void Update() { base.Update(); - if (!initialStateCache.IsValid) + if (!layoutCache.IsValid) { foreach (var cached in hitObjectInitialStateCache.Values) cached.Invalidate(); + combinedObjCache.Invalidate(); + scrollingInfo.Algorithm.Reset(); + + layoutCache.Validate(); + } + + if (!combinedObjCache.IsValid) + { switch (direction.Value) { case ScrollingDirection.Up: @@ -89,15 +120,21 @@ namespace osu.Game.Rulesets.UI.Scrolling break; } - scrollingInfo.Algorithm.Reset(); - foreach (var obj in Objects) { + if (!hitObjectInitialStateCache.TryGetValue(obj, out var objCache)) + objCache = hitObjectInitialStateCache[obj] = new Cached(); + + if (objCache.IsValid) + return; + computeLifetimeStartRecursive(obj); computeInitialStateRecursive(obj); + + objCache.Validate(); } - initialStateCache.Validate(); + combinedObjCache.Validate(); } } @@ -109,8 +146,6 @@ namespace osu.Game.Rulesets.UI.Scrolling computeLifetimeStartRecursive(obj); } - private readonly Dictionary hitObjectInitialStateCache = new Dictionary(); - private double computeOriginAdjustedLifetimeStart(DrawableHitObject hitObject) { float originAdjustment = 0.0f; @@ -142,12 +177,6 @@ namespace osu.Game.Rulesets.UI.Scrolling // Cant use AddOnce() since the delegate is re-constructed every invocation private void computeInitialStateRecursive(DrawableHitObject hitObject) => hitObject.Schedule(() => { - if (!hitObjectInitialStateCache.TryGetValue(hitObject, out var cached)) - cached = hitObjectInitialStateCache[hitObject] = new Cached(); - - if (cached.IsValid) - return; - if (hitObject.HitObject is IHasEndTime e) { switch (direction.Value) @@ -171,8 +200,6 @@ namespace osu.Game.Rulesets.UI.Scrolling // Nested hitobjects don't need to scroll, but they do need accurate positions updatePosition(obj, hitObject.HitObject.StartTime); } - - cached.Validate(); }); protected override void UpdateAfterChildrenLife() From d67facf8e4c28443840af1519ec7be669ef552d3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 May 2020 18:50:06 +0900 Subject: [PATCH 113/121] Add test scene --- .../TestSceneManiaHitObjectComposer.cs | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs index 286e3f6e50..1554a956a2 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs @@ -10,10 +10,13 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; +using osu.Framework.Utils; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Edit; +using osu.Game.Rulesets.Mania.Edit.Blueprints; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; @@ -48,6 +51,8 @@ namespace osu.Game.Rulesets.Mania.Tests DrawableHitObject lastObject = null; Vector2 originalPosition = Vector2.Zero; + setScrollStep(ScrollingDirection.Up); + AddStep("seek to last object", () => { lastObject = this.ChildrenOfType().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last()); @@ -81,7 +86,7 @@ namespace osu.Game.Rulesets.Mania.Tests DrawableHitObject lastObject = null; Vector2 originalPosition = Vector2.Zero; - AddStep("set down scroll", () => ((Bindable)composer.Composer.ScrollingInfo.Direction).Value = ScrollingDirection.Down); + setScrollStep(ScrollingDirection.Down); AddStep("seek to last object", () => { @@ -116,6 +121,8 @@ namespace osu.Game.Rulesets.Mania.Tests DrawableHitObject lastObject = null; Vector2 originalPosition = Vector2.Zero; + setScrollStep(ScrollingDirection.Down); + AddStep("seek to last object", () => { lastObject = this.ChildrenOfType().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last()); @@ -147,6 +154,46 @@ namespace osu.Game.Rulesets.Mania.Tests AddAssert("hitobjects not moved vertically", () => lastObject.DrawPosition.Y - originalPosition.Y <= DefaultNotePiece.NOTE_HEIGHT); } + [Test] + public void TestDragHoldNoteSelectionVertically() + { + setScrollStep(ScrollingDirection.Down); + + AddStep("setup beatmap", () => + { + composer.EditorBeatmap.Clear(); + composer.EditorBeatmap.Add(new HoldNote + { + Column = 1, + EndTime = 200 + }); + }); + + DrawableHoldNote holdNote = null; + + AddStep("grab hold note", () => + { + holdNote = this.ChildrenOfType().FirstOrDefault(); + InputManager.MoveMouseTo(holdNote); + InputManager.PressButton(MouseButton.Left); + }); + + AddStep("move drag upwards", () => + { + InputManager.MoveMouseTo(holdNote, new Vector2(0, -100)); + InputManager.ReleaseButton(MouseButton.Left); + }); + + AddAssert("head note positioned correctly", () => Precision.AlmostEquals(holdNote.ScreenSpaceDrawQuad.BottomLeft, holdNote.Head.ScreenSpaceDrawQuad.BottomLeft)); + AddAssert("tail note positioned correctly", () => Precision.AlmostEquals(holdNote.ScreenSpaceDrawQuad.TopLeft, holdNote.Tail.ScreenSpaceDrawQuad.BottomLeft)); + + AddAssert("head blueprint positioned correctly", () => this.ChildrenOfType().ElementAt(0).DrawPosition == holdNote.Head.DrawPosition); + AddAssert("tail blueprint positioned correctly", () => this.ChildrenOfType().ElementAt(1).DrawPosition == holdNote.Tail.DrawPosition); + } + + private void setScrollStep(ScrollingDirection direction) + => AddStep($"set scroll direction = {direction}", () => ((Bindable)composer.Composer.ScrollingInfo.Direction).Value = direction); + private class TestComposer : CompositeDrawable { [Cached(typeof(EditorBeatmap))] From be3b77cf25708ceb77d14d830a473f09d0403bfc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 May 2020 20:09:59 +0900 Subject: [PATCH 114/121] Fix potentially skipping hitobject updates --- osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 3e01bb1d31..ea72061216 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.UI.Scrolling objCache = hitObjectInitialStateCache[obj] = new Cached(); if (objCache.IsValid) - return; + continue; computeLifetimeStartRecursive(obj); computeInitialStateRecursive(obj); From 5c2778d5f08288211a1cd57b14beb85e456a513b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 May 2020 20:33:02 +0900 Subject: [PATCH 115/121] Change comparison direction --- osu.Game/Rulesets/RulesetStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index b8f2abd766..b3026bf2b7 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets { // todo: StartsWith can be changed to Equals on 2020-11-08 // This is to give users enough time to have their database use new abbreviated info). - if (context.RulesetInfo.FirstOrDefault(ri => r.RulesetInfo.InstantiationInfo.StartsWith(ri.InstantiationInfo)) == null) + if (context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo.StartsWith(r.RulesetInfo.InstantiationInfo)) == null) context.RulesetInfo.Add(r.RulesetInfo); } From d1976b194d9bd2314775eb56dcbb1125738d0075 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sat, 9 May 2020 10:42:56 +0300 Subject: [PATCH 116/121] Check local availability before disabling buttons --- .../BeatmapListing/Panels/BeatmapPanelDownloadButton.cs | 2 +- osu.Game/Overlays/BeatmapSet/Header.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs index 589f2d5072..6d5862ee2b 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs @@ -50,7 +50,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels [BackgroundDependencyLoader(true)] private void load(OsuGame game, BeatmapManager beatmaps, OsuConfigManager osuConfig) { - if (BeatmapSet.Value?.OnlineInfo?.Availability?.DownloadDisabled ?? false) + if ((BeatmapSet.Value?.OnlineInfo?.Availability?.DownloadDisabled ?? false) && State.Value != DownloadState.LocallyAvailable) { button.Enabled.Value = false; button.TooltipText = "this beatmap is currently not available for download."; diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 1ff08aab2c..06e31277dd 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -264,7 +264,7 @@ namespace osu.Game.Overlays.BeatmapSet { if (BeatmapSet.Value == null) return; - if (BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false) + if ((BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false) && State.Value != DownloadState.LocallyAvailable) { downloadButtonsContainer.Clear(); return; From 55e0d91f37591061869917968a0897ff10ef726d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 9 May 2020 18:09:17 +0900 Subject: [PATCH 117/121] Fix download button being disabled after importing a download disabled beatmap --- .../Panels/BeatmapPanelDownloadButton.cs | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs index 6d5862ee2b..67782dfe3f 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs @@ -50,13 +50,6 @@ namespace osu.Game.Overlays.BeatmapListing.Panels [BackgroundDependencyLoader(true)] private void load(OsuGame game, BeatmapManager beatmaps, OsuConfigManager osuConfig) { - if ((BeatmapSet.Value?.OnlineInfo?.Availability?.DownloadDisabled ?? false) && State.Value != DownloadState.LocallyAvailable) - { - button.Enabled.Value = false; - button.TooltipText = "this beatmap is currently not available for download."; - return; - } - noVideoSetting = osuConfig.GetBindable(OsuSetting.PreferNoVideo); button.Action = () => @@ -81,6 +74,26 @@ namespace osu.Game.Overlays.BeatmapListing.Panels break; } }; + + State.BindValueChanged(state => + { + switch (state.NewValue) + { + case DownloadState.LocallyAvailable: + button.Enabled.Value = true; + button.TooltipText = string.Empty; + break; + + default: + if (BeatmapSet.Value?.OnlineInfo?.Availability?.DownloadDisabled ?? false) + { + button.Enabled.Value = false; + button.TooltipText = "this beatmap is currently not available for download."; + } + + break; + } + }, true); } } } From fa711a6456a4ca25eb74a3fda8da3977e13a8a63 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 9 May 2020 19:11:51 +0900 Subject: [PATCH 118/121] Fix null reference causing hard freeze if game is forcefully closed during disclaimer --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index fdc8d94352..b86be9858f 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -914,7 +914,7 @@ namespace osu.Game if (ScreenStack.CurrentScreen is Loader) return false; - if (introScreen.DidLoadMenu && !(ScreenStack.CurrentScreen is IntroScreen)) + if (introScreen?.DidLoadMenu == true && !(ScreenStack.CurrentScreen is IntroScreen)) { Scheduler.Add(introScreen.MakeCurrent); return true; From 3f78ddf482b7b74c399aa4adf0791156813860e2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 9 May 2020 19:13:18 +0900 Subject: [PATCH 119/121] Add CanBeNull hinting --- osu.Game/OsuGame.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b86be9858f..3caffb6db5 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -18,6 +18,7 @@ using osu.Game.Screens.Menu; using System.Linq; using System.Threading; using System.Threading.Tasks; +using JetBrains.Annotations; using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Development; @@ -97,6 +98,7 @@ namespace osu.Game private MainMenu menuScreen; + [CanBeNull] private IntroScreen introScreen; private Bindable configRuleset; From 2f12c4126a10676982cc301c58e98ab376d2f493 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sun, 10 May 2020 13:49:08 +0900 Subject: [PATCH 120/121] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- .../TestSceneManiaHitObjectComposer.cs | 2 +- osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs index 1554a956a2..48159c817d 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs @@ -173,7 +173,7 @@ namespace osu.Game.Rulesets.Mania.Tests AddStep("grab hold note", () => { - holdNote = this.ChildrenOfType().FirstOrDefault(); + holdNote = this.ChildrenOfType().Single(); InputManager.MoveMouseTo(holdNote); InputManager.PressButton(MouseButton.Left); }); diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index ea72061216..15e625872d 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.UI.Scrolling [Resolved] private IScrollingInfo scrollingInfo { get; set; } - // Responds to changes in the layout. When the layout is changes, all hit object states must be recomputed. + // Responds to changes in the layout. When the layout changes, all hit object states must be recomputed. private readonly LayoutValue layoutCache = new LayoutValue(Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo); // A combined cache across all hit object states to reduce per-update iterations. From 44fdf1e6b05b033977287647e126e4ed99b898e4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 10 May 2020 20:09:41 +0900 Subject: [PATCH 121/121] Reword xmldoc --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 5f0a4b0975..9a10b7d1b2 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -488,12 +488,8 @@ namespace osu.Game.Rulesets.UI protected virtual ResumeOverlay CreateResumeOverlay() => null; /// - /// Whether to display gameplay overlays with this ruleset. - /// Override to false to completely disable the display of gameplay overlays. + /// Whether to display gameplay overlays, such as and . /// - /// - /// Gameplay overlays refer here to in player as well as . - /// public virtual bool AllowGameplayOverlays => true; ///