From a2bfb409d2c8edad8f3935a917faaf4f2c590b31 Mon Sep 17 00:00:00 2001 From: StanR Date: Sat, 22 Nov 2025 03:16:36 +0500 Subject: [PATCH 1/6] Use actual mod-adjusted map difficulty settings in the `SongBar` --- .../Components/TestSceneSongBar.cs | 1 + osu.Game.Tournament/Components/SongBar.cs | 44 ++++++++----------- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/osu.Game.Tournament.Tests/Components/TestSceneSongBar.cs b/osu.Game.Tournament.Tests/Components/TestSceneSongBar.cs index 95d6b6d107..285937ef03 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneSongBar.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneSongBar.cs @@ -61,6 +61,7 @@ namespace osu.Game.Tournament.Tests.Components AddStep("set mods to HR", () => songBar.Mods = LegacyMods.HardRock); AddStep("set mods to DT", () => songBar.Mods = LegacyMods.DoubleTime); + AddStep("set mods to HDHRDT", () => songBar.Mods = LegacyMods.Hidden | LegacyMods.HardRock | LegacyMods.DoubleTime); AddStep("unset mods", () => songBar.Mods = LegacyMods.None); AddToggleStep("toggle expanded", expanded => songBar.Expanded = expanded); diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index cff86cf0a1..11cb04e540 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.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.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -14,6 +15,7 @@ using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Models; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Screens.Menu; using osu.Game.Utils; using osuTK; @@ -123,27 +125,19 @@ namespace osu.Game.Tournament.Components }, }; - double bpm = beatmap.BPM; - double length = beatmap.Length; - string hardRockExtra = ""; + var rulesetInstance = ruleset.Value.CreateInstance(); + + var convertedMods = rulesetInstance.ConvertFromLegacyMods(mods).ToList(); + var adjustedDifficulty = rulesetInstance.GetAdjustedDisplayDifficulty(beatmap, convertedMods); + + double rate = ModUtils.CalculateRateWithMods(convertedMods); + double bpm = FormatUtils.RoundBPM(beatmap.BPM, rate); + double length = beatmap.Length / rate; + string srExtra = ""; - float ar = beatmap.Difficulty.ApproachRate; - - if ((mods & LegacyMods.HardRock) > 0) + if (convertedMods.Any(x => x is ModHardRock) || convertedMods.Any(x => x is ModDoubleTime)) { - hardRockExtra = "*"; - srExtra = "*"; - } - - if ((mods & LegacyMods.DoubleTime) > 0) - { - // temporary local calculation (taken from OsuDifficultyCalculator) - double preempt = (int)IBeatmapDifficultyInfo.DifficultyRange(ar, 1800, 1200, 450) / 1.5; - ar = (float)(preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5); - - bpm *= 1.5f; - length /= 1.5f; srExtra = "*"; } @@ -154,9 +148,9 @@ namespace osu.Game.Tournament.Components default: stats = new (string heading, string content)[] { - ("CS", $"{beatmap.Difficulty.CircleSize:0.#}{hardRockExtra}"), - ("AR", $"{ar:0.#}{hardRockExtra}"), - ("OD", $"{beatmap.Difficulty.OverallDifficulty:0.#}{hardRockExtra}"), + ("CS", $"{adjustedDifficulty.CircleSize:0.#}"), + ("AR", $"{adjustedDifficulty.ApproachRate:0.#}"), + ("OD", $"{adjustedDifficulty.OverallDifficulty:0.#}"), }; break; @@ -164,16 +158,16 @@ namespace osu.Game.Tournament.Components case 3: stats = new (string heading, string content)[] { - ("OD", $"{beatmap.Difficulty.OverallDifficulty:0.#}{hardRockExtra}"), - ("HP", $"{beatmap.Difficulty.DrainRate:0.#}{hardRockExtra}") + ("OD", $"{adjustedDifficulty.OverallDifficulty:0.#}"), + ("HP", $"{adjustedDifficulty.DrainRate:0.#}") }; break; case 2: stats = new (string heading, string content)[] { - ("CS", $"{beatmap.Difficulty.CircleSize:0.#}{hardRockExtra}"), - ("AR", $"{ar:0.#}"), + ("CS", $"{adjustedDifficulty.CircleSize:0.#}"), + ("AR", $"{adjustedDifficulty.ApproachRate:0.#}"), }; break; } From fd652982ceded9fca5bf57e7ee35c3d0358d05c0 Mon Sep 17 00:00:00 2001 From: StanR Date: Sat, 22 Nov 2025 03:29:39 +0500 Subject: [PATCH 2/6] Add ruleset tests --- .../Components/TestSceneSongBar.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game.Tournament.Tests/Components/TestSceneSongBar.cs b/osu.Game.Tournament.Tests/Components/TestSceneSongBar.cs index 285937ef03..28ced3e0ad 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneSongBar.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneSongBar.cs @@ -5,6 +5,10 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Beatmaps.Legacy; +using osu.Game.Rulesets.Catch; +using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Taiko; using osu.Game.Tournament.Components; using osu.Game.Tournament.Models; @@ -52,6 +56,7 @@ namespace osu.Game.Tournament.Tests.Components beatmap.ApproachRate = 6.8f; beatmap.OverallDifficulty = 5.5f; beatmap.StarRating = 4.56f; + beatmap.DrainRate = 1.23f; beatmap.Length = 123456; beatmap.BPM = 133; beatmap.OnlineID = ladderBeatmap.OnlineID; @@ -62,11 +67,17 @@ namespace osu.Game.Tournament.Tests.Components AddStep("set mods to HR", () => songBar.Mods = LegacyMods.HardRock); AddStep("set mods to DT", () => songBar.Mods = LegacyMods.DoubleTime); AddStep("set mods to HDHRDT", () => songBar.Mods = LegacyMods.Hidden | LegacyMods.HardRock | LegacyMods.DoubleTime); + AddStep("unset mods", () => songBar.Mods = LegacyMods.None); AddToggleStep("toggle expanded", expanded => songBar.Expanded = expanded); AddStep("set null beatmap", () => songBar.Beatmap = null); + + AddStep("set ruleset to osu", () => Ruleset.Value = new OsuRuleset().RulesetInfo); + AddStep("set ruleset to taiko", () => Ruleset.Value = new TaikoRuleset().RulesetInfo); + AddStep("set ruleset to catch", () => Ruleset.Value = new CatchRuleset().RulesetInfo); + AddStep("set ruleset to mania", () => Ruleset.Value = new ManiaRuleset().RulesetInfo); } } } From 8900c79758f576bd441d3f246bb6fe4f3046cf4f Mon Sep 17 00:00:00 2001 From: StanR Date: Sat, 22 Nov 2025 03:35:10 +0500 Subject: [PATCH 3/6] Set `TournamentBeatmap`'s `IBeatmapInfo.Ruleset` to a dummy ruleset. This is being queried by the https://github.com/ppy/osu/blob/master/osu.Game.Rulesets.Mania/ManiaRuleset.cs#L442 but since we don't actually draw column count anywhere nor are we supposed to be running converts in tournaments it should be safe to populate it with nothing. --- osu.Game.Tournament/Models/TournamentBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Models/TournamentBeatmap.cs b/osu.Game.Tournament/Models/TournamentBeatmap.cs index a7ba5b7db1..79dbb680d7 100644 --- a/osu.Game.Tournament/Models/TournamentBeatmap.cs +++ b/osu.Game.Tournament/Models/TournamentBeatmap.cs @@ -83,7 +83,7 @@ namespace osu.Game.Tournament.Models string IBeatmapInfo.MD5Hash => throw new NotImplementedException(); - IRulesetInfo IBeatmapInfo.Ruleset => throw new NotImplementedException(); + IRulesetInfo IBeatmapInfo.Ruleset => new RulesetInfo(); DateTimeOffset IBeatmapSetOnlineInfo.Submitted => throw new NotImplementedException(); From 83ce56b7183916a1f4f7c5ad3a96a51f3b5d8c40 Mon Sep 17 00:00:00 2001 From: StanR Date: Mon, 24 Nov 2025 15:11:47 +0500 Subject: [PATCH 4/6] Use `APIRuleset` instead of a blank `RulesetInfo` --- osu.Game.Tournament/Models/TournamentBeatmap.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Models/TournamentBeatmap.cs b/osu.Game.Tournament/Models/TournamentBeatmap.cs index 79dbb680d7..83c42b793d 100644 --- a/osu.Game.Tournament/Models/TournamentBeatmap.cs +++ b/osu.Game.Tournament/Models/TournamentBeatmap.cs @@ -6,6 +6,7 @@ using osu.Game.Beatmaps; using osu.Game.Extensions; using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; +using static osu.Game.Online.API.Requests.Responses.APIBeatmap; namespace osu.Game.Tournament.Models { @@ -31,6 +32,8 @@ namespace osu.Game.Tournament.Models public BeatmapSetOnlineCovers Covers { get; set; } + public APIRuleset Ruleset { get; set; } = new APIRuleset(); + public TournamentBeatmap() { } @@ -47,6 +50,7 @@ namespace osu.Game.Tournament.Models Covers = beatmap.BeatmapSet?.Covers ?? new BeatmapSetOnlineCovers(); EndTimeObjectCount = beatmap.EndTimeObjectCount; TotalObjectCount = beatmap.TotalObjectCount; + Ruleset = (APIRuleset)beatmap.Ruleset; } public bool Equals(IBeatmapInfo? other) => other is TournamentBeatmap b && this.MatchesOnlineID(b); @@ -83,7 +87,7 @@ namespace osu.Game.Tournament.Models string IBeatmapInfo.MD5Hash => throw new NotImplementedException(); - IRulesetInfo IBeatmapInfo.Ruleset => new RulesetInfo(); + IRulesetInfo IBeatmapInfo.Ruleset => Ruleset; DateTimeOffset IBeatmapSetOnlineInfo.Submitted => throw new NotImplementedException(); From e4975e8d3b33e00dc10196b0ef709381984855f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 24 Nov 2025 12:00:33 +0100 Subject: [PATCH 5/6] Remove unnecessary cast --- osu.Game.Tournament/Models/TournamentBeatmap.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Models/TournamentBeatmap.cs b/osu.Game.Tournament/Models/TournamentBeatmap.cs index 83c42b793d..72669c0ca7 100644 --- a/osu.Game.Tournament/Models/TournamentBeatmap.cs +++ b/osu.Game.Tournament/Models/TournamentBeatmap.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tournament.Models public BeatmapSetOnlineCovers Covers { get; set; } - public APIRuleset Ruleset { get; set; } = new APIRuleset(); + public IRulesetInfo Ruleset { get; set; } = new APIRuleset(); public TournamentBeatmap() { @@ -50,7 +50,7 @@ namespace osu.Game.Tournament.Models Covers = beatmap.BeatmapSet?.Covers ?? new BeatmapSetOnlineCovers(); EndTimeObjectCount = beatmap.EndTimeObjectCount; TotalObjectCount = beatmap.TotalObjectCount; - Ruleset = (APIRuleset)beatmap.Ruleset; + Ruleset = beatmap.Ruleset; } public bool Equals(IBeatmapInfo? other) => other is TournamentBeatmap b && this.MatchesOnlineID(b); From 9c981a52f865b611530db7318256c696b836ead0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 24 Nov 2025 12:52:57 +0100 Subject: [PATCH 6/6] Fix test failures This is dodgy as hell but `ShortName` is completely derived from `OnlineID` anyway so there should be no valid reason to ever attempt to serialise it anyway. --- osu.Game/Online/API/Requests/Responses/APIBeatmap.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index 20494a1cbf..cbd8833fe8 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -146,6 +146,7 @@ namespace osu.Game.Online.API.Requests.Responses public string Name => $@"{nameof(APIRuleset)} (ID: {OnlineID})"; + [JsonIgnore] public string ShortName { get