From 00e3af3366e80214c7f0c956d144000a78cd0b56 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Dec 2021 19:47:11 +0900 Subject: [PATCH] Update model manager and many related classes to get things compiling again --- .../Database/BeatmapImporterTests.cs | 42 +- ...eneOnlinePlayBeatmapAvailabilityTracker.cs | 8 +- .../Visual/Multiplayer/QueueModeTestScene.cs | 2 +- .../Multiplayer/TestSceneMultiplayer.cs | 4 +- .../TestSceneMultiplayerMatchSubScreen.cs | 2 +- .../TestSceneMultiplayerPlaylist.cs | 4 +- .../TestSceneMultiplayerQueueList.cs | 4 +- .../TestSceneMultiplayerReadyButton.cs | 2 +- .../TestSceneMultiplayerSpectateButton.cs | 2 +- .../TestScenePlaylistsRoomSettingsPlaylist.cs | 4 +- .../Visual/Multiplayer/TestSceneTeamVersus.cs | 2 +- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 3 +- .../TestSceneBeatmapMetadataDisplay.cs | 2 +- .../TestSceneDeleteLocalScore.cs | 5 +- osu.Game/Beatmaps/BeatmapManager.cs | 28 +- osu.Game/Beatmaps/BeatmapModelManager.cs | 399 +++--------------- osu.Game/Beatmaps/EFBeatmapSetInfo.cs | 4 +- osu.Game/Beatmaps/FlatFileWorkingBeatmap.cs | 2 +- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 4 +- osu.Game/Database/ImportTask.cs | 3 +- osu.Game/Overlays/MusicController.cs | 2 +- osu.Game/Scoring/LegacyDatabasedScore.cs | 4 +- osu.Game/Scoring/ScoreInfo.cs | 20 +- osu.Game/Scoring/ScoreManager.cs | 1 - osu.Game/Scoring/ScoreModelManager.cs | 5 +- osu.Game/Screens/Menu/IntroScreen.cs | 4 +- osu.Game/Screens/Ranking/SoloResultsScreen.cs | 2 +- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 +- .../Carousel/DrawableCarouselBeatmapSet.cs | 10 +- osu.Game/Screens/Select/SongSelect.cs | 2 +- osu.Game/Stores/BeatmapImporter.cs | 4 +- .../Drawables/DrawableStoryboard.cs | 4 +- osu.Game/Tests/Beatmaps/TestBeatmap.cs | 2 - osu.Game/Tests/Visual/EditorTestScene.cs | 8 +- 34 files changed, 165 insertions(+), 431 deletions(-) diff --git a/osu.Game.Tests/Database/BeatmapImporterTests.cs b/osu.Game.Tests/Database/BeatmapImporterTests.cs index c5dbcf155c..e144f863b0 100644 --- a/osu.Game.Tests/Database/BeatmapImporterTests.cs +++ b/osu.Game.Tests/Database/BeatmapImporterTests.cs @@ -40,7 +40,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealmAsync(async (realmFactory, storage) => { - using (var importer = new BeatmapImporter(realmFactory, storage)) + using (var importer = new BeatmapModelManager(realmFactory, storage)) using (new RulesetStore(realmFactory, storage)) { ILive? imported; @@ -69,7 +69,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealmAsync(async (realmFactory, storage) => { - using var importer = new BeatmapImporter(realmFactory, storage); + using var importer = new BeatmapModelManager(realmFactory, storage); using var store = new RulesetStore(realmFactory, storage); await LoadOszIntoStore(importer, realmFactory.Context); @@ -81,7 +81,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealmAsync(async (realmFactory, storage) => { - using var importer = new BeatmapImporter(realmFactory, storage); + using var importer = new BeatmapModelManager(realmFactory, storage); using var store = new RulesetStore(realmFactory, storage); var imported = await LoadOszIntoStore(importer, realmFactory.Context); @@ -99,7 +99,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealmAsync(async (realmFactory, storage) => { - using var importer = new BeatmapImporter(realmFactory, storage); + using var importer = new BeatmapModelManager(realmFactory, storage); using var store = new RulesetStore(realmFactory, storage); var imported = await LoadOszIntoStore(importer, realmFactory.Context); @@ -113,7 +113,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealmAsync(async (realmFactory, storage) => { - using var importer = new BeatmapImporter(realmFactory, storage); + using var importer = new BeatmapModelManager(realmFactory, storage); using var store = new RulesetStore(realmFactory, storage); string? tempPath = TestResources.GetTestBeatmapForImport(); @@ -143,7 +143,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealmAsync(async (realmFactory, storage) => { - using var importer = new BeatmapImporter(realmFactory, storage); + using var importer = new BeatmapModelManager(realmFactory, storage); using var store = new RulesetStore(realmFactory, storage); var imported = await LoadOszIntoStore(importer, realmFactory.Context); @@ -163,7 +163,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealmAsync(async (realmFactory, storage) => { - using var importer = new BeatmapImporter(realmFactory, storage); + using var importer = new BeatmapModelManager(realmFactory, storage); using var store = new RulesetStore(realmFactory, storage); string? temp = TestResources.GetTestBeatmapForImport(); @@ -212,7 +212,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealmAsync(async (realmFactory, storage) => { - using var importer = new BeatmapImporter(realmFactory, storage); + using var importer = new BeatmapModelManager(realmFactory, storage); using var store = new RulesetStore(realmFactory, storage); string? temp = TestResources.GetTestBeatmapForImport(); @@ -264,7 +264,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealmAsync(async (realmFactory, storage) => { - using var importer = new BeatmapImporter(realmFactory, storage); + using var importer = new BeatmapModelManager(realmFactory, storage); using var store = new RulesetStore(realmFactory, storage); string? temp = TestResources.GetTestBeatmapForImport(); @@ -312,7 +312,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealmAsync(async (realmFactory, storage) => { - using var importer = new BeatmapImporter(realmFactory, storage); + using var importer = new BeatmapModelManager(realmFactory, storage); using var store = new RulesetStore(realmFactory, storage); string? temp = TestResources.GetTestBeatmapForImport(); @@ -361,7 +361,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealmAsync(async (realmFactory, storage) => { - using var importer = new BeatmapImporter(realmFactory, storage); + using var importer = new BeatmapModelManager(realmFactory, storage); using var store = new RulesetStore(realmFactory, storage); var imported = await LoadOszIntoStore(importer, realmFactory.Context); @@ -394,7 +394,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealmAsync(async (realmFactory, storage) => { - using var importer = new BeatmapImporter(realmFactory, storage); + using var importer = new BeatmapModelManager(realmFactory, storage); using var store = new RulesetStore(realmFactory, storage); var progressNotification = new ImportProgressNotification(); @@ -430,7 +430,7 @@ namespace osu.Game.Tests.Database Interlocked.Increment(ref loggedExceptionCount); }; - using var importer = new BeatmapImporter(realmFactory, storage); + using var importer = new BeatmapModelManager(realmFactory, storage); using var store = new RulesetStore(realmFactory, storage); var imported = await LoadOszIntoStore(importer, realmFactory.Context); @@ -480,7 +480,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealmAsync(async (realmFactory, storage) => { - using var importer = new BeatmapImporter(realmFactory, storage); + using var importer = new BeatmapModelManager(realmFactory, storage); using var store = new RulesetStore(realmFactory, storage); var imported = await LoadOszIntoStore(importer, realmFactory.Context); @@ -528,7 +528,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealmAsync(async (realmFactory, storage) => { - using var importer = new BeatmapImporter(realmFactory, storage); + using var importer = new BeatmapModelManager(realmFactory, storage); using var store = new RulesetStore(realmFactory, storage); var imported = await LoadOszIntoStore(importer, realmFactory.Context); @@ -554,7 +554,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealmAsync(async (realmFactory, storage) => { - using var importer = new BeatmapImporter(realmFactory, storage); + using var importer = new BeatmapModelManager(realmFactory, storage); using var store = new RulesetStore(realmFactory, storage); var metadata = new BeatmapMetadata @@ -600,7 +600,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealmAsync(async (realmFactory, storage) => { - using var importer = new BeatmapImporter(realmFactory, storage); + using var importer = new BeatmapModelManager(realmFactory, storage); using var store = new RulesetStore(realmFactory, storage); string? temp = TestResources.GetTestBeatmapForImport(); @@ -617,7 +617,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealmAsync(async (realmFactory, storage) => { - using var importer = new BeatmapImporter(realmFactory, storage); + using var importer = new BeatmapModelManager(realmFactory, storage); using var store = new RulesetStore(realmFactory, storage); string? temp = TestResources.GetTestBeatmapForImport(); @@ -653,7 +653,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealmAsync(async (realmFactory, storage) => { - using var importer = new BeatmapImporter(realmFactory, storage); + using var importer = new BeatmapModelManager(realmFactory, storage); using var store = new RulesetStore(realmFactory, storage); string? temp = TestResources.GetTestBeatmapForImport(); @@ -695,7 +695,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealmAsync(async (realmFactory, storage) => { - using var importer = new BeatmapImporter(realmFactory, storage); + using var importer = new BeatmapModelManager(realmFactory, storage); using var store = new RulesetStore(realmFactory, storage); string? temp = TestResources.GetTestBeatmapForImport(); @@ -746,7 +746,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealmAsync(async (realmFactory, storage) => { - using var importer = new BeatmapImporter(realmFactory, storage); + using var importer = new BeatmapModelManager(realmFactory, storage); using var store = new RulesetStore(realmFactory, storage); string? temp = TestResources.GetTestBeatmapForImport(); diff --git a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs index 2cab823526..5a2349e776 100644 --- a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs @@ -172,17 +172,17 @@ namespace osu.Game.Tests.Online { } - protected override BeatmapModelManager CreateBeatmapModelManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, GameHost host) + protected override BeatmapModelManager CreateBeatmapModelManager(Storage storage, RealmContextFactory contextFactory, RulesetStore rulesets, BeatmapOnlineLookupQueue onlineLookupQueue) { - return new TestBeatmapModelManager(this, storage, contextFactory, rulesets, api, host); + return new TestBeatmapModelManager(this, storage, contextFactory, rulesets, onlineLookupQueue); } internal class TestBeatmapModelManager : BeatmapModelManager { private readonly TestBeatmapManager testBeatmapManager; - public TestBeatmapModelManager(TestBeatmapManager testBeatmapManager, Storage storage, IDatabaseContextFactory databaseContextFactory, RulesetStore rulesetStore, IAPIProvider apiProvider, GameHost gameHost) - : base(storage, databaseContextFactory, rulesetStore, gameHost) + public TestBeatmapModelManager(TestBeatmapManager testBeatmapManager, Storage storage, RealmContextFactory databaseContextFactory, RulesetStore rulesetStore, BeatmapOnlineLookupQueue beatmapOnlineLookupQueue) + : base(databaseContextFactory, storage, beatmapOnlineLookupQueue) { this.testBeatmapManager = testBeatmapManager; } diff --git a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs index c4d7bd7e6a..a993190a67 100644 --- a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs @@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("import beatmap", () => { beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); - importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); + importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable().First(); InitialBeatmap = importedSet.Beatmaps.First(b => b.RulesetID == 0); OtherBeatmap = importedSet.Beatmaps.Last(b => b.RulesetID == 0); }); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 3d8c5298dc..18f45086f6 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -72,7 +72,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("import beatmap", () => { beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); - importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); + importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable().First(); }); AddStep("load multiplayer", () => LoadScreen(multiplayerComponents = new TestMultiplayerComponents())); @@ -588,7 +588,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("restore beatmap", () => { beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); - importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); + importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable().First(); }); AddUntilStep("play started", () => multiplayerComponents.CurrentScreen is SpectatorScreen); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index 52e46ef5af..6b6a02906e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); - importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); + importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable().First(); } [SetUp] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs index 464c0ea5b6..251f407a85 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs @@ -55,7 +55,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("import beatmap", () => { beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); - importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); + importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable().First(); importedBeatmap = importedSet.Beatmaps.First(b => b.RulesetID == 0); }); @@ -169,7 +169,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void addItemStep(bool expired = false) => AddStep("add item", () => Client.AddPlaylistItem(new MultiplayerPlaylistItem(new PlaylistItem { Beatmap = { Value = importedBeatmap }, - BeatmapID = importedBeatmap.OnlineID ?? -1, + BeatmapID = importedBeatmap.OnlineID, Expired = expired, PlayedAt = DateTimeOffset.Now }))); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs index 29daff546d..ad47745642 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs @@ -60,7 +60,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("import beatmap", () => { beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); - importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); + importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable().First(); importedBeatmap = importedSet.Beatmaps.First(b => b.RulesetID == 0); }); @@ -126,7 +126,7 @@ namespace osu.Game.Tests.Visual.Multiplayer MultiplayerPlaylistItem item = new MultiplayerPlaylistItem(new PlaylistItem { Beatmap = { Value = importedBeatmap }, - BeatmapID = importedBeatmap.OnlineID ?? -1, + BeatmapID = importedBeatmap.OnlineID, }); Client.AddUserPlaylistItem(userId(), item); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index 8f51b1e381..59e5318263 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AvailabilityTracker.SelectedItem.BindTo(selectedItem); - importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); + importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable().First(); Beatmap.Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()); selectedItem.Value = new PlaylistItem { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs index d4ff9f8c41..5466a19657 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs @@ -51,7 +51,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AvailabilityTracker.SelectedItem.BindTo(selectedItem); - importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); + importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable().First(); Beatmap.Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()); selectedItem.Value = new PlaylistItem { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSettingsPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSettingsPlaylist.cs index 93ccd5f1e1..e63e58824f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSettingsPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSettingsPlaylist.cs @@ -12,7 +12,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Database; using osu.Game.Graphics.Containers; -using osu.Game.Online.API.Requests.Responses; +using osu.Game.Models; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; @@ -155,7 +155,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Metadata = new BeatmapMetadata { Artist = "Artist", - Author = new APIUser { Username = "Creator name here" }, + Author = new RealmUser { Username = "Creator name here" }, Title = "Long title used to check background colour", }, BeatmapSet = new BeatmapSetInfo() diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs index d20fbd3539..2140acb2e3 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("import beatmap", () => { beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); - importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); + importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable().First(); }); AddStep("load multiplayer", () => LoadScreen(multiplayerComponents = new TestMultiplayerComponents())); diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index 62b0f10fd8..a5f42545c1 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -20,7 +20,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Scoring; using osu.Game.Screens.Select.Leaderboards; -using osu.Game.Stores; using osu.Game.Tests.Resources; using osu.Game.Users; using osuTK; @@ -45,7 +44,7 @@ namespace osu.Game.Tests.Visual.SongSelect dependencies.Cache(rulesetStore = new RulesetStore(ContextFactory)); dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get(), Resources, dependencies.Get(), Beatmap.Default)); - dependencies.Cache(scoreManager = new ScoreManager(dependencies.Get(), () => beatmapManager, LocalStorage, dependencies.Get(), Scheduler)); + dependencies.Cache(scoreManager = new ScoreManager(dependencies.Get(), () => beatmapManager, LocalStorage, dependencies.Get(), Scheduler)); return dependencies; } diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index e573c96ce9..fb6d9a0b4b 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.SongSelect { showMetadataForBeatmap(() => { - var allBeatmapSets = manager.GetAllUsableBeatmapSets(IncludedDetails.Minimal); + var allBeatmapSets = manager.GetAllUsableBeatmapSets(); if (allBeatmapSets.Count == 0) return manager.DefaultBeatmap; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 5c4759a466..49200f6e1a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -5,10 +5,10 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Graphics; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Extensions; +using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Platform; using osu.Framework.Testing; @@ -24,7 +24,6 @@ using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Scoring; using osu.Game.Screens.Select.Leaderboards; -using osu.Game.Stores; using osu.Game.Tests.Resources; using osuTK; using osuTK.Input; @@ -89,7 +88,7 @@ namespace osu.Game.Tests.Visual.UserInterface dependencies.Cache(rulesetStore = new RulesetStore(ContextFactory)); dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get(), Resources, dependencies.Get(), Beatmap.Default)); - dependencies.Cache(scoreManager = new ScoreManager(dependencies.Get(), () => beatmapManager, LocalStorage, dependencies.Get(), Scheduler)); + dependencies.Cache(scoreManager = new ScoreManager(dependencies.Get(), () => beatmapManager, LocalStorage, dependencies.Get(), Scheduler)); beatmapInfo = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).GetResultSafely().Value.Beatmaps[0]; diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 5c8cf38682..45ae69e4ba 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -16,7 +16,6 @@ using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Database; -using osu.Game.IO; using osu.Game.IO.Archives; using osu.Game.Models; using osu.Game.Online.API; @@ -43,21 +42,18 @@ namespace osu.Game.Beatmaps public BeatmapManager(Storage storage, RealmContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore gameResources, GameHost host = null, WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false) { + if (performOnlineLookups) + onlineBeatmapLookupQueue = new BeatmapOnlineLookupQueue(api, storage); + var userResources = new RealmFileStore(contextFactory, storage).Store; BeatmapTrackStore = audioManager.GetTrackStore(userResources); - beatmapModelManager = CreateBeatmapModelManager(storage, contextFactory, rulesets, api, host); + beatmapModelManager = CreateBeatmapModelManager(storage, contextFactory, rulesets, onlineBeatmapLookupQueue); workingBeatmapCache = CreateWorkingBeatmapCache(audioManager, gameResources, userResources, defaultBeatmap, host); workingBeatmapCache.BeatmapManager = beatmapModelManager; beatmapModelManager.WorkingBeatmapCache = workingBeatmapCache; - - if (performOnlineLookups) - { - onlineBeatmapLookupQueue = new BeatmapOnlineLookupQueue(api, storage); - beatmapModelManager.OnlineLookupQueue = onlineBeatmapLookupQueue; - } } protected virtual WorkingBeatmapCache CreateWorkingBeatmapCache(AudioManager audioManager, IResourceStore resources, IResourceStore storage, WorkingBeatmap defaultBeatmap, GameHost host) @@ -65,8 +61,8 @@ namespace osu.Game.Beatmaps return new WorkingBeatmapCache(BeatmapTrackStore, audioManager, resources, storage, defaultBeatmap, host); } - protected virtual BeatmapModelManager CreateBeatmapModelManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, GameHost host) => - new BeatmapModelManager(storage, contextFactory, rulesets, host); + protected virtual BeatmapModelManager CreateBeatmapModelManager(Storage storage, RealmContextFactory contextFactory, RulesetStore rulesets, [CanBeNull] BeatmapOnlineLookupQueue onlineLookupQueue) => + new BeatmapModelManager(contextFactory, storage, onlineLookupQueue); /// /// Create a new . @@ -97,9 +93,9 @@ namespace osu.Game.Beatmaps } }; - var imported = beatmapModelManager.Import(set).GetResultSafely().Value; + var imported = beatmapModelManager.Import(set).GetResultSafely()?.Value; - return GetWorkingBeatmap(imported.Beatmaps.First()); + return GetWorkingBeatmap(imported?.Beatmaps.First()); } #region Delegation to BeatmapModelManager (methods which previously existed locally). @@ -135,23 +131,21 @@ namespace osu.Game.Beatmaps /// Returns a list of all usable s. /// /// A list of available . - public List GetAllUsableBeatmapSets(IncludedDetails includes = IncludedDetails.All, bool includeProtected = false) => beatmapModelManager.GetAllUsableBeatmapSets(includes, includeProtected); + public List GetAllUsableBeatmapSets(bool includeProtected = false) => beatmapModelManager.GetAllUsableBeatmapSets(includeProtected); /// /// Returns a list of all usable s. Note that files are not populated. /// - /// The level of detail to include in the returned objects. /// Whether to include protected (system) beatmaps. These should not be included for gameplay playable use cases. /// A list of available . - public IEnumerable GetAllUsableBeatmapSetsEnumerable(IncludedDetails includes, bool includeProtected = false) => beatmapModelManager.GetAllUsableBeatmapSetsEnumerable(includes, includeProtected); + public IEnumerable GetAllUsableBeatmapSetsEnumerable(bool includeProtected = false) => beatmapModelManager.GetAllUsableBeatmapSetsEnumerable(includeProtected); /// /// Perform a lookup query on available s. /// /// The query. - /// The level of detail to include in the returned objects. /// Results from the provided query. - public IEnumerable QueryBeatmapSets(Expression> query, IncludedDetails includes = IncludedDetails.All) => beatmapModelManager.QueryBeatmapSets(query, includes); + public IEnumerable QueryBeatmapSets(Expression> query) => beatmapModelManager.QueryBeatmapSets(query); /// /// Perform a lookup query on available s. diff --git a/osu.Game/Beatmaps/BeatmapModelManager.cs b/osu.Game/Beatmaps/BeatmapModelManager.cs index f6f2e16410..100189bf09 100644 --- a/osu.Game/Beatmaps/BeatmapModelManager.cs +++ b/osu.Game/Beatmaps/BeatmapModelManager.cs @@ -3,30 +3,21 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using osu.Framework.Audio.Track; using osu.Framework.Extensions; -using osu.Framework.Graphics.Textures; -using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Beatmaps.Formats; using osu.Game.Database; using osu.Game.Extensions; -using osu.Game.IO; -using osu.Game.IO.Archives; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Objects; using osu.Game.Skinning; using osu.Game.Stores; -using Realms; -using Decoder = osu.Game.Beatmaps.Formats.Decoder; + +#nullable enable namespace osu.Game.Beatmaps { @@ -34,145 +25,65 @@ namespace osu.Game.Beatmaps /// Handles ef-core storage of beatmaps. /// [ExcludeFromDynamicCompile] - public class BeatmapModelManager : RealmArchiveModelManager + public class BeatmapModelManager : BeatmapImporter { /// /// Fired when a single difficulty has been hidden. /// - public event Action BeatmapHidden; + public event Action? BeatmapHidden; /// /// Fired when a single difficulty has been restored. /// - public event Action BeatmapRestored; - - /// - /// An online lookup queue component which handles populating online beatmap metadata. - /// - public BeatmapOnlineLookupQueue OnlineLookupQueue { private get; set; } + public event Action? BeatmapRestored; /// /// The game working beatmap cache, used to invalidate entries on changes. /// - public IWorkingBeatmapCache WorkingBeatmapCache { private get; set; } + public IWorkingBeatmapCache? WorkingBeatmapCache { private get; set; } public override IEnumerable HandledExtensions => new[] { ".osz" }; protected override string[] HashableFileTypes => new[] { ".osu" }; - private readonly RulesetStore rulesets; - - public BeatmapModelManager(Storage storage, RealmContextFactory contextFactory, RulesetStore rulesets, GameHost host = null) - : base(storage, contextFactory) + public BeatmapModelManager(RealmContextFactory contextFactory, Storage storage, BeatmapOnlineLookupQueue? onlineLookupQueue = null) + : base(contextFactory, storage, onlineLookupQueue) { - this.rulesets = rulesets; - - // beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b); - // beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b); - // beatmaps.ItemRemoved += b => WorkingBeatmapCache?.Invalidate(b); - // beatmaps.ItemUpdated += obj => WorkingBeatmapCache?.Invalidate(obj); } protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osz"; - protected override async Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader? archive, Realm realm, CancellationToken cancellationToken) - { - if (archive != null) - beatmapSet.Beatmaps.AddRange(createBeatmapDifficulties(beatmapSet.Files)); - - foreach (BeatmapInfo b in beatmapSet.Beatmaps) - { - // remove metadata from difficulties where it matches the set - if (beatmapSet.Metadata.Equals(b.Metadata)) - b.Metadata = null; - - b.BeatmapSet = beatmapSet; - } - - validateOnlineIds(beatmapSet); - - bool hadOnlineIDs = beatmapSet.Beatmaps.Any(b => b.OnlineID > 0); - - if (OnlineLookupQueue != null) - await OnlineLookupQueue.UpdateAsync(beatmapSet, cancellationToken).ConfigureAwait(false); - - // ensure at least one beatmap was able to retrieve or keep an online ID, else drop the set ID. - if (hadOnlineIDs && !beatmapSet.Beatmaps.Any(b => b.OnlineID > 0)) - { - if (beatmapSet.OnlineID != null) - { - beatmapSet.OnlineID = -1; - LogForModel(beatmapSet, "Disassociating beatmap set ID due to loss of all beatmap IDs"); - } - } - } - - protected override void PreImport(BeatmapSetInfo beatmapSet) - { - if (beatmapSet.Beatmaps.Any(b => b.BaseDifficulty == null)) - throw new InvalidOperationException($"Cannot import {nameof(BeatmapInfo)} with null {nameof(BeatmapInfo.BaseDifficulty)}."); - - // check if a set already exists with the same online id, delete if it does. - if (beatmapSet.OnlineID != null) - { - var existingSetWithSameOnlineID = beatmaps.ConsumableItems.FirstOrDefault(b => b.OnlineID == beatmapSet.OnlineID); - - if (existingSetWithSameOnlineID != null) - { - Delete(existingSetWithSameOnlineID); - - // in order to avoid a unique key constraint, immediately remove the online ID from the previous set. - existingSetWithSameOnlineID.OnlineID = -1; - foreach (var b in existingSetWithSameOnlineID.Beatmaps) - b.OnlineID = -1; - - LogForModel(beatmapSet, $"Found existing beatmap set with same OnlineBeatmapSetID ({beatmapSet.OnlineID}). It has been deleted."); - } - } - } - - private void validateOnlineIds(BeatmapSetInfo beatmapSet) - { - var beatmapIds = beatmapSet.Beatmaps.Where(b => b.OnlineID.HasValue).Select(b => b.OnlineID).ToList(); - - // ensure all IDs are unique - if (beatmapIds.GroupBy(b => b).Any(g => g.Count() > 1)) - { - LogForModel(beatmapSet, "Found non-unique IDs, resetting..."); - resetIds(); - return; - } - - // find any existing beatmaps in the database that have matching online ids - var existingBeatmaps = QueryBeatmaps(b => beatmapIds.Contains(b.OnlineID)).ToList(); - - if (existingBeatmaps.Count > 0) - { - // reset the import ids (to force a re-fetch) *unless* they match the candidate CheckForExisting set. - // we can ignore the case where the new ids are contained by the CheckForExisting set as it will either be used (import skipped) or deleted. - var existing = CheckForExisting(beatmapSet); - - if (existing == null || existingBeatmaps.Any(b => !existing.Beatmaps.Contains(b))) - { - LogForModel(beatmapSet, "Found existing import with IDs already, resetting..."); - resetIds(); - } - } - - void resetIds() => beatmapSet.Beatmaps.ForEach(b => b.OnlineID = -1); - } - /// /// Delete a beatmap difficulty. /// /// The beatmap difficulty to hide. - public void Hide(BeatmapInfo beatmapInfo) => beatmaps.Hide(beatmapInfo); + public void Hide(BeatmapInfo beatmapInfo) + { + using (var realm = ContextFactory.CreateContext()) + using (var transaction = realm.BeginWrite()) + { + beatmapInfo.Hidden = true; + transaction.Commit(); + + BeatmapHidden?.Invoke(beatmapInfo); + } + } /// /// Restore a beatmap difficulty. /// /// The beatmap difficulty to restore. - public void Restore(BeatmapInfo beatmapInfo) => beatmaps.Restore(beatmapInfo); + public void Restore(BeatmapInfo beatmapInfo) + { + using (var realm = ContextFactory.CreateContext()) + using (var transaction = realm.BeginWrite()) + { + beatmapInfo.Hidden = false; + transaction.Commit(); + + BeatmapRestored?.Invoke(beatmapInfo); + } + } /// /// Saves an file against a given . @@ -180,10 +91,12 @@ namespace osu.Game.Beatmaps /// The to save the content against. The file referenced by will be replaced. /// The content to write. /// The beatmap content to write, null if to be omitted. - public virtual void Save(BeatmapInfo beatmapInfo, IBeatmap beatmapContent, ISkin beatmapSkin = null) + public virtual void Save(BeatmapInfo beatmapInfo, IBeatmap beatmapContent, ISkin? beatmapSkin = null) { var setInfo = beatmapInfo.BeatmapSet; + Debug.Assert(setInfo != null); + // Difficulty settings must be copied first due to the clone in `Beatmap<>.BeatmapInfo_Set`. // This should hopefully be temporary, assuming said clone is eventually removed. @@ -202,25 +115,29 @@ namespace osu.Game.Beatmaps stream.Seek(0, SeekOrigin.Begin); - using (ContextFactory.GetForWrite()) + using (var realm = ContextFactory.CreateContext()) + using (var transaction = realm.BeginWrite()) { beatmapInfo = setInfo.Beatmaps.Single(b => b.Equals(beatmapInfo)); - var metadata = beatmapInfo.Metadata ?? setInfo.Metadata; - // grab the original file (or create a new one if not found). - var fileInfo = setInfo.Files.SingleOrDefault(f => string.Equals(f.Filename, beatmapInfo.Path, StringComparison.OrdinalIgnoreCase)) ?? new BeatmapSetFileInfo(); + var existingFileInfo = setInfo.Files.SingleOrDefault(f => string.Equals(f.Filename, beatmapInfo.Path, StringComparison.OrdinalIgnoreCase)); + + if (existingFileInfo != null) + { + DeleteFile(setInfo, existingFileInfo); + } // metadata may have changed; update the path with the standard format. - beatmapInfo.Path = $"{metadata.Artist} - {metadata.Title} ({metadata.Author}) [{beatmapInfo.DifficultyName}].osu".GetValidArchiveContentFilename(); + var metadata = beatmapInfo.Metadata; + string filename = $"{metadata.Artist} - {metadata.Title} ({metadata.Author}) [{beatmapInfo.DifficultyName}].osu".GetValidArchiveContentFilename(); beatmapInfo.MD5Hash = stream.ComputeMD5Hash(); - // update existing or populate new file's filename. - fileInfo.Filename = beatmapInfo.Path; - stream.Seek(0, SeekOrigin.Begin); - ReplaceFile(setInfo, fileInfo, stream); + AddFile(setInfo, stream, filename, realm); + + transaction.Commit(); } } @@ -232,100 +149,43 @@ namespace osu.Game.Beatmaps /// /// The query. /// The first result for the provided query, or null if no results were found. - public BeatmapSetInfo QueryBeatmapSet(Expression> query) => beatmaps.ConsumableItems.AsNoTracking().FirstOrDefault(query); - - protected override bool CanSkipImport(BeatmapSetInfo existing, BeatmapSetInfo import) + public BeatmapSetInfo? QueryBeatmapSet(Expression> query) { - if (!base.CanSkipImport(existing, import)) - return false; - - return existing.Beatmaps.Any(b => b.OnlineID != null); - } - - protected override bool CanReuseExisting(BeatmapSetInfo existing, BeatmapSetInfo import) - { - if (!base.CanReuseExisting(existing, import)) - return false; - - var existingIds = existing.Beatmaps.Select(b => b.OnlineID).OrderBy(i => i); - var importIds = import.Beatmaps.Select(b => b.OnlineID).OrderBy(i => i); - - // force re-import if we are not in a sane state. - return existing.OnlineID == import.OnlineID && existingIds.SequenceEqual(importIds); + using (var context = ContextFactory.CreateContext()) + return context.All().FirstOrDefault(query); // TODO: ?.ToLive(); } /// /// Returns a list of all usable s. /// /// A list of available . - public List GetAllUsableBeatmapSets(IncludedDetails includes = IncludedDetails.All, bool includeProtected = false) => - GetAllUsableBeatmapSetsEnumerable(includes, includeProtected).ToList(); + public List GetAllUsableBeatmapSets(bool includeProtected = false) => + GetAllUsableBeatmapSetsEnumerable(includeProtected).ToList(); /// /// Returns a list of all usable s. Note that files are not populated. /// - /// The level of detail to include in the returned objects. /// Whether to include protected (system) beatmaps. These should not be included for gameplay playable use cases. /// A list of available . - public IEnumerable GetAllUsableBeatmapSetsEnumerable(IncludedDetails includes, bool includeProtected = false) + public IEnumerable GetAllUsableBeatmapSetsEnumerable(bool includeProtected = false) { - IQueryable queryable; - - switch (includes) - { - case IncludedDetails.Minimal: - queryable = beatmaps.BeatmapSetsOverview; - break; - - case IncludedDetails.AllButRuleset: - queryable = beatmaps.BeatmapSetsWithoutRuleset; - break; - - case IncludedDetails.AllButFiles: - queryable = beatmaps.BeatmapSetsWithoutFiles; - break; - - default: - queryable = beatmaps.ConsumableItems; - break; - } - - // AsEnumerable used here to avoid applying the WHERE in sql. When done so, ef core 2.x uses an incorrect ORDER BY - // clause which causes queries to take 5-10x longer. - // TODO: remove if upgrading to EF core 3.x. - return queryable.AsEnumerable().Where(s => !s.DeletePending && (includeProtected || !s.Protected)); + using (var context = ContextFactory.CreateContext()) + return context.All().Where(b => !b.DeletePending && (includeProtected || !b.Protected)); } /// /// Perform a lookup query on available s. /// /// The query. - /// The level of detail to include in the returned objects. /// Results from the provided query. - public IEnumerable QueryBeatmapSets(Expression> query, IncludedDetails includes = IncludedDetails.All) + public IEnumerable QueryBeatmapSets(Expression> query) { - IQueryable queryable; - - switch (includes) + using (var context = ContextFactory.CreateContext()) { - case IncludedDetails.Minimal: - queryable = beatmaps.BeatmapSetsOverview; - break; - - case IncludedDetails.AllButRuleset: - queryable = beatmaps.BeatmapSetsWithoutRuleset; - break; - - case IncludedDetails.AllButFiles: - queryable = beatmaps.BeatmapSetsWithoutFiles; - break; - - default: - queryable = beatmaps.ConsumableItems; - break; + return context.All() + .Where(b => !b.DeletePending) + .Where(query); } - - return queryable.AsNoTracking().Where(query); } /// @@ -333,145 +193,24 @@ namespace osu.Game.Beatmaps /// /// The query. /// The first result for the provided query, or null if no results were found. - public BeatmapInfo QueryBeatmap(Expression> query) => beatmaps.Beatmaps.AsNoTracking().FirstOrDefault(query); + public BeatmapInfo? QueryBeatmap(Expression> query) + { + using (var context = ContextFactory.CreateContext()) + return context.All().FirstOrDefault(query); // TODO: ?.ToLive(); + } /// /// Perform a lookup query on available s. /// /// The query. /// Results from the provided query. - public IQueryable QueryBeatmaps(Expression> query) => beatmaps.Beatmaps.AsNoTracking().Where(query); - - public override string HumanisedModelName => "beatmap"; - - protected override bool CheckLocalAvailability(BeatmapSetInfo model, IQueryable items) - => base.CheckLocalAvailability(model, items) - || (model.OnlineID != null && items.Any(b => b.OnlineID == model.OnlineID)); - - protected override BeatmapSetInfo CreateModel(ArchiveReader reader) + public IQueryable QueryBeatmaps(Expression> query) { - // let's make sure there are actually .osu files to import. - string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu", StringComparison.OrdinalIgnoreCase)); - - if (string.IsNullOrEmpty(mapName)) + using (var context = ContextFactory.CreateContext()) { - Logger.Log($"No beatmap files found in the beatmap archive ({reader.Name}).", LoggingTarget.Database); - return null; + return context.All() + .Where(query); } - - Beatmap beatmap; - using (var stream = new LineBufferedReader(reader.GetStream(mapName))) - beatmap = Decoder.GetDecoder(stream).Decode(stream); - - return new BeatmapSetInfo - { - OnlineID = beatmap.BeatmapInfo.BeatmapSet?.OnlineID, - Metadata = beatmap.Metadata, - DateAdded = DateTimeOffset.UtcNow - }; } - - /// - /// Create all required s for the provided archive. - /// - private List createBeatmapDifficulties(List files) - { - var beatmapInfos = new List(); - - foreach (var file in files.Where(f => f.Filename.EndsWith(".osu", StringComparison.OrdinalIgnoreCase))) - { - using (var raw = Files.Store.GetStream(file.FileInfo.GetStoragePath())) - using (var ms = new MemoryStream()) // we need a memory stream so we can seek - using (var sr = new LineBufferedReader(ms)) - { - raw.CopyTo(ms); - ms.Position = 0; - - var decoder = Decoder.GetDecoder(sr); - IBeatmap beatmap = decoder.Decode(sr); - - string hash = ms.ComputeSHA2Hash(); - - if (beatmapInfos.Any(b => b.Hash == hash)) - continue; - - beatmap.BeatmapInfo.Path = file.Filename; - beatmap.BeatmapInfo.Hash = hash; - beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash(); - - var ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID); - beatmap.BeatmapInfo.Ruleset = ruleset; - - // TODO: this should be done in a better place once we actually need to dynamically update it. - beatmap.BeatmapInfo.StarRating = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating ?? 0; - beatmap.BeatmapInfo.Length = calculateLength(beatmap); - beatmap.BeatmapInfo.BPM = 60000 / beatmap.GetMostCommonBeatLength(); - - beatmapInfos.Add(beatmap.BeatmapInfo); - } - } - - return beatmapInfos; - } - - private double calculateLength(IBeatmap b) - { - if (!b.HitObjects.Any()) - return 0; - - var lastObject = b.HitObjects.Last(); - - //TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list). - double endTime = lastObject.GetEndTime(); - double startTime = b.HitObjects.First().StartTime; - - return endTime - startTime; - } - - /// - /// A dummy WorkingBeatmap for the purpose of retrieving a beatmap for star difficulty calculation. - /// - private class DummyConversionBeatmap : WorkingBeatmap - { - private readonly IBeatmap beatmap; - - public DummyConversionBeatmap(IBeatmap beatmap) - : base(beatmap.BeatmapInfo, null) - { - this.beatmap = beatmap; - } - - protected override IBeatmap GetBeatmap() => beatmap; - protected override Texture GetBackground() => null; - protected override Track GetBeatmapTrack() => null; - protected internal override ISkin GetSkin() => null; - public override Stream GetStream(string storagePath) => null; - } - } - - /// - /// The level of detail to include in database results. - /// - public enum IncludedDetails - { - /// - /// Only include beatmap difficulties and set level metadata. - /// - Minimal, - - /// - /// Include all difficulties, rulesets, difficulty metadata but no files. - /// - AllButFiles, - - /// - /// Include everything except ruleset. Used for cases where we aren't sure the ruleset is present but still want to consume the beatmap. - /// - AllButRuleset, - - /// - /// Include everything. - /// - All } } diff --git a/osu.Game/Beatmaps/EFBeatmapSetInfo.cs b/osu.Game/Beatmaps/EFBeatmapSetInfo.cs index c0310f5a76..10910d9817 100644 --- a/osu.Game/Beatmaps/EFBeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/EFBeatmapSetInfo.cs @@ -34,7 +34,7 @@ namespace osu.Game.Beatmaps public EFBeatmapMetadata Metadata { get; set; } [NotNull] - public List Beatmaps { get; } = new List(); + public List Beatmaps { get; } = new List(); public BeatmapOnlineStatus Status { get; set; } = BeatmapOnlineStatus.None; @@ -95,7 +95,7 @@ namespace osu.Game.Beatmaps #region Implementation of IBeatmapSetInfo - IBeatmapMetadataInfo IBeatmapSetInfo.Metadata => Metadata ?? Beatmaps.FirstOrDefault()?.Metadata ?? new BeatmapMetadata(); + IBeatmapMetadataInfo IBeatmapSetInfo.Metadata => Metadata ?? Beatmaps.FirstOrDefault()?.Metadata ?? new EFBeatmapMetadata(); IEnumerable IBeatmapSetInfo.Beatmaps => Beatmaps; IEnumerable IHasNamedFiles.Files => Files; diff --git a/osu.Game/Beatmaps/FlatFileWorkingBeatmap.cs b/osu.Game/Beatmaps/FlatFileWorkingBeatmap.cs index 8c915e2872..dc8201a402 100644 --- a/osu.Game/Beatmaps/FlatFileWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/FlatFileWorkingBeatmap.cs @@ -33,7 +33,7 @@ namespace osu.Game.Beatmaps beatmap.BeatmapInfo.Ruleset = rulesetProvider(beatmap.BeatmapInfo.RulesetID).RulesetInfo; if (beatmapId.HasValue) - beatmap.BeatmapInfo.OnlineID = beatmapId; + beatmap.BeatmapInfo.OnlineID = beatmapId.Value; } private static Beatmap readFromFile(string filename) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index e5db9d045a..5c61d302c2 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -11,6 +11,7 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Legacy; using osu.Game.Beatmaps.Timing; using osu.Game.IO; +using osu.Game.Rulesets; using osu.Game.Rulesets.Objects.Legacy; namespace osu.Game.Beatmaps.Formats @@ -141,7 +142,8 @@ namespace osu.Game.Beatmaps.Formats break; case @"Mode": - beatmap.BeatmapInfo.RulesetID = Parsing.ParseInt(pair.Value); + // TODO: ha ha ha. + beatmap.BeatmapInfo.Ruleset = new RulesetInfo(Parsing.ParseInt(pair.Value), "some ruleset", "wangs", true /* probably not */); switch (beatmap.BeatmapInfo.RulesetID) { diff --git a/osu.Game/Database/ImportTask.cs b/osu.Game/Database/ImportTask.cs index 1fb5a42630..cd9e396d13 100644 --- a/osu.Game/Database/ImportTask.cs +++ b/osu.Game/Database/ImportTask.cs @@ -5,13 +5,14 @@ using System.IO; using osu.Game.IO.Archives; +using osu.Game.Stores; using osu.Game.Utils; using SharpCompress.Common; namespace osu.Game.Database { /// - /// An encapsulated import task to be imported to an . + /// An encapsulated import task to be imported to an . /// public class ImportTask { diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 3346c6d97d..310f4946f3 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -71,7 +71,7 @@ namespace osu.Game.Overlays beatmaps.ItemUpdated += beatmapUpdated; beatmaps.ItemRemoved += beatmapRemoved; - beatmapSets.AddRange(beatmaps.GetAllUsableBeatmapSets(IncludedDetails.Minimal, true).OrderBy(_ => RNG.Next())); + beatmapSets.AddRange(beatmaps.GetAllUsableBeatmapSets(true).OrderBy(_ => RNG.Next())); // Todo: These binds really shouldn't be here, but are unlikely to cause any issues for now. // They are placed here for now since some tests rely on setting the beatmap _and_ their hierarchies inside their load(), which runs before the MusicController's load(). diff --git a/osu.Game/Scoring/LegacyDatabasedScore.cs b/osu.Game/Scoring/LegacyDatabasedScore.cs index 6a8e5f8930..ac444c1bf3 100644 --- a/osu.Game/Scoring/LegacyDatabasedScore.cs +++ b/osu.Game/Scoring/LegacyDatabasedScore.cs @@ -6,14 +6,14 @@ using System.Linq; using osu.Framework.IO.Stores; using osu.Game.Beatmaps; using osu.Game.Extensions; +using osu.Game.Rulesets; using osu.Game.Scoring.Legacy; -using osu.Game.Stores; namespace osu.Game.Scoring { public class LegacyDatabasedScore : Score { - public LegacyDatabasedScore(ScoreInfo score, RealmRulesetStore rulesets, BeatmapManager beatmaps, IResourceStore store) + public LegacyDatabasedScore(ScoreInfo score, RulesetStore rulesets, BeatmapManager beatmaps, IResourceStore store) { ScoreInfo = score; diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 21339acd22..20b6ba7a93 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -80,17 +80,9 @@ namespace osu.Game.Scoring public double? PP { get; set; } - public RealmBeatmap Beatmap { get; set; } = null!; + public BeatmapInfo Beatmap { get; set; } = null!; - public BeatmapInfo BeatmapInfo - { - get => new BeatmapInfo(); - // .. todo - // ReSharper disable once ValueParameterNotUsed - set => Beatmap = new RealmBeatmap(new RealmRuleset("osu", "osu!", "wangs", 0), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata()); - } - - public RealmRuleset Ruleset { get; set; } = null!; + public RulesetInfo Ruleset { get; set; } = null!; [Ignored] public Dictionary Statistics @@ -132,7 +124,13 @@ namespace osu.Game.Scoring private Mod[]? mods; - public int BeatmapInfoID => BeatmapInfo.ID; + public Guid BeatmapInfoID => Beatmap.ID; + + public BeatmapInfo BeatmapInfo + { + get => Beatmap; + set => Beatmap = value; + } public int UserID => RealmUser.OnlineID; diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index d094e1f45c..0a1b616422 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -20,7 +20,6 @@ using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; -using osu.Game.Stores; namespace osu.Game.Scoring { diff --git a/osu.Game/Scoring/ScoreModelManager.cs b/osu.Game/Scoring/ScoreModelManager.cs index dd0ac79873..81f80df3ad 100644 --- a/osu.Game/Scoring/ScoreModelManager.cs +++ b/osu.Game/Scoring/ScoreModelManager.cs @@ -11,6 +11,7 @@ using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.IO.Archives; +using osu.Game.Rulesets; using osu.Game.Scoring.Legacy; using osu.Game.Stores; using Realms; @@ -25,10 +26,10 @@ namespace osu.Game.Scoring protected override string[] HashableFileTypes => new[] { ".osr" }; - private readonly RealmRulesetStore rulesets; + private readonly RulesetStore rulesets; private readonly Func beatmaps; - public ScoreModelManager(RealmRulesetStore rulesets, Func beatmaps, Storage storage, RealmContextFactory contextFactory) + public ScoreModelManager(RulesetStore rulesets, Func beatmaps, Storage storage, RealmContextFactory contextFactory) : base(storage, contextFactory) { this.rulesets = rulesets; diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index 948e3a7d88..94c48f5251 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -95,7 +95,7 @@ namespace osu.Game.Screens.Menu // if the user has requested not to play theme music, we should attempt to find a random beatmap from their collection. if (!MenuMusic.Value) { - var sets = beatmaps.GetAllUsableBeatmapSets(IncludedDetails.Minimal); + var sets = beatmaps.GetAllUsableBeatmapSets(); if (sets.Count > 0) { @@ -125,7 +125,7 @@ namespace osu.Game.Screens.Menu bool loadThemedIntro() { - setInfo = beatmaps.QueryBeatmapSets(b => b.Hash == BeatmapHash, IncludedDetails.AllButRuleset).FirstOrDefault(); + setInfo = beatmaps.QueryBeatmapSets(b => b.Hash == BeatmapHash).FirstOrDefault(); if (setInfo == null) return false; diff --git a/osu.Game/Screens/Ranking/SoloResultsScreen.cs b/osu.Game/Screens/Ranking/SoloResultsScreen.cs index afebc728b4..2ec6c38287 100644 --- a/osu.Game/Screens/Ranking/SoloResultsScreen.cs +++ b/osu.Game/Screens/Ranking/SoloResultsScreen.cs @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Ranking protected override APIRequest FetchScores(Action> scoresCallback) { - if (Score.BeatmapInfo.OnlineID == null || Score.BeatmapInfo.Status <= BeatmapOnlineStatus.Pending) + if (Score.BeatmapInfo.OnlineID <= 0 || Score.BeatmapInfo.Status <= BeatmapOnlineStatus.Pending) return null; getScoreRequest = new GetScoresRequest(Score.BeatmapInfo, Score.Ruleset); diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 7e292e2de7..f8d2c3cb9b 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -182,7 +182,7 @@ namespace osu.Game.Screens.Select loadBeatmapSets(GetLoadableBeatmaps()); } - protected virtual IEnumerable GetLoadableBeatmaps() => beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.AllButFiles); + protected virtual IEnumerable GetLoadableBeatmaps() => beatmaps.GetAllUsableBeatmapSetsEnumerable(); public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() => { diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 619b1e0fd0..618c5cf5ec 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -61,7 +61,11 @@ namespace osu.Game.Screens.Select.Carousel [BackgroundDependencyLoader(true)] private void load(BeatmapSetOverlay beatmapOverlay) { - restoreHiddenRequested = s => s.Beatmaps.ForEach(manager.Restore); + restoreHiddenRequested = s => + { + foreach (var b in s.Beatmaps) + manager.Restore(b); + }; if (beatmapOverlay != null) viewDetails = beatmapOverlay.FetchAndShowBeatmapSet; @@ -214,8 +218,8 @@ namespace osu.Game.Screens.Select.Carousel if (Item.State.Value == CarouselItemState.NotSelected) items.Add(new OsuMenuItem("Expand", MenuItemType.Highlighted, () => Item.State.Value = CarouselItemState.Selected)); - if (beatmapSet.OnlineID != null && viewDetails != null) - items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => viewDetails(beatmapSet.OnlineID.Value))); + if (beatmapSet.OnlineID > 0 && viewDetails != null) + items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => viewDetails(beatmapSet.OnlineID))); if (collectionManager != null) { diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index a40e8e2bde..1aab84183a 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -295,7 +295,7 @@ namespace osu.Game.Screens.Select Schedule(() => { // if we have no beatmaps, let's prompt the user to import from over a stable install if he has one. - if (!beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.Minimal).Any() && DisplayStableImportPrompt) + if (!beatmaps.GetAllUsableBeatmapSetsEnumerable().Any() && DisplayStableImportPrompt) { dialogOverlay.Push(new ImportFromStablePopup(() => { diff --git a/osu.Game/Stores/BeatmapImporter.cs b/osu.Game/Stores/BeatmapImporter.cs index de92da394d..2c73bd22eb 100644 --- a/osu.Game/Stores/BeatmapImporter.cs +++ b/osu.Game/Stores/BeatmapImporter.cs @@ -34,7 +34,7 @@ namespace osu.Game.Stores /// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps. /// [ExcludeFromDynamicCompile] - public class BeatmapImporter : RealmArchiveModelImporter, IDisposable + public abstract class BeatmapImporter : RealmArchiveModelManager, IDisposable { public override IEnumerable HandledExtensions => new[] { ".osz" }; @@ -45,7 +45,7 @@ namespace osu.Game.Stores private readonly BeatmapOnlineLookupQueue? onlineLookupQueue; - public BeatmapImporter(RealmContextFactory contextFactory, Storage storage, BeatmapOnlineLookupQueue? onlineLookupQueue = null) + protected BeatmapImporter(RealmContextFactory contextFactory, Storage storage, BeatmapOnlineLookupQueue? onlineLookupQueue = null) : base(storage, contextFactory) { this.onlineLookupQueue = onlineLookupQueue; diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index 8a31e4576a..d21813d97f 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -10,8 +10,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; using osu.Framework.Platform; -using osu.Game.IO; using osu.Game.Screens.Play; +using osu.Game.Stores; namespace osu.Game.Storyboards.Drawables { @@ -76,7 +76,7 @@ namespace osu.Game.Storyboards.Drawables } [BackgroundDependencyLoader(true)] - private void load(FileStore fileStore, GameplayClock clock, CancellationToken? cancellationToken, GameHost host) + private void load(RealmFileStore fileStore, GameplayClock clock, CancellationToken? cancellationToken, GameHost host) { if (clock != null) Clock = clock; diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs index 99944bcf6d..3b4547cb49 100644 --- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs @@ -32,14 +32,12 @@ namespace osu.Game.Tests.Beatmaps HitObjects = baseBeatmap.HitObjects; BeatmapInfo.Ruleset = ruleset; - BeatmapInfo.RulesetID = ruleset.ID ?? 0; BeatmapInfo.Length = 75000; BeatmapInfo.OnlineInfo = new APIBeatmap(); BeatmapInfo.OnlineID = Interlocked.Increment(ref onlineBeatmapID); Debug.Assert(BeatmapInfo.BeatmapSet != null); - BeatmapInfo.BeatmapSet.Metadata = BeatmapInfo.Metadata; BeatmapInfo.BeatmapSet.Beatmaps.Add(BeatmapInfo); BeatmapInfo.BeatmapSet.OnlineID = Interlocked.Increment(ref onlineSetID); } diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index a2f567f3b0..570e4c26b3 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -122,9 +122,9 @@ namespace osu.Game.Tests.Visual this.testBeatmap = testBeatmap; } - protected override BeatmapModelManager CreateBeatmapModelManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, GameHost host) + protected override BeatmapModelManager CreateBeatmapModelManager(Storage storage, RealmContextFactory contextFactory, RulesetStore rulesets, BeatmapOnlineLookupQueue onlineLookupQueue) { - return new TestBeatmapModelManager(storage, contextFactory, rulesets, api, host); + return new TestBeatmapModelManager(storage, contextFactory, rulesets, onlineLookupQueue); } protected override WorkingBeatmapCache CreateWorkingBeatmapCache(AudioManager audioManager, IResourceStore resources, IResourceStore storage, WorkingBeatmap defaultBeatmap, GameHost host) @@ -148,8 +148,8 @@ namespace osu.Game.Tests.Visual internal class TestBeatmapModelManager : BeatmapModelManager { - public TestBeatmapModelManager(Storage storage, IDatabaseContextFactory databaseContextFactory, RulesetStore rulesetStore, IAPIProvider apiProvider, GameHost gameHost) - : base(storage, databaseContextFactory, rulesetStore, gameHost) + public TestBeatmapModelManager(Storage storage, RealmContextFactory databaseContextFactory, RulesetStore rulesetStore, BeatmapOnlineLookupQueue beatmapOnlineLookupQueue) + : base(databaseContextFactory, storage, beatmapOnlineLookupQueue) { }