diff --git a/CodeAnalysis/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt index 550f7c8e11..08b79fc2c0 100644 --- a/CodeAnalysis/BannedSymbols.txt +++ b/CodeAnalysis/BannedSymbols.txt @@ -18,3 +18,6 @@ M:Humanizer.InflectorExtensions.Pascalize(System.String);Humanizer's .Pascalize( M:Humanizer.InflectorExtensions.Camelize(System.String);Humanizer's .Camelize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToCamelCase() instead. M:Humanizer.InflectorExtensions.Underscore(System.String);Humanizer's .Underscore() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToSnakeCase() instead. M:Humanizer.InflectorExtensions.Kebaberize(System.String);Humanizer's .Kebaberize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToKebabCase() instead. +M:osuTK.MathHelper.Clamp(System.Int32,System.Int32,System.Int32)~System.Int32;Use Math.Clamp() instead. +M:osuTK.MathHelper.Clamp(System.Single,System.Single,System.Single)~System.Single;This osuTK helper has unsafe semantics when one of the bounds provided is NaN. Use Math.Clamp() instead. +M:osuTK.MathHelper.Clamp(System.Double,System.Double,System.Double)~System.Double;This osuTK helper has unsafe semantics when one of the bounds provided is NaN. Use Math.Clamp() instead. diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Beatmaps/PippidonBeatmapConverter.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Beatmaps/PippidonBeatmapConverter.cs index 0a4fa84ce1..dd8337abee 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Beatmaps/PippidonBeatmapConverter.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Beatmaps/PippidonBeatmapConverter.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -9,7 +10,6 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Pippidon.Objects; using osu.Game.Rulesets.Pippidon.UI; -using osuTK; namespace osu.Game.Rulesets.Pippidon.Beatmaps { @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Pippidon.Beatmaps }; } - private int getLane(HitObject hitObject) => (int)MathHelper.Clamp( + private int getLane(HitObject hitObject) => (int)Math.Clamp( (getUsablePosition(hitObject) - minPosition) / (maxPosition - minPosition) * PippidonPlayfield.LANE_COUNT, 0, PippidonPlayfield.LANE_COUNT - 1); private float getUsablePosition(HitObject h) => (h as IHasYPosition)?.Y ?? ((IHasXPosition)h).X; diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs index 8cdb490922..810dc7eed5 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -110,7 +111,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon double duration = ObjectState.HitObject.StartTime - ObjectState.DisplayStartTime; - fadeContent.Alpha = MathHelper.Clamp( + fadeContent.Alpha = Math.Clamp( Interpolation.ValueAt( Time.Current, 1f, 0f, ObjectState.DisplayStartTime + duration * lens_flare_start, diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs index 8ed9d0476a..7a5b01ce79 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -76,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components if (hasReachedObject && showHitMarkers.Value) { float alpha = Interpolation.ValueAt(editorTime, 0, 1f, hitObjectTime, hitObjectTime + FADE_OUT_EXTENSION, Easing.In); - float ringScale = MathHelper.Clamp(Interpolation.ValueAt(editorTime, 0, 1f, hitObjectTime, hitObjectTime + FADE_OUT_EXTENSION / 2, Easing.OutQuint), 0, 1); + float ringScale = Math.Clamp(Interpolation.ValueAt(editorTime, 0, 1f, hitObjectTime, hitObjectTime + FADE_OUT_EXTENSION / 2, Easing.OutQuint), 0, 1); ring.Scale = new Vector2(1 + 0.1f * ringScale); content.Alpha = 0.9f * (1 - alpha); diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 60f335c419..9978c46027 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -270,14 +270,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders if (adjustVelocity) { proposedVelocity = proposedDistance / oldDuration; - proposedDistance = MathHelper.Clamp(proposedDistance, 0.1 * oldDuration, 10 * oldDuration); + proposedDistance = Math.Clamp(proposedDistance, 0.1 * oldDuration, 10 * oldDuration); } else { double minDistance = distanceSnapProvider?.GetBeatSnapDistance() * oldVelocityMultiplier ?? 1; // Add a small amount to the proposed distance to make it easier to snap to the full length of the slider. proposedDistance = distanceSnapProvider?.FindSnappedDistance((float)proposedDistance + 1, HitObject.StartTime, HitObject) ?? proposedDistance; - proposedDistance = MathHelper.Clamp(proposedDistance, minDistance, HitObject.Path.CalculatedDistance); + proposedDistance = Math.Clamp(proposedDistance, minDistance, HitObject.Path.CalculatedDistance); } if (Precision.AlmostEquals(proposedDistance, HitObject.Path.Distance) && Precision.AlmostEquals(proposedVelocity, HitObject.SliderVelocityMultiplier)) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs index e3ab95c402..4c3db207f2 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs @@ -263,12 +263,12 @@ namespace osu.Game.Rulesets.Osu.Edit { case Axes.X: (sLowerBound, sUpperBound) = computeBounds(lowerBounds - b, upperBounds - b, a); - s.X = MathHelper.Clamp(s.X, sLowerBound, sUpperBound); + s.X = Math.Clamp(s.X, sLowerBound, sUpperBound); break; case Axes.Y: (sLowerBound, sUpperBound) = computeBounds(lowerBounds - a, upperBounds - a, b); - s.Y = MathHelper.Clamp(s.Y, sLowerBound, sUpperBound); + s.Y = Math.Clamp(s.Y, sLowerBound, sUpperBound); break; case Axes.Both: @@ -276,11 +276,11 @@ namespace osu.Game.Rulesets.Osu.Edit // Therefore the ratio s.X / s.Y will be maintained (sLowerBound, sUpperBound) = computeBounds(lowerBounds, upperBounds, a * s.X + b * s.Y); s.X = s.X < 0 - ? MathHelper.Clamp(s.X, s.X * sUpperBound, s.X * sLowerBound) - : MathHelper.Clamp(s.X, s.X * sLowerBound, s.X * sUpperBound); + ? Math.Clamp(s.X, s.X * sUpperBound, s.X * sLowerBound) + : Math.Clamp(s.X, s.X * sLowerBound, s.X * sUpperBound); s.Y = s.Y < 0 - ? MathHelper.Clamp(s.Y, s.Y * sUpperBound, s.Y * sLowerBound) - : MathHelper.Clamp(s.Y, s.Y * sLowerBound, s.Y * sUpperBound); + ? Math.Clamp(s.Y, s.Y * sUpperBound, s.Y * sLowerBound) + : Math.Clamp(s.Y, s.Y * sLowerBound, s.Y * sUpperBound); break; } diff --git a/osu.Game.Tests/Beatmaps/BeatmapExtensionsTest.cs b/osu.Game.Tests/Beatmaps/BeatmapExtensionsTest.cs new file mode 100644 index 0000000000..1dda2e314d --- /dev/null +++ b/osu.Game.Tests/Beatmaps/BeatmapExtensionsTest.cs @@ -0,0 +1,58 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Osu.Objects; + +namespace osu.Game.Tests.Beatmaps +{ + public class BeatmapExtensionsTest + { + [Test] + public void TestLengthCalculations() + { + var beatmap = new Beatmap + { + HitObjects = + { + new HitCircle { StartTime = 5_000 }, + new HitCircle { StartTime = 300_000 }, + new Spinner { StartTime = 280_000, Duration = 40_000 } + }, + Breaks = + { + new BreakPeriod(50_000, 75_000), + new BreakPeriod(100_000, 150_000), + } + }; + + Assert.That(beatmap.CalculatePlayableBounds(), Is.EqualTo((5_000, 320_000))); + Assert.That(beatmap.CalculatePlayableLength(), Is.EqualTo(315_000)); // 320_000 - 5_000 + Assert.That(beatmap.CalculateDrainLength(), Is.EqualTo(240_000)); // 315_000 - (25_000 + 50_000) = 315_000 - 75_000 + } + + [Test] + public void TestDrainLengthCannotGoNegative() + { + var beatmap = new Beatmap + { + HitObjects = + { + new HitCircle { StartTime = 5_000 }, + new HitCircle { StartTime = 300_000 }, + new Spinner { StartTime = 280_000, Duration = 40_000 } + }, + Breaks = + { + new BreakPeriod(0, 350_000), + } + }; + + Assert.That(beatmap.CalculatePlayableBounds(), Is.EqualTo((5_000, 320_000))); + Assert.That(beatmap.CalculatePlayableLength(), Is.EqualTo(315_000)); // 320_000 - 5_000 + Assert.That(beatmap.CalculateDrainLength(), Is.EqualTo(0)); // break period encompasses entire beatmap + } + } +} diff --git a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs index 959f09361f..4019ff6730 100644 --- a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs +++ b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs @@ -97,16 +97,12 @@ namespace osu.Game.Tests.NonVisual.Multiplayer AddStep("create room with many users", () => { - var newRoom = new Room(); - newRoom.CopyFrom(SelectedRoom.Value!); - - newRoom.RoomID = null; MultiplayerClient.RoomSetupAction = room => { room.Users.AddRange(Enumerable.Range(PLAYER_1_ID, 100).Select(id => new MultiplayerRoomUser(id))); }; - RoomManager.CreateRoom(newRoom); + MultiplayerClient.JoinRoom(MultiplayerClient.ServerSideRooms.Single()).ConfigureAwait(false); }); AddUntilStep("wait for room join", () => RoomJoined); diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs index 826d4e19a7..f95fcefd7e 100644 --- a/osu.Game/Beatmaps/IBeatmap.cs +++ b/osu.Game/Beatmaps/IBeatmap.cs @@ -161,7 +161,7 @@ namespace osu.Game.Beatmaps /// /// Find the total milliseconds between the first and last hittable objects, excluding any break time. /// - public static double CalculateDrainLength(this IBeatmap beatmap) => CalculatePlayableLength(beatmap.HitObjects) - beatmap.TotalBreakTime; + public static double CalculateDrainLength(this IBeatmap beatmap) => Math.Max(CalculatePlayableLength(beatmap.HitObjects) - beatmap.TotalBreakTime, 0); /// /// Find the timestamps in milliseconds of the start and end of the playable region. diff --git a/osu.Game/Overlays/NotificationOverlayToastTray.cs b/osu.Game/Overlays/NotificationOverlayToastTray.cs index ddb2e02fb8..dd60e303f6 100644 --- a/osu.Game/Overlays/NotificationOverlayToastTray.cs +++ b/osu.Game/Overlays/NotificationOverlayToastTray.cs @@ -174,7 +174,7 @@ namespace osu.Game.Overlays } height = toastFlow.DrawHeight + 120; - alpha = MathHelper.Clamp(toastFlow.DrawHeight / 41, 0, 1) * maxNotificationAlpha; + alpha = Math.Clamp(toastFlow.DrawHeight / 41, 0, 1) * maxNotificationAlpha; } toastContentBackground.Height = (float)Interpolation.DampContinuously(toastContentBackground.Height, height, 10, Clock.ElapsedFrameTime); diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 8bfa8dd6ff..78c602d8f1 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -277,18 +277,18 @@ namespace osu.Game.Screens.Play if (rulesetComponents != null) processDrawables(rulesetComponents); - if (lowestTopScreenSpaceRight.HasValue) - TopRightElements.Y = MathHelper.Clamp(ToLocalSpace(new Vector2(0, lowestTopScreenSpaceRight.Value)).Y, 0, DrawHeight - TopRightElements.DrawHeight); + if (lowestTopScreenSpaceRight.HasValue && DrawHeight - TopRightElements.DrawHeight > 0) + TopRightElements.Y = Math.Clamp(ToLocalSpace(new Vector2(0, lowestTopScreenSpaceRight.Value)).Y, 0, DrawHeight - TopRightElements.DrawHeight); else TopRightElements.Y = 0; - if (lowestTopScreenSpaceLeft.HasValue) - LeaderboardFlow.Y = MathHelper.Clamp(ToLocalSpace(new Vector2(0, lowestTopScreenSpaceLeft.Value)).Y, 0, DrawHeight - LeaderboardFlow.DrawHeight); + if (lowestTopScreenSpaceLeft.HasValue && DrawHeight - LeaderboardFlow.DrawHeight > 0) + LeaderboardFlow.Y = Math.Clamp(ToLocalSpace(new Vector2(0, lowestTopScreenSpaceLeft.Value)).Y, 0, DrawHeight - LeaderboardFlow.DrawHeight); else LeaderboardFlow.Y = 0; - if (highestBottomScreenSpace.HasValue) - bottomRightElements.Y = BottomScoringElementsHeight = -MathHelper.Clamp(DrawHeight - ToLocalSpace(highestBottomScreenSpace.Value).Y, 0, DrawHeight - bottomRightElements.DrawHeight); + if (highestBottomScreenSpace.HasValue && DrawHeight - bottomRightElements.DrawHeight > 0) + bottomRightElements.Y = BottomScoringElementsHeight = -Math.Clamp(DrawHeight - ToLocalSpace(highestBottomScreenSpace.Value).Y, 0, DrawHeight - bottomRightElements.DrawHeight); else bottomRightElements.Y = 0; diff --git a/osu.Game/Screens/Utility/CircleGameplay.cs b/osu.Game/Screens/Utility/CircleGameplay.cs index 1f970c5121..0f328d04fb 100644 --- a/osu.Game/Screens/Utility/CircleGameplay.cs +++ b/osu.Game/Screens/Utility/CircleGameplay.cs @@ -201,8 +201,8 @@ namespace osu.Game.Screens.Utility { double preempt = (float)IBeatmapDifficultyInfo.DifficultyRange(SampleApproachRate.Value, 1800, 1200, 450); - approach.Scale = new Vector2(1 + 4 * (float)MathHelper.Clamp((HitTime - Clock.CurrentTime) / preempt, 0, 100)); - Alpha = (float)MathHelper.Clamp((Clock.CurrentTime - HitTime + 600) / 400, 0, 1); + approach.Scale = new Vector2(1 + 4 * (float)Math.Clamp((HitTime - Clock.CurrentTime) / preempt, 0, 100)); + Alpha = (float)Math.Clamp((Clock.CurrentTime - HitTime + 600) / 400, 0, 1); if (Clock.CurrentTime > HitTime + duration) Expire(); diff --git a/osu.Game/Screens/Utility/SampleComponents/LatencyMovableBox.cs b/osu.Game/Screens/Utility/SampleComponents/LatencyMovableBox.cs index dcfcf602bf..ef1b848945 100644 --- a/osu.Game/Screens/Utility/SampleComponents/LatencyMovableBox.cs +++ b/osu.Game/Screens/Utility/SampleComponents/LatencyMovableBox.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; @@ -55,22 +56,22 @@ namespace osu.Game.Screens.Utility.SampleComponents { case Key.F: case Key.Up: - box.Y = MathHelper.Clamp(box.Y - movementAmount, 0.1f, 0.9f); + box.Y = Math.Clamp(box.Y - movementAmount, 0.1f, 0.9f); break; case Key.J: case Key.Down: - box.Y = MathHelper.Clamp(box.Y + movementAmount, 0.1f, 0.9f); + box.Y = Math.Clamp(box.Y + movementAmount, 0.1f, 0.9f); break; case Key.Z: case Key.Left: - box.X = MathHelper.Clamp(box.X - movementAmount, 0.1f, 0.9f); + box.X = Math.Clamp(box.X - movementAmount, 0.1f, 0.9f); break; case Key.X: case Key.Right: - box.X = MathHelper.Clamp(box.X + movementAmount, 0.1f, 0.9f); + box.X = Math.Clamp(box.X + movementAmount, 0.1f, 0.9f); break; } } diff --git a/osu.Game/Screens/Utility/ScrollingGameplay.cs b/osu.Game/Screens/Utility/ScrollingGameplay.cs index 5038c53b4a..c0264f5734 100644 --- a/osu.Game/Screens/Utility/ScrollingGameplay.cs +++ b/osu.Game/Screens/Utility/ScrollingGameplay.cs @@ -165,7 +165,7 @@ namespace osu.Game.Screens.Utility { double preempt = (float)IBeatmapDifficultyInfo.DifficultyRange(SampleApproachRate.Value, 1800, 1200, 450); - Alpha = (float)MathHelper.Clamp((Clock.CurrentTime - HitTime + 600) / 400, 0, 1); + Alpha = (float)Math.Clamp((Clock.CurrentTime - HitTime + 600) / 400, 0, 1); Y = judgement_position - (float)((HitTime - Clock.CurrentTime) / preempt); if (Clock.CurrentTime > HitTime + duration)