From 9e20a02c0aab7e68c646d5b487e8d4543657eb54 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Jul 2017 16:28:32 +0900 Subject: [PATCH] Split out BeatmapDatabase into BeatmapStore Hide database functionality at a lower level in preparation from eventually making it private. --- .../Tests/TestCasePlaySongSelect.cs | 22 +- .../Tests/TestCaseResults.cs | 6 +- osu.Desktop/OsuGameDesktop.cs | 2 +- .../Beatmaps/IO/ImportBeatmapTest.cs | 24 +- osu.Game/Beatmaps/BeatmapDatabase.cs | 314 ++++-------------- .../Beatmaps/BeatmapDatabaseWorkingBeatmap.cs | 75 ----- osu.Game/Beatmaps/BeatmapStore.cs | 212 ++++++++++++ .../Beatmaps/BeatmapStoreWorkingBeatmap.cs | 70 ++++ osu.Game/Beatmaps/Drawables/BeatmapGroup.cs | 5 +- .../{DatabaseBacking.cs => DatabaseStore.cs} | 7 +- osu.Game/IPC/BeatmapIPCChannel.cs | 4 +- osu.Game/OsuGame.cs | 4 +- osu.Game/OsuGameBase.cs | 10 +- osu.Game/Overlays/Music/PlaylistOverlay.cs | 6 +- osu.Game/Rulesets/RulesetDatabase.cs | 10 +- osu.Game/Rulesets/RulesetInfo.cs | 3 +- osu.Game/Rulesets/Scoring/ScoreDatabase.cs | 11 +- osu.Game/Screens/Menu/Intro.cs | 10 +- osu.Game/Screens/Select/BeatmapCarousel.cs | 8 +- .../Screens/Select/BeatmapDeleteDialog.cs | 8 +- osu.Game/Screens/Select/SongSelect.cs | 22 +- osu.Game/osu.Game.csproj | 23 +- 22 files changed, 428 insertions(+), 428 deletions(-) delete mode 100644 osu.Game/Beatmaps/BeatmapDatabaseWorkingBeatmap.cs create mode 100644 osu.Game/Beatmaps/BeatmapStore.cs create mode 100644 osu.Game/Beatmaps/BeatmapStoreWorkingBeatmap.cs rename osu.Game/Database/{DatabaseBacking.cs => DatabaseStore.cs} (87%) diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs index 55fd8b45cc..6d41be72c7 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs @@ -5,7 +5,8 @@ using System.Collections.Generic; using osu.Desktop.VisualTests.Platform; using osu.Framework.Testing; using osu.Framework.MathUtils; -using osu.Game.Database; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Filter; @@ -13,7 +14,7 @@ namespace osu.Desktop.VisualTests.Tests { internal class TestCasePlaySongSelect : TestCase { - private readonly BeatmapStore db; + private readonly BeatmapStore store; public override string Description => @"with fake data"; @@ -23,21 +24,21 @@ namespace osu.Desktop.VisualTests.Tests { PlaySongSelect songSelect; - if (db == null) + if (store == null) { var storage = new TestStorage(@"TestCasePlaySongSelect"); var backingDatabase = storage.GetDatabase(@"client"); - rulesets = new RulesetDatabase(storage, backingDatabase); - db = new BeatmapStore(storage, backingDatabase, rulesets); + rulesets = new RulesetDatabase(backingDatabase); + store = new BeatmapStore(storage, backingDatabase, rulesets); var sets = new List(); for (int i = 0; i < 100; i += 10) sets.Add(createTestBeatmapSet(i)); - db.Import(sets); + store.Database.Import(sets); } Add(songSelect = new PlaySongSelect()); @@ -48,21 +49,12 @@ namespace osu.Desktop.VisualTests.Tests AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; }); } - //protected override void Dispose(bool isDisposing) - //{ - // if (oldDb != null) - // db = null; - - // base.Dispose(isDisposing); - //} - private BeatmapSetInfo createTestBeatmapSet(int i) { return new BeatmapSetInfo { OnlineBeatmapSetID = 1234 + i, Hash = "d8e8fca2dc0f896fd7cb4cb0031ba249", - Path = string.Empty, Metadata = new BeatmapMetadata { OnlineBeatmapSetID = 1234 + i, diff --git a/osu.Desktop.VisualTests/Tests/TestCaseResults.cs b/osu.Desktop.VisualTests/Tests/TestCaseResults.cs index a553814ce8..5b9ee2198d 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseResults.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseResults.cs @@ -15,12 +15,12 @@ namespace osu.Desktop.VisualTests.Tests { internal class TestCaseResults : TestCase { - private BeatmapDatabase db; + private BeatmapStore db; public override string Description => @"Results after playing."; [BackgroundDependencyLoader] - private void load(BeatmapDatabase db) + private void load(BeatmapStore db) { this.db = db; } @@ -33,7 +33,7 @@ namespace osu.Desktop.VisualTests.Tests if (beatmap == null) { - var beatmapInfo = db.Query().FirstOrDefault(b => b.RulesetID == 0); + var beatmapInfo = db.Database.Query().FirstOrDefault(b => b.RulesetID == 0); if (beatmapInfo != null) beatmap = db.GetWorkingBeatmap(beatmapInfo); } diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index a2bfe6bf2e..ccedcc07d5 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -65,7 +65,7 @@ namespace osu.Desktop var filePaths = dropData.Select(f => f.ToString()).ToArray(); if (filePaths.All(f => Path.GetExtension(f) == @".osz")) - Task.Run(() => BeatmapDatabase.Import(filePaths)); + Task.Run(() => BeatmapStore.Import(filePaths)); else if (filePaths.All(f => Path.GetExtension(f) == @".osr")) Task.Run(() => { diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index a3da9ca3ca..5c1fd381e2 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -34,7 +34,7 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.IsTrue(File.Exists(temp)); - osu.Dependencies.Get().Import(temp); + osu.Dependencies.Get().Import(temp); ensureLoaded(osu); @@ -80,7 +80,7 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.IsTrue(File.Exists(temp), "Temporary file copy never substantiated"); using (File.OpenRead(temp)) - osu.Dependencies.Get().Import(temp); + osu.Dependencies.Get().Import(temp); ensureLoaded(osu); @@ -106,7 +106,7 @@ namespace osu.Game.Tests.Beatmaps.IO //reset beatmap database (sqlite and storage backing) osu.Dependencies.Get().Reset(); - osu.Dependencies.Get().Reset(); + osu.Dependencies.Get().Reset(); return osu; } @@ -117,8 +117,8 @@ namespace osu.Game.Tests.Beatmaps.IO Action waitAction = () => { - while (!(resultSets = osu.Dependencies.Get() - .Query().Where(s => s.OnlineBeatmapSetID == 241526)).Any()) + while (!(resultSets = osu.Dependencies.Get().Database. + Query().Where(s => s.OnlineBeatmapSetID == 241526)).Any()) Thread.Sleep(50); }; @@ -134,15 +134,15 @@ namespace osu.Game.Tests.Beatmaps.IO //if we don't re-check here, the set will be inserted but the beatmaps won't be present yet. waitAction = () => { - while ((resultBeatmaps = osu.Dependencies.Get() - .GetAllWithChildren(s => s.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0)).Count() != 12) + while ((resultBeatmaps = osu.Dependencies.Get().Database. + GetAllWithChildren(s => s.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0)).Count() != 12) Thread.Sleep(50); }; Assert.IsTrue(waitAction.BeginInvoke(null, null).AsyncWaitHandle.WaitOne(timeout), @"Beatmaps did not import to the database in allocated time"); - var set = osu.Dependencies.Get().GetChildren(resultSets.First()); + var set = osu.Dependencies.Get().Database.GetChildren(resultSets.First()); Assert.IsTrue(set.Beatmaps.Count == resultBeatmaps.Count(), $@"Incorrect database beatmap count post-import ({resultBeatmaps.Count()} but should be {set.Beatmaps.Count})."); @@ -152,16 +152,16 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.IsTrue(set.Beatmaps.Count > 0); - var beatmap = osu.Dependencies.Get().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 0))?.Beatmap; + var beatmap = osu.Dependencies.Get().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 0))?.Beatmap; Assert.IsTrue(beatmap?.HitObjects.Count > 0); - beatmap = osu.Dependencies.Get().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 1))?.Beatmap; + beatmap = osu.Dependencies.Get().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 1))?.Beatmap; Assert.IsTrue(beatmap?.HitObjects.Count > 0); - beatmap = osu.Dependencies.Get().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 2))?.Beatmap; + beatmap = osu.Dependencies.Get().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 2))?.Beatmap; Assert.IsTrue(beatmap?.HitObjects.Count > 0); - beatmap = osu.Dependencies.Get().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 3))?.Beatmap; + beatmap = osu.Dependencies.Get().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 3))?.Beatmap; Assert.IsTrue(beatmap?.HitObjects.Count > 0); } } diff --git a/osu.Game/Beatmaps/BeatmapDatabase.cs b/osu.Game/Beatmaps/BeatmapDatabase.cs index acf41197da..2e8e5da65b 100644 --- a/osu.Game/Beatmaps/BeatmapDatabase.cs +++ b/osu.Game/Beatmaps/BeatmapDatabase.cs @@ -1,43 +1,77 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . +// Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; using System.Collections.Generic; -using System.IO; -using System.Linq; -using osu.Framework.Extensions; using osu.Framework.Logging; -using osu.Framework.Platform; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Formats; -using osu.Game.Beatmaps.IO; -using osu.Game.IPC; +using osu.Game.Database; using osu.Game.Screens.Menu; using SQLite.Net; using SQLiteNetExtensions.Extensions; -namespace osu.Game.Database +namespace osu.Game.Beatmaps { - public class BeatmapStore : DatabaseBacking + /// + /// Handles the storage and retrieval of Beatmaps/BeatmapSets to the database backing + /// + public class BeatmapDatabase : DatabaseStore { - private readonly RulesetDatabase rulesets; - public event Action BeatmapSetAdded; public event Action BeatmapSetRemoved; - // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) - private BeatmapIPCChannel ipc; - - /// - /// A default representation of a WorkingBeatmap to use when no beatmap is available. - /// - public WorkingBeatmap DefaultBeatmap { private get; set; } - - public BeatmapStore(Storage storage, SQLiteConnection connection, RulesetDatabase rulesets, IIpcHost importHost = null) : base(storage, connection) + public BeatmapDatabase(SQLiteConnection connection) : base(connection) { - this.rulesets = rulesets; - if (importHost != null) - ipc = new BeatmapIPCChannel(importHost, this); + } + + protected override Type[] ValidTypes => new[] { + typeof(BeatmapSetInfo), + typeof(BeatmapInfo), + typeof(BeatmapMetadata), + typeof(BeatmapDifficulty), + }; + + protected override void Prepare(bool reset = false) + { + Connection.CreateTable(); + Connection.CreateTable(); + Connection.CreateTable(); + Connection.CreateTable(); + + if (reset) + { + Connection.DropTable(); + Connection.DropTable(); + Connection.DropTable(); + Connection.DropTable(); + } + + deletePending(); + } + + public void Import(IEnumerable beatmapSets) + { + lock (Connection) + { + Connection.BeginTransaction(); + + foreach (var s in beatmapSets) + { + Connection.InsertOrReplaceWithChildren(s, true); + BeatmapSetAdded?.Invoke(s); + } + + Connection.Commit(); + } + } + + public void Delete(IEnumerable beatmapSets) + { + foreach (var s in beatmapSets) + { + s.DeletePending = true; + Update(s, false); + BeatmapSetRemoved?.Invoke(s); + } } private void deletePending() @@ -50,8 +84,6 @@ namespace osu.Game.Database try { - Storage.Delete(b.Path); - foreach (var i in b.Beatmaps) { if (i.Metadata != null) Connection.Delete(i.Metadata); @@ -73,231 +105,5 @@ namespace osu.Game.Database //see https://github.com/praeclarum/sqlite-net/issues/326 Connection.Query("UPDATE BeatmapSetInfo SET DeletePending = 0 WHERE DeletePending IS NULL"); } - - protected override void Prepare(bool reset = false) - { - Connection.CreateTable(); - Connection.CreateTable(); - Connection.CreateTable(); - Connection.CreateTable(); - - if (reset) - { - Storage.DeleteDatabase(@"beatmaps"); - - foreach (var setInfo in Query()) - { - if (Storage.Exists(setInfo.Path)) - Storage.Delete(setInfo.Path); - } - - Connection.DeleteAll(); - Connection.DeleteAll(); - Connection.DeleteAll(); - Connection.DeleteAll(); - } - - deletePending(); - } - - protected override Type[] ValidTypes => new[] { - typeof(BeatmapSetInfo), - typeof(BeatmapInfo), - typeof(BeatmapMetadata), - typeof(BeatmapDifficulty), - }; - - public void Import(string path) - { - try - { - Import(ArchiveReader.GetReader(Storage, path)); - - // We may or may not want to delete the file depending on where it is stored. - // e.g. reconstructing/repairing database with beatmaps from default storage. - // Also, not always a single file, i.e. for LegacyFilesystemReader - // TODO: Add a check to prevent files from storage to be deleted. - try - { - File.Delete(path); - } - catch (Exception e) - { - Logger.Error(e, $@"Could not delete file at {path}"); - } - } - catch (Exception e) - { - e = e.InnerException ?? e; - Logger.Error(e, @"Could not import beatmap set"); - } - } - - public void Import(ArchiveReader archiveReader) - { - BeatmapSetInfo set = getBeatmapSet(archiveReader); - - //If we have an ID then we already exist in the database. - if (set.ID == 0) - Import(new[] { set }); - } - - /// - /// Import multiple from . - /// - /// Multiple locations on disk - public void Import(params string[] paths) - { - foreach (string p in paths) - { - //In case the file was imported twice and deleted after the first time - if (File.Exists(p)) - Import(p); - } - } - - /// - /// Duplicates content from to storage and returns a representing . - /// - /// Content location - /// - private BeatmapSetInfo getBeatmapSet(string path) => getBeatmapSet(ArchiveReader.GetReader(Storage, path)); - - private BeatmapSetInfo getBeatmapSet(ArchiveReader archiveReader) - { - BeatmapMetadata metadata; - - using (var stream = new StreamReader(archiveReader.GetStream(archiveReader.BeatmapFilenames[0]))) - metadata = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata; - - string hash; - string path; - - using (var input = archiveReader.GetUnderlyingStream()) - { - hash = input.GetMd5Hash(); - input.Seek(0, SeekOrigin.Begin); - path = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash); - if (!Storage.Exists(path)) - using (var output = Storage.GetStream(path, FileAccess.Write)) - input.CopyTo(output); - } - - var existing = Connection.Table().FirstOrDefault(b => b.Hash == hash); - - if (existing != null) - { - GetChildren(existing); - - if (existing.DeletePending) - { - existing.DeletePending = false; - Update(existing, false); - BeatmapSetAdded?.Invoke(existing); - } - - return existing; - } - - var beatmapSet = new BeatmapSetInfo - { - OnlineBeatmapSetID = metadata.OnlineBeatmapSetID, - Beatmaps = new List(), - Path = path, - Hash = hash, - Metadata = metadata - }; - - using (var archive = ArchiveReader.GetReader(Storage, path)) - { - string[] mapNames = archive.BeatmapFilenames; - foreach (var name in mapNames) - using (var raw = archive.GetStream(name)) - using (var ms = new MemoryStream()) //we need a memory stream so we can seek and shit - using (var sr = new StreamReader(ms)) - { - raw.CopyTo(ms); - ms.Position = 0; - - var decoder = BeatmapDecoder.GetDecoder(sr); - Beatmap beatmap = decoder.Decode(sr); - - beatmap.BeatmapInfo.Path = name; - beatmap.BeatmapInfo.Hash = ms.GetMd5Hash(); - - // TODO: Diff beatmap metadata with set metadata and leave it here if necessary - beatmap.BeatmapInfo.Metadata = null; - - // TODO: this should be done in a better place once we actually need to dynamically update it. - beatmap.BeatmapInfo.Ruleset = rulesets.Query().FirstOrDefault(r => r.ID == beatmap.BeatmapInfo.RulesetID); - beatmap.BeatmapInfo.StarDifficulty = rulesets.Query().FirstOrDefault(r => r.ID == beatmap.BeatmapInfo.RulesetID)?.CreateInstance()?.CreateDifficultyCalculator(beatmap).Calculate() ?? 0; - - beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo); - } - beatmapSet.StoryboardFile = archive.StoryboardFilename; - } - - return beatmapSet; - } - - public void Import(IEnumerable beatmapSets) - { - lock (Connection) - { - Connection.BeginTransaction(); - - foreach (var s in beatmapSets) - { - Connection.InsertOrReplaceWithChildren(s, true); - BeatmapSetAdded?.Invoke(s); - } - - Connection.Commit(); - } - } - - public void Delete(BeatmapSetInfo beatmapSet) - { - beatmapSet.DeletePending = true; - Update(beatmapSet, false); - - BeatmapSetRemoved?.Invoke(beatmapSet); - } - - public ArchiveReader GetReader(BeatmapSetInfo beatmapSet) - { - if (string.IsNullOrEmpty(beatmapSet.Path)) - return null; - - return ArchiveReader.GetReader(Storage, beatmapSet.Path); - } - - public BeatmapSetInfo GetBeatmapSet(int id) - { - return Query().FirstOrDefault(s => s.OnlineBeatmapSetID == id); - } - - public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null, bool withStoryboard = false) - { - if (beatmapInfo == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) - return DefaultBeatmap; - - if (beatmapInfo.BeatmapSet == null || beatmapInfo.Ruleset == null) - beatmapInfo = GetChildren(beatmapInfo, true); - - if (beatmapInfo.BeatmapSet == null) - throw new InvalidOperationException($@"Beatmap set {beatmapInfo.BeatmapSetInfoID} is not in the local database."); - - if (beatmapInfo.Metadata == null) - beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata; - - WorkingBeatmap working = new BeatmapStoreWorkingBeatmap(this, beatmapInfo); - - previous?.TransferTo(working); - - return working; - } - - public bool Exists(BeatmapSetInfo beatmapSet) => Storage.Exists(beatmapSet.Path); } -} +} \ No newline at end of file diff --git a/osu.Game/Beatmaps/BeatmapDatabaseWorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapDatabaseWorkingBeatmap.cs deleted file mode 100644 index 7c3959f53b..0000000000 --- a/osu.Game/Beatmaps/BeatmapDatabaseWorkingBeatmap.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.IO; -using osu.Framework.Audio.Track; -using osu.Framework.Graphics.Textures; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Formats; -using osu.Game.Beatmaps.IO; - -namespace osu.Game.Database -{ - internal class BeatmapDatabaseWorkingBeatmap : WorkingBeatmap - { - private readonly BeatmapStore store; - - public BeatmapDatabaseWorkingBeatmap(BeatmapStore store, BeatmapInfo beatmapInfo) - : base(beatmapInfo) - { - this.store = store; - } - - private ArchiveReader getReader() => store?.GetReader(BeatmapSetInfo); - - protected override Beatmap GetBeatmap() - { - try - { - Beatmap beatmap; - - using (var reader = getReader()) - { - BeatmapDecoder decoder; - using (var stream = new StreamReader(reader.GetStream(BeatmapInfo.Path))) - { - decoder = BeatmapDecoder.GetDecoder(stream); - beatmap = decoder.Decode(stream); - } - - if (beatmap == null || BeatmapSetInfo.StoryboardFile == null) - return beatmap; - - using (var stream = new StreamReader(reader.GetStream(BeatmapSetInfo.StoryboardFile))) - decoder.Decode(stream, beatmap); - } - - return beatmap; - } - catch { return null; } - } - - protected override Texture GetBackground() - { - if (Metadata?.BackgroundFile == null) - return null; - - try - { - using (var reader = getReader()) - return new TextureStore(new RawTextureLoaderStore(reader), false).Get(Metadata.BackgroundFile); - } - catch { return null; } - } - - protected override Track GetTrack() - { - try - { - var trackData = getReader()?.GetStream(Metadata.AudioFile); - return trackData == null ? null : new TrackBass(trackData); - } - catch { return new TrackVirtual(); } - } - } -} diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs new file mode 100644 index 0000000000..4e5eeee0a4 --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -0,0 +1,212 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using osu.Framework.Extensions; +using osu.Framework.Logging; +using osu.Framework.Platform; +using osu.Game.Beatmaps.Formats; +using osu.Game.Beatmaps.IO; +using osu.Game.IPC; +using osu.Game.Rulesets; +using SQLite.Net; + +namespace osu.Game.Beatmaps +{ + /// + /// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps. + /// + public class BeatmapStore + { + // todo: make this private + public readonly BeatmapDatabase Database; + + private readonly Storage storage; + + private readonly RulesetDatabase rulesets; + + public event Action BeatmapSetAdded; + public event Action BeatmapSetRemoved; + + // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) + private BeatmapIPCChannel ipc; + + /// + /// A default representation of a WorkingBeatmap to use when no beatmap is available. + /// + public WorkingBeatmap DefaultBeatmap { private get; set; } + + public BeatmapStore(Storage storage, SQLiteConnection connection, RulesetDatabase rulesets, IIpcHost importHost = null) + { + Database = new BeatmapDatabase(connection); + Database.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s); + Database.BeatmapSetRemoved += s => BeatmapSetRemoved?.Invoke(s); + + this.storage = storage; + this.rulesets = rulesets; + if (importHost != null) + ipc = new BeatmapIPCChannel(importHost, this); + } + + /// + /// Import multiple from filesystem . + /// + /// Multiple locations on disk. + public void Import(params string[] paths) + { + foreach (string path in paths) + { + try + { + Import(ArchiveReader.GetReader(storage, path)); + + // We may or may not want to delete the file depending on where it is stored. + // e.g. reconstructing/repairing database with beatmaps from default storage. + // Also, not always a single file, i.e. for LegacyFilesystemReader + // TODO: Add a check to prevent files from storage to be deleted. + try + { + File.Delete(path); + } + catch (Exception e) + { + Logger.Error(e, $@"Could not delete file at {path}"); + } + } + catch (Exception e) + { + e = e.InnerException ?? e; + Logger.Error(e, @"Could not import beatmap set"); + } + } + } + + /// + /// Import a beatmap from an . + /// + /// The beatmap to be imported. + public void Import(ArchiveReader archiveReader) + { + BeatmapSetInfo set = importToStorage(archiveReader); + + //If we have an ID then we already exist in the database. + if (set.ID == 0) + Database.Import(new[] { set }); + } + + /// + /// Delete a beatmap from the store. + /// + /// The beatmap to delete. + public void Delete(BeatmapSetInfo beatmapSet) => Database.Delete(new[] { beatmapSet }); + + public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) + { + if (beatmapInfo == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) + return DefaultBeatmap; + + if (beatmapInfo.BeatmapSet == null || beatmapInfo.Ruleset == null) + beatmapInfo = Database.GetChildren(beatmapInfo, true); + + if (beatmapInfo.BeatmapSet == null) + throw new InvalidOperationException($@"Beatmap set {beatmapInfo.BeatmapSetInfoID} is not in the local database."); + + if (beatmapInfo.Metadata == null) + beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata; + + WorkingBeatmap working = new BeatmapStoreWorkingBeatmap(() => string.IsNullOrEmpty(beatmapInfo.BeatmapSet.Path) ? null : ArchiveReader.GetReader(storage, beatmapInfo.BeatmapSet.Path), beatmapInfo); + + previous?.TransferTo(working); + + return working; + } + + /// + /// Reset the store to an empty state. + /// + public void Reset() + { + Database.Reset(); + } + + private BeatmapSetInfo importToStorage(ArchiveReader archiveReader) + { + BeatmapMetadata metadata; + + using (var stream = new StreamReader(archiveReader.GetStream(archiveReader.BeatmapFilenames[0]))) + metadata = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata; + + string hash; + string path; + + using (var input = archiveReader.GetUnderlyingStream()) + { + hash = input.GetMd5Hash(); + input.Seek(0, SeekOrigin.Begin); + path = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash); + if (!storage.Exists(path)) + using (var output = storage.GetStream(path, FileAccess.Write)) + input.CopyTo(output); + } + + var existing = Database.Query().FirstOrDefault(b => b.Hash == hash); + + if (existing != null) + { + Database.GetChildren(existing); + + if (existing.DeletePending) + { + existing.DeletePending = false; + Database.Update(existing, false); + BeatmapSetAdded?.Invoke(existing); + } + + return existing; + } + + var beatmapSet = new BeatmapSetInfo + { + OnlineBeatmapSetID = metadata.OnlineBeatmapSetID, + Beatmaps = new List(), + Path = path, + Hash = hash, + Metadata = metadata + }; + + using (var archive = ArchiveReader.GetReader(storage, path)) + { + string[] mapNames = archive.BeatmapFilenames; + foreach (var name in mapNames) + using (var raw = archive.GetStream(name)) + using (var ms = new MemoryStream()) //we need a memory stream so we can seek and shit + using (var sr = new StreamReader(ms)) + { + raw.CopyTo(ms); + ms.Position = 0; + + var decoder = BeatmapDecoder.GetDecoder(sr); + Beatmap beatmap = decoder.Decode(sr); + + beatmap.BeatmapInfo.Path = name; + beatmap.BeatmapInfo.Hash = ms.GetMd5Hash(); + + // TODO: Diff beatmap metadata with set metadata and leave it here if necessary + beatmap.BeatmapInfo.Metadata = null; + + // TODO: this should be done in a better place once we actually need to dynamically update it. + beatmap.BeatmapInfo.Ruleset = rulesets.Query().FirstOrDefault(r => r.ID == beatmap.BeatmapInfo.RulesetID); + beatmap.BeatmapInfo.StarDifficulty = rulesets.Query().FirstOrDefault(r => r.ID == beatmap.BeatmapInfo.RulesetID)?.CreateInstance()?.CreateDifficultyCalculator(beatmap).Calculate() ?? 0; + + beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo); + } + beatmapSet.StoryboardFile = archive.StoryboardFilename; + } + + return beatmapSet; + } + } +} diff --git a/osu.Game/Beatmaps/BeatmapStoreWorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapStoreWorkingBeatmap.cs new file mode 100644 index 0000000000..478cd7d87d --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapStoreWorkingBeatmap.cs @@ -0,0 +1,70 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.IO; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics.Textures; +using osu.Framework.IO.Stores; +using osu.Game.Beatmaps.Formats; + +namespace osu.Game.Beatmaps +{ + internal class BeatmapStoreWorkingBeatmap : WorkingBeatmap + { + private readonly Func> getStore; + + public BeatmapStoreWorkingBeatmap(Func> getStore, BeatmapInfo beatmapInfo) + : base(beatmapInfo) + { + this.getStore = getStore; + } + + protected override Beatmap GetBeatmap() + { + try + { + Beatmap beatmap; + + BeatmapDecoder decoder; + using (var stream = new StreamReader(getStore().GetStream(BeatmapInfo.Path))) + { + decoder = BeatmapDecoder.GetDecoder(stream); + beatmap = decoder.Decode(stream); + } + + if (beatmap == null || BeatmapSetInfo.StoryboardFile == null) + return beatmap; + + using (var stream = new StreamReader(getStore().GetStream(BeatmapSetInfo.StoryboardFile))) + decoder.Decode(stream, beatmap); + + + return beatmap; + } + catch { return null; } + } + + protected override Texture GetBackground() + { + if (Metadata?.BackgroundFile == null) + return null; + + try + { + return new TextureStore(new RawTextureLoaderStore(getStore()), false).Get(Metadata.BackgroundFile); + } + catch { return null; } + } + + protected override Track GetTrack() + { + try + { + var trackData = getStore().GetStream(Metadata.AudioFile); + return trackData == null ? null : new TrackBass(trackData); + } + catch { return new TrackVirtual(); } + } + } +} diff --git a/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs b/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs index 11cc6122ac..89ba9133c1 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Linq; using osu.Framework; using osu.Framework.Graphics; -using osu.Game.Database; namespace osu.Game.Beatmaps.Drawables { @@ -59,10 +58,10 @@ namespace osu.Game.Beatmaps.Drawables } } - public BeatmapGroup(BeatmapSetInfo beatmapSet, BeatmapDatabase database) + public BeatmapGroup(BeatmapSetInfo beatmapSet, BeatmapStore store) { BeatmapSet = beatmapSet; - WorkingBeatmap beatmap = database.GetWorkingBeatmap(BeatmapSet.Beatmaps.FirstOrDefault()); + WorkingBeatmap beatmap = store.GetWorkingBeatmap(BeatmapSet.Beatmaps.FirstOrDefault()); Header = new BeatmapSetHeader(beatmap) { diff --git a/osu.Game/Database/DatabaseBacking.cs b/osu.Game/Database/DatabaseStore.cs similarity index 87% rename from osu.Game/Database/DatabaseBacking.cs rename to osu.Game/Database/DatabaseStore.cs index 92d99cb834..0102998604 100644 --- a/osu.Game/Database/DatabaseBacking.cs +++ b/osu.Game/Database/DatabaseStore.cs @@ -6,20 +6,17 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using osu.Framework.Logging; -using osu.Framework.Platform; using SQLite.Net; using SQLiteNetExtensions.Extensions; namespace osu.Game.Database { - public abstract class DatabaseBacking + public abstract class DatabaseStore { protected SQLiteConnection Connection { get; } - protected Storage Storage { get; } - protected DatabaseBacking(Storage storage, SQLiteConnection connection) + protected DatabaseStore(SQLiteConnection connection) { - Storage = storage; Connection = connection; try diff --git a/osu.Game/IPC/BeatmapIPCChannel.cs b/osu.Game/IPC/BeatmapIPCChannel.cs index e28c488708..f42e750a6f 100644 --- a/osu.Game/IPC/BeatmapIPCChannel.cs +++ b/osu.Game/IPC/BeatmapIPCChannel.cs @@ -10,9 +10,9 @@ namespace osu.Game.IPC { public class BeatmapIPCChannel : IpcChannel { - private readonly BeatmapDatabase beatmaps; + private readonly BeatmapStore beatmaps; - public BeatmapIPCChannel(IIpcHost host, BeatmapDatabase beatmaps = null) + public BeatmapIPCChannel(IIpcHost host, BeatmapStore beatmaps = null) : base(host) { this.beatmaps = beatmaps; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index aa55fc025a..44af591cc8 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -99,7 +99,7 @@ namespace osu.Game if (args?.Length > 0) { var paths = args.Where(a => !a.StartsWith(@"-")); - Task.Run(() => BeatmapDatabase.Import(paths.ToArray())); + Task.Run(() => BeatmapStore.Import(paths.ToArray())); } dependencies.Cache(this); @@ -140,7 +140,7 @@ namespace osu.Game return; } - Beatmap.Value = BeatmapDatabase.GetWorkingBeatmap(s.Beatmap); + Beatmap.Value = BeatmapStore.GetWorkingBeatmap(s.Beatmap); menu.Push(new PlayerLoader(new ReplayPlayer(s.Replay))); } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 312734773e..82e4b5d4ca 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -28,7 +28,7 @@ namespace osu.Game { protected OsuConfigManager LocalConfig; - protected BeatmapDatabase BeatmapDatabase; + protected BeatmapStore BeatmapStore; protected RulesetDatabase RulesetDatabase; @@ -95,9 +95,9 @@ namespace osu.Game SQLiteConnection connection = Host.Storage.GetDatabase(@"client"); - dependencies.Cache(RulesetDatabase = new RulesetDatabase(Host.Storage, connection)); - dependencies.Cache(BeatmapDatabase = new BeatmapDatabase(Host.Storage, connection, RulesetDatabase, Host)); - dependencies.Cache(ScoreDatabase = new ScoreDatabase(Host.Storage, connection, Host, BeatmapDatabase)); + dependencies.Cache(RulesetDatabase = new RulesetDatabase(connection)); + dependencies.Cache(BeatmapStore = new BeatmapStore(Host.Storage, connection, RulesetDatabase, Host)); + dependencies.Cache(ScoreDatabase = new ScoreDatabase(Host.Storage, connection, Host, BeatmapStore)); dependencies.Cache(new OsuColour()); //this completely overrides the framework default. will need to change once we make a proper FontStore. @@ -129,7 +129,7 @@ namespace osu.Game var defaultBeatmap = new DummyWorkingBeatmap(this); Beatmap = new NonNullableBindable(defaultBeatmap); - BeatmapDatabase.DefaultBeatmap = defaultBeatmap; + BeatmapStore.DefaultBeatmap = defaultBeatmap; OszArchiveReader.Register(); diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 6283368138..37ec90ad32 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -26,7 +26,7 @@ namespace osu.Game.Overlays.Music private FilterControl filter; private PlaylistList list; - private BeatmapDatabase beatmaps; + private BeatmapStore beatmaps; private readonly Bindable beatmapBacking = new Bindable(); @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Music private InputManager inputManager; [BackgroundDependencyLoader] - private void load(OsuGameBase game, BeatmapDatabase beatmaps, OsuColour colours, UserInputManager inputManager) + private void load(OsuGameBase game, BeatmapStore beatmaps, OsuColour colours, UserInputManager inputManager) { this.inputManager = inputManager; this.beatmaps = beatmaps; @@ -77,7 +77,7 @@ namespace osu.Game.Overlays.Music }, }; - list.BeatmapSets = BeatmapSets = beatmaps.GetAllWithChildren(b => !b.DeletePending).ToList(); + list.BeatmapSets = BeatmapSets = beatmaps.Database.GetAllWithChildren(b => !b.DeletePending).ToList(); beatmaps.BeatmapSetAdded += s => list.AddBeatmapSet(s); beatmaps.BeatmapSetRemoved += s => list.RemoveBeatmapSet(s); diff --git a/osu.Game/Rulesets/RulesetDatabase.cs b/osu.Game/Rulesets/RulesetDatabase.cs index b78ca5ffc6..be2ba19c0c 100644 --- a/osu.Game/Rulesets/RulesetDatabase.cs +++ b/osu.Game/Rulesets/RulesetDatabase.cs @@ -6,21 +6,19 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; -using osu.Framework.Platform; -using osu.Game.Rulesets; +using osu.Game.Database; using SQLite.Net; -namespace osu.Game.Database +namespace osu.Game.Rulesets { /// /// Todo: All of this needs to be moved to a RulesetDatabase. /// - public class RulesetDatabase : Database + public class RulesetDatabase : DatabaseStore { public IEnumerable AllRulesets => Query().Where(r => r.Available); - public RulesetDatabase(Storage storage, SQLiteConnection connection) - : base(storage, connection) + public RulesetDatabase(SQLiteConnection connection) : base(connection) { } diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs index 3990f9e8ae..84ecb7718e 100644 --- a/osu.Game/Rulesets/RulesetInfo.cs +++ b/osu.Game/Rulesets/RulesetInfo.cs @@ -2,10 +2,9 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using osu.Game.Rulesets; using SQLite.Net.Attributes; -namespace osu.Game.Database +namespace osu.Game.Rulesets { public class RulesetInfo { diff --git a/osu.Game/Rulesets/Scoring/ScoreDatabase.cs b/osu.Game/Rulesets/Scoring/ScoreDatabase.cs index efefa03e8d..84a532ec14 100644 --- a/osu.Game/Rulesets/Scoring/ScoreDatabase.cs +++ b/osu.Game/Rulesets/Scoring/ScoreDatabase.cs @@ -6,16 +6,17 @@ using System.Collections.Generic; using System.IO; using System.Linq; using osu.Framework.Platform; +using osu.Game.Beatmaps; +using osu.Game.Database; using osu.Game.IO.Legacy; using osu.Game.IPC; using osu.Game.Rulesets.Replays; -using osu.Game.Rulesets.Scoring; using SharpCompress.Compressors.LZMA; using SQLite.Net; -namespace osu.Game.Database +namespace osu.Game.Rulesets.Scoring { - public class ScoreDatabase : DatabaseBacking + public class ScoreDatabase : DatabaseStore { private readonly Storage storage; @@ -27,7 +28,7 @@ namespace osu.Game.Database // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) private ScoreIPCChannel ipc; - public ScoreDatabase(Storage storage, SQLiteConnection connection, IIpcHost importHost = null, BeatmapStore beatmaps = null, RulesetDatabase rulesets = null) : base(storage, connection) + public ScoreDatabase(Storage storage, SQLiteConnection connection, IIpcHost importHost = null, BeatmapStore beatmaps = null, RulesetDatabase rulesets = null) : base(connection) { this.storage = storage; this.beatmaps = beatmaps; @@ -53,7 +54,7 @@ namespace osu.Game.Database var version = sr.ReadInt32(); /* score.FileChecksum = */ var beatmapHash = sr.ReadString(); - score.Beatmap = beatmaps.Query().FirstOrDefault(b => b.Hash == beatmapHash); + score.Beatmap = beatmaps.Database.Query().FirstOrDefault(b => b.Hash == beatmapHash); /* score.PlayerName = */ sr.ReadString(); /* var localScoreChecksum = */ diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index 4f484e9638..a8f2c3c78f 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -67,7 +67,7 @@ namespace osu.Game.Screens.Menu private Track track; [BackgroundDependencyLoader] - private void load(AudioManager audio, OsuConfigManager config, BeatmapDatabase beatmaps, Framework.Game game) + private void load(AudioManager audio, OsuConfigManager config, BeatmapStore beatmaps, Framework.Game game) { menuVoice = config.GetBindable(OsuSetting.MenuVoice); menuMusic = config.GetBindable(OsuSetting.MenuMusic); @@ -76,7 +76,7 @@ namespace osu.Game.Screens.Menu if (!menuMusic) { - var query = beatmaps.Query().Where(b => !b.DeletePending); + var query = beatmaps.Database.Query().Where(b => !b.DeletePending); int count = query.Count(); if (count > 0) setInfo = query.ElementAt(RNG.Next(0, count - 1)); @@ -84,7 +84,7 @@ namespace osu.Game.Screens.Menu if (setInfo == null) { - var query = beatmaps.Query().Where(b => b.Hash == MENU_MUSIC_BEATMAP_HASH); + var query = beatmaps.Database.Query().Where(b => b.Hash == MENU_MUSIC_BEATMAP_HASH); setInfo = query.FirstOrDefault(); @@ -96,11 +96,11 @@ namespace osu.Game.Screens.Menu setInfo = query.First(); setInfo.DeletePending = true; - beatmaps.Update(setInfo, false); + beatmaps.Database.Update(setInfo, false); } } - beatmaps.GetChildren(setInfo); + beatmaps.Database.GetChildren(setInfo); Beatmap.Value = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); track = Beatmap.Value.Track; diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index b26d6d4385..736ed0c1d8 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -68,7 +68,7 @@ namespace osu.Game.Screens.Select /// /// Required for now unfortunately. /// - private BeatmapDatabase database; + private BeatmapStore store; private readonly Container scrollableContent; @@ -289,7 +289,7 @@ namespace osu.Game.Screens.Select b.Metadata = beatmapSet.Metadata; } - return new BeatmapGroup(beatmapSet, database) + return new BeatmapGroup(beatmapSet, store) { SelectionChanged = (g, p) => selectGroup(g, p), StartRequested = b => StartRequested?.Invoke(), @@ -298,9 +298,9 @@ namespace osu.Game.Screens.Select } [BackgroundDependencyLoader(permitNulls: true)] - private void load(BeatmapDatabase database, OsuConfigManager config) + private void load(BeatmapStore store, OsuConfigManager config) { - this.database = database; + this.store = store; randomType = config.GetBindable(OsuSetting.SelectionRandomType); } diff --git a/osu.Game/Screens/Select/BeatmapDeleteDialog.cs b/osu.Game/Screens/Select/BeatmapDeleteDialog.cs index b27119a16d..a605691ec5 100644 --- a/osu.Game/Screens/Select/BeatmapDeleteDialog.cs +++ b/osu.Game/Screens/Select/BeatmapDeleteDialog.cs @@ -11,12 +11,12 @@ namespace osu.Game.Screens.Select { public class BeatmapDeleteDialog : PopupDialog { - private BeatmapDatabase database; + private BeatmapStore store; [BackgroundDependencyLoader] - private void load(BeatmapDatabase beatmapDatabase) + private void load(BeatmapStore beatmapStore) { - database = beatmapDatabase; + store = beatmapStore; } public BeatmapDeleteDialog(WorkingBeatmap beatmap) @@ -34,7 +34,7 @@ namespace osu.Game.Screens.Select Action = () => { beatmap.Dispose(); - database.Delete(beatmap.BeatmapSetInfo); + store.Delete(beatmap.BeatmapSetInfo); }, }, new PopupDialogCancelButton diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index cee5e88deb..ef1a65d914 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Select public abstract class SongSelect : OsuScreen { private readonly Bindable ruleset = new Bindable(); - private BeatmapDatabase database; + private BeatmapStore store; protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(); private readonly BeatmapCarousel carousel; @@ -154,7 +154,7 @@ namespace osu.Game.Screens.Select } [BackgroundDependencyLoader(permitNulls: true)] - private void load(BeatmapDatabase beatmaps, AudioManager audio, DialogOverlay dialog, OsuGame osu, OsuColour colours, UserInputManager input) + private void load(BeatmapStore beatmaps, AudioManager audio, DialogOverlay dialog, OsuGame osu, OsuColour colours, UserInputManager input) { if (Footer != null) { @@ -164,14 +164,14 @@ namespace osu.Game.Screens.Select BeatmapOptions.AddButton(@"Delete", @"Beatmap", FontAwesome.fa_trash, colours.Pink, promptDelete, Key.Number4, float.MaxValue); } - if (database == null) - database = beatmaps; + if (store == null) + store = beatmaps; if (osu != null) ruleset.BindTo(osu.Ruleset); - database.BeatmapSetAdded += onBeatmapSetAdded; - database.BeatmapSetRemoved += onBeatmapSetRemoved; + store.BeatmapSetAdded += onBeatmapSetAdded; + store.BeatmapSetRemoved += onBeatmapSetRemoved; dialogOverlay = dialog; @@ -180,7 +180,7 @@ namespace osu.Game.Screens.Select initialAddSetsTask = new CancellationTokenSource(); - carousel.Beatmaps = database.GetAllWithChildren(b => !b.DeletePending); + carousel.Beatmaps = store.Database.GetAllWithChildren(b => !b.DeletePending); Beatmap.ValueChanged += beatmap_ValueChanged; @@ -230,7 +230,7 @@ namespace osu.Game.Screens.Select { bool preview = beatmap?.BeatmapSetInfoID != Beatmap.Value.BeatmapInfo.BeatmapSetInfoID; - Beatmap.Value = database.GetWorkingBeatmap(beatmap, Beatmap); + Beatmap.Value = store.GetWorkingBeatmap(beatmap, Beatmap); ensurePlayingSelected(preview); } @@ -341,10 +341,10 @@ namespace osu.Game.Screens.Select { base.Dispose(isDisposing); - if (database != null) + if (store != null) { - database.BeatmapSetAdded -= onBeatmapSetAdded; - database.BeatmapSetRemoved -= onBeatmapSetRemoved; + store.BeatmapSetAdded -= onBeatmapSetAdded; + store.BeatmapSetRemoved -= onBeatmapSetRemoved; } initialAddSetsTask?.Cancel(); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 42067fb8d5..43c300dc30 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -74,6 +74,7 @@ + @@ -125,10 +126,10 @@ - - + + - + @@ -215,7 +216,7 @@ - + @@ -390,10 +391,10 @@ - - - - + + + + @@ -488,10 +489,10 @@ - + - - + +