Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 33 additions & 2 deletions osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using osu.Game.Rulesets;
using osu.Game.Rulesets.Filter;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Filter;

Expand Down Expand Up @@ -454,6 +455,36 @@
ClassicAssert.True(carouselItem.Filtered.Value);
}

[Test]
[TestCase("toprank=None", 0)]
[TestCase("toprank=X", 1)]
[TestCase("toprank=A")]
public void TestTopRank(string query, params int[] expectedBeatmapIndexes)
{
var carouselBeatmaps = new[]
{
Guid.NewGuid(),
Guid.Empty
}.Select(info => new CarouselBeatmap(new BeatmapInfo
{
ID = info
})).ToList();

Dictionary<Guid, ScoreRank> localUserTopRanks = new Dictionary<Guid, ScoreRank> {

Check warning

Code scanning / InspectCode

Incorrect line breaks: Around expression braces Warning test

Line break is missing around expression braces
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
{ Guid.Empty, ScoreRank.X }
};
//localUserTopRanks.Add();

var criteria = new FilterCriteria();
FilterQueryParser.ApplyQueries(criteria, query);
carouselBeatmaps.ForEach(b => b.Filter(criteria, localUserTopRanks));

int[] visibleBeatmaps = carouselBeatmaps
.Where(b => !b.Filtered.Value)
.Select(b => carouselBeatmaps.IndexOf(b)).ToArray();
Assert.That(visibleBeatmaps, Is.EqualTo(expectedBeatmapIndexes));
}

[Test]
public void TestCustomRulesetCriteria([Values(null, true, false)] bool? matchCustomCriteria)
{
Expand Down Expand Up @@ -642,9 +673,9 @@
BeatmapInfo = beatmapInfo;
}

public void Filter(FilterCriteria criteria)
public void Filter(FilterCriteria criteria, IReadOnlyDictionary<Guid, ScoreRank>? localUserTopRanks = null)
{
Filtered.Value = !BeatmapCarouselFilterMatching.CheckCriteriaMatch(BeatmapInfo, criteria);
Filtered.Value = !BeatmapCarouselFilterMatching.CheckCriteriaMatch(BeatmapInfo, criteria, localUserTopRanks ?? new Dictionary<Guid, ScoreRank>());
}
}

