Skip to content

Commit

Permalink
Merge pull request #25711 from smoogipoo/mania-convert-song-select-ke…
Browse files Browse the repository at this point in the history
…ycount

Display osu!mania keycount in song select carousel panels and details
  • Loading branch information
peppy authored Dec 13, 2023
2 parents ec6200b + eff81be commit fdcf875
Show file tree
Hide file tree
Showing 15 changed files with 203 additions and 40 deletions.
13 changes: 4 additions & 9 deletions osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,14 @@ public ManiaBeatmapConverter(IBeatmap beatmap, Ruleset ruleset)

public static int GetColumnCount(LegacyBeatmapConversionDifficultyInfo difficulty)
{
double roundedCircleSize = Math.Round(difficulty.CircleSize);

if (new ManiaRuleset().RulesetInfo.Equals(difficulty.SourceRuleset))
return GetColumnCountForNonConvert(difficulty);
return (int)Math.Max(1, roundedCircleSize);

double roundedCircleSize = Math.Round(difficulty.CircleSize);
double roundedOverallDifficulty = Math.Round(difficulty.OverallDifficulty);

int countSliderOrSpinner = difficulty.TotalObjectCount - difficulty.CircleCount;
int countSliderOrSpinner = difficulty.EndTimeObjectCount;

// In osu!stable, this division appears as if it happens on floats, but due to release-mode
// optimisations, it actually ends up happening on doubles.
Expand All @@ -79,12 +80,6 @@ public static int GetColumnCount(LegacyBeatmapConversionDifficultyInfo difficult
return Math.Max(4, Math.Min((int)roundedOverallDifficulty + 1, 7));
}

public static int GetColumnCountForNonConvert(IBeatmapDifficultyInfo difficulty)
{
double roundedCircleSize = Math.Round(difficulty.CircleSize);
return (int)Math.Max(1, roundedCircleSize);
}

public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasXPosition);

protected override Beatmap<ManiaHitObject> ConvertBeatmap(IBeatmap original, CancellationToken cancellationToken)
Expand Down
3 changes: 2 additions & 1 deletion osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Filter;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Scoring.Legacy;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Filter;

Expand All @@ -15,7 +16,7 @@ public class ManiaFilterCriteria : IRulesetFilterCriteria

public bool Matches(BeatmapInfo beatmapInfo)
{
return !keys.HasFilter || (beatmapInfo.Ruleset.OnlineID == new ManiaRuleset().LegacyID && keys.IsInRange(ManiaBeatmapConverter.GetColumnCountForNonConvert(beatmapInfo.Difficulty)));
return !keys.HasFilter || keys.IsInRange(ManiaBeatmapConverter.GetColumnCount(LegacyBeatmapConversionDifficultyInfo.FromBeatmapInfo(beatmapInfo)));
}

public bool TryParseCustomKeywordCriteria(string key, Operator op, string value)
Expand Down
3 changes: 3 additions & 0 deletions osu.Game.Rulesets.Mania/ManiaRuleset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,9 @@ public override IRulesetFilterCriteria CreateRulesetFilterCriteria()
public override RulesetSetupSection CreateEditorSetupSection() => new ManiaSetupSection();

public override DifficultySection CreateEditorDifficultySection() => new ManiaDifficultySection();

public int GetKeyCount(IBeatmapInfo beatmapInfo)
=> ManiaBeatmapConverter.GetColumnCount(LegacyBeatmapConversionDifficultyInfo.FromBeatmapInfo(beatmapInfo));
}

public enum PlayfieldType
Expand Down
33 changes: 32 additions & 1 deletion osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
using osu.Game.Graphics;
using osu.Game.Resources.Localisation.Web;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.Select.Details;
using osuTK.Graphics;
Expand All @@ -38,6 +40,12 @@ public partial class TestSceneAdvancedStats : OsuTestScene
Width = 500
});

[SetUpSteps]
public void SetUpSteps()
{
AddStep("reset game ruleset", () => Ruleset.Value = new OsuRuleset().RulesetInfo);
}

