1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-27 04:42:55 +08:00

Reimplement all query methods

This commit is contained in:
Dean Herbert 2021-12-13 19:01:20 +09:00
parent 53792811b2
commit 4f6a05ce3d
9 changed files with 81 additions and 41 deletions

View File

@ -142,7 +142,7 @@ namespace osu.Game.Tests.Scores.IO
var scoreManager = osu.Dependencies.Get<ScoreManager>();
beatmapManager.Delete(beatmapManager.QueryBeatmapSet(s => s.Beatmaps.Any(b => b.ID == imported.BeatmapInfo.ID)));
Assert.That(scoreManager.Query(s => s.Equals(imported)).DeletePending, Is.EqualTo(true));
Assert.That(scoreManager.Query(s => s.Equals(imported)).Value.DeletePending, Is.EqualTo(true));
var secondImport = await LoadScoreIntoOsu(osu, imported);
Assert.That(secondImport, Is.Null);
@ -187,7 +187,7 @@ namespace osu.Game.Tests.Scores.IO
var scoreManager = osu.Dependencies.Get<ScoreManager>();
await scoreManager.Import(score, archive);
return scoreManager.GetAllUsableScores().FirstOrDefault();
return scoreManager.Query(_ => true).Value;
}
internal class TestArchiveReader : ArchiveReader

View File

@ -184,7 +184,7 @@ namespace osu.Game.Tests.Visual.SongSelect
private void clearScores()
{
AddStep("Clear all scores", () => scoreManager.Delete(scoreManager.GetAllUsableScores()));
AddStep("Clear all scores", () => scoreManager.Delete());
}
private void checkCount(int expected) =>

View File

@ -44,6 +44,9 @@ namespace osu.Game.Tests.Visual.UserInterface
private BeatmapInfo beatmapInfo;
[Resolved]
private RealmContextFactory realmFactory { get; set; }
[Cached]
private readonly DialogOverlay dialogOverlay;
@ -112,8 +115,11 @@ namespace osu.Game.Tests.Visual.UserInterface
[SetUp]
public void Setup() => Schedule(() =>
{
// Due to soft deletions, we can re-use deleted scores between test runs
scoreManager.Undelete(scoreManager.QueryScores(s => s.DeletePending).ToList());
using (var realm = realmFactory.CreateContext())
{
// Due to soft deletions, we can re-use deleted scores between test runs
scoreManager.Undelete(realm.All<ScoreInfo>().Where(s => s.DeletePending).ToList());
}
leaderboard.Scores = null;
leaderboard.FinishTransforms(true); // After setting scores, we may be waiting for transforms to expire drawables

View File

@ -488,10 +488,10 @@ namespace osu.Game
ScoreInfo databasedScoreInfo = null;
if (score.OnlineID > 0)
databasedScoreInfo = ScoreManager.Query(s => s.OnlineID == score.OnlineID);
databasedScoreInfo = ScoreManager.Query(s => s.OnlineID == score.OnlineID)?.Value;
if (score is ScoreInfo scoreInfo)
databasedScoreInfo ??= ScoreManager.Query(s => s.Hash == scoreInfo.Hash);
databasedScoreInfo ??= ScoreManager.Query(s => s.Hash == scoreInfo.Hash)?.Value;
if (databasedScoreInfo == null)
{

View File

@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
dialogOverlay?.Push(new MassDeleteConfirmationDialog(() =>
{
deleteScoresButton.Enabled.Value = false;
Task.Run(() => scores.Delete(scores.GetAllUsableScores())).ContinueWith(t => Schedule(() => deleteScoresButton.Enabled.Value = true));
Task.Run(() => scores.Delete()).ContinueWith(t => Schedule(() => deleteScoresButton.Enabled.Value = true));
}));
}
});

View File

