Skip to content

Commit

Permalink
Merge pull request ppy#31076 from peppy/beatmap-store-interface
Browse files Browse the repository at this point in the history
Access beatmap store via abstract base class
  • Loading branch information
bdach authored Dec 11, 2024
2 parents 99f9e28 + a868c33 commit 4e4a99d
Show file tree
Hide file tree
Showing 17 changed files with 126 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,17 @@ public partial class TestSceneUserDimBackgrounds : ScreenTestScene
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
{
DetachedBeatmapStore detachedBeatmapStore;
BeatmapStore beatmapStore;

Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default));
Dependencies.Cache(new OsuConfigManager(LocalStorage));
Dependencies.Cache(detachedBeatmapStore = new DetachedBeatmapStore());
Dependencies.CacheAs(beatmapStore = new RealmDetachedBeatmapStore());
Dependencies.Cache(Realm);

manager.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely();

Add(detachedBeatmapStore);
Add(beatmapStore);

Beatmap.SetDefault();
}
Expand Down
6 changes: 3 additions & 3 deletions osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ public abstract partial class QueueModeTestScene : ScreenTestScene
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
{
DetachedBeatmapStore detachedBeatmapStore;
BeatmapStore beatmapStore;

Dependencies.Cache(new RealmRulesetStore(Realm));
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default));
Dependencies.Cache(detachedBeatmapStore = new DetachedBeatmapStore());
Dependencies.CacheAs(beatmapStore = new RealmDetachedBeatmapStore());
Dependencies.Cache(Realm);

Add(detachedBeatmapStore);
Add(beatmapStore);
}

public override void SetUpSteps()
Expand Down
6 changes: 3 additions & 3 deletions osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,14 @@ public partial class TestSceneMultiplayer : ScreenTestScene
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
{
DetachedBeatmapStore detachedBeatmapStore;
BeatmapStore beatmapStore;

Dependencies.Cache(new RealmRulesetStore(Realm));
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, API, audio, Resources, host, Beatmap.Default));
Dependencies.Cache(detachedBeatmapStore = new DetachedBeatmapStore());
Dependencies.CacheAs(beatmapStore = new RealmDetachedBeatmapStore());
Dependencies.Cache(Realm);

Add(detachedBeatmapStore);
Add(beatmapStore);
}

public override void SetUpSteps()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,16 @@ public partial class TestSceneMultiplayerMatchSongSelect : MultiplayerTestScene
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
{
DetachedBeatmapStore detachedBeatmapStore;
BeatmapStore beatmapStore;

Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default));
Dependencies.Cache(detachedBeatmapStore = new DetachedBeatmapStore());
Dependencies.CacheAs(beatmapStore = new RealmDetachedBeatmapStore());
Dependencies.Cache(Realm);

importedBeatmapSet = manager.Import(TestResources.CreateTestBeatmapSetInfo(8, rulesets.AvailableRulesets.ToArray()))!;

Add(detachedBeatmapStore);
Add(beatmapStore);
}

private void setUp()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,18 @@ public partial class TestScenePlaylistsSongSelect : OnlinePlayTestScene
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
{
DetachedBeatmapStore detachedBeatmapStore;
BeatmapStore beatmapStore;

Dependencies.Cache(new RealmRulesetStore(Realm));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default));
Dependencies.Cache(detachedBeatmapStore = new DetachedBeatmapStore());
Dependencies.CacheAs(beatmapStore = new RealmDetachedBeatmapStore());
Dependencies.Cache(Realm);

var beatmapSet = TestResources.CreateTestBeatmapSetInfo();

manager.Import(beatmapSet);

Add(detachedBeatmapStore);
Add(beatmapStore);
}

public override void SetUpSteps()
Expand Down
8 changes: 7 additions & 1 deletion osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Database;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Resources;
using osuTK.Input;

Expand All @@ -42,6 +44,9 @@ public partial class TestSceneBeatmapCarousel : OsuManualInputManagerTestScene
private const int set_count = 5;
private const int diff_count = 3;

[Cached(typeof(BeatmapStore))]
private TestBeatmapStore beatmaps = new TestBeatmapStore();