private BeatmapInfo exampleBeatmapInfo => new BeatmapInfo
{
Ruleset = rulesets.AvailableRulesets.First(),
Expand Down Expand Up @@ -66,8 +74,10 @@ public void TestNoMod()
}

[Test]
public void TestManiaFirstBarText()
public void TestManiaFirstBarTextManiaBeatmap()
{
AddStep("set game ruleset to mania", () => Ruleset.Value = new ManiaRuleset().RulesetInfo);

AddStep("set beatmap", () => advancedStats.BeatmapInfo = new BeatmapInfo
{
Ruleset = rulesets.GetRuleset(3) ?? throw new InvalidOperationException("osu!mania ruleset not found"),
Expand All @@ -84,6 +94,27 @@ public void TestManiaFirstBarText()
AddAssert("first bar text is correct", () => advancedStats.ChildrenOfType<SpriteText>().First().Text == BeatmapsetsStrings.ShowStatsCsMania);
}

[Test]
public void TestManiaFirstBarTextConvert()
{
AddStep("set game ruleset to mania", () => Ruleset.Value = new ManiaRuleset().RulesetInfo);

AddStep("set beatmap", () => advancedStats.BeatmapInfo = new BeatmapInfo
{
Ruleset = new OsuRuleset().RulesetInfo,
Difficulty = new BeatmapDifficulty
{
CircleSize = 5,
DrainRate = 4.3f,
OverallDifficulty = 4.5f,
ApproachRate = 3.1f
},
StarRating = 8
});

AddAssert("first bar text is correct", () => advancedStats.ChildrenOfType<SpriteText>().First().Text == BeatmapsetsStrings.ShowStatsCsMania);
}

[Test]
public void TestEasyMod()
{
Expand Down
53 changes: 42 additions & 11 deletions osu.Game/BackgroundDataStoreProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -21,6 +20,7 @@
using osu.Game.Scoring;
using osu.Game.Scoring.Legacy;
using osu.Game.Screens.Play;
using Realms;

namespace osu.Game
{
Expand Down Expand Up @@ -68,6 +68,7 @@ protected override void LoadComplete()
checkForOutdatedStarRatings();
processBeatmapSetsWithMissingMetrics();
processBeatmapsWithMissingObjectCounts();
processScoresWithMissingStatistics();
convertLegacyTotalScoreToStandardised();
}, TaskCreationOptions.LongRunning).ContinueWith(t =>
Expand Down Expand Up @@ -134,19 +135,13 @@ private void processBeatmapSetsWithMissingMetrics()
// of other possible ways), but for now avoid queueing if the user isn't logged in at startup.
if (api.IsLoggedIn)
{
foreach (var b in r.All<BeatmapInfo>().Where(b => b.StarRating < 0 || (b.OnlineID > 0 && b.LastOnlineUpdate == null)))
{
Debug.Assert(b.BeatmapSet != null);
beatmapSetIds.Add(b.BeatmapSet.ID);
}
foreach (var b in r.All<BeatmapInfo>().Where(b => (b.StarRating < 0 || (b.OnlineID > 0 && b.LastOnlineUpdate == null)) && b.BeatmapSet != null))
beatmapSetIds.Add(b.BeatmapSet!.ID);
}
else
{
foreach (var b in r.All<BeatmapInfo>().Where(b => b.StarRating < 0))
{
Debug.Assert(b.BeatmapSet != null);
beatmapSetIds.Add(b.BeatmapSet.ID);
}
foreach (var b in r.All<BeatmapInfo>().Where(b => b.StarRating < 0 && b.BeatmapSet != null))
beatmapSetIds.Add(b.BeatmapSet!.ID);
}
});

Expand Down Expand Up @@ -178,6 +173,42 @@ private void processBeatmapSetsWithMissingMetrics()
}
}

