From 05b44f5df46697ef6989ad1cb5e934e85b734478 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Thu, 18 Nov 2021 09:55:04 +1100 Subject: [PATCH 01/57] Add slider bonus to Flashlight skill --- .../Difficulty/Skills/Flashlight.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 466f0556ab..53634e9125 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -24,6 +24,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override double DecayWeight => 1.0; protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. + private const double min_velocity = 0.5; + private const double slider_multiplier = 2.5; + private double currentStrain; private double strainValueOf(DifficultyHitObject current) @@ -62,7 +65,21 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } } - return Math.Pow(smallDistNerf * result, 2.0); + result = Math.Pow(smallDistNerf * result, 2.0); + + double sliderBonus = 0.0; + if (osuCurrent.TravelTime != 0) + { + // Reward sliders based on velocity. + double normalisedTravelDistance = osuCurrent.TravelDistance / scalingFactor; + sliderBonus = Math.Max(0.0, (normalisedTravelDistance) / osuCurrent.TravelTime - min_velocity); + // Longer sliders require more memorisation. + sliderBonus *= normalisedTravelDistance; + } + + result += sliderBonus * slider_multiplier; + + return result; } private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); From a77a9a2309ae0068c4a50689981959a85a8f8cf8 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Thu, 18 Nov 2021 10:13:25 +1100 Subject: [PATCH 02/57] Balancing slider bonus --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 53634e9125..29ad7b2911 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. private const double min_velocity = 0.5; - private const double slider_multiplier = 2.5; + private const double slider_multiplier = 1.3; private double currentStrain; @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { // Reward sliders based on velocity. double normalisedTravelDistance = osuCurrent.TravelDistance / scalingFactor; - sliderBonus = Math.Max(0.0, (normalisedTravelDistance) / osuCurrent.TravelTime - min_velocity); + sliderBonus = Math.Pow(Math.Max(0.0, (normalisedTravelDistance) / osuCurrent.TravelTime - min_velocity), 0.5); // Longer sliders require more memorisation. sliderBonus *= normalisedTravelDistance; } From e42f28990b6db55357ca95fab01b192e9a91de73 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Thu, 18 Nov 2021 10:30:17 +1100 Subject: [PATCH 03/57] Add blank line --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 29ad7b2911..c926222875 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -68,6 +68,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills result = Math.Pow(smallDistNerf * result, 2.0); double sliderBonus = 0.0; + if (osuCurrent.TravelTime != 0) { // Reward sliders based on velocity. From 7b2a5d4f76bbacdd7707c98be261fc0158d5efb7 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 25 May 2022 13:01:12 +0900 Subject: [PATCH 04/57] Adjust xmldoc for correctness --- .../Difficulty/Preprocessing/OsuDifficultyHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index cf4802d282..abb9e64a65 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing public double TravelDistance { get; private set; } /// - /// The time taken to travel through , with a minimum value of 25ms for a non-zero distance. + /// The time taken to travel through , with a minimum value of 25ms for objects. /// public double TravelTime { get; private set; } From cde06ecf17093ec10e19190db6326e94bc1d0395 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 25 May 2022 13:03:08 +0900 Subject: [PATCH 05/57] Apply code reviews --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 6482f6c962..b8f5fd8461 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using System.Linq; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Mods; @@ -89,13 +90,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double sliderBonus = 0.0; - if (osuCurrent.TravelTime != 0) + if (osuCurrent.BaseObject is Slider) { + Debug.Assert(osuCurrent.TravelTime > 0); + // Reward sliders based on velocity. - double normalisedTravelDistance = osuCurrent.TravelDistance / scalingFactor; - sliderBonus = Math.Pow(Math.Max(0.0, (normalisedTravelDistance) / osuCurrent.TravelTime - min_velocity), 0.5); + sliderBonus = Math.Pow(Math.Max(0.0, osuCurrent.TravelDistance / osuCurrent.TravelTime - min_velocity), 0.5); + // Longer sliders require more memorisation. - sliderBonus *= normalisedTravelDistance; + sliderBonus *= osuCurrent.TravelDistance; } result += sliderBonus * slider_multiplier; From 8a4f52287c8d06dbe84120ab9f09e0e95f5cd05b Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 25 May 2022 13:38:23 +0900 Subject: [PATCH 06/57] Re-invert distances, cleanup, use actual normalised distance --- .../Preprocessing/OsuDifficultyHitObject.cs | 16 ++++++++++------ .../Difficulty/Skills/Flashlight.cs | 14 ++++++++++---- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index abb9e64a65..258df97a7c 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -12,10 +12,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing { public class OsuDifficultyHitObject : DifficultyHitObject { - private const int normalised_radius = 50; // Change radius to 50 to make 100 the diameter. Easier for mental maths. + /// + /// A distance by which all distances should be scaled in order to assume a uniform circle size. + /// + public const int NORMALISED_RADIUS = 50; // Change radius to 50 to make 100 the diameter. Easier for mental maths. + private const int min_delta_time = 25; - private const float maximum_slider_radius = normalised_radius * 2.4f; - private const float assumed_slider_radius = normalised_radius * 1.8f; + private const float maximum_slider_radius = NORMALISED_RADIUS * 2.4f; + private const float assumed_slider_radius = NORMALISED_RADIUS * 1.8f; protected new OsuHitObject BaseObject => (OsuHitObject)base.BaseObject; @@ -129,7 +133,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing return; // We will scale distances by this factor, so we can assume a uniform CircleSize among beatmaps. - float scalingFactor = normalised_radius / (float)BaseObject.Radius; + float scalingFactor = NORMALISED_RADIUS / (float)BaseObject.Radius; if (BaseObject.Radius < 30) { @@ -203,7 +207,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing slider.LazyEndPosition = slider.StackedPosition + slider.Path.PositionAt(endTimeMin); // temporary lazy end position until a real result can be derived. var currCursorPosition = slider.StackedPosition; - double scalingFactor = normalised_radius / slider.Radius; // lazySliderDistance is coded to be sensitive to scaling, this makes the maths easier with the thresholds being used. + double scalingFactor = NORMALISED_RADIUS / slider.Radius; // lazySliderDistance is coded to be sensitive to scaling, this makes the maths easier with the thresholds being used. for (int i = 1; i < slider.NestedHitObjects.Count; i++) { @@ -231,7 +235,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing else if (currMovementObj is SliderRepeat) { // For a slider repeat, assume a tighter movement threshold to better assess repeat sliders. - requiredMovement = normalised_radius; + requiredMovement = NORMALISED_RADIUS; } if (currMovementLength > requiredMovement) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index b8f5fd8461..f850f45162 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills var osuCurrent = (OsuDifficultyHitObject)current; var osuHitObject = (OsuHitObject)(osuCurrent.BaseObject); - double scalingFactor = 52.0 / osuHitObject.Radius; + double scalingFactor = OsuDifficultyHitObject.NORMALISED_RADIUS / osuHitObject.Radius; double smallDistNerf = 1.0; double cumulativeStrainTime = 0.0; @@ -70,8 +70,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills if (i == 0) smallDistNerf = Math.Min(1.0, jumpDistance / 75.0); + // Invert the scaling factor to determine the true jump distance independent of circle size. + double pixelJumpDistance = osuCurrent.LazyJumpDistance / scalingFactor; + // We also want to nerf stacks so that only the first object of the stack is accounted for. - double stackNerf = Math.Min(1.0, (currentObj.LazyJumpDistance / scalingFactor) / 25.0); + double stackNerf = Math.Min(1.0, pixelJumpDistance / 25.0); // Bonus based on how visible the object is. double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - osuCurrent.OpacityAt(currentHitObject.StartTime, hidden)); @@ -94,11 +97,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { Debug.Assert(osuCurrent.TravelTime > 0); + // Invert the scaling factor to determine the true travel distance independent of circle size. + double pixelTravelDistance = osuCurrent.TravelDistance / scalingFactor; + // Reward sliders based on velocity. - sliderBonus = Math.Pow(Math.Max(0.0, osuCurrent.TravelDistance / osuCurrent.TravelTime - min_velocity), 0.5); + sliderBonus = Math.Pow(Math.Max(0.0, pixelTravelDistance / osuCurrent.TravelTime - min_velocity), 0.5); // Longer sliders require more memorisation. - sliderBonus *= osuCurrent.TravelDistance; + sliderBonus *= pixelTravelDistance; } result += sliderBonus * slider_multiplier; From 45258b3f14b3a0e0847b7f9159bc6eed7a15afba Mon Sep 17 00:00:00 2001 From: StanR Date: Mon, 4 Jul 2022 19:53:34 +0300 Subject: [PATCH 07/57] Buff aim slightly --- osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs | 4 ++-- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs index 0694746cbf..6c2ef12b1d 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs @@ -13,8 +13,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators public static class AimEvaluator { private const double wide_angle_multiplier = 1.5; - private const double acute_angle_multiplier = 2.0; - private const double slider_multiplier = 1.5; + private const double acute_angle_multiplier = 1.95; + private const double slider_multiplier = 1.35; private const double velocity_change_multiplier = 0.75; /// diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index c3b7834009..21ff566b00 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss); effectiveMissCount = calculateEffectiveMissCount(osuAttributes); - double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things. + double multiplier = 1.11; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things. if (score.Mods.Any(m => m is OsuModNoFail)) multiplier *= Math.Max(0.90, 1.0 - 0.02 * effectiveMissCount); diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index 9b1fbf9a2e..e4f61b65cd 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private double currentStrain; - private double skillMultiplier => 23.25; + private double skillMultiplier => 24.15; private double strainDecayBase => 0.15; private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); From 212360f67e5c73f1f06e78314fd2d0e95f1b79f3 Mon Sep 17 00:00:00 2001 From: StanR Date: Mon, 4 Jul 2022 19:59:30 +0300 Subject: [PATCH 08/57] Make relax ok/meh nerfs less drastic, add flashlight nerf, remove ar bonus for relax --- .../Difficulty/OsuDifficultyCalculator.cs | 3 +++ .../Difficulty/OsuPerformanceCalculator.cs | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 75d9469da3..7d00f59a9b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -44,7 +44,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1; if (mods.Any(h => h is OsuModRelax)) + { speedRating = 0.0; + flashlightRating *= 0.75; + } double baseAimPerformance = Math.Pow(5 * Math.Max(1, aimRating / 0.0675) - 4, 3) / 100000; double baseSpeedPerformance = Math.Pow(5 * Math.Max(1, speedRating / 0.0675) - 4, 3) / 100000; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 21ff566b00..2cd1d11cbe 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -52,9 +52,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (score.Mods.Any(h => h is OsuModRelax)) { // As we're adding Oks and Mehs to an approximated number of combo breaks the result can be higher than total hits in specific scenarios (which breaks some calculations) so we need to clamp it. - effectiveMissCount = Math.Min(effectiveMissCount + countOk + countMeh, totalHits); + effectiveMissCount = Math.Min(effectiveMissCount + countOk * 0.33 + countMeh * 0.66, totalHits); - multiplier *= 0.6; + multiplier *= 0.7; } double aimValue = computeAimValue(score, osuAttributes); @@ -105,6 +105,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty else if (attributes.ApproachRate < 8.0) approachRateFactor = 0.1 * (8.0 - attributes.ApproachRate); + if (score.Mods.Any(h => h is OsuModRelax)) + approachRateFactor = 0.0; + aimValue *= 1.0 + approachRateFactor * lengthBonus; // Buff for longer maps with high AR. if (score.Mods.Any(m => m is OsuModBlinds)) @@ -134,6 +137,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty private double computeSpeedValue(ScoreInfo score, OsuDifficultyAttributes attributes) { + if (score.Mods.Any(h => h is OsuModRelax)) + return 0.0; + double speedValue = Math.Pow(5.0 * Math.Max(1.0, attributes.SpeedDifficulty / 0.0675) - 4.0, 3.0) / 100000.0; double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) + From db8bb07c782cc09d8ab10a7d525ddd3e5123dbcd Mon Sep 17 00:00:00 2001 From: StanR Date: Mon, 4 Jul 2022 20:10:26 +0300 Subject: [PATCH 09/57] Reduce 50s nerf effect --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 2cd1d11cbe..e879fd233b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -180,7 +180,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty speedValue *= (0.95 + Math.Pow(attributes.OverallDifficulty, 2) / 750) * Math.Pow((accuracy + relevantAccuracy) / 2.0, (14.5 - Math.Max(attributes.OverallDifficulty, 8)) / 2); // Scale the speed value with # of 50s to punish doubletapping. - speedValue *= Math.Pow(0.98, countMeh < totalHits / 500.0 ? 0 : countMeh - totalHits / 500.0); + speedValue *= Math.Pow(0.99, countMeh < totalHits / 500.0 ? 0 : countMeh - totalHits / 500.0); return speedValue; } From 11eb344476e4afa4bd4c71ab5037d2870b17d6a7 Mon Sep 17 00:00:00 2001 From: StanR Date: Mon, 4 Jul 2022 20:28:15 +0300 Subject: [PATCH 10/57] Reduce 50s nerf further --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index e879fd233b..083c7e9b44 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -180,7 +180,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty speedValue *= (0.95 + Math.Pow(attributes.OverallDifficulty, 2) / 750) * Math.Pow((accuracy + relevantAccuracy) / 2.0, (14.5 - Math.Max(attributes.OverallDifficulty, 8)) / 2); // Scale the speed value with # of 50s to punish doubletapping. - speedValue *= Math.Pow(0.99, countMeh < totalHits / 500.0 ? 0 : countMeh - totalHits / 500.0); + speedValue *= Math.Pow(0.995, countMeh < totalHits / 500.0 ? 0 : countMeh - totalHits / 500.0); return speedValue; } From afa3f8cda38c9fef3f27ae61b9b1b7abec467319 Mon Sep 17 00:00:00 2001 From: StanR Date: Mon, 4 Jul 2022 20:53:20 +0300 Subject: [PATCH 11/57] Make relax ok/meh multipliers dependent on OD --- .../Difficulty/OsuPerformanceCalculator.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 083c7e9b44..6f747e4904 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -51,8 +51,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (score.Mods.Any(h => h is OsuModRelax)) { + // https://www.desmos.com/calculator/adhhgjtuyp + double okMultiplier = osuAttributes.OverallDifficulty > 0.0 ? 1 - Math.Pow(osuAttributes.OverallDifficulty / 12.0, 2) : 1.0; + double mehMultiplier = osuAttributes.OverallDifficulty > 0.0 ? 1 - Math.Pow(osuAttributes.OverallDifficulty / 12.0, 6) : 1.0; + // As we're adding Oks and Mehs to an approximated number of combo breaks the result can be higher than total hits in specific scenarios (which breaks some calculations) so we need to clamp it. - effectiveMissCount = Math.Min(effectiveMissCount + countOk * 0.33 + countMeh * 0.66, totalHits); + effectiveMissCount = Math.Min(effectiveMissCount + countOk * okMultiplier + countMeh * mehMultiplier, totalHits); multiplier *= 0.7; } From 6e1d32dc6ddc7f8a037c898974b84f218972ff20 Mon Sep 17 00:00:00 2001 From: StanR Date: Mon, 4 Jul 2022 21:00:11 +0300 Subject: [PATCH 12/57] Update tests --- .../OsuDifficultyCalculatorTest.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index bb593c2fb3..b7fafc0fcd 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -17,18 +17,18 @@ namespace osu.Game.Rulesets.Osu.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; - [TestCase(6.6972307565739273d, 206, "diffcalc-test")] - [TestCase(1.4484754139145539d, 45, "zero-length-sliders")] + [TestCase(6.8043847243906566d, 206, "diffcalc-test")] + [TestCase(1.449091582269485d, 45, "zero-length-sliders")] public void Test(double expectedStarRating, int expectedMaxCombo, string name) => base.Test(expectedStarRating, expectedMaxCombo, name); - [TestCase(8.9382559208689809d, 206, "diffcalc-test")] - [TestCase(1.7548875851757628d, 45, "zero-length-sliders")] + [TestCase(9.0768518847360937d, 206, "diffcalc-test")] + [TestCase(1.7555890739194639d, 45, "zero-length-sliders")] public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name) => Test(expectedStarRating, expectedMaxCombo, name, new OsuModDoubleTime()); - [TestCase(6.6972307218715166d, 239, "diffcalc-test")] - [TestCase(1.4484754139145537d, 54, "zero-length-sliders")] + [TestCase(6.8043847243906566d, 239, "diffcalc-test")] + [TestCase(1.449091582269485d, 54, "zero-length-sliders")] public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name) => Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic()); From bf738aa04fd241eece09ab37a91d6471325ee3c5 Mon Sep 17 00:00:00 2001 From: StanR Date: Mon, 4 Jul 2022 21:49:45 +0300 Subject: [PATCH 13/57] Account for extreme ODs in relax multipliers --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 6f747e4904..f0d4bb5252 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -52,8 +52,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (score.Mods.Any(h => h is OsuModRelax)) { // https://www.desmos.com/calculator/adhhgjtuyp - double okMultiplier = osuAttributes.OverallDifficulty > 0.0 ? 1 - Math.Pow(osuAttributes.OverallDifficulty / 12.0, 2) : 1.0; - double mehMultiplier = osuAttributes.OverallDifficulty > 0.0 ? 1 - Math.Pow(osuAttributes.OverallDifficulty / 12.0, 6) : 1.0; + double okMultiplier = Math.Max(0.0, osuAttributes.OverallDifficulty > 0.0 ? 1 - Math.Pow(osuAttributes.OverallDifficulty / 13.33, 1.8) : 1.0); + double mehMultiplier = Math.Max(0.0, osuAttributes.OverallDifficulty > 0.0 ? 1 - Math.Pow(osuAttributes.OverallDifficulty / 13.33, 5) : 1.0); // As we're adding Oks and Mehs to an approximated number of combo breaks the result can be higher than total hits in specific scenarios (which breaks some calculations) so we need to clamp it. effectiveMissCount = Math.Min(effectiveMissCount + countOk * okMultiplier + countMeh * mehMultiplier, totalHits); From 4f7763794642fb412722b07004e6d02b3ae48092 Mon Sep 17 00:00:00 2001 From: StanR Date: Mon, 4 Jul 2022 21:52:57 +0300 Subject: [PATCH 14/57] Update desmos --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index f0d4bb5252..bf5a6e517a 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (score.Mods.Any(h => h is OsuModRelax)) { - // https://www.desmos.com/calculator/adhhgjtuyp + // https://www.desmos.com/calculator/bc9eybdthb double okMultiplier = Math.Max(0.0, osuAttributes.OverallDifficulty > 0.0 ? 1 - Math.Pow(osuAttributes.OverallDifficulty / 13.33, 1.8) : 1.0); double mehMultiplier = Math.Max(0.0, osuAttributes.OverallDifficulty > 0.0 ? 1 - Math.Pow(osuAttributes.OverallDifficulty / 13.33, 5) : 1.0); From 58c687172b311e248b20629495ac64f5d8f2892f Mon Sep 17 00:00:00 2001 From: StanR Date: Tue, 12 Jul 2022 10:52:44 +0300 Subject: [PATCH 15/57] Reduce low AR bonus --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index bf5a6e517a..1f94defb67 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -52,6 +52,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (score.Mods.Any(h => h is OsuModRelax)) { // https://www.desmos.com/calculator/bc9eybdthb + // we use OD13.3 as maximum since it's the value at which great hitwidow becomes 0 + // this is well beyond currently maximum achievable OD which is 12.17 (DTx2 + DA with OD11) double okMultiplier = Math.Max(0.0, osuAttributes.OverallDifficulty > 0.0 ? 1 - Math.Pow(osuAttributes.OverallDifficulty / 13.33, 1.8) : 1.0); double mehMultiplier = Math.Max(0.0, osuAttributes.OverallDifficulty > 0.0 ? 1 - Math.Pow(osuAttributes.OverallDifficulty / 13.33, 5) : 1.0); @@ -107,7 +109,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (attributes.ApproachRate > 10.33) approachRateFactor = 0.3 * (attributes.ApproachRate - 10.33); else if (attributes.ApproachRate < 8.0) - approachRateFactor = 0.1 * (8.0 - attributes.ApproachRate); + approachRateFactor = 0.05 * (8.0 - attributes.ApproachRate); if (score.Mods.Any(h => h is OsuModRelax)) approachRateFactor = 0.0; From 0983e4f81e7296c419cfb85a0bc6a30586c57b4f Mon Sep 17 00:00:00 2001 From: StanR Date: Tue, 12 Jul 2022 17:57:00 +0300 Subject: [PATCH 16/57] Increase 50s nerf again --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 1f94defb67..7eed5749b6 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -186,7 +186,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty speedValue *= (0.95 + Math.Pow(attributes.OverallDifficulty, 2) / 750) * Math.Pow((accuracy + relevantAccuracy) / 2.0, (14.5 - Math.Max(attributes.OverallDifficulty, 8)) / 2); // Scale the speed value with # of 50s to punish doubletapping. - speedValue *= Math.Pow(0.995, countMeh < totalHits / 500.0 ? 0 : countMeh - totalHits / 500.0); + speedValue *= Math.Pow(0.99, countMeh < totalHits / 500.0 ? 0 : countMeh - totalHits / 500.0); return speedValue; } From 760742e358a3a12d995bdf544baf87871a178df3 Mon Sep 17 00:00:00 2001 From: StanR Date: Thu, 14 Jul 2022 00:42:50 +0300 Subject: [PATCH 17/57] Move relax global multiplier to diffcalc --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 3 ++- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 7d00f59a9b..9748a00b12 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -45,8 +45,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(h => h is OsuModRelax)) { + aimRating *= 0.9; speedRating = 0.0; - flashlightRating *= 0.75; + flashlightRating *= 0.7; } double baseAimPerformance = Math.Pow(5 * Math.Max(1, aimRating / 0.0675) - 4, 3) / 100000; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 7eed5749b6..6eed778561 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -59,8 +59,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty // As we're adding Oks and Mehs to an approximated number of combo breaks the result can be higher than total hits in specific scenarios (which breaks some calculations) so we need to clamp it. effectiveMissCount = Math.Min(effectiveMissCount + countOk * okMultiplier + countMeh * mehMultiplier, totalHits); - - multiplier *= 0.7; } double aimValue = computeAimValue(score, osuAttributes); From a950deb7db021d3c499b3231dd0f270ab94470c4 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Sun, 17 Jul 2022 16:27:55 +1000 Subject: [PATCH 18/57] Re-implement slider changes to FlashlightEvaluator --- .../Evaluators/FlashlightEvaluator.cs | 22 +++++++++++++++++++ .../Difficulty/Skills/Flashlight.cs | 1 - 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs index 3b0826394c..e60695f94f 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs @@ -4,6 +4,7 @@ #nullable disable using System; +using System.Diagnostics; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Objects; @@ -15,6 +16,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators private const double max_opacity_bonus = 0.4; private const double hidden_bonus = 0.2; + private const double min_velocity = 0.5; + private const double slider_multiplier = 1.3; + /// /// Evaluates the difficulty of memorising and hitting an object, based on: /// @@ -73,6 +77,24 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators if (hidden) result *= 1.0 + hidden_bonus; + double sliderBonus = 0.0; + + if (osuCurrent.BaseObject is Slider) + { + Debug.Assert(osuCurrent.TravelTime > 0); + + // Invert the scaling factor to determine the true travel distance independent of circle size. + double pixelTravelDistance = osuCurrent.TravelDistance / scalingFactor; + + // Reward sliders based on velocity. + sliderBonus = Math.Pow(Math.Max(0.0, pixelTravelDistance / osuCurrent.TravelTime - min_velocity), 0.5); + + // Longer sliders require more memorisation. + sliderBonus *= pixelTravelDistance; + } + + result += sliderBonus * slider_multiplier; + return result; } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 0c84ace1c7..84ef109598 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -4,7 +4,6 @@ #nullable disable using System; -using System.Diagnostics; using System.Linq; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; From dae698638ce59aae208cad6d67076d602ce71f87 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Sun, 17 Jul 2022 16:56:05 +1000 Subject: [PATCH 19/57] Add repeat bonus to Flashlight, move repeat multiplier to AimEvaluator --- .../Difficulty/Evaluators/AimEvaluator.cs | 8 +++++++- .../Difficulty/Evaluators/FlashlightEvaluator.cs | 7 +++++++ .../Difficulty/Preprocessing/OsuDifficultyHitObject.cs | 2 -- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs index 0694746cbf..911b7d10fd 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs @@ -4,6 +4,7 @@ #nullable disable using System; +using System.Diagnostics; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Objects; @@ -120,10 +121,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators velocityChangeBonus *= Math.Pow(Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime) / Math.Max(osuCurrObj.StrainTime, osuLastObj.StrainTime), 2); } - if (osuLastObj.TravelTime != 0) + if (osuLastObj.BaseObject is Slider) { + Debug.Assert(osuLastObj.TravelTime > 0); + // Reward sliders based on velocity. sliderBonus = osuLastObj.TravelDistance / osuLastObj.TravelTime; + + Slider osuSlider = (Slider)(osuLastObj.BaseObject); + sliderBonus *= (float)Math.Pow(1 + osuSlider.RepeatCount / 2.5, 1.0 / 2.5); // Bonus for repeat sliders until a better per nested object strain system can be achieved. } // Add in acute angle bonus or wide angle bonus + velocity change bonus, whichever is larger. diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs index e60695f94f..444c2db884 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs @@ -18,6 +18,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators private const double min_velocity = 0.5; private const double slider_multiplier = 1.3; + private const double repeat_bonus = 3.0; /// /// Evaluates the difficulty of memorising and hitting an object, based on: @@ -83,6 +84,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators { Debug.Assert(osuCurrent.TravelTime > 0); + Slider osuSlider = (Slider)(osuCurrent.BaseObject); + // Invert the scaling factor to determine the true travel distance independent of circle size. double pixelTravelDistance = osuCurrent.TravelDistance / scalingFactor; @@ -91,6 +94,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators // Longer sliders require more memorisation. sliderBonus *= pixelTravelDistance; + + // Reward sliders with repeats. + if (osuSlider.RepeatCount > 0) + sliderBonus *= repeat_bonus; } result += sliderBonus * slider_multiplier; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index 1aa085a629..107fae709b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -252,8 +252,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing if (i == slider.NestedHitObjects.Count - 1) slider.LazyEndPosition = currCursorPosition; } - - slider.LazyTravelDistance *= (float)Math.Pow(1 + slider.RepeatCount / 2.5, 1.0 / 2.5); // Bonus for repeat sliders until a better per nested object strain system can be achieved. } private Vector2 getEndCursorPosition(OsuHitObject hitObject) From 68caafa210f4dd234e2acabd4e04b8a6871d1ea6 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Sun, 17 Jul 2022 17:02:30 +1000 Subject: [PATCH 20/57] Update xmldoc --- .../Difficulty/Evaluators/FlashlightEvaluator.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs index 444c2db884..4d9afb4fb2 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs @@ -23,8 +23,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators /// /// Evaluates the difficulty of memorising and hitting an object, based on: /// - /// distance between the previous and current object, + /// distance between previous objects and the current object, /// the visual opacity of the current object, + /// length and speed of the current object (for sliders), /// and whether the hidden mod is enabled. /// /// From 8413c40442cc1bd033b8b403f422caff8e50a47c Mon Sep 17 00:00:00 2001 From: MBmasher Date: Mon, 18 Jul 2022 15:58:09 +1000 Subject: [PATCH 21/57] Remove debug code --- osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs index 911b7d10fd..0b6a9fb59b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs @@ -4,7 +4,6 @@ #nullable disable using System; -using System.Diagnostics; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Objects; @@ -123,8 +122,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators if (osuLastObj.BaseObject is Slider) { - Debug.Assert(osuLastObj.TravelTime > 0); - // Reward sliders based on velocity. sliderBonus = osuLastObj.TravelDistance / osuLastObj.TravelTime; From 204fbde07b38762900ba9a3b95ba4b35d5da3096 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Mon, 18 Jul 2022 15:58:32 +1000 Subject: [PATCH 22/57] Remove debug code --- .../Difficulty/Evaluators/FlashlightEvaluator.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs index 4d9afb4fb2..bd516382d7 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs @@ -4,7 +4,6 @@ #nullable disable using System; -using System.Diagnostics; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Objects; @@ -83,8 +82,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators if (osuCurrent.BaseObject is Slider) { - Debug.Assert(osuCurrent.TravelTime > 0); - Slider osuSlider = (Slider)(osuCurrent.BaseObject); // Invert the scaling factor to determine the true travel distance independent of circle size. From 7c680afc3c1f7f189a6f4731fc1cd29b872f4512 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Mon, 18 Jul 2022 15:59:00 +1000 Subject: [PATCH 23/57] Change initialisation of osuSlider --- osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs | 4 +--- .../Difficulty/Evaluators/FlashlightEvaluator.cs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs index 0b6a9fb59b..79f1f95017 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs @@ -120,12 +120,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators velocityChangeBonus *= Math.Pow(Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime) / Math.Max(osuCurrObj.StrainTime, osuLastObj.StrainTime), 2); } - if (osuLastObj.BaseObject is Slider) + if (osuLastObj.BaseObject is Slider osuSlider) { // Reward sliders based on velocity. sliderBonus = osuLastObj.TravelDistance / osuLastObj.TravelTime; - - Slider osuSlider = (Slider)(osuLastObj.BaseObject); sliderBonus *= (float)Math.Pow(1 + osuSlider.RepeatCount / 2.5, 1.0 / 2.5); // Bonus for repeat sliders until a better per nested object strain system can be achieved. } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs index bd516382d7..217ef2a46f 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs @@ -80,10 +80,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators double sliderBonus = 0.0; - if (osuCurrent.BaseObject is Slider) + if (osuCurrent.BaseObject is Slider osuSlider) { - Slider osuSlider = (Slider)(osuCurrent.BaseObject); - // Invert the scaling factor to determine the true travel distance independent of circle size. double pixelTravelDistance = osuCurrent.TravelDistance / scalingFactor; From 72c096f9af1e83a4fb6c75af3d3fc5c8f462b639 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Mon, 18 Jul 2022 15:59:20 +1000 Subject: [PATCH 24/57] Update xmldoc --- .../Difficulty/Evaluators/FlashlightEvaluator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs index 217ef2a46f..042ce7a1f7 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators /// /// Evaluates the difficulty of memorising and hitting an object, based on: /// - /// distance between previous objects and the current object, + /// distance between a number of previous objects and the current object, /// the visual opacity of the current object, /// length and speed of the current object (for sliders), /// and whether the hidden mod is enabled. From 42b9dc877dff4c64c7b00a999d8e1dae633f5f0d Mon Sep 17 00:00:00 2001 From: MBmasher Date: Mon, 18 Jul 2022 16:14:06 +1000 Subject: [PATCH 25/57] Divide slider bonus by repeat count --- .../Difficulty/Evaluators/FlashlightEvaluator.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs index 042ce7a1f7..cfdcd0af9a 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs @@ -17,7 +17,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators private const double min_velocity = 0.5; private const double slider_multiplier = 1.3; - private const double repeat_bonus = 3.0; /// /// Evaluates the difficulty of memorising and hitting an object, based on: @@ -91,9 +90,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators // Longer sliders require more memorisation. sliderBonus *= pixelTravelDistance; - // Reward sliders with repeats. + // Nerf sliders with repeats, as less memorisation is required. if (osuSlider.RepeatCount > 0) - sliderBonus *= repeat_bonus; + sliderBonus /= (osuSlider.RepeatCount + 1); } result += sliderBonus * slider_multiplier; From 633f6fe62082bfdea5e5be38434e233b6a9d6777 Mon Sep 17 00:00:00 2001 From: StanR Date: Mon, 18 Jul 2022 21:58:11 +0300 Subject: [PATCH 26/57] Increase global multiplier --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 6eed778561..ee5ff3596a 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss); effectiveMissCount = calculateEffectiveMissCount(osuAttributes); - double multiplier = 1.11; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things. + double multiplier = 1.125; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things. if (score.Mods.Any(m => m is OsuModNoFail)) multiplier *= Math.Max(0.90, 1.0 - 0.02 * effectiveMissCount); @@ -276,6 +276,5 @@ namespace osu.Game.Rulesets.Osu.Difficulty private double getComboScalingFactor(OsuDifficultyAttributes attributes) => attributes.MaxCombo <= 0 ? 1.0 : Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(attributes.MaxCombo, 0.8), 1.0); private int totalHits => countGreat + countOk + countMeh + countMiss; - private int totalSuccessfulHits => countGreat + countOk + countMeh; } } From 35e841de95966b96bc78f349ea784207ed9c7646 Mon Sep 17 00:00:00 2001 From: StanR Date: Wed, 20 Jul 2022 15:54:49 +0300 Subject: [PATCH 27/57] Move base performance multiplier to a const --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 2 +- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 9748a00b12..5b91ff3fce 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty Math.Pow(baseFlashlightPerformance, 1.1), 1.0 / 1.1 ); - double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; + double starRating = basePerformance > 0.00001 ? Math.Cbrt(OsuPerformanceCalculator.PERFORMANCE_BASE_MULTIPLIER) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate; double drainRate = beatmap.Difficulty.DrainRate; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index ee5ff3596a..1c19325891 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -15,6 +15,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty { public class OsuPerformanceCalculator : PerformanceCalculator { + public const double PERFORMANCE_BASE_MULTIPLIER = 1.125; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things. + private double accuracy; private int scoreMaxCombo; private int countGreat; @@ -41,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss); effectiveMissCount = calculateEffectiveMissCount(osuAttributes); - double multiplier = 1.125; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things. + double multiplier = PERFORMANCE_BASE_MULTIPLIER; if (score.Mods.Any(m => m is OsuModNoFail)) multiplier *= Math.Max(0.90, 1.0 - 0.02 * effectiveMissCount); From 163c3f9c2d821a3b74c18b3df9e92e77f826e66a Mon Sep 17 00:00:00 2001 From: StanR Date: Wed, 20 Jul 2022 16:10:34 +0300 Subject: [PATCH 28/57] Adjust multipliers to account for speed changes --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 1c19325891..3c82c2dc33 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty { public class OsuPerformanceCalculator : PerformanceCalculator { - public const double PERFORMANCE_BASE_MULTIPLIER = 1.125; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things. + public const double PERFORMANCE_BASE_MULTIPLIER = 1.14; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things. private double accuracy; private int scoreMaxCombo; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index e4f61b65cd..38e0e5b677 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private double currentStrain; - private double skillMultiplier => 24.15; + private double skillMultiplier => 23.55; private double strainDecayBase => 0.15; private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); From f44a5def90d8a52f737fc5a6b33af45380f8e39f Mon Sep 17 00:00:00 2001 From: MBmasher Date: Sat, 23 Jul 2022 14:40:16 +1000 Subject: [PATCH 29/57] Move repeat bonus to TravelDistance --- osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs | 1 - .../Difficulty/Evaluators/FlashlightEvaluator.cs | 2 +- .../Difficulty/Preprocessing/OsuDifficultyHitObject.cs | 3 ++- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs index 79f1f95017..fac7cd549a 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs @@ -124,7 +124,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators { // Reward sliders based on velocity. sliderBonus = osuLastObj.TravelDistance / osuLastObj.TravelTime; - sliderBonus *= (float)Math.Pow(1 + osuSlider.RepeatCount / 2.5, 1.0 / 2.5); // Bonus for repeat sliders until a better per nested object strain system can be achieved. } // Add in acute angle bonus or wide angle bonus + velocity change bonus, whichever is larger. diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs index cfdcd0af9a..fcf4179a3b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs @@ -82,7 +82,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators if (osuCurrent.BaseObject is Slider osuSlider) { // Invert the scaling factor to determine the true travel distance independent of circle size. - double pixelTravelDistance = osuCurrent.TravelDistance / scalingFactor; + double pixelTravelDistance = osuSlider.LazyTravelDistance / scalingFactor; // Reward sliders based on velocity. sliderBonus = Math.Pow(Math.Max(0.0, pixelTravelDistance / osuCurrent.TravelTime - min_velocity), 0.5); diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index 107fae709b..c7c5650184 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -127,7 +127,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing if (BaseObject is Slider currentSlider) { computeSliderCursorPosition(currentSlider); - TravelDistance = currentSlider.LazyTravelDistance; + // Bonus for repeat sliders until a better per nested object strain system can be achieved. + TravelDistance = currentSlider.LazyTravelDistance * (float)Math.Pow(1 + currentSlider.RepeatCount / 2.5, 1.0 / 2.5); TravelTime = Math.Max(currentSlider.LazyTravelTime / clockRate, min_delta_time); } From 267d55a6a8f46598c4d7b016129459a92d09160e Mon Sep 17 00:00:00 2001 From: MBmasher Date: Sat, 23 Jul 2022 14:48:39 +1000 Subject: [PATCH 30/57] Remove osuSlider from statement --- osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs index 9c832a04fc..b2d6aaf83c 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs @@ -114,7 +114,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators velocityChangeBonus *= Math.Pow(Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime) / Math.Max(osuCurrObj.StrainTime, osuLastObj.StrainTime), 2); } - if (osuLastObj.BaseObject is Slider osuSlider) + if (osuLastObj.BaseObject is Slider) { // Reward sliders based on velocity. sliderBonus = osuLastObj.TravelDistance / osuLastObj.TravelTime; From f65b7ef058b4ad3a20f9df593fcd4fb428ac4660 Mon Sep 17 00:00:00 2001 From: Andrew Hong Date: Tue, 9 Aug 2022 02:49:53 -0400 Subject: [PATCH 31/57] Add keybind for showing profile --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 4 ++++ osu.Game/Localisation/GlobalActionKeyBindingStrings.cs | 5 +++++ osu.Game/OsuGame.cs | 4 ++++ osu.Game/Overlays/Toolbar/ToolbarUserButton.cs | 1 + 4 files changed, 14 insertions(+) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 1ee03a6964..d7a3bbba55 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -52,6 +52,7 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings), new KeyBinding(new[] { InputKey.Control, InputKey.D }, GlobalAction.ToggleBeatmapListing), new KeyBinding(new[] { InputKey.Control, InputKey.N }, GlobalAction.ToggleNotifications), + new KeyBinding(new[] { InputKey.Control, InputKey.P }, GlobalAction.ShowProfile), new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.S }, GlobalAction.ToggleSkinEditor), new KeyBinding(InputKey.Escape, GlobalAction.Back), @@ -232,6 +233,9 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleNotifications))] ToggleNotifications, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ShowProfile))] + ShowProfile, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.PauseGameplay))] PauseGameplay, diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index de1a5b189c..9089834c30 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -149,6 +149,11 @@ namespace osu.Game.Localisation /// public static LocalisableString ToggleNotifications => new TranslatableString(getKey(@"toggle_notifications"), @"Toggle notifications"); + /// + /// "Show Profile" + /// + public static LocalisableString ShowProfile => new TranslatableString(getKey(@"toggle_notifications"), @"Show Profile"); + /// /// "Pause gameplay" /// diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 78cc4d7f70..9c160d39fa 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1138,6 +1138,10 @@ namespace osu.Game mouseDisableButtons.Value = !mouseDisableButtons.Value; return true; + case GlobalAction.ShowProfile: + ShowUser(new APIUser { Id = API.LocalUser.Value.Id }); + return true; + case GlobalAction.RandomSkin: // Don't allow random skin selection while in the skin editor. // This is mainly to stop many "osu! default (modified)" skins being created via the SkinManager.EnsureMutableSkin() path. diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs index a93ba17c5b..ef8157394b 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs @@ -15,6 +15,7 @@ using osu.Game.Resources.Localisation.Web; using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; +using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { From 3473347f35335877eb8c7ecbb4c134505d885302 Mon Sep 17 00:00:00 2001 From: Andrew Hong Date: Tue, 9 Aug 2022 02:56:12 -0400 Subject: [PATCH 32/57] Lowercase "p" --- osu.Game/Localisation/GlobalActionKeyBindingStrings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index 9089834c30..e28ec4eb09 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -150,9 +150,9 @@ namespace osu.Game.Localisation public static LocalisableString ToggleNotifications => new TranslatableString(getKey(@"toggle_notifications"), @"Toggle notifications"); /// - /// "Show Profile" + /// "Show profile" /// - public static LocalisableString ShowProfile => new TranslatableString(getKey(@"toggle_notifications"), @"Show Profile"); + public static LocalisableString ShowProfile => new TranslatableString(getKey(@"toggle_notifications"), @"Show profile"); /// /// "Pause gameplay" From ededaed5ef3dad1798063ed698efda25cf4b35b1 Mon Sep 17 00:00:00 2001 From: Andrew Hong Date: Tue, 9 Aug 2022 02:58:28 -0400 Subject: [PATCH 33/57] Remove unused import --- osu.Game/Overlays/Toolbar/ToolbarUserButton.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs index ef8157394b..a93ba17c5b 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs @@ -15,7 +15,6 @@ using osu.Game.Resources.Localisation.Web; using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; -using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { From 04108a749e9703be3349fb3c278513854be390f2 Mon Sep 17 00:00:00 2001 From: Andrew Hong Date: Tue, 9 Aug 2022 03:03:14 -0400 Subject: [PATCH 34/57] Rename translation key --- osu.Game/Localisation/GlobalActionKeyBindingStrings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index e28ec4eb09..0624a5b34b 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -152,7 +152,7 @@ namespace osu.Game.Localisation /// /// "Show profile" /// - public static LocalisableString ShowProfile => new TranslatableString(getKey(@"toggle_notifications"), @"Show profile"); + public static LocalisableString ShowProfile => new TranslatableString(getKey(@"show_profile"), @"Show profile"); /// /// "Pause gameplay" From a705c4f5d2ebfdf4d2f79758500b1ddc13e40d7e Mon Sep 17 00:00:00 2001 From: Andrew Hong Date: Tue, 9 Aug 2022 03:17:55 -0400 Subject: [PATCH 35/57] Moved ShowProfile to the bottom of the enum --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index d7a3bbba55..4fc85b135f 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -233,9 +233,6 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleNotifications))] ToggleNotifications, - [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ShowProfile))] - ShowProfile, - [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.PauseGameplay))] PauseGameplay, @@ -336,5 +333,8 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleFPSCounter))] ToggleFPSDisplay, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ShowProfile))] + ShowProfile, } } From aee18135a98be2610af30321d238328d04c1b59f Mon Sep 17 00:00:00 2001 From: Andrew Hong Date: Tue, 9 Aug 2022 04:09:22 -0400 Subject: [PATCH 36/57] Switch to toggle --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 6 +++--- osu.Game/Localisation/GlobalActionKeyBindingStrings.cs | 4 ++-- osu.Game/OsuGame.cs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 4fc85b135f..8b69e22324 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -52,7 +52,7 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings), new KeyBinding(new[] { InputKey.Control, InputKey.D }, GlobalAction.ToggleBeatmapListing), new KeyBinding(new[] { InputKey.Control, InputKey.N }, GlobalAction.ToggleNotifications), - new KeyBinding(new[] { InputKey.Control, InputKey.P }, GlobalAction.ShowProfile), + new KeyBinding(new[] { InputKey.Control, InputKey.P }, GlobalAction.ToggleProfile), new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.S }, GlobalAction.ToggleSkinEditor), new KeyBinding(InputKey.Escape, GlobalAction.Back), @@ -334,7 +334,7 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleFPSCounter))] ToggleFPSDisplay, - [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ShowProfile))] - ShowProfile, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleProfile))] + ToggleProfile, } } diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index 0624a5b34b..172818c1c0 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -150,9 +150,9 @@ namespace osu.Game.Localisation public static LocalisableString ToggleNotifications => new TranslatableString(getKey(@"toggle_notifications"), @"Toggle notifications"); /// - /// "Show profile" + /// "Toggle profile" /// - public static LocalisableString ShowProfile => new TranslatableString(getKey(@"show_profile"), @"Show profile"); + public static LocalisableString ToggleProfile => new TranslatableString(getKey(@"toggle_profile"), @"Toggle profile"); /// /// "Pause gameplay" diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 9c160d39fa..f0d61ed07a 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1138,7 +1138,7 @@ namespace osu.Game mouseDisableButtons.Value = !mouseDisableButtons.Value; return true; - case GlobalAction.ShowProfile: + case GlobalAction.ToggleProfile: ShowUser(new APIUser { Id = API.LocalUser.Value.Id }); return true; From 7ed489b56d47c7e96758f9a17fdb02e300443724 Mon Sep 17 00:00:00 2001 From: Andrew Hong Date: Tue, 9 Aug 2022 14:10:38 -0400 Subject: [PATCH 37/57] Add hotkey to Toolbar --- osu.Game/OsuGame.cs | 4 ---- osu.Game/Overlays/Toolbar/ToolbarUserButton.cs | 3 +++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index f0d61ed07a..78cc4d7f70 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1138,10 +1138,6 @@ namespace osu.Game mouseDisableButtons.Value = !mouseDisableButtons.Value; return true; - case GlobalAction.ToggleProfile: - ShowUser(new APIUser { Id = API.LocalUser.Value.Id }); - return true; - case GlobalAction.RandomSkin: // Don't allow random skin selection while in the skin editor. // This is mainly to stop many "osu! default (modified)" skins being created via the SkinManager.EnsureMutableSkin() path. diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs index 4ebd19a1f7..35f72f0ff8 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs @@ -15,6 +15,7 @@ using osu.Game.Localisation; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Users.Drawables; +using osu.Game.Input.Bindings; using osuTK; using osuTK.Graphics; @@ -34,6 +35,8 @@ namespace osu.Game.Overlays.Toolbar public ToolbarUserButton() { + Hotkey = GlobalAction.ToggleProfile; + AutoSizeAxes = Axes.X; } From 8c7ede611162bfb64faf86927e7390812bfacf8c Mon Sep 17 00:00:00 2001 From: Andrew Hong Date: Tue, 9 Aug 2022 14:43:37 -0400 Subject: [PATCH 38/57] Add proper toggling --- osu.Game/OsuGame.cs | 7 +++++++ osu.Game/Overlays/Toolbar/ToolbarUserButton.cs | 3 --- osu.Game/Overlays/UserProfileOverlay.cs | 13 +++++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 78cc4d7f70..b89a0dc843 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1138,6 +1138,13 @@ namespace osu.Game mouseDisableButtons.Value = !mouseDisableButtons.Value; return true; + case GlobalAction.ToggleProfile: + if (userProfile.State.Value == Visibility.Visible) + return false; + + ShowUser(new APIUser { Id = API.LocalUser.Value.Id }); + return true; + case GlobalAction.RandomSkin: // Don't allow random skin selection while in the skin editor. // This is mainly to stop many "osu! default (modified)" skins being created via the SkinManager.EnsureMutableSkin() path. diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs index 35f72f0ff8..4ebd19a1f7 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs @@ -15,7 +15,6 @@ using osu.Game.Localisation; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Users.Drawables; -using osu.Game.Input.Bindings; using osuTK; using osuTK.Graphics; @@ -35,8 +34,6 @@ namespace osu.Game.Overlays.Toolbar public ToolbarUserButton() { - Hotkey = GlobalAction.ToggleProfile; - AutoSizeAxes = Axes.X; } diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index c0ca63bbd9..fb119b92f2 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -19,6 +19,7 @@ using osu.Game.Overlays.Profile.Sections; using osu.Game.Users; using osuTK; using osuTK.Graphics; +using osu.Game.Input.Bindings; namespace osu.Game.Overlays { @@ -41,6 +42,18 @@ namespace osu.Game.Overlays protected override Color4 BackgroundColour => ColourProvider.Background6; + public override bool OnPressed(KeyBindingPressEvent e) + { + switch (e.Action) + { + case GlobalAction.ToggleProfile: + Hide(); + return true; + } + + return base.OnPressed(e); + } + public void ShowUser(IUser user) { if (user.OnlineID == APIUser.SYSTEM_USER_ID) From 396860d9e8f4a804fd7a72304f88dbf5de153e8d Mon Sep 17 00:00:00 2001 From: Andrew Hong Date: Wed, 10 Aug 2022 13:31:58 -0400 Subject: [PATCH 39/57] Move Hide() to OsuGame --- osu.Game/OsuGame.cs | 12 +++++++++--- osu.Game/Overlays/UserProfileOverlay.cs | 13 ------------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b89a0dc843..ca0ce916a2 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1140,10 +1140,16 @@ namespace osu.Game case GlobalAction.ToggleProfile: if (userProfile.State.Value == Visibility.Visible) - return false; + { + userProfile.Hide(); + return true; + } - ShowUser(new APIUser { Id = API.LocalUser.Value.Id }); - return true; + else + { + ShowUser(new APIUser { Id = API.LocalUser.Value.Id }); + return true; + } case GlobalAction.RandomSkin: // Don't allow random skin selection while in the skin editor. diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index fb119b92f2..c0ca63bbd9 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -19,7 +19,6 @@ using osu.Game.Overlays.Profile.Sections; using osu.Game.Users; using osuTK; using osuTK.Graphics; -using osu.Game.Input.Bindings; namespace osu.Game.Overlays { @@ -42,18 +41,6 @@ namespace osu.Game.Overlays protected override Color4 BackgroundColour => ColourProvider.Background6; - public override bool OnPressed(KeyBindingPressEvent e) - { - switch (e.Action) - { - case GlobalAction.ToggleProfile: - Hide(); - return true; - } - - return base.OnPressed(e); - } - public void ShowUser(IUser user) { if (user.OnlineID == APIUser.SYSTEM_USER_ID) From 60abe8339801b2ddfd70d282f3d6e9dd02e403ee Mon Sep 17 00:00:00 2001 From: Andrew Hong Date: Wed, 10 Aug 2022 17:56:36 -0400 Subject: [PATCH 40/57] Remove newline --- osu.Game/OsuGame.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index ca0ce916a2..073a99e406 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1144,7 +1144,6 @@ namespace osu.Game userProfile.Hide(); return true; } - else { ShowUser(new APIUser { Id = API.LocalUser.Value.Id }); From e5b534bb26b748cdfe1f756e80cb0fcc66f2367d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 11 Aug 2022 12:44:58 +0900 Subject: [PATCH 41/57] Add thread safety to `APIAccess.LocalUser` --- osu.Game/Online/API/APIAccess.cs | 16 +++++++++------- osu.Game/Online/API/IAPIProvider.cs | 3 --- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 42133160ca..66bd22df52 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -142,10 +142,10 @@ namespace osu.Game.Online.API // Show a placeholder user if saved credentials are available. // This is useful for storing local scores and showing a placeholder username after starting the game, // until a valid connection has been established. - localUser.Value = new APIUser + setLocalUser(new APIUser { Username = ProvidedUsername, - }; + }); } // save the username at this point, if the user requested for it to be. @@ -188,12 +188,12 @@ namespace osu.Game.Online.API else failConnectionProcess(); }; - userReq.Success += u => + userReq.Success += user => { - localUser.Value = u; - // todo: save/pull from settings - localUser.Value.Status.Value = new UserStatusOnline(); + user.Status.Value = new UserStatusOnline(); + + setLocalUser(user); failureCount = 0; }; @@ -453,7 +453,7 @@ namespace osu.Game.Online.API // Scheduled prior to state change such that the state changed event is invoked with the correct user and their friends present Schedule(() => { - localUser.Value = createGuestUser(); + setLocalUser(createGuestUser()); friends.Clear(); }); @@ -463,6 +463,8 @@ namespace osu.Game.Online.API private static APIUser createGuestUser() => new GuestUser(); + private void setLocalUser(APIUser user) => Scheduler.Add(() => localUser.Value = user, false); + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); diff --git a/osu.Game/Online/API/IAPIProvider.cs b/osu.Game/Online/API/IAPIProvider.cs index 812aa7f09f..a90b11e354 100644 --- a/osu.Game/Online/API/IAPIProvider.cs +++ b/osu.Game/Online/API/IAPIProvider.cs @@ -13,19 +13,16 @@ namespace osu.Game.Online.API { /// /// The local user. - /// This is not thread-safe and should be scheduled locally if consumed from a drawable component. /// IBindable LocalUser { get; } /// /// The user's friends. - /// This is not thread-safe and should be scheduled locally if consumed from a drawable component. /// IBindableList Friends { get; } /// /// The current user's activity. - /// This is not thread-safe and should be scheduled locally if consumed from a drawable component. /// IBindable Activity { get; } From e01383b138f33407a4efc04c6fbcb8a7d6082c91 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 11 Aug 2022 13:17:14 +0900 Subject: [PATCH 42/57] Tidy up user passing logic --- osu.Game/OsuGame.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 073a99e406..f7747c5d64 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1140,15 +1140,10 @@ namespace osu.Game case GlobalAction.ToggleProfile: if (userProfile.State.Value == Visibility.Visible) - { userProfile.Hide(); - return true; - } else - { - ShowUser(new APIUser { Id = API.LocalUser.Value.Id }); - return true; - } + ShowUser(API.LocalUser.Value); + return true; case GlobalAction.RandomSkin: // Don't allow random skin selection while in the skin editor. From 7ec67c28b90d8bd0d197026f8fdf6a571d00c6a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 11 Aug 2022 14:35:55 +0900 Subject: [PATCH 43/57] Set `Online` state sooner in connection process This isn't really required as such, but feels more correct. There was no reason for it to wait for the friend population to complete before deeming things to be "online". --- osu.Game/Online/API/APIAccess.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 66bd22df52..f353c48f03 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -195,6 +195,8 @@ namespace osu.Game.Online.API setLocalUser(user); + //we're connected! + state.Value = APIState.Online; failureCount = 0; }; @@ -208,13 +210,7 @@ namespace osu.Game.Online.API var friendsReq = new GetFriendsRequest(); friendsReq.Failure += _ => failConnectionProcess(); - friendsReq.Success += res => - { - friends.AddRange(res); - - //we're connected! - state.Value = APIState.Online; - }; + friendsReq.Success += res => friends.AddRange(res); if (!handleRequest(friendsReq)) { From 47196b19a5e4a025c073d9abb11e17ce63b4ee83 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 11 Aug 2022 14:36:28 +0900 Subject: [PATCH 44/57] Stop setting `Online` state in `handleRequest` This is already handled amicably by the `Failing` -> `Connecting` flow. Having this set in `handleRequest` throws things off, potentially leading to the `Online` state change before the user has been populated. --- osu.Game/Online/API/APIAccess.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index f353c48f03..72c88b1e92 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -334,8 +334,7 @@ namespace osu.Game.Online.API if (req.CompletionState != APIRequestCompletionState.Completed) return false; - // we could still be in initialisation, at which point we don't want to say we're Online yet. - if (IsLoggedIn) state.Value = APIState.Online; + // Reset failure count if this request succeeded. failureCount = 0; return true; } From 865d63f7681b6b2eb10f4e8ef5234f576e86942c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 11 Aug 2022 15:43:39 +0900 Subject: [PATCH 45/57] Refactor `APIAccess` main loop to read better --- osu.Game/Online/API/APIAccess.cs | 273 ++++++++++++++++--------------- 1 file changed, 143 insertions(+), 130 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 72c88b1e92..a0c8e0d555 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -104,127 +104,39 @@ namespace osu.Game.Online.API /// private int failureCount; + /// + /// The main API thread loop, which will continue to run until the game is shut down. + /// private void run() { while (!cancellationToken.IsCancellationRequested) { - switch (State.Value) + if (state.Value == APIState.Failing) { - case APIState.Failing: - //todo: replace this with a ping request. - log.Add(@"In a failing state, waiting a bit before we try again..."); - Thread.Sleep(5000); + // To recover from a failing state, falling through and running the full reconnection process seems safest for now. + // This could probably be replaced with a ping-style request if we want to avoid the reconnection overheads. + log.Add($@"{nameof(APIAccess)} is in a failing state, waiting a bit before we try again..."); + Thread.Sleep(5000); + } - if (!IsLoggedIn) goto case APIState.Connecting; + // Ensure that we have valid credentials. + // If not, setting the offline state will allow the game to prompt the user to provide new credentials. + if (!HasLogin) + { + state.Value = APIState.Offline; + Thread.Sleep(50); + continue; + } - if (queue.Count == 0) - { - log.Add(@"Queueing a ping request"); - Queue(new GetUserRequest()); - } + Debug.Assert(HasLogin); - break; + // Ensure that we are in an online state. If not, attempt a connect. + if (state.Value != APIState.Online) + { + attemptConnect(); - case APIState.Offline: - case APIState.Connecting: - // work to restore a connection... - if (!HasLogin) - { - state.Value = APIState.Offline; - Thread.Sleep(50); - continue; - } - - state.Value = APIState.Connecting; - - if (localUser.IsDefault) - { - // Show a placeholder user if saved credentials are available. - // This is useful for storing local scores and showing a placeholder username after starting the game, - // until a valid connection has been established. - setLocalUser(new APIUser - { - Username = ProvidedUsername, - }); - } - - // save the username at this point, if the user requested for it to be. - config.SetValue(OsuSetting.Username, config.Get(OsuSetting.SaveUsername) ? ProvidedUsername : string.Empty); - - if (!authentication.HasValidAccessToken) - { - LastLoginError = null; - - try - { - authentication.AuthenticateWithLogin(ProvidedUsername, password); - } - catch (Exception e) - { - //todo: this fails even on network-related issues. we should probably handle those differently. - LastLoginError = e; - log.Add(@"Login failed!"); - password = null; - authentication.Clear(); - continue; - } - } - - var userReq = new GetUserRequest(); - - userReq.Failure += ex => - { - if (ex is APIException) - { - LastLoginError = ex; - log.Add("Login failed on local user retrieval!"); - Logout(); - } - else if (ex is WebException webException && webException.Message == @"Unauthorized") - { - log.Add(@"Login no longer valid"); - Logout(); - } - else - failConnectionProcess(); - }; - userReq.Success += user => - { - // todo: save/pull from settings - user.Status.Value = new UserStatusOnline(); - - setLocalUser(user); - - //we're connected! - state.Value = APIState.Online; - failureCount = 0; - }; - - if (!handleRequest(userReq)) - { - failConnectionProcess(); - continue; - } - - // getting user's friends is considered part of the connection process. - var friendsReq = new GetFriendsRequest(); - - friendsReq.Failure += _ => failConnectionProcess(); - friendsReq.Success += res => friends.AddRange(res); - - if (!handleRequest(friendsReq)) - { - failConnectionProcess(); - continue; - } - - // The Success callback event is fired on the main thread, so we should wait for that to run before proceeding. - // Without this, we will end up circulating this Connecting loop multiple times and queueing up many web requests - // before actually going online. - while (State.Value > APIState.Offline && State.Value < APIState.Online) - Thread.Sleep(500); - - break; + if (state.Value != APIState.Online) + continue; } // hard bail if we can't get a valid access token. @@ -234,31 +146,132 @@ namespace osu.Game.Online.API continue; } - while (true) - { - APIRequest req; - - lock (queue) - { - if (queue.Count == 0) break; - - req = queue.Dequeue(); - } - - handleRequest(req); - } - + processQueuedRequests(); Thread.Sleep(50); } + } - void failConnectionProcess() + /// + /// Dequeue from the queue and run each request synchronously until the queue is empty. + /// + private void processQueuedRequests() + { + while (true) { - // if something went wrong during the connection process, we want to reset the state (but only if still connecting). - if (State.Value == APIState.Connecting) - state.Value = APIState.Failing; + APIRequest req; + + lock (queue) + { + if (queue.Count == 0) return; + + req = queue.Dequeue(); + } + + handleRequest(req); } } + /// + /// From a non-connected state, perform a full connection flow, obtaining OAuth tokens and populating the local user and friends. + /// + /// + /// This method takes control of and transitions from to either + /// - (successful connection) + /// - (failed connection but retrying) + /// - (failed and can't retry, clear credentials and require user interaction) + /// + /// Whether the connection attempt was successful. + private void attemptConnect() + { + state.Value = APIState.Connecting; + + if (localUser.IsDefault) + { + // Show a placeholder user if saved credentials are available. + // This is useful for storing local scores and showing a placeholder username after starting the game, + // until a valid connection has been established. + setLocalUser(new APIUser + { + Username = ProvidedUsername, + }); + } + + // save the username at this point, if the user requested for it to be. + config.SetValue(OsuSetting.Username, config.Get(OsuSetting.SaveUsername) ? ProvidedUsername : string.Empty); + + if (!authentication.HasValidAccessToken) + { + LastLoginError = null; + + try + { + authentication.AuthenticateWithLogin(ProvidedUsername, password); + } + catch (Exception e) + { + //todo: this fails even on network-related issues. we should probably handle those differently. + LastLoginError = e; + log.Add($@"Login failed for username {ProvidedUsername} ({LastLoginError.Message})!"); + + Logout(); + return; + } + } + + var userReq = new GetUserRequest(); + userReq.Failure += ex => + { + if (ex is APIException) + { + LastLoginError = ex; + log.Add($@"Login failed for username {ProvidedUsername} on user retrieval ({LastLoginError.Message})!"); + Logout(); + } + else if (ex is WebException webException && webException.Message == @"Unauthorized") + { + log.Add(@"Login no longer valid"); + Logout(); + } + else + { + state.Value = APIState.Failing; + } + }; + userReq.Success += user => + { + // todo: save/pull from settings + user.Status.Value = new UserStatusOnline(); + + setLocalUser(user); + + // we're connected! + state.Value = APIState.Online; + failureCount = 0; + }; + + if (!handleRequest(userReq)) + { + state.Value = APIState.Failing; + return; + } + + var friendsReq = new GetFriendsRequest(); + friendsReq.Failure += _ => state.Value = APIState.Failing; + friendsReq.Success += res => friends.AddRange(res); + + if (!handleRequest(friendsReq)) + { + state.Value = APIState.Failing; + return; + } + + // The Success callback event is fired on the main thread, so we should wait for that to run before proceeding. + // Without this, we will end up circulating this Connecting loop multiple times and queueing up many web requests + // before actually going online. + while (State.Value == APIState.Connecting && !cancellationToken.IsCancellationRequested) + Thread.Sleep(500); + } + public void Perform(APIRequest request) { try From 9f28c4c033264d8b7a8701c5b326a73abf65125b Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 11 Aug 2022 19:47:32 +0900 Subject: [PATCH 46/57] Adjust test values --- .../OsuDifficultyCalculatorTest.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index 46f7c461f8..7e995f2dde 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -17,18 +17,18 @@ namespace osu.Game.Rulesets.Osu.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; - [TestCase(6.6369583000323935d, 206, "diffcalc-test")] - [TestCase(1.4476531024675374d, 45, "zero-length-sliders")] + [TestCase(6.7115569159190587d, 206, "diffcalc-test")] + [TestCase(1.4391311903612753d, 45, "zero-length-sliders")] public void Test(double expectedStarRating, int expectedMaxCombo, string name) => base.Test(expectedStarRating, expectedMaxCombo, name); - [TestCase(8.8816128335486386d, 206, "diffcalc-test")] - [TestCase(1.7540389962596916d, 45, "zero-length-sliders")] + [TestCase(8.9757300665532966d, 206, "diffcalc-test")] + [TestCase(1.7437232654020756d, 45, "zero-length-sliders")] public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name) => Test(expectedStarRating, expectedMaxCombo, name, new OsuModDoubleTime()); - [TestCase(6.6369583000323935d, 239, "diffcalc-test")] - [TestCase(1.4476531024675374d, 54, "zero-length-sliders")] + [TestCase(6.7115569159190587d, 239, "diffcalc-test")] + [TestCase(1.4391311903612753d, 54, "zero-length-sliders")] public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name) => Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic()); From d88f247594374bb74be92bc9ac1c1eaba7569280 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 11 Aug 2022 20:38:08 +0900 Subject: [PATCH 47/57] Fix possible null reference inspection --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 8a358e24d0..ded5b978ba 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -17,7 +17,7 @@ namespace osu.Game.Input.Bindings private InputManager? parentInputManager; - public GlobalActionContainer(OsuGameBase game) + public GlobalActionContainer(OsuGameBase? game) : base(matchingMode: KeyCombinationMatchingMode.Modifiers) { if (game is IKeyBindingHandler) From ac4213ecee5c2d2e22c8b7b0d0fbec1cc295443d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Aug 2022 12:30:11 +0900 Subject: [PATCH 48/57] Adjust relax mod multiplayer to 0.5x Has previously been discussed internally. Probably good to get this out before the next full reprocess of scores server-side. The multiplier here was @smoogipoo's suggested value. I'd be willing to go lower if this is seen at too high, but it should be a round number to make it easy for users to understand the max score available to them. --- osu.Game/Rulesets/Mods/ModRelax.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModRelax.cs b/osu.Game/Rulesets/Mods/ModRelax.cs index e5995ff180..a79c69b416 100644 --- a/osu.Game/Rulesets/Mods/ModRelax.cs +++ b/osu.Game/Rulesets/Mods/ModRelax.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mods public override string Acronym => "RX"; public override IconUsage? Icon => OsuIcon.ModRelax; public override ModType Type => ModType.Automation; - public override double ScoreMultiplier => 1; + public override double ScoreMultiplier => 0.5; public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModNoFail), typeof(ModFailCondition) }; } } From 38afc53bad96157459e66ee0920b8120e6bf375d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Aug 2022 13:40:29 +0900 Subject: [PATCH 49/57] Update interactive visual test runs to use development directory --- osu.Game/Tests/VisualTestRunner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Tests/VisualTestRunner.cs b/osu.Game/Tests/VisualTestRunner.cs index bd98482768..c8279b9e3c 100644 --- a/osu.Game/Tests/VisualTestRunner.cs +++ b/osu.Game/Tests/VisualTestRunner.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests [STAThread] public static int Main(string[] args) { - using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu", new HostOptions { BindIPC = true, })) + using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu-development", new HostOptions { BindIPC = true, })) { host.Run(new OsuTestBrowser()); return 0; From 5111bad86ce647bc34c2873b7d9407a73323a477 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Aug 2022 14:15:12 +0900 Subject: [PATCH 50/57] Refactor `TestScenePlaylistOverlay` to use realm for testing Removes the dual-purpose flow which existed only for testing. --- .../UserInterface/TestScenePlaylistOverlay.cs | 48 ++++++++++++------- osu.Game/Overlays/Music/PlaylistOverlay.cs | 6 +-- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePlaylistOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePlaylistOverlay.cs index ee5ef2f364..652afa80be 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePlaylistOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePlaylistOverlay.cs @@ -1,18 +1,20 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - +using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Bindables; +using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Collections; using osu.Game.Database; -using osu.Game.Graphics.Containers; using osu.Game.Overlays.Music; +using osu.Game.Rulesets; using osu.Game.Tests.Resources; using osuTK; using osuTK.Input; @@ -21,13 +23,27 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestScenePlaylistOverlay : OsuManualInputManagerTestScene { - private readonly BindableList> beatmapSets = new BindableList>(); + protected override bool UseFreshStoragePerRun => true; - private PlaylistOverlay playlistOverlay; + private PlaylistOverlay playlistOverlay = null!; - private Live first; + private Live first = null!; - private const int item_count = 100; + private BeatmapManager beatmapManager = null!; + + private const int item_count = 20; + + private List beatmapSets => beatmapManager.GetAllUsableBeatmapSets(); + + [BackgroundDependencyLoader] + private void load(GameHost host) + { + Dependencies.Cache(new RealmRulesetStore(Realm)); + Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, null, Audio, Resources, host, Beatmap.Default)); + Dependencies.Cache(Realm); + + beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); + } [SetUp] public void Setup() => Schedule(() => @@ -46,16 +62,12 @@ namespace osu.Game.Tests.Visual.UserInterface } }; - beatmapSets.Clear(); - for (int i = 0; i < item_count; i++) { - beatmapSets.Add(TestResources.CreateTestBeatmapSetInfo().ToLiveUnmanaged()); + beatmapManager.Import(TestResources.CreateTestBeatmapSetInfo()); } - first = beatmapSets.First(); - - playlistOverlay.BeatmapSets.BindTo(beatmapSets); + first = beatmapSets.First().ToLive(Realm); }); [Test] @@ -70,9 +82,13 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("wait for animations to complete", () => !playlistOverlay.Transforms.Any()); + PlaylistItem firstItem = null!; + AddStep("hold 1st item handle", () => { - var handle = this.ChildrenOfType>.PlaylistItemHandle>().First(); + firstItem = this.ChildrenOfType().First(); + var handle = firstItem.ChildrenOfType().First(); + InputManager.MoveMouseTo(handle.ScreenSpaceDrawQuad.Centre); InputManager.PressButton(MouseButton.Left); }); @@ -83,7 +99,7 @@ namespace osu.Game.Tests.Visual.UserInterface InputManager.MoveMouseTo(item.ScreenSpaceDrawQuad.BottomLeft); }); - AddAssert("song 1 is 5th", () => beatmapSets[4].Equals(first)); + AddAssert("first is moved", () => playlistOverlay.ChildrenOfType().Single().Items.ElementAt(4).Value.Equals(firstItem.Model.Value)); AddStep("release handle", () => InputManager.ReleaseButton(MouseButton.Left)); } diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index e33fc8064f..9fe2fd5279 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -26,8 +26,6 @@ namespace osu.Game.Overlays.Music private const float transition_duration = 600; private const float playlist_height = 510; - public IBindableList> BeatmapSets => beatmapSets; - private readonly BindableList> beatmapSets = new BindableList>(); private readonly Bindable beatmap = new Bindable(); @@ -104,9 +102,7 @@ namespace osu.Game.Overlays.Music { base.LoadComplete(); - // tests might bind externally, in which case we don't want to involve realm. - if (beatmapSets.Count == 0) - beatmapSubscription = realm.RegisterForNotifications(r => r.All().Where(s => !s.DeletePending), beatmapsChanged); + beatmapSubscription = realm.RegisterForNotifications(r => r.All().Where(s => !s.DeletePending), beatmapsChanged); list.Items.BindTo(beatmapSets); beatmap.BindValueChanged(working => list.SelectedSet.Value = working.NewValue.BeatmapSetInfo.ToLive(realm), true); From a90967715cdaa1b8b9830f37ef3c8681999cf64b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Aug 2022 14:44:19 +0900 Subject: [PATCH 51/57] Add test coverage of new imports not correctly being filtered by collection filter --- osu.Game.Tests/Resources/TestResources.cs | 5 +- .../UserInterface/TestScenePlaylistOverlay.cs | 66 ++++++++++++++++++- 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Resources/TestResources.cs b/osu.Game.Tests/Resources/TestResources.cs index ee29cc8644..6bce03869d 100644 --- a/osu.Game.Tests/Resources/TestResources.cs +++ b/osu.Game.Tests/Resources/TestResources.cs @@ -128,6 +128,8 @@ namespace osu.Game.Tests.Resources var rulesetInfo = getRuleset(); + string hash = Guid.NewGuid().ToString().ComputeMD5Hash(); + yield return new BeatmapInfo { OnlineID = beatmapId, @@ -136,7 +138,8 @@ namespace osu.Game.Tests.Resources Length = length, BeatmapSet = beatmapSet, BPM = bpm, - Hash = Guid.NewGuid().ToString().ComputeMD5Hash(), + Hash = hash, + MD5Hash = hash, Ruleset = rulesetInfo, Metadata = metadata.DeepClone(), Difficulty = new BeatmapDifficulty diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePlaylistOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePlaylistOverlay.cs index 652afa80be..e67eebf721 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePlaylistOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePlaylistOverlay.cs @@ -27,8 +27,6 @@ namespace osu.Game.Tests.Visual.UserInterface private PlaylistOverlay playlistOverlay = null!; - private Live first = null!; - private BeatmapManager beatmapManager = null!; private const int item_count = 20; @@ -67,7 +65,7 @@ namespace osu.Game.Tests.Visual.UserInterface beatmapManager.Import(TestResources.CreateTestBeatmapSetInfo()); } - first = beatmapSets.First().ToLive(Realm); + beatmapSets.First().ToLive(Realm); }); [Test] @@ -117,6 +115,68 @@ namespace osu.Game.Tests.Visual.UserInterface () => playlistOverlay.ChildrenOfType() .Where(item => item.MatchingFilter) .All(item => item.FilterTerms.Any(term => term.ToString().Contains("10")))); + + AddStep("Import new non-matching beatmap", () => + { + var testBeatmapSetInfo = TestResources.CreateTestBeatmapSetInfo(1); + testBeatmapSetInfo.Beatmaps.Single().Metadata.Title = "no guid"; + beatmapManager.Import(testBeatmapSetInfo); + }); + + AddStep("Force realm refresh", () => Realm.Run(r => r.Refresh())); + + AddAssert("results filtered correctly", + () => playlistOverlay.ChildrenOfType() + .Where(item => item.MatchingFilter) + .All(item => item.FilterTerms.Any(term => term.ToString().Contains("10")))); + } + + [Test] + public void TestCollectionFiltering() + { + NowPlayingCollectionDropdown collectionDropdown() => playlistOverlay.ChildrenOfType().Single(); + + AddStep("Add collection", () => + { + Dependencies.Get().Write(r => + { + r.RemoveAll(); + r.Add(new BeatmapCollection("wang")); + }); + }); + + AddUntilStep("wait for dropdown to have new collection", () => collectionDropdown().Items.Count() == 2); + + AddStep("Filter to collection", () => + { + collectionDropdown().Current.Value = collectionDropdown().Items.Last(); + }); + + AddUntilStep("No items present", () => !playlistOverlay.ChildrenOfType().Any(i => i.MatchingFilter)); + + AddStep("Import new non-matching beatmap", () => + { + beatmapManager.Import(TestResources.CreateTestBeatmapSetInfo(1)); + }); + + AddStep("Force realm refresh", () => Realm.Run(r => r.Refresh())); + + AddUntilStep("No items matching", () => !playlistOverlay.ChildrenOfType().Any(i => i.MatchingFilter)); + + BeatmapSetInfo collectionAddedBeatmapSet = null!; + + AddStep("Import new matching beatmap", () => + { + collectionAddedBeatmapSet = TestResources.CreateTestBeatmapSetInfo(1); + + beatmapManager.Import(collectionAddedBeatmapSet); + Realm.Write(r => r.All().First().BeatmapMD5Hashes.Add(collectionAddedBeatmapSet.Beatmaps.First().MD5Hash)); + }); + + AddStep("Force realm refresh", () => Realm.Run(r => r.Refresh())); + + AddUntilStep("Only matching item", + () => playlistOverlay.ChildrenOfType().Where(i => i.MatchingFilter).Select(i => i.Model.ID), () => Is.EquivalentTo(new[] { collectionAddedBeatmapSet.ID })); } } } From b76e5757e17100c171df813235dc43d104e52c04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Aug 2022 14:44:05 +0900 Subject: [PATCH 52/57] Fix `InSelectedCollection` not being applied to newly imported beatmaps --- osu.Game/Overlays/Music/Playlist.cs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/osu.Game/Overlays/Music/Playlist.cs b/osu.Game/Overlays/Music/Playlist.cs index 2bb0ff1085..15fc54a337 100644 --- a/osu.Game/Overlays/Music/Playlist.cs +++ b/osu.Game/Overlays/Music/Playlist.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Linq; using osu.Framework.Bindables; @@ -17,10 +15,12 @@ namespace osu.Game.Overlays.Music { public class Playlist : OsuRearrangeableListContainer> { - public Action> RequestSelection; + public Action>? RequestSelection; public readonly Bindable> SelectedSet = new Bindable>(); + private FilterCriteria currentCriteria = new FilterCriteria(); + public new MarginPadding Padding { get => base.Padding; @@ -31,26 +31,22 @@ namespace osu.Game.Overlays.Music { var items = (SearchContainer>>)ListContainer; - string[] currentCollectionHashes = criteria.Collection?.PerformRead(c => c.BeatmapMD5Hashes.ToArray()); + string[]? currentCollectionHashes = criteria.Collection?.PerformRead(c => c.BeatmapMD5Hashes.ToArray()); foreach (var item in items.OfType()) { - if (currentCollectionHashes == null) - item.InSelectedCollection = true; - else - { - item.InSelectedCollection = item.Model.Value.Beatmaps.Select(b => b.MD5Hash) - .Any(currentCollectionHashes.Contains); - } + item.InSelectedCollection = currentCollectionHashes == null || item.Model.Value.Beatmaps.Select(b => b.MD5Hash).Any(currentCollectionHashes.Contains); } items.SearchTerm = criteria.SearchText; + currentCriteria = criteria; } - public Live FirstVisibleSet => Items.FirstOrDefault(i => ((PlaylistItem)ItemMap[i]).MatchingFilter); + public Live? FirstVisibleSet => Items.FirstOrDefault(i => ((PlaylistItem)ItemMap[i]).MatchingFilter); protected override OsuRearrangeableListItem> CreateOsuDrawable(Live item) => new PlaylistItem(item) { + InSelectedCollection = currentCriteria.Collection?.PerformRead(c => item.Value.Beatmaps.Select(b => b.MD5Hash).Any(c.BeatmapMD5Hashes.Contains)) != false, SelectedSet = { BindTarget = SelectedSet }, RequestSelection = set => RequestSelection?.Invoke(set) }; From e5e9841652c84ac5ab688d6c1137f83349b56a85 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Aug 2022 15:25:35 +0900 Subject: [PATCH 53/57] Apply multiple other mod debuffs as decided in pull request discussion --- osu.Game.Rulesets.Mania/Mods/ManiaModConstantSpeed.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs | 2 +- osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs | 2 +- osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs | 2 +- osu.Game/Rulesets/Mods/ModRelax.cs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModConstantSpeed.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModConstantSpeed.cs index 614ef76a3b..73dfaaa878 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModConstantSpeed.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModConstantSpeed.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania.Mods public override string Acronym => "CS"; - public override double ScoreMultiplier => 1; + public override double ScoreMultiplier => 0.9; public override string Description => "No more tricky speed changes!"; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs index 872fcf7e9b..9229c0393d 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override IconUsage? Icon => OsuIcon.ModAutopilot; public override ModType Type => ModType.Automation; public override string Description => @"Automatic cursor movement - just follow the rhythm."; - public override double ScoreMultiplier => 1; + public override double ScoreMultiplier => 0.1; public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail), typeof(ModAutoplay), typeof(OsuModMagnetised), typeof(OsuModRepel) }; public bool PerformFail() => false; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs index 9316f9ed74..7f7d6f70d2 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override IconUsage? Icon => FontAwesome.Solid.Magnet; public override ModType Type => ModType.Fun; public override string Description => "No need to chase the circles – your cursor is a magnet!"; - public override double ScoreMultiplier => 1; + public override double ScoreMultiplier => 0.5; public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModRelax), typeof(OsuModRepel) }; private IFrameStableClock gameplayClock = null!; diff --git a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs index 38d26ed05a..7b84db844b 100644 --- a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs +++ b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.Fun; - public override double ScoreMultiplier => 1; + public override double ScoreMultiplier => 0.5; public override bool ValidForMultiplayer => false; public override bool ValidForMultiplayerAsFreeMod => false; diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs index eefa1531c4..62a257608a 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mods public override IconUsage? Icon => FontAwesome.Solid.Hammer; - public override double ScoreMultiplier => 1.0; + public sealed override double ScoreMultiplier => 0.5; public override bool RequiresConfiguration => true; diff --git a/osu.Game/Rulesets/Mods/ModRelax.cs b/osu.Game/Rulesets/Mods/ModRelax.cs index a79c69b416..e1506e3a12 100644 --- a/osu.Game/Rulesets/Mods/ModRelax.cs +++ b/osu.Game/Rulesets/Mods/ModRelax.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mods public override string Acronym => "RX"; public override IconUsage? Icon => OsuIcon.ModRelax; public override ModType Type => ModType.Automation; - public override double ScoreMultiplier => 0.5; + public sealed override double ScoreMultiplier => 0.1; public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModNoFail), typeof(ModFailCondition) }; } } From 9d1b0b5836395db80779f25e3b184904857641d0 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 12 Aug 2022 22:32:27 +0900 Subject: [PATCH 54/57] Revert sealing --- osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs | 2 +- osu.Game/Rulesets/Mods/ModRelax.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs index 62a257608a..b7435ec3ec 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mods public override IconUsage? Icon => FontAwesome.Solid.Hammer; - public sealed override double ScoreMultiplier => 0.5; + public override double ScoreMultiplier => 0.5; public override bool RequiresConfiguration => true; diff --git a/osu.Game/Rulesets/Mods/ModRelax.cs b/osu.Game/Rulesets/Mods/ModRelax.cs index e1506e3a12..49c10339ee 100644 --- a/osu.Game/Rulesets/Mods/ModRelax.cs +++ b/osu.Game/Rulesets/Mods/ModRelax.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mods public override string Acronym => "RX"; public override IconUsage? Icon => OsuIcon.ModRelax; public override ModType Type => ModType.Automation; - public sealed override double ScoreMultiplier => 0.1; + public override double ScoreMultiplier => 0.1; public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModNoFail), typeof(ModFailCondition) }; } } From b05acb00738ed25463fbdacbee9b7da68ed01f58 Mon Sep 17 00:00:00 2001 From: basseX Date: Sat, 13 Aug 2022 21:32:24 +0200 Subject: [PATCH 55/57] Make `CommentMarkdownTextFlowContainer` render images --- osu.Game/Overlays/Comments/CommentMarkdownContainer.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs b/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs index e94a1b0147..58f020fd9e 100644 --- a/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs +++ b/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs @@ -18,8 +18,10 @@ namespace osu.Game.Overlays.Comments private class CommentMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer { - // Don't render image in comment for now - protected override void AddImage(LinkInline linkInline) { } + protected override void AddImage(LinkInline linkInline) + { + AddDrawable(new OsuMarkdownImage(linkInline)); + } } private class CommentMarkdownHeading : OsuMarkdownHeading From 932becc4b227ff2eef4419be68a16fae5151888e Mon Sep 17 00:00:00 2001 From: basseX Date: Sun, 14 Aug 2022 10:11:49 +0200 Subject: [PATCH 56/57] Remove `CommentMarkdownTextFlowContainer` and rather use base-class `OsuMarkdownTextFlowContainer` --- .../Overlays/Comments/CommentMarkdownContainer.cs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs b/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs index 58f020fd9e..45b8172994 100644 --- a/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs +++ b/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs @@ -4,7 +4,6 @@ #nullable disable using Markdig.Syntax; -using Markdig.Syntax.Inlines; using osu.Framework.Graphics.Containers.Markdown; using osu.Game.Graphics.Containers.Markdown; @@ -12,18 +11,10 @@ namespace osu.Game.Overlays.Comments { public class CommentMarkdownContainer : OsuMarkdownContainer { - public override MarkdownTextFlowContainer CreateTextFlow() => new CommentMarkdownTextFlowContainer(); + public override MarkdownTextFlowContainer CreateTextFlow() => new OsuMarkdownTextFlowContainer(); protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new CommentMarkdownHeading(headingBlock); - private class CommentMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer - { - protected override void AddImage(LinkInline linkInline) - { - AddDrawable(new OsuMarkdownImage(linkInline)); - } - } - private class CommentMarkdownHeading : OsuMarkdownHeading { public CommentMarkdownHeading(HeadingBlock headingBlock) From 383afe04f35159262e34d437c7835e292d81125a Mon Sep 17 00:00:00 2001 From: basseX Date: Sun, 14 Aug 2022 15:15:36 +0200 Subject: [PATCH 57/57] Remove not needed override --- osu.Game/Overlays/Comments/CommentMarkdownContainer.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs b/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs index 45b8172994..8fc011b2bf 100644 --- a/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs +++ b/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs @@ -11,8 +11,6 @@ namespace osu.Game.Overlays.Comments { public class CommentMarkdownContainer : OsuMarkdownContainer { - public override MarkdownTextFlowContainer CreateTextFlow() => new OsuMarkdownTextFlowContainer(); - protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new CommentMarkdownHeading(headingBlock); private class CommentMarkdownHeading : OsuMarkdownHeading