[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
Expand Down Expand Up @@ -1329,7 +1334,8 @@ private void createCarousel(List<BeatmapSetInfo> beatmapSets, [CanBeNull] Func<F

carouselAdjust?.Invoke(carousel);

carousel.BeatmapSets = beatmapSets;
beatmaps.BeatmapSets.Clear();
beatmaps.BeatmapSets.AddRange(beatmapSets);

(target ?? this).Child = carousel;
});
Expand Down
6 changes: 3 additions & 3 deletions osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,20 @@ public partial class TestScenePlaySongSelect : ScreenTestScene
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
{
DetachedBeatmapStore detachedBeatmapStore;
BeatmapStore beatmapStore;

// These DI caches are required to ensure for interactive runs this test scene doesn't nuke all user beatmaps in the local install.
// At a point we have isolated interactive test runs enough, this can likely be removed.
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
Dependencies.Cache(Realm);
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, defaultBeatmap = Beatmap.Default));
Dependencies.Cache(detachedBeatmapStore = new DetachedBeatmapStore());
Dependencies.CacheAs(beatmapStore = new RealmDetachedBeatmapStore());

Dependencies.Cache(music = new MusicController());

// required to get bindables attached
Add(music);
Add(detachedBeatmapStore);
Add(beatmapStore);

Dependencies.Cache(config = new OsuConfigManager(LocalStorage));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Online.API;
using osu.Game.Overlays;
using osu.Game.Overlays.Dialog;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Online;
using osu.Game.Tests.Resources;
using osuTK.Input;
Expand All @@ -31,6 +32,9 @@ public partial class TestSceneUpdateBeatmapSetButton : OsuManualInputManagerTest

private BeatmapSetInfo testBeatmapSetInfo = null!;

[Cached(typeof(BeatmapStore))]
private TestBeatmapStore beatmaps = new TestBeatmapStore();

protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
Expand Down Expand Up @@ -246,13 +250,12 @@ public void TestSplitDisplay()