private void processBeatmapsWithMissingObjectCounts()
{
Logger.Log("Querying for beatmaps with missing hitobject counts to reprocess...");

HashSet<Guid> beatmapIds = realmAccess.Run(r => new HashSet<Guid>(r.All<BeatmapInfo>()
.Filter($"{nameof(BeatmapInfo.Difficulty)}.{nameof(BeatmapDifficulty.TotalObjectCount)} == 0")
.AsEnumerable().Select(b => b.ID)));

Logger.Log($"Found {beatmapIds.Count} beatmaps which require reprocessing.");

int i = 0;

foreach (var id in beatmapIds)
{
sleepIfRequired();

realmAccess.Run(r =>
{
var beatmap = r.Find<BeatmapInfo>(id);
if (beatmap != null)
{
try
{
Logger.Log($"Background processing {beatmap} ({++i} / {beatmapIds.Count})");
beatmapUpdater.ProcessObjectCounts(beatmap);
}
catch (Exception e)
{
Logger.Log($"Background processing failed on {beatmap}: {e}");
}
}
});
}
}

private void processScoresWithMissingStatistics()
{
HashSet<Guid> scoreIds = new HashSet<Guid>();
Expand Down
9 changes: 9 additions & 0 deletions osu.Game/Beatmaps/BeatmapDifficulty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ public class BeatmapDifficulty : EmbeddedObject, IBeatmapDifficultyInfo
public double SliderMultiplier { get; set; } = 1.4;
public double SliderTickRate { get; set; } = 1;

public int EndTimeObjectCount { get; set; }
public int TotalObjectCount { get; set; }

public BeatmapDifficulty()
{
}
Expand All @@ -44,6 +47,9 @@ public virtual void CopyTo(BeatmapDifficulty difficulty)

difficulty.SliderMultiplier = SliderMultiplier;
difficulty.SliderTickRate = SliderTickRate;

difficulty.EndTimeObjectCount = EndTimeObjectCount;
difficulty.TotalObjectCount = TotalObjectCount;
}

public virtual void CopyFrom(IBeatmapDifficultyInfo other)
Expand All @@ -55,6 +61,9 @@ public virtual void CopyFrom(IBeatmapDifficultyInfo other)

SliderMultiplier = other.SliderMultiplier;
SliderTickRate = other.SliderTickRate;

EndTimeObjectCount = other.EndTimeObjectCount;
TotalObjectCount = other.TotalObjectCount;
}
}
}
3 changes: 3 additions & 0 deletions osu.Game/Beatmaps/BeatmapImporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using osu.Game.IO.Archives;
using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects.Types;
using Realms;

namespace osu.Game.Beatmaps
Expand Down Expand Up @@ -388,6 +389,8 @@ private List<BeatmapInfo> createBeatmapDifficulties(BeatmapSetInfo beatmapSet, R
ApproachRate = decodedDifficulty.ApproachRate,
SliderMultiplier = decodedDifficulty.SliderMultiplier,
SliderTickRate = decodedDifficulty.SliderTickRate,
EndTimeObjectCount = decoded.HitObjects.Count(h => h is IHasDuration),
TotalObjectCount = decoded.HitObjects.Count
};

var metadata = new BeatmapMetadata
Expand Down
20 changes: 19 additions & 1 deletion osu.Game/Beatmaps/BeatmapUpdater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@

using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Framework.Threading;
using osu.Game.Database;
using osu.Game.Online.API;
using osu.Game.Rulesets.Objects.Types;