@ -26,6 +26,7 @@ namespace osu.Game.Scoring
{
public class ScoreManager : IModelManager<ScoreInfo>, IModelImporter<ScoreInfo>
{
private readonly RealmContextFactory contextFactory;
private readonly Scheduler scheduler;
private readonly Func<BeatmapDifficultyCache> difficulties;
private readonly OsuConfigManager configManager;
@ -34,6 +35,7 @@ namespace osu.Game.Scoring
public ScoreManager(RulesetStore rulesets, Func<BeatmapManager> beatmaps, Storage storage, RealmContextFactory contextFactory, Scheduler scheduler,
IIpcHost importHost = null, Func<BeatmapDifficultyCache> difficulties = null, OsuConfigManager configManager = null)
{
this.contextFactory = contextFactory;
this.scheduler = scheduler;
this.difficulties = difficulties;
this.configManager = configManager;
@ -43,11 +45,16 @@ namespace osu.Game.Scoring
public Score GetScore(ScoreInfo score) => scoreModelManager.GetScore(score);
public List<ScoreInfo> GetAllUsableScores() => scoreModelManager.GetAllUsableScores();
public IEnumerable<ScoreInfo> QueryScores(Expression<Func<ScoreInfo, bool>> query) => scoreModelManager.QueryScores(query);
public ScoreInfo Query(Expression<Func<ScoreInfo, bool>> query) => scoreModelManager.Query(query);
/// <summary>
/// Perform a lookup query on available <see cref="ScoreInfo"/>s.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>The first result for the provided query, or null if no results were found.</returns>
public ILive<ScoreInfo> Query(Expression<Func<ScoreInfo, bool>> query)
{
using (var context = contextFactory.CreateContext())
return context.All<ScoreInfo>().FirstOrDefault(query)?.ToLive();
}
/// <summary>
/// Orders an array of <see cref="ScoreInfo"/>s by total score.
@ -267,6 +274,20 @@ namespace osu.Game.Scoring
return scoreModelManager.Delete(item);
}
public void Delete([CanBeNull] Expression<Func<ScoreInfo, bool>> filter = null, bool silent = false)
{
using (var context = contextFactory.CreateContext())
{
var items = context.All<ScoreInfo>()
.Where(s => !s.DeletePending);
if (filter != null)
items = items.Where(filter);
scoreModelManager.Delete(items.ToList(), silent);
}
}
public void Delete(List<ScoreInfo> items, bool silent = false)
{
scoreModelManager.Delete(items, silent);

View File

@ -1,14 +1,13 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Overlays.Dialog;
using osu.Game.Scoring;
using System;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Graphics.Sprites;
namespace osu.Game.Screens.Select
{
@ -29,7 +28,7 @@ namespace osu.Game.Screens.Select
Text = @"Yes. Please.",
Action = () =>
{
Task.Run(() => scoreManager.Delete(scoreManager.QueryScores(s => !s.DeletePending && s.BeatmapInfo.ID == beatmapInfo.ID).ToList()))
Task.Run(() => scoreManager.Delete(s => !s.DeletePending && s.BeatmapInfo.ID == beatmapInfo.ID))
.ContinueWith(_ => onCompletion);
}
},

View File

@ -7,6 +7,7 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Online.API;
using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets;
@ -24,6 +25,9 @@ namespace osu.Game.Screens.Select.Carousel
[Resolved]
private IBindable<RulesetInfo> ruleset { get; set; }
[Resolved]
private RealmContextFactory realmFactory { get; set; }
[Resolved]
private IAPIProvider api { get; set; }
@ -54,7 +58,7 @@ namespace osu.Game.Screens.Select.Carousel
private void fetchAndLoadTopScore()
{
var rank = fetchTopScore()?.Rank;
var rank = fetchTopScoreRank();
scheduledRankUpdate = Schedule(() =>
{
Rank = rank;
@ -67,14 +71,18 @@ namespace osu.Game.Screens.Select.Carousel
// We're present if a rank is set, or if there is a pending rank update (IsPresent = true is required for the scheduler to run).
public override bool IsPresent => base.IsPresent && (Rank != null || scheduledRankUpdate?.Completed == false);
private ScoreInfo fetchTopScore()
private ScoreRank? fetchTopScoreRank()
{
if (scores == null || beatmapInfo == null || ruleset?.Value == null || api?.LocalUser.Value == null)
if (realmFactory == null || beatmapInfo == null || ruleset?.Value == null || api?.LocalUser.Value == null)
return null;
return scores.QueryScores(s => s.UserID == api.LocalUser.Value.Id && s.BeatmapInfoID == beatmapInfo.ID && s.RulesetID == ruleset.Value.ID && !s.DeletePending)
.OrderByDescending(s => s.TotalScore)
.FirstOrDefault();
using (var realm = realmFactory.CreateContext())
{
return realm.All<ScoreInfo>().Where(s => s.UserID == api.LocalUser.Value.Id && s.BeatmapInfoID == beatmapInfo.ID && s.RulesetID == ruleset.Value.ID && !s.DeletePending)
.OrderByDescending(s => s.TotalScore)
.FirstOrDefault()
?.Rank;
}
}
protected override void Dispose(bool isDisposing)

View File

@ -10,6 +10,7 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.Leaderboards;
@ -26,6 +27,9 @@ namespace osu.Game.Screens.Select.Leaderboards
[Resolved]
private RulesetStore rulesets { get; set; }
[Resolved]
private RealmContextFactory realmFactory { get; set; }
private BeatmapInfo beatmapInfo;
public BeatmapInfo BeatmapInfo
@ -126,26 +130,28 @@ namespace osu.Game.Screens.Select.Leaderboards
if (Scope == BeatmapLeaderboardScope.Local)
{
var scores = scoreManager
.QueryScores(s => !s.DeletePending && s.BeatmapInfo.ID == fetchBeatmapInfo.ID && s.Ruleset.OnlineID == ruleset.Value.ID);
if (filterMods && !mods.Value.Any())
using (var realm = realmFactory.CreateContext())
{
// we need to filter out all scores that have any mods to get all local nomod scores
scores = scores.Where(s => !s.Mods.Any());
}
else if (filterMods)
{
// otherwise find all the scores that have *any* of the currently selected mods (similar to how web applies mod filters)
// we're creating and using a string list representation of selected mods so that it can be translated into the DB query itself
var selectedMods = mods.Value.Select(m => m.Acronym);
scores = scores.Where(s => s.Mods.Any(m => selectedMods.Contains(m.Acronym)));
}
var scores = realm.All<ScoreInfo>().Where(s => !s.DeletePending && s.BeatmapInfo.ID == fetchBeatmapInfo.ID && s.Ruleset.OnlineID == ruleset.Value.ID);
scoreManager.OrderByTotalScoreAsync(scores.ToArray(), cancellationToken)
.ContinueWith(task => scoresCallback?.Invoke(task.GetResultSafely()), TaskContinuationOptions.OnlyOnRanToCompletion);
if (filterMods && !mods.Value.Any())
{
// we need to filter out all scores that have any mods to get all local nomod scores
scores = scores.Where(s => !s.Mods.Any());
}
else if (filterMods)
{
// otherwise find all the scores that have *any* of the currently selected mods (similar to how web applies mod filters)
// we're creating and using a string list representation of selected mods so that it can be translated into the DB query itself
var selectedMods = mods.Value.Select(m => m.Acronym);
scores = scores.Where(s => s.Mods.Any(m => selectedMods.Contains(m.Acronym)));
}
return null;
scoreManager.OrderByTotalScoreAsync(scores.ToArray(), cancellationToken)
.ContinueWith(ordered => scoresCallback?.Invoke(ordered.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
return null;
}
}
if (api?.IsLoggedIn != true)