From 3441a9a5b50bc5093dd06c232a96c4a4cbb5ccc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 23 Nov 2023 08:12:34 +0900 Subject: [PATCH 1/2] Add test coverage for classic scoring overflowing in osu! ruleset --- .../Rulesets/Scoring/ScoreProcessorTest.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs index cba90b2ebe..c957ddd7d3 100644 --- a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs +++ b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs @@ -10,14 +10,17 @@ using NUnit.Framework; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Rulesets; +using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko; using osu.Game.Rulesets.UI; using osu.Game.Scoring.Legacy; using osu.Game.Tests.Beatmaps; @@ -117,6 +120,35 @@ namespace osu.Game.Tests.Rulesets.Scoring Assert.That(scoreProcessor.GetDisplayScore(scoringMode), Is.EqualTo(expectedScore).Within(0.5d)); } + [TestCase(typeof(OsuRuleset))] + [TestCase(typeof(TaikoRuleset))] + [TestCase(typeof(CatchRuleset))] + [TestCase(typeof(ManiaRuleset))] + public void TestBeatmapWithALotOfObjectsDoesNotOverflowClassicScore(Type rulesetType) + { + const int object_count = 999999; + + var ruleset = (Ruleset)Activator.CreateInstance(rulesetType)!; + scoreProcessor = new ScoreProcessor(ruleset); + + var largeBeatmap = new TestBeatmap(ruleset.RulesetInfo) + { + HitObjects = new List(Enumerable.Repeat(new TestHitObject(HitResult.Great), object_count)) + }; + scoreProcessor.ApplyBeatmap(largeBeatmap); + + for (int i = 0; i < object_count; ++i) + { + var judgementResult = new JudgementResult(largeBeatmap.HitObjects[i], largeBeatmap.HitObjects[i].CreateJudgement()) + { + Type = HitResult.Great + }; + scoreProcessor.ApplyResult(judgementResult); + } + + Assert.That(scoreProcessor.GetDisplayScore(ScoringMode.Classic), Is.GreaterThan(0)); + } + [Test] public void TestEmptyBeatmap( [Values(ScoringMode.Standardised, ScoringMode.Classic)] From e28e0ef1cc5847ef4c596ea380c77270089bbcef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 23 Nov 2023 08:15:46 +0900 Subject: [PATCH 2/2] Fix classic scoring overflowing in osu! ruleset due to integer multiplication overflow Closes https://github.com/ppy/osu/issues/25545. --- osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs b/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs index f6ea5aa455..07c35a334f 100644 --- a/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs +++ b/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs @@ -50,7 +50,7 @@ namespace osu.Game.Scoring.Legacy switch (rulesetId) { case 0: - return (long)Math.Round((objectCount * objectCount * 32.57 + 100000) * standardisedTotalScore / ScoreProcessor.MAX_SCORE); + return (long)Math.Round((Math.Pow(objectCount, 2) * 32.57 + 100000) * standardisedTotalScore / ScoreProcessor.MAX_SCORE); case 1: return (long)Math.Round((objectCount * 1109 + 100000) * standardisedTotalScore / ScoreProcessor.MAX_SCORE);