Various updates to ruleset and primary key usages to move closer to realm support

This commit is contained in:
Dean Herbert
2021-11-24 12:16:08 +09:00
parent b77bb2f12b
commit b8cd3cdbbc
22 changed files with 64 additions and 73 deletions

View File

@ -505,7 +505,7 @@ namespace osu.Game.Tests.Database
RunTestWithRealmAsync(async (realmFactory, storage) => RunTestWithRealmAsync(async (realmFactory, storage) =>
{ {
using var importer = new NonOptimisedBeatmapImporter(realmFactory, storage); using var importer = new NonOptimisedBeatmapImporter(realmFactory, storage);
using var store = new RealmRulesetStore(realmFactory, storage); using var store = new RulesetStore(realmFactory, storage);
var imported = await LoadOszIntoStore(importer, realmFactory.Context); var imported = await LoadOszIntoStore(importer, realmFactory.Context);

View File

@ -71,7 +71,7 @@ namespace osu.Game.Tests.Visual.UserInterface
InputManager.MoveMouseTo(item.ScreenSpaceDrawQuad.Centre); InputManager.MoveMouseTo(item.ScreenSpaceDrawQuad.Centre);
}); });
AddAssert("song 1 is 5th", () => beatmapSets[4] == first); AddAssert("song 1 is 5th", () => beatmapSets[4].Equals(first));
AddStep("release handle", () => InputManager.ReleaseButton(MouseButton.Left)); AddStep("release handle", () => InputManager.ReleaseButton(MouseButton.Left));
} }

View File

@ -62,7 +62,7 @@ namespace osu.Game.Beatmaps
} }
[JsonIgnore] [JsonIgnore]
public BeatmapMetadata Metadata => BeatmapInfo?.Metadata ?? BeatmapInfo?.BeatmapSet?.Metadata; public BeatmapMetadata Metadata => BeatmapInfo.Metadata;
public ControlPointInfo ControlPointInfo { get; set; } = new ControlPointInfo(); public ControlPointInfo ControlPointInfo { get; set; } = new ControlPointInfo();

View File