Expand Down
2 changes: 2 additions & 0 deletions osu.Game/Scoring/ScoreRank.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ namespace osu.Game.Scoring
{
public enum ScoreRank
{
None = -2,

// TODO: Localisable?
F = -1,

Expand Down
2 changes: 1 addition & 1 deletion osu.Game/Screens/Select/BeatmapCarousel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public BeatmapCarousel()

Filters = new ICarouselFilter[]
{
new BeatmapCarouselFilterMatching(() => Criteria!),
new BeatmapCarouselFilterMatching(() => Criteria!, GetBeatmapInfoGuidToTopRankMapping),
new BeatmapCarouselFilterSorting(() => Criteria!),
grouping = new BeatmapCarouselFilterGrouping
{
Expand Down
21 changes: 14 additions & 7 deletions osu.Game/Screens/Select/BeatmapCarouselFilterMatching.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,31 @@
using System.Threading.Tasks;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Carousel;
using osu.Game.Scoring;
using osu.Game.Utils;

namespace osu.Game.Screens.Select
{
public class BeatmapCarouselFilterMatching : ICarouselFilter
{
private readonly Func<FilterCriteria> getCriteria;

private readonly Func<FilterCriteria, IReadOnlyDictionary<Guid, ScoreRank>> getLocalUserTopRanks;
public int BeatmapItemsCount { get; private set; }

public BeatmapCarouselFilterMatching(Func<FilterCriteria> getCriteria)
public BeatmapCarouselFilterMatching(Func<FilterCriteria> getCriteria, Func<FilterCriteria, IReadOnlyDictionary<Guid, ScoreRank>> getLocalUserTopRanks)
{
this.getCriteria = getCriteria;
this.getLocalUserTopRanks = getLocalUserTopRanks;
}

public async Task<List<CarouselItem>> Run(IEnumerable<CarouselItem> items, CancellationToken cancellationToken) => await Task.Run(() =>
{
var criteria = getCriteria();

return matchItems(items, criteria).ToList();
var localUserTopRanks = getLocalUserTopRanks(criteria);
return matchItems(items, criteria, localUserTopRanks).ToList();
}, cancellationToken).ConfigureAwait(false);

private IEnumerable<CarouselItem> matchItems(IEnumerable<CarouselItem> items, FilterCriteria criteria)
private IEnumerable<CarouselItem> matchItems(IEnumerable<CarouselItem> items, FilterCriteria criteria, IReadOnlyDictionary<Guid, ScoreRank> localUserTopRanks)
{
int countMatching = 0;

Expand All @@ -41,7 +43,7 @@ private IEnumerable<CarouselItem> matchItems(IEnumerable<CarouselItem> items, Fi
if (beatmap.Hidden)
continue;

if (!CheckCriteriaMatch(beatmap, criteria))
if (!CheckCriteriaMatch(beatmap, criteria, localUserTopRanks))
continue;

countMatching++;
Expand All @@ -51,7 +53,7 @@ private IEnumerable<CarouselItem> matchItems(IEnumerable<CarouselItem> items, Fi
BeatmapItemsCount = countMatching;
}

public static bool CheckCriteriaMatch(BeatmapInfo beatmap, FilterCriteria criteria)
public static bool CheckCriteriaMatch(BeatmapInfo beatmap, FilterCriteria criteria, IReadOnlyDictionary<Guid, ScoreRank> localUserTopRanks)
{
bool match = criteria.Ruleset == null || beatmap.AllowGameplayWithRuleset(criteria.Ruleset!, criteria.AllowConvertedBeatmaps);

Expand Down Expand Up @@ -92,6 +94,11 @@ public static bool CheckCriteriaMatch(BeatmapInfo beatmap, FilterCriteria criter
match &= !criteria.BeatDivisor.HasFilter || criteria.BeatDivisor.IsInRange(beatmap.BeatDivisor);
match &= !criteria.OnlineStatus.HasFilter || criteria.OnlineStatus.IsInRange(beatmap.Status);

if (localUserTopRanks.TryGetValue(beatmap.ID, out ScoreRank scoreRank))
match &= !criteria.TopRank.HasFilter || criteria.TopRank.IsInRange(scoreRank);
else
match &= !criteria.TopRank.HasFilter || criteria.TopRank.IsInRange(ScoreRank.None);

if (!match) return false;

match &= !criteria.Creator.HasFilter || criteria.Creator.Matches(beatmap.Metadata.Author.Username);
Expand Down
2 changes: 2 additions & 0 deletions osu.Game/Screens/Select/FilterCriteria.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using osu.Game.Rulesets;
using osu.Game.Rulesets.Filter;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
using osu.Game.Screens.Select.Filter;

namespace osu.Game.Screens.Select
Expand Down Expand Up @@ -40,6 +41,7 @@ public class FilterCriteria
public OptionalTextFilter DifficultyName;
public OptionalTextFilter Source;
public List<OptionalTextFilter> UserTags = [];
public OptionalSet<ScoreRank> TopRank = new OptionalSet<ScoreRank>();

public OptionalRange<double> UserStarDifficulty = new OptionalRange<double>
{
Expand Down
3 changes: 3 additions & 0 deletions osu.Game/Screens/Select/FilterQueryParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ private static bool tryParseKeywordCriteria(FilterCriteria criteria, string key,
criteria.UserTags.Add(tagFilter);
return true;

case "toprank":
return TryUpdateCriteriaSet(ref criteria.TopRank, op, value);

default:
return criteria.RulesetCriteria?.TryParseCustomKeywordCriteria(key, op, value) ?? false;
}
Expand Down
Loading