namespace osu.Game.Beatmaps
{
Expand Down Expand Up @@ -44,7 +46,8 @@ public BeatmapUpdater(IWorkingBeatmapCache workingBeatmapCache, BeatmapDifficult
public void Queue(Live<BeatmapSetInfo> beatmapSet, MetadataLookupScope lookupScope = MetadataLookupScope.LocalCacheFirst)
{
Logger.Log($"Queueing change for local beatmap {beatmapSet}");
Task.Factory.StartNew(() => beatmapSet.PerformRead(b => Process(b, lookupScope)), default, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler);
Task.Factory.StartNew(() => beatmapSet.PerformRead(b => Process(b, lookupScope)), default, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously,
updateScheduler);
}

/// <summary>
Expand Down Expand Up @@ -80,6 +83,21 @@ public void Process(BeatmapSetInfo beatmapSet, MetadataLookupScope lookupScope =
workingBeatmapCache.Invalidate(beatmapSet);
});

public void ProcessObjectCounts(BeatmapInfo beatmapInfo, MetadataLookupScope lookupScope = MetadataLookupScope.LocalCacheFirst) => beatmapInfo.Realm!.Write(_ =>
{
// Before we use below, we want to invalidate.
workingBeatmapCache.Invalidate(beatmapInfo);
var working = workingBeatmapCache.GetWorkingBeatmap(beatmapInfo);
var beatmap = working.Beatmap;
beatmapInfo.Difficulty.EndTimeObjectCount = beatmap.HitObjects.Count(h => h is IHasDuration);
beatmapInfo.Difficulty.TotalObjectCount = beatmap.HitObjects.Count;
// And invalidate again afterwards as re-fetching the most up-to-date database metadata will be required.
workingBeatmapCache.Invalidate(beatmapInfo);
});

#region Implementation of IDisposable

public void Dispose()
Expand Down
13 changes: 13 additions & 0 deletions osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,19 @@ public interface IBeatmapDifficultyInfo
/// </summary>
double SliderTickRate { get; }

/// <summary>
/// The number of hitobjects in the beatmap with a distinct end time.
/// </summary>
/// <remarks>
/// Canonically, these are hitobjects are either sliders or spinners.
/// </remarks>
int EndTimeObjectCount { get; }

/// <summary>
/// The total number of hitobjects in the beatmap.
/// </summary>
int TotalObjectCount { get; }

/// <summary>
/// Maps a difficulty value [0, 10] to a two-piece linear range of values.
/// </summary>
Expand Down
3 changes: 2 additions & 1 deletion osu.Game/Database/RealmAccess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ public class RealmAccess : IDisposable
/// 34 2023-08-21 Add BackgroundReprocessingFailed flag to ScoreInfo to track upgrade failures.
/// 35 2023-10-16 Clear key combinations of keybindings that are assigned to more than one action in a given settings section.
/// 36 2023-10-26 Add LegacyOnlineID to ScoreInfo. Move osu_scores_*_high IDs stored in OnlineID to LegacyOnlineID. Reset anomalous OnlineIDs.
/// 37 2023-12-10 Add EndTimeObjectCount and TotalObjectCount to BeatmapDifficulty.
/// </summary>
private const int schema_version = 36;
private const int schema_version = 37;

/// <summary>
/// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking realm retrieval during blocking periods.
Expand Down
2 changes: 2 additions & 0 deletions osu.Game/Online/API/Requests/Responses/APIBeatmap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ private double hitLengthInSeconds
CircleSize = CircleSize,
ApproachRate = ApproachRate,
OverallDifficulty = OverallDifficulty,
EndTimeObjectCount = SliderCount + SpinnerCount,
TotalObjectCount = CircleCount + SliderCount + SpinnerCount
};

IBeatmapSetInfo? IBeatmapInfo.BeatmapSet => BeatmapSet;
Expand Down
7 changes: 7 additions & 0 deletions osu.Game/Rulesets/ILegacyRuleset.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using osu.Game.Beatmaps;
using osu.Game.Rulesets.Scoring.Legacy;

namespace osu.Game.Rulesets
Expand All @@ -14,6 +15,12 @@ public interface ILegacyRuleset
/// </summary>
int LegacyID { get; }

/// <summary>
/// Retrieves the number of mania keys required to play the beatmap.
/// </summary>
/// <returns></returns>
int GetKeyCount(IBeatmapInfo beatmapInfo) => 0;

ILegacyScoreSimulator CreateLegacyScoreSimulator();
}
}
Loading

0 comments on commit fdcf875

Please sign in to comment.