@ -32,7 +32,7 @@ namespace osu.Game.Beatmaps
/// Handles general operations related to global beatmap management. /// Handles general operations related to global beatmap management.
/// </summary> /// </summary>
[ExcludeFromDynamicCompile] [ExcludeFromDynamicCompile]
public class BeatmapManager : IModelManager<BeatmapSetInfo>, IModelFileManager<BeatmapSetInfo, BeatmapSetFileInfo>, IModelImporter<BeatmapSetInfo>, IWorkingBeatmapCache, IDisposable public class BeatmapManager : IModelManager<BeatmapSetInfo>, IModelFileManager<BeatmapSetInfo, RealmNamedFileUsage>, IModelImporter<BeatmapSetInfo>, IWorkingBeatmapCache, IDisposable
{ {
public ITrackStore BeatmapTrackStore { get; } public ITrackStore BeatmapTrackStore { get; }
@ -294,12 +294,12 @@ namespace osu.Game.Beatmaps
#region Implementation of IModelFileManager<in BeatmapSetInfo,in BeatmapSetFileInfo> #region Implementation of IModelFileManager<in BeatmapSetInfo,in BeatmapSetFileInfo>
public void ReplaceFile(BeatmapSetInfo model, BeatmapSetFileInfo file, Stream contents) public void ReplaceFile(BeatmapSetInfo model, RealmNamedFileUsage file, Stream contents)
{ {
beatmapModelManager.ReplaceFile(model, file, contents); beatmapModelManager.ReplaceFile(model, file, contents);
} }
public void DeleteFile(BeatmapSetInfo model, BeatmapSetFileInfo file) public void DeleteFile(BeatmapSetInfo model, RealmNamedFileUsage file)
{ {
beatmapModelManager.DeleteFile(model, file); beatmapModelManager.DeleteFile(model, file);
} }

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
@ -16,6 +17,7 @@ using osu.Framework.Threading;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
using osu.Game.Stores;
using SharpCompress.Compressors; using SharpCompress.Compressors;
using SharpCompress.Compressors.BZip2; using SharpCompress.Compressors.BZip2;
@ -83,15 +85,14 @@ namespace osu.Game.Beatmaps
if (res != null) if (res != null)
{ {
beatmapInfo.Status = res.Status; beatmapInfo.Status = res.Status;
Debug.Assert(beatmapInfo.BeatmapSet != null);
beatmapInfo.BeatmapSet.Status = res.BeatmapSet?.Status ?? BeatmapOnlineStatus.None; beatmapInfo.BeatmapSet.Status = res.BeatmapSet?.Status ?? BeatmapOnlineStatus.None;
beatmapInfo.BeatmapSet.OnlineID = res.OnlineBeatmapSetID; beatmapInfo.BeatmapSet.OnlineID = res.OnlineBeatmapSetID;
beatmapInfo.OnlineID = res.OnlineID; beatmapInfo.OnlineID = res.OnlineID;
if (beatmapInfo.Metadata != null) beatmapInfo.Metadata.Author.OnlineID = res.AuthorID;
beatmapInfo.Metadata.AuthorID = res.AuthorID;
if (beatmapInfo.BeatmapSet.Metadata != null)
beatmapInfo.BeatmapSet.Metadata.AuthorID = res.AuthorID;
logForModel(set, $"Online retrieval mapped {beatmapInfo} to {res.OnlineBeatmapSetID} / {res.OnlineID}."); logForModel(set, $"Online retrieval mapped {beatmapInfo} to {res.OnlineBeatmapSetID} / {res.OnlineID}.");
} }
@ -185,15 +186,14 @@ namespace osu.Game.Beatmaps
var status = (BeatmapOnlineStatus)reader.GetByte(2); var status = (BeatmapOnlineStatus)reader.GetByte(2);
beatmapInfo.Status = status; beatmapInfo.Status = status;
Debug.Assert(beatmapInfo.BeatmapSet != null);
beatmapInfo.BeatmapSet.Status = status; beatmapInfo.BeatmapSet.Status = status;
beatmapInfo.BeatmapSet.OnlineID = reader.GetInt32(0); beatmapInfo.BeatmapSet.OnlineID = reader.GetInt32(0);
beatmapInfo.OnlineID = reader.GetInt32(1); beatmapInfo.OnlineID = reader.GetInt32(1);
if (beatmapInfo.Metadata != null) beatmapInfo.Metadata.Author.OnlineID = reader.GetInt32(3);
beatmapInfo.Metadata.AuthorID = reader.GetInt32(3);
if (beatmapInfo.BeatmapSet.Metadata != null)
beatmapInfo.BeatmapSet.Metadata.AuthorID = reader.GetInt32(3);
logForModel(set, $"Cached local retrieval for {beatmapInfo}."); logForModel(set, $"Cached local retrieval for {beatmapInfo}.");
return true; return true;
@ -211,7 +211,7 @@ namespace osu.Game.Beatmaps
} }
private void logForModel(BeatmapSetInfo set, string message) => private void logForModel(BeatmapSetInfo set, string message) =>
ArchiveModelManager<BeatmapSetInfo, BeatmapSetFileInfo>.LogForModel(set, $"[{nameof(BeatmapOnlineLookupQueue)}] {message}"); RealmArchiveModelImporter<BeatmapSetInfo>.LogForModel(set, $"[{nameof(BeatmapOnlineLookupQueue)}] {message}");
public void Dispose() public void Dispose()
{ {

View File

@ -79,7 +79,7 @@ namespace osu.Game.Beatmaps
// if there are no files, presume the full beatmap info has not yet been fetched from the database. // if there are no files, presume the full beatmap info has not yet been fetched from the database.
if (beatmapInfo?.BeatmapSet?.Files.Count == 0) if (beatmapInfo?.BeatmapSet?.Files.Count == 0)
{ {
int lookupId = beatmapInfo.ID; var lookupId = beatmapInfo.ID;
beatmapInfo = BeatmapManager.QueryBeatmap(b => b.ID == lookupId); beatmapInfo = BeatmapManager.QueryBeatmap(b => b.ID == lookupId);
} }
@ -93,7 +93,8 @@ namespace osu.Game.Beatmaps
if (working != null) if (working != null)
return working; return working;
beatmapInfo.Metadata ??= beatmapInfo.BeatmapSet.Metadata; // TODO: is this still required..?
//beatmapInfo.Metadata ??= beatmapInfo.BeatmapSet.Metadata;
workingCache.Add(working = new BeatmapManagerWorkingBeatmap(beatmapInfo, this)); workingCache.Add(working = new BeatmapManagerWorkingBeatmap(beatmapInfo, this));

View File

@ -11,7 +11,7 @@ namespace osu.Game.Database
{ {
protected readonly Storage Storage; protected readonly Storage Storage;
protected readonly IDatabaseContextFactory ContextFactory; protected readonly RealmContextFactory ContextFactory;
/// <summary> /// <summary>
/// Refresh an instance potentially from a different thread with a local context-tracked instance. /// Refresh an instance potentially from a different thread with a local context-tracked instance.
@ -36,7 +36,7 @@ namespace osu.Game.Database
} }
} }
protected DatabaseBackedStore(IDatabaseContextFactory contextFactory, Storage storage = null) protected DatabaseBackedStore(RealmContextFactory contextFactory, Storage storage = null)
{ {
ContextFactory = contextFactory; ContextFactory = contextFactory;
Storage = storage; Storage = storage;

View File

@ -14,7 +14,7 @@ using osu.Game.Overlays.Notifications;
namespace osu.Game.Database namespace osu.Game.Database
{ {
public abstract class ModelDownloader<TModel, T> : IModelDownloader<T> public abstract class ModelDownloader<TModel, T> : IModelDownloader<T>
where TModel : class, IHasPrimaryKey, ISoftDelete, IEquatable<TModel>, T where TModel : class, IHasGuidPrimaryKey, ISoftDelete, IEquatable<TModel>, T
where T : class where T : class
{ {
public Action<Notification> PostNotification { protected get; set; } public Action<Notification> PostNotification { protected get; set; }

View File

@ -78,7 +78,7 @@ namespace osu.Game.Input.Bindings
{ {
var defaults = DefaultKeyBindings.ToList(); var defaults = DefaultKeyBindings.ToList();
if (ruleset != null && !ruleset.ID.HasValue) if (ruleset != null && !ruleset.IsManaged)
// some tests instantiate a ruleset which is not present in the database. // some tests instantiate a ruleset which is not present in the database.
// in these cases we still want key bindings to work, but matching to database instances would result in none being present, // in these cases we still want key bindings to work, but matching to database instances would result in none being present,
// so let's populate the defaults directly. // so let's populate the defaults directly.

View File

@ -190,9 +190,6 @@ namespace osu.Game
runMigrations(); runMigrations();
dependencies.Cache(RulesetStore = new RulesetStore(contextFactory, Storage));
dependencies.CacheAs<IRulesetStore>(RulesetStore);
dependencies.Cache(realmFactory = new RealmContextFactory(Storage, "client", contextFactory)); dependencies.Cache(realmFactory = new RealmContextFactory(Storage, "client", contextFactory));
new EFToRealmMigrator(contextFactory, realmFactory, LocalConfig).Run(); new EFToRealmMigrator(contextFactory, realmFactory, LocalConfig).Run();
@ -227,24 +224,12 @@ namespace osu.Game
dependencies.Cache(RulesetStore = new RulesetStore(realmFactory, Storage)); dependencies.Cache(RulesetStore = new RulesetStore(realmFactory, Storage));
// ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup() // ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup()
dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, contextFactory, Scheduler, Host, () => difficultyCache, LocalConfig)); dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, realmFactory, Scheduler, Host, () => difficultyCache, LocalConfig));
dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Resources, Host, defaultBeatmap, performOnlineLookups: true)); dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, realmFactory, RulesetStore, API, Audio, Resources, Host, defaultBeatmap, performOnlineLookups: true));
dependencies.Cache(BeatmapDownloader = new BeatmapModelDownloader(BeatmapManager, API)); dependencies.Cache(BeatmapDownloader = new BeatmapModelDownloader(BeatmapManager, API));
dependencies.Cache(ScoreDownloader = new ScoreModelDownloader(ScoreManager, API)); dependencies.Cache(ScoreDownloader = new ScoreModelDownloader(ScoreManager, API));
// this should likely be moved to ArchiveModelManager when another case appears where it is necessary
// to have inter-dependent model managers. this could be obtained with an IHasForeign<T> interface to
// allow lookups to be done on the child (ScoreManager in this case) to perform the cascading delete.
List<ScoreInfo> getBeatmapScores(BeatmapSetInfo set)
{
var beatmapIds = BeatmapManager.QueryBeatmaps(b => b.BeatmapSetInfoID == set.ID).Select(b => b.ID).ToList();
return ScoreManager.QueryScores(s => beatmapIds.Contains(s.BeatmapInfo.ID)).ToList();
}
BeatmapManager.ItemRemoved += item => ScoreManager.Delete(getBeatmapScores(item), true);
BeatmapManager.ItemUpdated += item => ScoreManager.Undelete(getBeatmapScores(item), true);
dependencies.Cache(difficultyCache = new BeatmapDifficultyCache()); dependencies.Cache(difficultyCache = new BeatmapDifficultyCache());
AddInternal(difficultyCache); AddInternal(difficultyCache);
@ -438,7 +423,9 @@ namespace osu.Game
private void onRulesetChanged(ValueChangedEvent<RulesetInfo> r) private void onRulesetChanged(ValueChangedEvent<RulesetInfo> r)
{ {
if (r.NewValue?.Available != true) Ruleset instance;
if (r.NewValue?.Available != true || (instance = r.NewValue.CreateInstance()) == null)
{ {
// reject the change if the ruleset is not available. // reject the change if the ruleset is not available.
Ruleset.Value = r.OldValue?.Available == true ? r.OldValue : RulesetStore.AvailableRulesets.First(); Ruleset.Value = r.OldValue?.Available == true ? r.OldValue : RulesetStore.AvailableRulesets.First();
@ -448,7 +435,9 @@ namespace osu.Game
var dict = new Dictionary<ModType, IReadOnlyList<Mod>>(); var dict = new Dictionary<ModType, IReadOnlyList<Mod>>();
foreach (ModType type in Enum.GetValues(typeof(ModType))) foreach (ModType type in Enum.GetValues(typeof(ModType)))
dict[type] = r.NewValue.CreateInstance().GetModsFor(type).ToList(); {
dict[type] = instance.GetModsFor(type).ToList();
}
if (!SelectedMods.Disabled) if (!SelectedMods.Disabled)
SelectedMods.Value = Array.Empty<Mod>(); SelectedMods.Value = Array.Empty<Mod>();
@ -489,7 +478,6 @@ namespace osu.Game
contextFactory?.FlushConnections(); contextFactory?.FlushConnections();
realmRulesetStore?.Dispose();
realmFactory?.Dispose(); realmFactory?.Dispose();
} }
} }