private BeatmapCarousel createCarousel()
{
beatmaps.BeatmapSets.Clear();
beatmaps.BeatmapSets.Add(testBeatmapSetInfo = TestResources.CreateTestBeatmapSetInfo(5));

return carousel = new BeatmapCarousel(new FilterCriteria())
{
RelativeSizeAxes = Axes.Both,
BeatmapSets = new List<BeatmapSetInfo>
{
(testBeatmapSetInfo = TestResources.CreateTestBeatmapSetInfo(5)),
}
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

using osu.Framework.Allocation;
using osu.Framework.Screens;
using osu.Game.Database;
using osu.Game.Overlays;
using osu.Game.Overlays.FirstRunSetup;
using osu.Game.Tests.Beatmaps;

namespace osu.Game.Tests.Visual.UserInterface
{
Expand All @@ -13,6 +15,9 @@ public partial class TestSceneFirstRunScreenUIScale : OsuManualInputManagerTestS
[Cached]
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);

[Cached(typeof(BeatmapStore))]
private BeatmapStore beatmapStore = new TestBeatmapStore();

public TestSceneFirstRunScreenUIScale()
{
AddStep("load screen", () =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Configuration;
using osu.Game.Database;
using osu.Game.Localisation;
using osu.Game.Overlays;
using osu.Game.Overlays.FirstRunSetup;
using osu.Game.Overlays.Notifications;
using osu.Game.Screens;
using osu.Game.Screens.Footer;
using osu.Game.Tests.Beatmaps;
using osuTK;
using osuTK.Input;

Expand All @@ -47,6 +49,7 @@ private void load()
Dependencies.Cache(LocalConfig = new OsuConfigManager(LocalStorage));
Dependencies.CacheAs<IPerformFromScreenRunner>(performer.Object);
Dependencies.CacheAs<INotificationOverlay>(notificationOverlay.Object);
Dependencies.CacheAs<BeatmapStore>(new TestBeatmapStore());
}

[SetUpSteps]
Expand Down
35 changes: 35 additions & 0 deletions osu.Game/Database/BeatmapStore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// 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 System.Threading;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;

namespace osu.Game.Database
{
/// <summary>
/// A store which contains a thread-safe representation of beatmaps available game-wide.
/// This exposes changes to available beatmaps, such as post-import or deletion.
/// </summary>
/// <remarks>
/// The main goal of classes which implement this interface should be to provide change
/// tracking and thread safety in a performant way, rather than having to worry about such
/// concerns at the point of usage.
/// </remarks>
public abstract partial class BeatmapStore : Component
{
/// <summary>
/// Get all available beatmaps.
/// </summary>
/// <param name="cancellationToken">A cancellation token which allows early abort from the operation.</param>
/// <returns>A bindable list of all available beatmap sets.</returns>
/// <remarks>
/// This operation may block during the initial load process.
///
/// It is generally expected that once a beatmap store is in a good state, the overhead of this call
/// should be negligible.
/// </remarks>
public abstract IBindableList<BeatmapSetInfo> GetBeatmapSets(CancellationToken? cancellationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Online.Multiplayer;
using Realms;

namespace osu.Game.Database
{
public partial class DetachedBeatmapStore : Component
public partial class RealmDetachedBeatmapStore : BeatmapStore
{
private readonly ManualResetEventSlim loaded = new ManualResetEventSlim();

Expand All @@ -28,7 +27,7 @@ public partial class DetachedBeatmapStore : Component
[Resolved]
private RealmAccess realm { get; set; } = null!;

public IBindableList<BeatmapSetInfo> GetDetachedBeatmaps(CancellationToken? cancellationToken)
public override IBindableList<BeatmapSetInfo> GetBeatmapSets(CancellationToken? cancellationToken)
{
loaded.Wait(cancellationToken ?? CancellationToken.None);
return detachedBeatmapSets.GetBoundCopy();
Expand Down
2 changes: 1 addition & 1 deletion osu.Game/OsuGame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1143,7 +1143,7 @@ protected override void LoadComplete()
loadComponentSingleFile(new MedalOverlay(), topMostOverlayContent.Add);

loadComponentSingleFile(new BackgroundDataStoreProcessor(), Add);
loadComponentSingleFile(new DetachedBeatmapStore(), Add, true);
loadComponentSingleFile<BeatmapStore>(new RealmDetachedBeatmapStore(), Add, true);

Add(externalLinkOpener = new ExternalLinkOpener());
Add(new MusicKeyBindingHandler());
Expand Down
30 changes: 5 additions & 25 deletions osu.Game/Screens/Select/BeatmapCarousel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,27 +112,13 @@ public partial class BeatmapCarousel : CompositeDrawable, IKeyBindingHandler<Glo
[Resolved]
private RealmAccess realm { get; set; } = null!;

[Resolved]
private DetachedBeatmapStore? detachedBeatmapStore { get; set; }

private IBindableList<BeatmapSetInfo>? detachedBeatmapSets;

private readonly NoResultsPlaceholder noResultsPlaceholder;

private IEnumerable<CarouselBeatmapSet> beatmapSets => root.Items.OfType<CarouselBeatmapSet>();

internal IEnumerable<BeatmapSetInfo> BeatmapSets
{
get => beatmapSets.Select(g => g.BeatmapSet);
set
{
if (LoadState != LoadState.NotLoaded)
throw new InvalidOperationException("If not using a realm source, beatmap sets must be set before load.");

detachedBeatmapSets = new BindableList<BeatmapSetInfo>(value);
Schedule(loadNewRoot);
}
}
internal IEnumerable<BeatmapSetInfo> BeatmapSets => beatmapSets.Select(g => g.BeatmapSet);

private void loadNewRoot()
{
Expand Down Expand Up @@ -234,7 +220,7 @@ public BeatmapCarousel(FilterCriteria initialCriteria)
}

[BackgroundDependencyLoader]
private void load(OsuConfigManager config, AudioManager audio, CancellationToken? cancellationToken)
private void load(OsuConfigManager config, AudioManager audio, BeatmapStore beatmaps, CancellationToken? cancellationToken)
{
spinSample = audio.Samples.Get("SongSelect/random-spin");
randomSelectSample = audio.Samples.Get(@"SongSelect/select-random");
Expand All @@ -244,15 +230,9 @@ private void load(OsuConfigManager config, AudioManager audio, CancellationToken

RightClickScrollingEnabled.BindValueChanged(enabled => Scroll.RightMouseScrollbar = enabled.NewValue, true);

if (detachedBeatmapStore != null && detachedBeatmapSets == null)
{
// This is performing an unnecessary second lookup on realm (in addition to the subscription), but for performance reasons
// we require it to be separate: the subscription's initial callback (with `ChangeSet` of `null`) will run on the update
// thread. If we attempt to detach beatmaps in this callback the game will fall over (it takes time).
detachedBeatmapSets = detachedBeatmapStore.GetDetachedBeatmaps(cancellationToken);
detachedBeatmapSets.BindCollectionChanged(beatmapSetsChanged);
loadNewRoot();
}
detachedBeatmapSets = beatmaps.GetBeatmapSets(cancellationToken);
detachedBeatmapSets.BindCollectionChanged(beatmapSetsChanged);
loadNewRoot();
}

private readonly HashSet<BeatmapSetInfo> setsRequiringUpdate = new HashSet<BeatmapSetInfo>();
Expand Down
Loading

0 comments on commit 4e4a99d

Please sign in to comment.