mirror of
https://github.com/ppy/osu.git
synced 2025-01-18 08:32:54 +08:00
Merge pull request #31076 from peppy/beatmap-store-interface
Access beatmap store via abstract base class
This commit is contained in:
commit
4e4a99decc
@ -49,17 +49,17 @@ namespace osu.Game.Tests.Visual.Background
|
||||
[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();
|
||||
}
|
||||
|
@ -44,14 +44,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[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()
|
||||
|
@ -66,14 +66,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[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()
|
||||
|
@ -46,16 +46,16 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[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()
|
||||
|
@ -31,18 +31,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[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()
|
||||
|
@ -16,6 +16,7 @@ using osu.Framework.Testing;
|
||||
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;
|
||||
@ -23,6 +24,7 @@ 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;
|
||||
|
||||
@ -42,6 +44,9 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
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)
|
||||
{
|
||||
@ -1329,7 +1334,8 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
carouselAdjust?.Invoke(carousel);
|
||||
|
||||
carousel.BeatmapSets = beatmapSets;
|
||||
beatmaps.BeatmapSets.Clear();
|
||||
beatmaps.BeatmapSets.AddRange(beatmapSets);
|
||||
|
||||
(target ?? this).Child = carousel;
|
||||
});
|
||||
|
@ -56,20 +56,20 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
[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));
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
// 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;
|
||||
@ -10,12 +9,14 @@ 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;
|
||||
@ -31,6 +32,9 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
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));
|
||||
@ -246,13 +250,12 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
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)),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
@ -13,6 +15,9 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
[Cached]
|
||||
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
|
||||
|
||||
[Cached(typeof(BeatmapStore))]
|
||||
private BeatmapStore beatmapStore = new TestBeatmapStore();
|
||||
|
||||
public TestSceneFirstRunScreenUIScale()
|
||||
{
|
||||
AddStep("load screen", () =>
|
||||
|
@ -17,12 +17,14 @@ using osu.Framework.Graphics.Containers;
|
||||
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;
|
||||
|
||||
@ -47,6 +49,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
Dependencies.Cache(LocalConfig = new OsuConfigManager(LocalStorage));
|
||||
Dependencies.CacheAs<IPerformFromScreenRunner>(performer.Object);
|
||||
Dependencies.CacheAs<INotificationOverlay>(notificationOverlay.Object);
|
||||
Dependencies.CacheAs<BeatmapStore>(new TestBeatmapStore());
|
||||
}
|
||||
|
||||
[SetUpSteps]
|
||||
|
35
osu.Game/Database/BeatmapStore.cs
Normal file
35
osu.Game/Database/BeatmapStore.cs
Normal file
@ -0,0 +1,35 @@
|
||||
// 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.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);
|
||||
}
|
||||
}
|
@ -8,14 +8,13 @@ using System.Threading;
|
||||
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();
|
||||
|
||||
@ -28,7 +27,7 @@ namespace osu.Game.Database
|
||||
[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();
|
@ -1143,7 +1143,7 @@ namespace osu.Game
|
||||
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());
|
||||
|
@ -112,27 +112,13 @@ namespace osu.Game.Screens.Select
|
||||
[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()
|
||||
{
|
||||
@ -234,7 +220,7 @@ namespace osu.Game.Screens.Select
|
||||
}
|
||||
|
||||
[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");
|
||||
@ -244,15 +230,9 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
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>();
|
||||
|
@ -10,8 +10,31 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
/// <summary>
|
||||
/// A group which ensures only one item is selected.
|
||||
/// </summary>
|
||||
public class CarouselGroup : CarouselItem
|
||||
public abstract class CarouselGroup : CarouselItem
|
||||
{
|
||||
protected CarouselGroup(List<CarouselItem>? items = null)
|
||||
{
|
||||
if (items != null) this.items = items;
|
||||
|
||||
State.ValueChanged += state =>
|
||||
{
|
||||
switch (state.NewValue)
|
||||
{
|
||||
case CarouselItemState.Collapsed:
|
||||
case CarouselItemState.NotSelected:
|
||||
this.items.ForEach(c => c.State.Value = CarouselItemState.Collapsed);
|
||||
break;
|
||||
|
||||
case CarouselItemState.Selected:
|
||||
this.items.ForEach(c =>
|
||||
{
|
||||
if (c.State.Value == CarouselItemState.Collapsed) c.State.Value = CarouselItemState.NotSelected;
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public override DrawableCarouselItem? CreateDrawableRepresentation() => null;
|
||||
|
||||
public SlimReadOnlyListWrapper<CarouselItem> Items => items.AsSlimReadOnly();
|
||||
@ -67,29 +90,6 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
TotalItemsNotFiltered++;
|
||||
}
|
||||
|
||||
public CarouselGroup(List<CarouselItem>? items = null)
|
||||
{
|
||||
if (items != null) this.items = items;
|
||||
|
||||
State.ValueChanged += state =>
|
||||
{
|
||||
switch (state.NewValue)
|
||||
{
|
||||
case CarouselItemState.Collapsed:
|
||||
case CarouselItemState.NotSelected:
|
||||
this.items.ForEach(c => c.State.Value = CarouselItemState.Collapsed);
|
||||
break;
|
||||
|
||||
case CarouselItemState.Selected:
|
||||
this.items.ForEach(c =>
|
||||
{
|
||||
if (c.State.Value == CarouselItemState.Collapsed) c.State.Value = CarouselItemState.NotSelected;
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public override void Filter(FilterCriteria criteria)
|
||||
{
|
||||
base.Filter(criteria);
|
||||
|
@ -10,9 +10,9 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
/// <summary>
|
||||
/// A group which ensures at least one item is selected (if the group itself is selected).
|
||||
/// </summary>
|
||||
public class CarouselGroupEagerSelect : CarouselGroup
|
||||
public abstract class CarouselGroupEagerSelect : CarouselGroup
|
||||
{
|
||||
public CarouselGroupEagerSelect()
|
||||
protected CarouselGroupEagerSelect()
|
||||
{
|
||||
State.ValueChanged += state =>
|
||||
{
|
||||
|
16
osu.Game/Tests/Beatmaps/TestBeatmapStore.cs
Normal file
16
osu.Game/Tests/Beatmaps/TestBeatmapStore.cs
Normal file
@ -0,0 +1,16 @@
|
||||
// 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.Threading;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps
|
||||
{
|
||||
internal partial class TestBeatmapStore : BeatmapStore
|
||||
{
|
||||
public readonly BindableList<BeatmapSetInfo> BeatmapSets = new BindableList<BeatmapSetInfo>();
|
||||
public override IBindableList<BeatmapSetInfo> GetBeatmapSets(CancellationToken? cancellationToken) => BeatmapSets;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user