View File

@ -40,7 +40,7 @@ namespace osu.Game.Rulesets
throw new RulesetLoadException(@"Instantiation failure"); throw new RulesetLoadException(@"Instantiation failure");
// overwrite the pre-populated RulesetInfo with a potentially database attached copy. // overwrite the pre-populated RulesetInfo with a potentially database attached copy.
ruleset.RulesetInfo = this; // ruleset.RulesetInfo = this;
return ruleset; return ruleset;
} }

View File

@ -29,7 +29,7 @@ namespace osu.Game.Screens.Edit.Verify
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
InterpretedDifficulty.Default = EditorBeatmap.BeatmapInfo.DifficultyRating; InterpretedDifficulty.Default = BeatmapDifficultyCache.GetDifficultyRating(EditorBeatmap.BeatmapInfo.StarRating);
InterpretedDifficulty.SetDefault(); InterpretedDifficulty.SetDefault();
IssueList = new IssueList(); IssueList = new IssueList();

View File

@ -226,8 +226,8 @@ namespace osu.Game.Screens.Play
// ensure the score is in a consistent state with the current player. // ensure the score is in a consistent state with the current player.
Score.ScoreInfo.BeatmapInfo = Beatmap.Value.BeatmapInfo; Score.ScoreInfo.BeatmapInfo = Beatmap.Value.BeatmapInfo;
Score.ScoreInfo.Ruleset = ruleset.RulesetInfo; Score.ScoreInfo.Ruleset = ruleset.RulesetInfo;
if (ruleset.RulesetInfo.ID != null) if (ruleset.RulesetInfo.OnlineID >= 0)
Score.ScoreInfo.RulesetID = ruleset.RulesetInfo.ID.Value; Score.ScoreInfo.RulesetID = ruleset.RulesetInfo.OnlineID;
Score.ScoreInfo.Mods = gameplayMods; Score.ScoreInfo.Mods = gameplayMods;
dependencies.CacheAs(GameplayState = new GameplayState(playableBeatmap, ruleset, gameplayMods, Score)); dependencies.CacheAs(GameplayState = new GameplayState(playableBeatmap, ruleset, gameplayMods, Score));
@ -488,6 +488,9 @@ namespace osu.Game.Screens.Play
var rulesetInfo = Ruleset.Value ?? Beatmap.Value.BeatmapInfo.Ruleset; var rulesetInfo = Ruleset.Value ?? Beatmap.Value.BeatmapInfo.Ruleset;
ruleset = rulesetInfo.CreateInstance(); ruleset = rulesetInfo.CreateInstance();
if (ruleset == null)
throw new RulesetLoadException("Instantiation failure");
try try
{ {
playable = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, gameplayMods); playable = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, gameplayMods);

View File

@ -26,7 +26,7 @@ namespace osu.Game.Screens.Play
protected override APIRequest<APIScoreToken> CreateTokenRequest() protected override APIRequest<APIScoreToken> CreateTokenRequest()
{ {
int beatmapId = Beatmap.Value.BeatmapInfo.OnlineID ?? -1; int beatmapId = Beatmap.Value.BeatmapInfo.OnlineID;
int rulesetId = Ruleset.Value.OnlineID; int rulesetId = Ruleset.Value.OnlineID;
if (beatmapId <= 0) if (beatmapId <= 0)

View File

@ -197,7 +197,8 @@ namespace osu.Game.Screens.Select
public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() => public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() =>
{ {
int? previouslySelectedID = null; Guid? previouslySelectedID = null;
CarouselBeatmapSet existingSet = beatmapSets.FirstOrDefault(b => b.BeatmapSet.Equals(beatmapSet)); CarouselBeatmapSet existingSet = beatmapSets.FirstOrDefault(b => b.BeatmapSet.Equals(beatmapSet));
// If the selected beatmap is about to be removed, store its ID so it can be re-selected if required // If the selected beatmap is about to be removed, store its ID so it can be re-selected if required
@ -626,9 +627,9 @@ namespace osu.Game.Screens.Select
if (beatmapSet.Beatmaps.All(b => b.Hidden)) if (beatmapSet.Beatmaps.All(b => b.Hidden))
return null; return null;
// todo: remove the need for this. // todo: probably not required any more.
foreach (var b in beatmapSet.Beatmaps) // foreach (var b in beatmapSet.Beatmaps)
b.Metadata ??= beatmapSet.Metadata; // b.Metadata ??= beatmapSet.Metadata;
var set = new CarouselBeatmapSet(beatmapSet) var set = new CarouselBeatmapSet(beatmapSet)
{ {

View File

@ -48,7 +48,7 @@ namespace osu.Game.Screens.Select.Carousel
if (LastSelected == null || LastSelected.Filtered.Value) if (LastSelected == null || LastSelected.Filtered.Value)
{ {
if (GetRecommendedBeatmap?.Invoke(Children.OfType<CarouselBeatmap>().Where(b => !b.Filtered.Value).Select(b => b.BeatmapInfo)) is BeatmapInfo recommended) if (GetRecommendedBeatmap?.Invoke(Children.OfType<CarouselBeatmap>().Where(b => !b.Filtered.Value).Select(b => b.BeatmapInfo)) is BeatmapInfo recommended)
return Children.OfType<CarouselBeatmap>().First(b => b.BeatmapInfo == recommended); return Children.OfType<CarouselBeatmap>().First(b => b.BeatmapInfo.Equals(recommended));
} }
return base.GetNextToSelect(); return base.GetNextToSelect();
@ -63,16 +63,16 @@ namespace osu.Game.Screens.Select.Carousel
{ {
default: default:
case SortMode.Artist: case SortMode.Artist:
return string.Compare(BeatmapSet.Metadata.Artist, otherSet.BeatmapSet.Metadata.Artist, StringComparison.OrdinalIgnoreCase); return string.Compare(BeatmapSet.Metadata?.Artist, otherSet.BeatmapSet.Metadata?.Artist, StringComparison.OrdinalIgnoreCase);
case SortMode.Title: case SortMode.Title:
return string.Compare(BeatmapSet.Metadata.Title, otherSet.BeatmapSet.Metadata.Title, StringComparison.OrdinalIgnoreCase); return string.Compare(BeatmapSet.Metadata?.Title, otherSet.BeatmapSet.Metadata?.Title, StringComparison.OrdinalIgnoreCase);
case SortMode.Author: case SortMode.Author:
return string.Compare(BeatmapSet.Metadata.Author.Username, otherSet.BeatmapSet.Metadata.Author.Username, StringComparison.OrdinalIgnoreCase); return string.Compare(BeatmapSet.Metadata?.Author.Username, otherSet.BeatmapSet.Metadata?.Author.Username, StringComparison.OrdinalIgnoreCase);
case SortMode.Source: case SortMode.Source:
return string.Compare(BeatmapSet.Metadata.Source, otherSet.BeatmapSet.Metadata.Source, StringComparison.OrdinalIgnoreCase); return string.Compare(BeatmapSet.Metadata?.Source, otherSet.BeatmapSet.Metadata?.Source, StringComparison.OrdinalIgnoreCase);
case SortMode.DateAdded: case SortMode.DateAdded:
return otherSet.BeatmapSet.DateAdded.CompareTo(BeatmapSet.DateAdded); return otherSet.BeatmapSet.DateAdded.CompareTo(BeatmapSet.DateAdded);

View File

@ -41,13 +41,13 @@ namespace osu.Game.Screens.Select.Carousel
{ {
new OsuSpriteText new OsuSpriteText
{ {
Text = new RomanisableString(beatmapSet.Metadata.TitleUnicode, beatmapSet.Metadata.Title), Text = new RomanisableString(beatmapSet.Metadata?.TitleUnicode, beatmapSet.Metadata?.Title),
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 22, italics: true), Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 22, italics: true),
Shadow = true, Shadow = true,
}, },
new OsuSpriteText new OsuSpriteText
{ {
Text = new RomanisableString(beatmapSet.Metadata.ArtistUnicode, beatmapSet.Metadata.Artist), Text = new RomanisableString(beatmapSet.Metadata?.ArtistUnicode, beatmapSet.Metadata?.Artist),
Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 17, italics: true), Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 17, italics: true),
Shadow = true, Shadow = true,
}, },

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Diagnostics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -37,8 +36,6 @@ namespace osu.Game.Screens.Select
public FilterCriteria CreateCriteria() public FilterCriteria CreateCriteria()
{ {
Debug.Assert(ruleset.Value.ID != null);
string query = searchTextBox.Text; string query = searchTextBox.Text;
var criteria = new FilterCriteria var criteria = new FilterCriteria
@ -56,7 +53,7 @@ namespace osu.Game.Screens.Select
if (!maximumStars.IsDefault) if (!maximumStars.IsDefault)
criteria.UserStarDifficulty.Max = maximumStars.Value; criteria.UserStarDifficulty.Max = maximumStars.Value;
criteria.RulesetCriteria = ruleset.Value.CreateInstance().CreateRulesetFilterCriteria(); criteria.RulesetCriteria = ruleset.Value.CreateInstance()?.CreateRulesetFilterCriteria();
FilterQueryParser.ApplyQueries(criteria, query); FilterQueryParser.ApplyQueries(criteria, query);
return criteria; return criteria;

View File

@ -33,7 +33,7 @@ namespace osu.Game.Screens.Select.Leaderboards
get => beatmapInfo; get => beatmapInfo;
set set
{ {
if (beatmapInfo == value) if (beatmapInfo.Equals(value))
return; return;
beatmapInfo = value; beatmapInfo = value;
@ -154,7 +154,7 @@ namespace osu.Game.Screens.Select.Leaderboards
return null; return null;
} }
if (fetchBeatmapInfo.OnlineID == null || fetchBeatmapInfo.Status <= BeatmapOnlineStatus.Pending) if (fetchBeatmapInfo.OnlineID <= 0 || fetchBeatmapInfo.Status <= BeatmapOnlineStatus.Pending)
{ {
PlaceholderState = PlaceholderState.Unavailable; PlaceholderState = PlaceholderState.Unavailable;
return null; return null;

View File

@ -482,7 +482,7 @@ namespace osu.Game.Screens.Select
else else
selectionChangedDebounce = Scheduler.AddDelayed(run, 200); selectionChangedDebounce = Scheduler.AddDelayed(run, 200);
if (beatmap != beatmapInfoPrevious) if (!beatmap.Equals(beatmapInfoPrevious))
{ {
if (beatmap != null && beatmapInfoPrevious != null && Time.Current - audioFeedbackLastPlaybackTime >= 50) if (beatmap != null && beatmapInfoPrevious != null && Time.Current - audioFeedbackLastPlaybackTime >= 50)
{ {

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
@ -78,7 +79,11 @@ namespace osu.Game.Tests.Beatmaps
currentTestBeatmap = Decoder.GetDecoder<Beatmap>(reader).Decode(reader); currentTestBeatmap = Decoder.GetDecoder<Beatmap>(reader).Decode(reader);
// populate ruleset for beatmap converters that require it to be present. // populate ruleset for beatmap converters that require it to be present.
currentTestBeatmap.BeatmapInfo.Ruleset = rulesetStore.GetRuleset(currentTestBeatmap.BeatmapInfo.RulesetID); var ruleset = rulesetStore.GetRuleset(currentTestBeatmap.BeatmapInfo.RulesetID);
Debug.Assert(ruleset != null);
currentTestBeatmap.BeatmapInfo.Ruleset = ruleset;
}); });
}); });
@ -94,11 +99,7 @@ namespace osu.Game.Tests.Beatmaps
userSkinInfo.Files.Add(new RealmNamedFileUsage(new RealmFile { Hash = userFile }, userFile)); userSkinInfo.Files.Add(new RealmNamedFileUsage(new RealmFile { Hash = userFile }, userFile));
beatmapInfo.BeatmapSet.Files.Clear(); beatmapInfo.BeatmapSet.Files.Clear();
beatmapInfo.BeatmapSet.Files.Add(new BeatmapSetFileInfo beatmapInfo.BeatmapSet.Files.Add(new RealmNamedFileUsage(new RealmFile { Hash = beatmapFile }, beatmapFile));
{
Filename = beatmapFile,
FileInfo = new IO.FileInfo { Hash = beatmapFile }
});
// Need to refresh the cached skin source to refresh the skin resource store. // Need to refresh the cached skin source to refresh the skin resource store.
dependencies.SkinSource = new SkinProvidingContainer(Skin = new LegacySkin(userSkinInfo, this)); dependencies.SkinSource = new SkinProvidingContainer(Skin = new LegacySkin(userSkinInfo, this));

View File

@ -116,7 +116,7 @@ namespace osu.Game.Tests.Visual
{ {
private readonly WorkingBeatmap testBeatmap; private readonly WorkingBeatmap testBeatmap;
public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore<byte[]> resources, GameHost host, WorkingBeatmap defaultBeatmap, WorkingBeatmap testBeatmap) public TestBeatmapManager(Storage storage, RealmContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore<byte[]> resources, GameHost host, WorkingBeatmap defaultBeatmap, WorkingBeatmap testBeatmap)
: base(storage, contextFactory, rulesets, api, audioManager, resources, host, defaultBeatmap) : base(storage, contextFactory, rulesets, api, audioManager, resources, host, defaultBeatmap)
{ {
this.testBeatmap = testBeatmap; this.testBeatmap = testBeatmap;