From 5f53426a9a75b2a1390e602116957830526e48e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert <pe@ppy.sh> Date: Thu, 27 Jul 2017 16:56:41 +0900 Subject: [PATCH] *Database -> *Store Welcome back BeatmapManager --- .../Tests/TestCaseDirect.cs | 4 +- .../Tests/TestCaseDrawableRoom.cs | 4 +- .../Tests/TestCaseGamefield.cs | 4 +- osu.Desktop.VisualTests/Tests/TestCaseMods.cs | 4 +- .../Tests/TestCasePlaySongSelect.cs | 12 +- .../Tests/TestCasePlayer.cs | 4 +- .../Tests/TestCaseResults.cs | 4 +- .../Tests/TestCaseRoomInspector.cs | 4 +- osu.Desktop/OsuGameDesktop.cs | 4 +- .../Beatmaps/IO/ImportBeatmapTest.cs | 10 +- osu.Game/Beatmaps/BeatmapDatabase.cs | 116 ------ osu.Game/Beatmaps/BeatmapManager.cs | 394 ++++++++++++++++++ osu.Game/Beatmaps/BeatmapStore.cs | 379 ++++------------- .../Beatmaps/BeatmapStoreWorkingBeatmap.cs | 72 ---- osu.Game/Beatmaps/Drawables/BeatmapGroup.cs | 4 +- ...atabaseStore.cs => DatabaseBackedStore.cs} | 6 +- osu.Game/IO/{FileDatabase.cs => FileStore.cs} | 4 +- osu.Game/IPC/BeatmapIPCChannel.cs | 4 +- osu.Game/IPC/ScoreIPCChannel.cs | 4 +- .../API/Requests/GetBeatmapSetsRequest.cs | 4 +- osu.Game/OsuGame.cs | 6 +- osu.Game/OsuGameBase.cs | 18 +- osu.Game/Overlays/Direct/FilterControl.cs | 2 +- osu.Game/Overlays/DirectOverlay.cs | 4 +- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- osu.Game/Overlays/Music/PlaylistOverlay.cs | 4 +- .../Settings/Sections/GameplaySection.cs | 2 +- osu.Game/Overlays/Settings/SettingsFooter.cs | 2 +- .../Overlays/Toolbar/ToolbarModeSelector.cs | 2 +- .../{RulesetDatabase.cs => RulesetStore.cs} | 6 +- .../{ScoreDatabase.cs => ScoreStore.cs} | 8 +- osu.Game/Screens/Menu/Intro.cs | 2 +- osu.Game/Screens/Select/BeatmapCarousel.cs | 8 +- .../Screens/Select/BeatmapDeleteDialog.cs | 8 +- osu.Game/Screens/Select/SongSelect.cs | 22 +- osu.Game/osu.Game.csproj | 13 +- 36 files changed, 570 insertions(+), 580 deletions(-) delete mode 100644 osu.Game/Beatmaps/BeatmapDatabase.cs create mode 100644 osu.Game/Beatmaps/BeatmapManager.cs delete mode 100644 osu.Game/Beatmaps/BeatmapStoreWorkingBeatmap.cs rename osu.Game/Database/{DatabaseStore.cs => DatabaseBackedStore.cs} (90%) rename osu.Game/IO/{FileDatabase.cs => FileStore.cs} (91%) rename osu.Game/Rulesets/{RulesetDatabase.cs => RulesetStore.cs} (90%) rename osu.Game/Rulesets/Scoring/{ScoreDatabase.cs => ScoreStore.cs} (91%) diff --git a/osu.Desktop.VisualTests/Tests/TestCaseDirect.cs b/osu.Desktop.VisualTests/Tests/TestCaseDirect.cs index 0bda3c013f..4a5ff1b576 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseDirect.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseDirect.cs @@ -15,7 +15,7 @@ namespace osu.Desktop.VisualTests.Tests public override string Description => @"osu!direct overlay"; private DirectOverlay direct; - private RulesetDatabase rulesets; + private RulesetStore rulesets; protected override void LoadComplete() { @@ -29,7 +29,7 @@ namespace osu.Desktop.VisualTests.Tests } [BackgroundDependencyLoader] - private void load(RulesetDatabase rulesets) + private void load(RulesetStore rulesets) { this.rulesets = rulesets; } diff --git a/osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs b/osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs index 20c8156a33..38cf03d60e 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs @@ -17,7 +17,7 @@ namespace osu.Desktop.VisualTests.Tests { public override string Description => @"Select your favourite room"; - private RulesetDatabase rulesets; + private RulesetStore rulesets; protected override void LoadComplete() { @@ -125,7 +125,7 @@ namespace osu.Desktop.VisualTests.Tests } [BackgroundDependencyLoader] - private void load(RulesetDatabase rulesets) + private void load(RulesetStore rulesets) { this.rulesets = rulesets; } diff --git a/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs b/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs index c7af7550b9..0b08065241 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs @@ -24,12 +24,12 @@ namespace osu.Desktop.VisualTests.Tests { internal class TestCaseGamefield : TestCase { - private RulesetDatabase rulesets; + private RulesetStore rulesets; public override string Description => @"Showing hitobjects and what not."; [BackgroundDependencyLoader] - private void load(RulesetDatabase rulesets) + private void load(RulesetStore rulesets) { this.rulesets = rulesets; } diff --git a/osu.Desktop.VisualTests/Tests/TestCaseMods.cs b/osu.Desktop.VisualTests/Tests/TestCaseMods.cs index f9dc2caa56..1604be603a 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseMods.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseMods.cs @@ -18,11 +18,11 @@ namespace osu.Desktop.VisualTests.Tests private ModSelectOverlay modSelect; private ModDisplay modDisplay; - private RulesetDatabase rulesets; + private RulesetStore rulesets; [BackgroundDependencyLoader] - private void load(RulesetDatabase rulesets) + private void load(RulesetStore rulesets) { this.rulesets = rulesets; } diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs index 2fb720c03e..61e87a6621 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs @@ -14,27 +14,27 @@ namespace osu.Desktop.VisualTests.Tests { internal class TestCasePlaySongSelect : TestCase { - private readonly BeatmapStore store; + private readonly BeatmapManager manager; public override string Description => @"with fake data"; - private readonly RulesetDatabase rulesets; + private readonly RulesetStore rulesets; public TestCasePlaySongSelect() { PlaySongSelect songSelect; - if (store == null) + if (manager == null) { var storage = new TestStorage(@"TestCasePlaySongSelect"); var backingDatabase = storage.GetDatabase(@"client"); - rulesets = new RulesetDatabase(backingDatabase); - store = new BeatmapStore(storage, null, backingDatabase, rulesets); + rulesets = new RulesetStore(backingDatabase); + manager = new BeatmapManager(storage, null, backingDatabase, rulesets); for (int i = 0; i < 100; i += 10) - store.Import(createTestBeatmapSet(i)); + manager.Import(createTestBeatmapSet(i)); } Add(songSelect = new PlaySongSelect()); diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs b/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs index c72c014d09..f38cabd618 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs @@ -20,12 +20,12 @@ namespace osu.Desktop.VisualTests.Tests internal class TestCasePlayer : TestCase { protected Player Player; - private RulesetDatabase rulesets; + private RulesetStore rulesets; public override string Description => @"Showing everything to play the game."; [BackgroundDependencyLoader] - private void load(RulesetDatabase rulesets) + private void load(RulesetStore rulesets) { this.rulesets = rulesets; } diff --git a/osu.Desktop.VisualTests/Tests/TestCaseResults.cs b/osu.Desktop.VisualTests/Tests/TestCaseResults.cs index f0a8a8394c..288fff346f 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseResults.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseResults.cs @@ -14,12 +14,12 @@ namespace osu.Desktop.VisualTests.Tests { internal class TestCaseResults : TestCase { - private BeatmapStore beatmaps; + private BeatmapManager beatmaps; public override string Description => @"Results after playing."; [BackgroundDependencyLoader] - private void load(BeatmapStore beatmaps) + private void load(BeatmapManager beatmaps) { this.beatmaps = beatmaps; } diff --git a/osu.Desktop.VisualTests/Tests/TestCaseRoomInspector.cs b/osu.Desktop.VisualTests/Tests/TestCaseRoomInspector.cs index 232d7bf6e3..043f072b25 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseRoomInspector.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseRoomInspector.cs @@ -16,7 +16,7 @@ namespace osu.Desktop.VisualTests.Tests { public override string Description => @"from the multiplayer lobby"; - private RulesetDatabase rulesets; + private RulesetStore rulesets; protected override void LoadComplete() { @@ -136,7 +136,7 @@ namespace osu.Desktop.VisualTests.Tests } [BackgroundDependencyLoader] - private void load(RulesetDatabase rulesets) + private void load(RulesetStore rulesets) { this.rulesets = rulesets; } diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index ccedcc07d5..bd5c6c6790 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -65,11 +65,11 @@ namespace osu.Desktop var filePaths = dropData.Select(f => f.ToString()).ToArray(); if (filePaths.All(f => Path.GetExtension(f) == @".osz")) - Task.Run(() => BeatmapStore.Import(filePaths)); + Task.Run(() => BeatmapManager.Import(filePaths)); else if (filePaths.All(f => Path.GetExtension(f) == @".osr")) Task.Run(() => { - var score = ScoreDatabase.ReadReplayFile(filePaths.First()); + var score = ScoreStore.ReadReplayFile(filePaths.First()); Schedule(() => LoadScore(score)); }); } diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 4582f5b2fb..ecaf0db096 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<BeatmapStore>().Import(temp); + osu.Dependencies.Get<BeatmapManager>().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<BeatmapStore>().Import(temp); + osu.Dependencies.Get<BeatmapManager>().Import(temp); ensureLoaded(osu); @@ -105,8 +105,8 @@ namespace osu.Game.Tests.Beatmaps.IO Thread.Sleep(1); //reset beatmap database (sqlite and storage backing) - osu.Dependencies.Get<RulesetDatabase>().Reset(); - osu.Dependencies.Get<BeatmapStore>().Reset(); + osu.Dependencies.Get<RulesetStore>().Reset(); + osu.Dependencies.Get<BeatmapManager>().Reset(); return osu; } @@ -115,7 +115,7 @@ namespace osu.Game.Tests.Beatmaps.IO { IEnumerable<BeatmapSetInfo> resultSets = null; - var store = osu.Dependencies.Get<BeatmapStore>(); + var store = osu.Dependencies.Get<BeatmapManager>(); Action waitAction = () => { diff --git a/osu.Game/Beatmaps/BeatmapDatabase.cs b/osu.Game/Beatmaps/BeatmapDatabase.cs deleted file mode 100644 index 5906e72f50..0000000000 --- a/osu.Game/Beatmaps/BeatmapDatabase.cs +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Logging; -using osu.Game.Database; -using SQLite.Net; -using SQLiteNetExtensions.Extensions; - -namespace osu.Game.Beatmaps -{ - /// <summary> - /// Handles the storage and retrieval of Beatmaps/BeatmapSets to the database backing - /// </summary> - public class BeatmapDatabase : DatabaseStore - { - public event Action<BeatmapSetInfo> BeatmapSetAdded; - public event Action<BeatmapSetInfo> BeatmapSetRemoved; - - public BeatmapDatabase(SQLiteConnection connection) - : base(connection) - { - } - - protected override Type[] ValidTypes => new[] - { - typeof(BeatmapSetInfo), - typeof(BeatmapInfo), - typeof(BeatmapMetadata), - typeof(BeatmapDifficulty), - }; - - protected override void Prepare(bool reset = false) - { - if (reset) - { - Connection.DropTable<BeatmapMetadata>(); - Connection.DropTable<BeatmapDifficulty>(); - Connection.DropTable<BeatmapSetInfo>(); - Connection.DropTable<BeatmapSetFileInfo>(); - Connection.DropTable<BeatmapInfo>(); - } - - Connection.CreateTable<BeatmapMetadata>(); - Connection.CreateTable<BeatmapDifficulty>(); - Connection.CreateTable<BeatmapSetInfo>(); - Connection.CreateTable<BeatmapSetFileInfo>(); - Connection.CreateTable<BeatmapInfo>(); - - cleanupPendingDeletions(); - } - - /// <summary> - /// Add a <see cref="BeatmapSetInfo"/> to the database. - /// </summary> - /// <param name="beatmapSet">The beatmap to add.</param> - public void Add(BeatmapSetInfo beatmapSet) - { - Connection.InsertOrReplaceWithChildren(beatmapSet, true); - BeatmapSetAdded?.Invoke(beatmapSet); - } - - /// <summary> - /// Delete a <see cref="BeatmapSetInfo"/> to the database. - /// </summary> - /// <param name="beatmapSet">The beatmap to delete.</param> - /// <returns>Whether the beatmap's <see cref="BeatmapSetInfo.DeletePending"/> was changed.</returns> - public bool Delete(BeatmapSetInfo beatmapSet) - { - if (beatmapSet.DeletePending) return false; - - beatmapSet.DeletePending = true; - Connection.Update(beatmapSet); - - BeatmapSetRemoved?.Invoke(beatmapSet); - return true; - } - - /// <summary> - /// Restore a previously deleted <see cref="BeatmapSetInfo"/>. - /// </summary> - /// <param name="beatmapSet">The beatmap to restore.</param> - /// <returns>Whether the beatmap's <see cref="BeatmapSetInfo.DeletePending"/> was changed.</returns> - public bool Undelete(BeatmapSetInfo beatmapSet) - { - if (!beatmapSet.DeletePending) return false; - - beatmapSet.DeletePending = false; - Connection.Update(beatmapSet); - - BeatmapSetAdded?.Invoke(beatmapSet); - return true; - } - - private void cleanupPendingDeletions() - { - foreach (var b in QueryAndPopulate<BeatmapSetInfo>(b => b.DeletePending && !b.Protected)) - { - try - { - // many-to-many join table entries are not automatically tidied. - Connection.Table<BeatmapSetFileInfo>().Delete(f => f.BeatmapSetInfoID == b.ID); - Connection.Delete(b, true); - } - catch (Exception e) - { - Logger.Error(e, $@"Could not delete beatmap {b}"); - } - } - - //this is required because sqlite migrations don't work, initially inserting nulls into this field. - //see https://github.com/praeclarum/sqlite-net/issues/326 - Connection.Query<BeatmapSetInfo>("UPDATE BeatmapSetInfo SET DeletePending = 0 WHERE DeletePending IS NULL"); - } - } -} diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs new file mode 100644 index 0000000000..4ec8be3f36 --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -0,0 +1,394 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. +// 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 System.Linq.Expressions; +using Ionic.Zip; +using osu.Framework.Audio.Track; +using osu.Framework.Extensions; +using osu.Framework.Graphics.Textures; +using osu.Framework.IO.Stores; +using osu.Framework.Logging; +using osu.Framework.Platform; +using osu.Game.Beatmaps.Formats; +using osu.Game.Beatmaps.IO; +using osu.Game.IO; +using osu.Game.IPC; +using osu.Game.Rulesets; +using SQLite.Net; +using FileInfo = osu.Game.IO.FileInfo; + +namespace osu.Game.Beatmaps +{ + /// <summary> + /// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps. + /// </summary> + public class BeatmapManager + { + /// <summary> + /// Fired when a new <see cref="BeatmapSetInfo"/> becomes available in the database. + /// </summary> + public event Action<BeatmapSetInfo> BeatmapSetAdded; + + /// <summary> + /// Fired when a <see cref="BeatmapSetInfo"/> is removed from the database. + /// </summary> + public event Action<BeatmapSetInfo> BeatmapSetRemoved; + + /// <summary> + /// A default representation of a WorkingBeatmap to use when no beatmap is available. + /// </summary> + public WorkingBeatmap DefaultBeatmap { private get; set; } + + private readonly Storage storage; + + private readonly FileStore files; + + private readonly RulesetStore rulesets; + + private readonly BeatmapStore beatmaps; + + // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) + private BeatmapIPCChannel ipc; + + public BeatmapManager(Storage storage, FileStore files, SQLiteConnection connection, RulesetStore rulesets, IIpcHost importHost = null) + { + beatmaps = new BeatmapStore(connection); + beatmaps.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s); + beatmaps.BeatmapSetRemoved += s => BeatmapSetRemoved?.Invoke(s); + + this.storage = storage; + this.files = files; + this.rulesets = rulesets; + + if (importHost != null) + ipc = new BeatmapIPCChannel(importHost, this); + } + + /// <summary> + /// Import multiple <see cref="BeatmapSetInfo"/> from filesystem <paramref name="paths"/>. + /// </summary> + /// <param name="paths">Multiple locations on disk.</param> + public void Import(params string[] paths) + { + foreach (string path in paths) + { + try + { + using (ArchiveReader reader = getReaderFrom(path)) + Import(reader); + + // 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"); + } + } + } + + /// <summary> + /// Import a beatmap from an <see cref="ArchiveReader"/>. + /// </summary> + /// <param name="archiveReader">The beatmap to be imported.</param> + public BeatmapSetInfo Import(ArchiveReader archiveReader) + { + BeatmapSetInfo set = importToStorage(archiveReader); + Import(set); + return set; + } + + /// <summary> + /// Import a beatmap from a <see cref="BeatmapSetInfo"/>. + /// </summary> + /// <param name="beatmapSetInfo">The beatmap to be imported.</param> + public void Import(BeatmapSetInfo beatmapSetInfo) + { + // If we have an ID then we already exist in the database. + if (beatmapSetInfo.ID != 0) return; + + beatmaps.Add(beatmapSetInfo); + } + + /// <summary> + /// Delete a beatmap from the manager. + /// Is a no-op for already deleted beatmaps. + /// </summary> + /// <param name="beatmapSet">The beatmap to delete.</param> + public void Delete(BeatmapSetInfo beatmapSet) + { + if (!beatmaps.Delete(beatmapSet)) return; + + if (!beatmapSet.Protected) + files.Dereference(beatmapSet.Files); + } + + /// <summary> + /// Returns a <see cref="BeatmapSetInfo"/> to a usable state if it has previously been deleted but not yet purged. + /// Is a no-op for already usable beatmaps. + /// </summary> + /// <param name="beatmapSet">The beatmap to restore.</param> + public void Undelete(BeatmapSetInfo beatmapSet) + { + if (!beatmaps.Undelete(beatmapSet)) return; + + files.Reference(beatmapSet.Files); + } + + /// <summary> + /// Retrieve a <see cref="WorkingBeatmap"/> instance for the provided <see cref="BeatmapInfo"/> + /// </summary> + /// <param name="beatmapInfo">The beatmap to lookup.</param> + /// <param name="previous">The currently loaded <see cref="WorkingBeatmap"/>. Allows for optimisation where elements are shared with the new beatmap.</param> + /// <returns>A <see cref="WorkingBeatmap"/> instance correlating to the provided <see cref="BeatmapInfo"/>.</returns> + public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) + { + if (beatmapInfo == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) + return DefaultBeatmap; + + beatmaps.Populate(beatmapInfo); + + 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 BeatmapManagerWorkingBeatmap(files.Store, beatmapInfo); + + previous?.TransferTo(working); + + return working; + } + + /// <summary> + /// Reset the manager to an empty state. + /// </summary> + public void Reset() + { + beatmaps.Reset(); + } + + /// <summary> + /// Perform a lookup query on available <see cref="BeatmapSetInfo"/>s. + /// </summary> + /// <param name="query">The query.</param> + /// <returns>The first result for the provided query, or null if no results were found.</returns> + public BeatmapSetInfo QueryBeatmapSet(Func<BeatmapSetInfo, bool> query) + { + BeatmapSetInfo set = beatmaps.Query<BeatmapSetInfo>().FirstOrDefault(query); + + if (set != null) + beatmaps.Populate(set); + + return set; + } + + /// <summary> + /// Perform a lookup query on available <see cref="BeatmapSetInfo"/>s. + /// </summary> + /// <param name="query">The query.</param> + /// <returns>Results from the provided query.</returns> + public List<BeatmapSetInfo> QueryBeatmapSets(Expression<Func<BeatmapSetInfo, bool>> query) => beatmaps.QueryAndPopulate(query); + + /// <summary> + /// Perform a lookup query on available <see cref="BeatmapInfo"/>s. + /// </summary> + /// <param name="query">The query.</param> + /// <returns>The first result for the provided query, or null if no results were found.</returns> + public BeatmapInfo QueryBeatmap(Func<BeatmapInfo, bool> query) + { + BeatmapInfo set = beatmaps.Query<BeatmapInfo>().FirstOrDefault(query); + + if (set != null) + beatmaps.Populate(set); + + return set; + } + + /// <summary> + /// Perform a lookup query on available <see cref="BeatmapInfo"/>s. + /// </summary> + /// <param name="query">The query.</param> + /// <returns>Results from the provided query.</returns> + public List<BeatmapInfo> QueryBeatmaps(Expression<Func<BeatmapInfo, bool>> query) => beatmaps.QueryAndPopulate(query); + + /// <summary> + /// Creates an <see cref="ArchiveReader"/> from a valid storage path. + /// </summary> + /// <param name="path">A file or folder path resolving the beatmap content.</param> + /// <returns>A reader giving access to the beatmap's content.</returns> + private ArchiveReader getReaderFrom(string path) + { + if (ZipFile.IsZipFile(path)) + return new OszArchiveReader(storage.GetStream(path)); + else + return new LegacyFilesystemReader(path); + } + + /// <summary> + /// Import a beamap into our local <see cref="FileStore"/> storage. + /// If the beatmap is already imported, the existing instance will be returned. + /// </summary> + /// <param name="reader">The beatmap archive to be read.</param> + /// <returns>The imported beatmap, or an existing instance if it is already present.</returns> + private BeatmapSetInfo importToStorage(ArchiveReader reader) + { + // for now, concatenate all .osu files in the set to create a unique hash. + MemoryStream hashable = new MemoryStream(); + foreach (string file in reader.Filenames.Where(f => f.EndsWith(".osu"))) + using (Stream s = reader.GetStream(file)) + s.CopyTo(hashable); + + var hash = hashable.GetMd5Hash(); + + // check if this beatmap has already been imported and exit early if so. + var beatmapSet = beatmaps.QueryAndPopulate<BeatmapSetInfo>().FirstOrDefault(b => b.Hash == hash); + if (beatmapSet != null) + { + Undelete(beatmapSet); + return beatmapSet; + } + + List<FileInfo> fileInfos = new List<FileInfo>(); + + // import files to manager + foreach (string file in reader.Filenames) + using (Stream s = reader.GetStream(file)) + fileInfos.Add(files.Add(s, file)); + + BeatmapMetadata metadata; + + using (var stream = new StreamReader(reader.GetStream(reader.Filenames.First(f => f.EndsWith(".osu"))))) + metadata = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata; + + beatmapSet = new BeatmapSetInfo + { + OnlineBeatmapSetID = metadata.OnlineBeatmapSetID, + Beatmaps = new List<BeatmapInfo>(), + Hash = hash, + Files = fileInfos, + Metadata = metadata + }; + + var mapNames = reader.Filenames.Where(f => f.EndsWith(".osu")); + + foreach (var name in mapNames) + { + using (var raw = reader.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<RulesetInfo>().FirstOrDefault(r => r.ID == beatmap.BeatmapInfo.RulesetID); + beatmap.BeatmapInfo.StarDifficulty = rulesets.Query<RulesetInfo>().FirstOrDefault(r => r.ID == beatmap.BeatmapInfo.RulesetID)?.CreateInstance()?.CreateDifficultyCalculator(beatmap) + .Calculate() ?? 0; + + beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo); + } + } + + return beatmapSet; + } + + /// <summary> + /// Returns a list of all usable <see cref="BeatmapSetInfo"/>s. + /// </summary> + /// <param name="populate">Whether returned objects should be pre-populated with all data.</param> + /// <returns>A list of available <see cref="BeatmapSetInfo"/>.</returns> + public List<BeatmapSetInfo> GetAllUsableBeatmapSets(bool populate = true) + { + if (populate) + return beatmaps.QueryAndPopulate<BeatmapSetInfo>(b => !b.DeletePending).ToList(); + else + return beatmaps.Query<BeatmapSetInfo>(b => !b.DeletePending).ToList(); + } + + protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap + { + private readonly IResourceStore<byte[]> store; + + public BeatmapManagerWorkingBeatmap(IResourceStore<byte[]> store, BeatmapInfo beatmapInfo) + : base(beatmapInfo) + { + this.store = store; + } + + protected override Beatmap GetBeatmap() + { + try + { + Beatmap beatmap; + + BeatmapDecoder decoder; + using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) + { + decoder = BeatmapDecoder.GetDecoder(stream); + beatmap = decoder.Decode(stream); + } + + if (beatmap == null || BeatmapSetInfo.StoryboardFile == null) + return beatmap; + + using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) + decoder.Decode(stream, beatmap); + + + return beatmap; + } + catch { return null; } + } + + private string getPathForFile(string filename) => BeatmapSetInfo.Files.First(f => f.Filename == filename).StoragePath; + + protected override Texture GetBackground() + { + if (Metadata?.BackgroundFile == null) + return null; + + try + { + return new TextureStore(new RawTextureLoaderStore(store), false).Get(getPathForFile(Metadata.BackgroundFile)); + } + catch { return null; } + } + + protected override Track GetTrack() + { + try + { + var trackData = store.GetStream(getPathForFile(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 index facb68e808..4a7336535e 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -1,331 +1,116 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. +// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // 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 System.Linq.Expressions; -using Ionic.Zip; -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.IO; -using osu.Game.IPC; -using osu.Game.Rulesets; +using osu.Game.Database; using SQLite.Net; -using FileInfo = osu.Game.IO.FileInfo; +using SQLiteNetExtensions.Extensions; namespace osu.Game.Beatmaps { /// <summary> - /// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps. + /// Handles the storage and retrieval of Beatmaps/BeatmapSets to the database backing /// </summary> - public class BeatmapStore + public class BeatmapStore : DatabaseBackedStore { - /// <summary> - /// Fired when a new <see cref="BeatmapSetInfo"/> becomes available in the database. - /// </summary> public event Action<BeatmapSetInfo> BeatmapSetAdded; - - /// <summary> - /// Fired when a <see cref="BeatmapSetInfo"/> is removed from the database. - /// </summary> public event Action<BeatmapSetInfo> BeatmapSetRemoved; - /// <summary> - /// A default representation of a WorkingBeatmap to use when no beatmap is available. - /// </summary> - public WorkingBeatmap DefaultBeatmap { private get; set; } - - private readonly Storage storage; - - private readonly FileDatabase files; - - private readonly RulesetDatabase rulesets; - - private readonly BeatmapDatabase beatmaps; - - // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) - private BeatmapIPCChannel ipc; - - public BeatmapStore(Storage storage, FileDatabase files, SQLiteConnection connection, RulesetDatabase rulesets, IIpcHost importHost = null) + public BeatmapStore(SQLiteConnection connection) + : base(connection) { - beatmaps = new BeatmapDatabase(connection); - beatmaps.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s); - beatmaps.BeatmapSetRemoved += s => BeatmapSetRemoved?.Invoke(s); + } - this.storage = storage; - this.files = files; - this.rulesets = rulesets; + protected override Type[] ValidTypes => new[] + { + typeof(BeatmapSetInfo), + typeof(BeatmapInfo), + typeof(BeatmapMetadata), + typeof(BeatmapDifficulty), + }; - if (importHost != null) - ipc = new BeatmapIPCChannel(importHost, this); + protected override void Prepare(bool reset = false) + { + if (reset) + { + Connection.DropTable<BeatmapMetadata>(); + Connection.DropTable<BeatmapDifficulty>(); + Connection.DropTable<BeatmapSetInfo>(); + Connection.DropTable<BeatmapSetFileInfo>(); + Connection.DropTable<BeatmapInfo>(); + } + + Connection.CreateTable<BeatmapMetadata>(); + Connection.CreateTable<BeatmapDifficulty>(); + Connection.CreateTable<BeatmapSetInfo>(); + Connection.CreateTable<BeatmapSetFileInfo>(); + Connection.CreateTable<BeatmapInfo>(); + + cleanupPendingDeletions(); } /// <summary> - /// Import multiple <see cref="BeatmapSetInfo"/> from filesystem <paramref name="paths"/>. + /// Add a <see cref="BeatmapSetInfo"/> to the database. /// </summary> - /// <param name="paths">Multiple locations on disk.</param> - public void Import(params string[] paths) + /// <param name="beatmapSet">The beatmap to add.</param> + public void Add(BeatmapSetInfo beatmapSet) { - foreach (string path in paths) + Connection.InsertOrReplaceWithChildren(beatmapSet, true); + BeatmapSetAdded?.Invoke(beatmapSet); + } + + /// <summary> + /// Delete a <see cref="BeatmapSetInfo"/> to the database. + /// </summary> + /// <param name="beatmapSet">The beatmap to delete.</param> + /// <returns>Whether the beatmap's <see cref="BeatmapSetInfo.DeletePending"/> was changed.</returns> + public bool Delete(BeatmapSetInfo beatmapSet) + { + if (beatmapSet.DeletePending) return false; + + beatmapSet.DeletePending = true; + Connection.Update(beatmapSet); + + BeatmapSetRemoved?.Invoke(beatmapSet); + return true; + } + + /// <summary> + /// Restore a previously deleted <see cref="BeatmapSetInfo"/>. + /// </summary> + /// <param name="beatmapSet">The beatmap to restore.</param> + /// <returns>Whether the beatmap's <see cref="BeatmapSetInfo.DeletePending"/> was changed.</returns> + public bool Undelete(BeatmapSetInfo beatmapSet) + { + if (!beatmapSet.DeletePending) return false; + + beatmapSet.DeletePending = false; + Connection.Update(beatmapSet); + + BeatmapSetAdded?.Invoke(beatmapSet); + return true; + } + + private void cleanupPendingDeletions() + { + foreach (var b in QueryAndPopulate<BeatmapSetInfo>(b => b.DeletePending && !b.Protected)) { try { - using (ArchiveReader reader = getReaderFrom(path)) - Import(reader); - - // 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}"); - } + // many-to-many join table entries are not automatically tidied. + Connection.Table<BeatmapSetFileInfo>().Delete(f => f.BeatmapSetInfoID == b.ID); + Connection.Delete(b, true); } catch (Exception e) { - e = e.InnerException ?? e; - Logger.Error(e, @"Could not import beatmap set"); - } - } - } - - /// <summary> - /// Import a beatmap from an <see cref="ArchiveReader"/>. - /// </summary> - /// <param name="archiveReader">The beatmap to be imported.</param> - public BeatmapSetInfo Import(ArchiveReader archiveReader) - { - BeatmapSetInfo set = importToStorage(archiveReader); - Import(set); - return set; - } - - /// <summary> - /// Import a beatmap from a <see cref="BeatmapSetInfo"/>. - /// </summary> - /// <param name="beatmapSetInfo">The beatmap to be imported.</param> - public void Import(BeatmapSetInfo beatmapSetInfo) - { - // If we have an ID then we already exist in the database. - if (beatmapSetInfo.ID != 0) return; - - beatmaps.Add(beatmapSetInfo); - } - - /// <summary> - /// Delete a beatmap from the store. - /// Is a no-op for already deleted beatmaps. - /// </summary> - /// <param name="beatmapSet">The beatmap to delete.</param> - public void Delete(BeatmapSetInfo beatmapSet) - { - if (!beatmaps.Delete(beatmapSet)) return; - - if (!beatmapSet.Protected) - files.Dereference(beatmapSet.Files); - } - - /// <summary> - /// Returns a <see cref="BeatmapSetInfo"/> to a usable state if it has previously been deleted but not yet purged. - /// Is a no-op for already usable beatmaps. - /// </summary> - /// <param name="beatmapSet">The beatmap to restore.</param> - public void Undelete(BeatmapSetInfo beatmapSet) - { - if (!beatmaps.Undelete(beatmapSet)) return; - - files.Reference(beatmapSet.Files); - } - - /// <summary> - /// Retrieve a <see cref="WorkingBeatmap"/> instance for the provided <see cref="BeatmapInfo"/> - /// </summary> - /// <param name="beatmapInfo">The beatmap to lookup.</param> - /// <param name="previous">The currently loaded <see cref="WorkingBeatmap"/>. Allows for optimisation where elements are shared with the new beatmap.</param> - /// <returns>A <see cref="WorkingBeatmap"/> instance correlating to the provided <see cref="BeatmapInfo"/>.</returns> - public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) - { - if (beatmapInfo == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) - return DefaultBeatmap; - - beatmaps.Populate(beatmapInfo); - - 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(files.Store, beatmapInfo); - - previous?.TransferTo(working); - - return working; - } - - /// <summary> - /// Reset the store to an empty state. - /// </summary> - public void Reset() - { - beatmaps.Reset(); - } - - /// <summary> - /// Perform a lookup query on available <see cref="BeatmapSetInfo"/>s. - /// </summary> - /// <param name="query">The query.</param> - /// <returns>The first result for the provided query, or null if no results were found.</returns> - public BeatmapSetInfo QueryBeatmapSet(Func<BeatmapSetInfo, bool> query) - { - BeatmapSetInfo set = beatmaps.Query<BeatmapSetInfo>().FirstOrDefault(query); - - if (set != null) - beatmaps.Populate(set); - - return set; - } - - /// <summary> - /// Perform a lookup query on available <see cref="BeatmapSetInfo"/>s. - /// </summary> - /// <param name="query">The query.</param> - /// <returns>Results from the provided query.</returns> - public List<BeatmapSetInfo> QueryBeatmapSets(Expression<Func<BeatmapSetInfo, bool>> query) => beatmaps.QueryAndPopulate(query); - - /// <summary> - /// Perform a lookup query on available <see cref="BeatmapInfo"/>s. - /// </summary> - /// <param name="query">The query.</param> - /// <returns>The first result for the provided query, or null if no results were found.</returns> - public BeatmapInfo QueryBeatmap(Func<BeatmapInfo, bool> query) - { - BeatmapInfo set = beatmaps.Query<BeatmapInfo>().FirstOrDefault(query); - - if (set != null) - beatmaps.Populate(set); - - return set; - } - - /// <summary> - /// Perform a lookup query on available <see cref="BeatmapInfo"/>s. - /// </summary> - /// <param name="query">The query.</param> - /// <returns>Results from the provided query.</returns> - public List<BeatmapInfo> QueryBeatmaps(Expression<Func<BeatmapInfo, bool>> query) => beatmaps.QueryAndPopulate(query); - - /// <summary> - /// Creates an <see cref="ArchiveReader"/> from a valid storage path. - /// </summary> - /// <param name="path">A file or folder path resolving the beatmap content.</param> - /// <returns>A reader giving access to the beatmap's content.</returns> - private ArchiveReader getReaderFrom(string path) - { - if (ZipFile.IsZipFile(path)) - return new OszArchiveReader(storage.GetStream(path)); - else - return new LegacyFilesystemReader(path); - } - - /// <summary> - /// Import a beamap into our local <see cref="FileDatabase"/> storage. - /// If the beatmap is already imported, the existing instance will be returned. - /// </summary> - /// <param name="reader">The beatmap archive to be read.</param> - /// <returns>The imported beatmap, or an existing instance if it is already present.</returns> - private BeatmapSetInfo importToStorage(ArchiveReader reader) - { - // for now, concatenate all .osu files in the set to create a unique hash. - MemoryStream hashable = new MemoryStream(); - foreach (string file in reader.Filenames.Where(f => f.EndsWith(".osu"))) - using (Stream s = reader.GetStream(file)) - s.CopyTo(hashable); - - var hash = hashable.GetMd5Hash(); - - // check if this beatmap has already been imported and exit early if so. - var beatmapSet = beatmaps.QueryAndPopulate<BeatmapSetInfo>().FirstOrDefault(b => b.Hash == hash); - if (beatmapSet != null) - { - Undelete(beatmapSet); - return beatmapSet; - } - - List<FileInfo> fileInfos = new List<FileInfo>(); - - // import files to store - foreach (string file in reader.Filenames) - using (Stream s = reader.GetStream(file)) - fileInfos.Add(files.Add(s, file)); - - BeatmapMetadata metadata; - - using (var stream = new StreamReader(reader.GetStream(reader.Filenames.First(f => f.EndsWith(".osu"))))) - metadata = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata; - - beatmapSet = new BeatmapSetInfo - { - OnlineBeatmapSetID = metadata.OnlineBeatmapSetID, - Beatmaps = new List<BeatmapInfo>(), - Hash = hash, - Files = fileInfos, - Metadata = metadata - }; - - var mapNames = reader.Filenames.Where(f => f.EndsWith(".osu")); - - foreach (var name in mapNames) - { - using (var raw = reader.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<RulesetInfo>().FirstOrDefault(r => r.ID == beatmap.BeatmapInfo.RulesetID); - beatmap.BeatmapInfo.StarDifficulty = rulesets.Query<RulesetInfo>().FirstOrDefault(r => r.ID == beatmap.BeatmapInfo.RulesetID)?.CreateInstance()?.CreateDifficultyCalculator(beatmap) - .Calculate() ?? 0; - - beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo); + Logger.Error(e, $@"Could not delete beatmap {b}"); } } - return beatmapSet; - } - - /// <summary> - /// Returns a list of all usable <see cref="BeatmapSetInfo"/>s. - /// </summary> - /// <param name="populate">Whether returned objects should be pre-populated with all data.</param> - /// <returns>A list of available <see cref="BeatmapSetInfo"/>.</returns> - public List<BeatmapSetInfo> GetAllUsableBeatmapSets(bool populate = true) - { - if (populate) - return beatmaps.QueryAndPopulate<BeatmapSetInfo>(b => !b.DeletePending).ToList(); - else - return beatmaps.Query<BeatmapSetInfo>(b => !b.DeletePending).ToList(); + //this is required because sqlite migrations don't work, initially inserting nulls into this field. + //see https://github.com/praeclarum/sqlite-net/issues/326 + Connection.Query<BeatmapSetInfo>("UPDATE BeatmapSetInfo SET DeletePending = 0 WHERE DeletePending IS NULL"); } } } diff --git a/osu.Game/Beatmaps/BeatmapStoreWorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapStoreWorkingBeatmap.cs deleted file mode 100644 index 3ddd7eecd6..0000000000 --- a/osu.Game/Beatmaps/BeatmapStoreWorkingBeatmap.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.IO; -using System.Linq; -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 IResourceStore<byte[]> store; - - public BeatmapStoreWorkingBeatmap(IResourceStore<byte[]> store, BeatmapInfo beatmapInfo) - : base(beatmapInfo) - { - this.store = store; - } - - protected override Beatmap GetBeatmap() - { - try - { - Beatmap beatmap; - - BeatmapDecoder decoder; - using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) - { - decoder = BeatmapDecoder.GetDecoder(stream); - beatmap = decoder.Decode(stream); - } - - if (beatmap == null || BeatmapSetInfo.StoryboardFile == null) - return beatmap; - - using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) - decoder.Decode(stream, beatmap); - - - return beatmap; - } - catch { return null; } - } - - private string getPathForFile(string filename) => BeatmapSetInfo.Files.First(f => f.Filename == filename).StoragePath; - - protected override Texture GetBackground() - { - if (Metadata?.BackgroundFile == null) - return null; - - try - { - return new TextureStore(new RawTextureLoaderStore(store), false).Get(getPathForFile(Metadata.BackgroundFile)); - } - catch { return null; } - } - - protected override Track GetTrack() - { - try - { - var trackData = store.GetStream(getPathForFile(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 89ba9133c1..ad9a0a787b 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs @@ -58,10 +58,10 @@ namespace osu.Game.Beatmaps.Drawables } } - public BeatmapGroup(BeatmapSetInfo beatmapSet, BeatmapStore store) + public BeatmapGroup(BeatmapSetInfo beatmapSet, BeatmapManager manager) { BeatmapSet = beatmapSet; - WorkingBeatmap beatmap = store.GetWorkingBeatmap(BeatmapSet.Beatmaps.FirstOrDefault()); + WorkingBeatmap beatmap = manager.GetWorkingBeatmap(BeatmapSet.Beatmaps.FirstOrDefault()); Header = new BeatmapSetHeader(beatmap) { diff --git a/osu.Game/Database/DatabaseStore.cs b/osu.Game/Database/DatabaseBackedStore.cs similarity index 90% rename from osu.Game/Database/DatabaseStore.cs rename to osu.Game/Database/DatabaseBackedStore.cs index 8e884f062a..8366775483 100644 --- a/osu.Game/Database/DatabaseStore.cs +++ b/osu.Game/Database/DatabaseBackedStore.cs @@ -12,12 +12,12 @@ using SQLiteNetExtensions.Extensions; namespace osu.Game.Database { - public abstract class DatabaseStore + public abstract class DatabaseBackedStore { protected readonly Storage Storage; protected readonly SQLiteConnection Connection; - protected DatabaseStore(SQLiteConnection connection, Storage storage = null) + protected DatabaseBackedStore(SQLiteConnection connection, Storage storage = null) { Storage = storage; Connection = connection; @@ -84,7 +84,7 @@ namespace osu.Game.Database private void checkType(Type type) { if (!ValidTypes.Contains(type)) - throw new InvalidOperationException($"The requested operation specified a type of {type}, which is invalid for this {nameof(DatabaseStore)}."); + throw new InvalidOperationException($"The requested operation specified a type of {type}, which is invalid for this {nameof(DatabaseBackedStore)}."); } protected abstract Type[] ValidTypes { get; } diff --git a/osu.Game/IO/FileDatabase.cs b/osu.Game/IO/FileStore.cs similarity index 91% rename from osu.Game/IO/FileDatabase.cs rename to osu.Game/IO/FileStore.cs index 2fb43f9a57..95d32785ec 100644 --- a/osu.Game/IO/FileDatabase.cs +++ b/osu.Game/IO/FileStore.cs @@ -17,13 +17,13 @@ namespace osu.Game.IO /// <summary> /// Handles the Store and retrieval of Files/FileSets to the database backing /// </summary> - public class FileDatabase : DatabaseStore + public class FileStore : DatabaseBackedStore { private const string prefix = "files"; public readonly ResourceStore<byte[]> Store; - public FileDatabase(SQLiteConnection connection, Storage storage) : base(connection, storage) + public FileStore(SQLiteConnection connection, Storage storage) : base(connection, storage) { Store = new NamespacedResourceStore<byte[]>(new StorageBackedResourceStore(storage), prefix); } diff --git a/osu.Game/IPC/BeatmapIPCChannel.cs b/osu.Game/IPC/BeatmapIPCChannel.cs index f42e750a6f..6a9019251c 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<BeatmapImportMessage> { - private readonly BeatmapStore beatmaps; + private readonly BeatmapManager beatmaps; - public BeatmapIPCChannel(IIpcHost host, BeatmapStore beatmaps = null) + public BeatmapIPCChannel(IIpcHost host, BeatmapManager beatmaps = null) : base(host) { this.beatmaps = beatmaps; diff --git a/osu.Game/IPC/ScoreIPCChannel.cs b/osu.Game/IPC/ScoreIPCChannel.cs index cfc74c4824..ae44250e8d 100644 --- a/osu.Game/IPC/ScoreIPCChannel.cs +++ b/osu.Game/IPC/ScoreIPCChannel.cs @@ -10,9 +10,9 @@ namespace osu.Game.IPC { public class ScoreIPCChannel : IpcChannel<ScoreImportMessage> { - private readonly ScoreDatabase scores; + private readonly ScoreStore scores; - public ScoreIPCChannel(IIpcHost host, ScoreDatabase scores = null) + public ScoreIPCChannel(IIpcHost host, ScoreStore scores = null) : base(host) { this.scores = scores; diff --git a/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs index 3b7711677e..ca984d3511 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs @@ -49,7 +49,7 @@ namespace osu.Game.Online.API.Requests [JsonProperty(@"beatmaps")] private IEnumerable<GetBeatmapSetsBeatmapResponse> beatmaps { get; set; } - public BeatmapSetInfo ToBeatmapSet(RulesetDatabase rulesets) + public BeatmapSetInfo ToBeatmapSet(RulesetStore rulesets) { return new BeatmapSetInfo { @@ -79,7 +79,7 @@ namespace osu.Game.Online.API.Requests [JsonProperty(@"difficulty_rating")] private double starDifficulty { get; set; } - public BeatmapInfo ToBeatmap(RulesetDatabase rulesets) + public BeatmapInfo ToBeatmap(RulesetStore rulesets) { return new BeatmapInfo { diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 44af591cc8..4f4c2e2883 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -99,13 +99,13 @@ namespace osu.Game if (args?.Length > 0) { var paths = args.Where(a => !a.StartsWith(@"-")); - Task.Run(() => BeatmapStore.Import(paths.ToArray())); + Task.Run(() => BeatmapManager.Import(paths.ToArray())); } dependencies.Cache(this); configRuleset = LocalConfig.GetBindable<int>(OsuSetting.Ruleset); - Ruleset.Value = RulesetDatabase.GetRuleset(configRuleset.Value); + Ruleset.Value = RulesetStore.GetRuleset(configRuleset.Value); Ruleset.ValueChanged += r => configRuleset.Value = r.ID ?? 0; } @@ -140,7 +140,7 @@ namespace osu.Game return; } - Beatmap.Value = BeatmapStore.GetWorkingBeatmap(s.Beatmap); + Beatmap.Value = BeatmapManager.GetWorkingBeatmap(s.Beatmap); menu.Push(new PlayerLoader(new ReplayPlayer(s.Replay))); } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 8ad07cd7bc..b507aa2315 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -28,13 +28,13 @@ namespace osu.Game { protected OsuConfigManager LocalConfig; - protected BeatmapStore BeatmapStore; + protected BeatmapManager BeatmapManager; - protected RulesetDatabase RulesetDatabase; + protected RulesetStore RulesetStore; - protected FileDatabase FileDatabase; + protected FileStore FileStore; - protected ScoreDatabase ScoreDatabase; + protected ScoreStore ScoreStore; protected override string MainResourceFile => @"osu.Game.Resources.dll"; @@ -97,10 +97,10 @@ namespace osu.Game SQLiteConnection connection = Host.Storage.GetDatabase(@"client"); - dependencies.Cache(RulesetDatabase = new RulesetDatabase(connection)); - dependencies.Cache(FileDatabase = new FileDatabase(connection, Host.Storage)); - dependencies.Cache(BeatmapStore = new BeatmapStore(Host.Storage, FileDatabase, connection, RulesetDatabase, Host)); - dependencies.Cache(ScoreDatabase = new ScoreDatabase(Host.Storage, connection, Host, BeatmapStore)); + dependencies.Cache(RulesetStore = new RulesetStore(connection)); + dependencies.Cache(FileStore = new FileStore(connection, Host.Storage)); + dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, FileStore, connection, RulesetStore, Host)); + dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, connection, Host, BeatmapManager)); dependencies.Cache(new OsuColour()); //this completely overrides the framework default. will need to change once we make a proper FontStore. @@ -132,7 +132,7 @@ namespace osu.Game var defaultBeatmap = new DummyWorkingBeatmap(this); Beatmap = new NonNullableBindable<WorkingBeatmap>(defaultBeatmap); - BeatmapStore.DefaultBeatmap = defaultBeatmap; + BeatmapManager.DefaultBeatmap = defaultBeatmap; dependencies.Cache(API = new APIAccess { diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs index 24a878da1d..4f815f220c 100644 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ b/osu.Game/Overlays/Direct/FilterControl.cs @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Direct } [BackgroundDependencyLoader(true)] - private void load(OsuGame game, RulesetDatabase rulesets, OsuColour colours) + private void load(OsuGame game, RulesetStore rulesets, OsuColour colours) { DisplayStyleControl.Dropdown.AccentColour = colours.BlueDark; diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 3724c3e191..b1c7dab778 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -26,7 +26,7 @@ namespace osu.Game.Overlays private const float panel_padding = 10f; private APIAccess api; - private RulesetDatabase rulesets; + private RulesetStore rulesets; private readonly FillFlowContainer resultCountsContainer; private readonly OsuSpriteText resultCountsText; @@ -161,7 +161,7 @@ namespace osu.Game.Overlays } [BackgroundDependencyLoader] - private void load(OsuColour colours, APIAccess api, RulesetDatabase rulesets) + private void load(OsuColour colours, APIAccess api, RulesetStore rulesets) { this.api = api; this.rulesets = rulesets; diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 6324b9ac3d..eb643f390f 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -48,7 +48,7 @@ namespace osu.Game.Overlays.Mods } [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuColour colours, OsuGame osu, RulesetDatabase rulesets) + private void load(OsuColour colours, OsuGame osu, RulesetStore rulesets) { lowMultiplierColour = colours.Red; highMultiplierColour = colours.Green; diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 395456ad1b..31fe755d2b 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 BeatmapStore beatmaps; + private BeatmapManager beatmaps; private readonly Bindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>(); @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Music private InputManager inputManager; [BackgroundDependencyLoader] - private void load(OsuGameBase game, BeatmapStore beatmaps, OsuColour colours, UserInputManager inputManager) + private void load(OsuGameBase game, BeatmapManager beatmaps, OsuColour colours, UserInputManager inputManager) { this.inputManager = inputManager; this.beatmaps = beatmaps; diff --git a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs index 32cc0fcca5..326cb582e2 100644 --- a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs +++ b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Settings.Sections } [BackgroundDependencyLoader] - private void load(RulesetDatabase rulesets) + private void load(RulesetStore rulesets) { foreach(Ruleset ruleset in rulesets.AllRulesets.Select(info => info.CreateInstance())) { diff --git a/osu.Game/Overlays/Settings/SettingsFooter.cs b/osu.Game/Overlays/Settings/SettingsFooter.cs index f5f7fe0ace..6c25b146a1 100644 --- a/osu.Game/Overlays/Settings/SettingsFooter.cs +++ b/osu.Game/Overlays/Settings/SettingsFooter.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Settings public class SettingsFooter : FillFlowContainer { [BackgroundDependencyLoader] - private void load(OsuGameBase game, OsuColour colours, RulesetDatabase rulesets) + private void load(OsuGameBase game, OsuColour colours, RulesetStore rulesets) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; diff --git a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs index f6d7a05f61..60c1261190 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs @@ -65,7 +65,7 @@ namespace osu.Game.Overlays.Toolbar } [BackgroundDependencyLoader] - private void load(RulesetDatabase rulesets, OsuGame game) + private void load(RulesetStore rulesets, OsuGame game) { foreach (var r in rulesets.AllRulesets) { diff --git a/osu.Game/Rulesets/RulesetDatabase.cs b/osu.Game/Rulesets/RulesetStore.cs similarity index 90% rename from osu.Game/Rulesets/RulesetDatabase.cs rename to osu.Game/Rulesets/RulesetStore.cs index be2ba19c0c..88aee2bffc 100644 --- a/osu.Game/Rulesets/RulesetDatabase.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -12,13 +12,13 @@ using SQLite.Net; namespace osu.Game.Rulesets { /// <summary> - /// Todo: All of this needs to be moved to a RulesetDatabase. + /// Todo: All of this needs to be moved to a RulesetStore. /// </summary> - public class RulesetDatabase : DatabaseStore + public class RulesetStore : DatabaseBackedStore { public IEnumerable<RulesetInfo> AllRulesets => Query<RulesetInfo>().Where(r => r.Available); - public RulesetDatabase(SQLiteConnection connection) : base(connection) + public RulesetStore(SQLiteConnection connection) : base(connection) { } diff --git a/osu.Game/Rulesets/Scoring/ScoreDatabase.cs b/osu.Game/Rulesets/Scoring/ScoreStore.cs similarity index 91% rename from osu.Game/Rulesets/Scoring/ScoreDatabase.cs rename to osu.Game/Rulesets/Scoring/ScoreStore.cs index 19dbf30b28..e69fec4b54 100644 --- a/osu.Game/Rulesets/Scoring/ScoreDatabase.cs +++ b/osu.Game/Rulesets/Scoring/ScoreStore.cs @@ -15,19 +15,19 @@ using SQLite.Net; namespace osu.Game.Rulesets.Scoring { - public class ScoreDatabase : DatabaseStore + public class ScoreStore : DatabaseBackedStore { private readonly Storage storage; - private readonly BeatmapStore beatmaps; - private readonly RulesetDatabase rulesets; + private readonly BeatmapManager beatmaps; + private readonly RulesetStore rulesets; private const string replay_folder = @"replays"; // 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(connection) + public ScoreStore(Storage storage, SQLiteConnection connection, IIpcHost importHost = null, BeatmapManager beatmaps = null, RulesetStore rulesets = null) : base(connection) { this.storage = storage; this.beatmaps = beatmaps; diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index d05ef8af65..a0cf6eec66 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, BeatmapStore beatmaps, Framework.Game game) + private void load(AudioManager audio, OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game) { menuVoice = config.GetBindable<bool>(OsuSetting.MenuVoice); menuMusic = config.GetBindable<bool>(OsuSetting.MenuMusic); diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 736ed0c1d8..743a0a0f63 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -68,7 +68,7 @@ namespace osu.Game.Screens.Select /// <summary> /// Required for now unfortunately. /// </summary> - private BeatmapStore store; + private BeatmapManager manager; private readonly Container<Panel> scrollableContent; @@ -289,7 +289,7 @@ namespace osu.Game.Screens.Select b.Metadata = beatmapSet.Metadata; } - return new BeatmapGroup(beatmapSet, store) + return new BeatmapGroup(beatmapSet, manager) { 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(BeatmapStore store, OsuConfigManager config) + private void load(BeatmapManager manager, OsuConfigManager config) { - this.store = store; + this.manager = manager; randomType = config.GetBindable<SelectionRandomType>(OsuSetting.SelectionRandomType); } diff --git a/osu.Game/Screens/Select/BeatmapDeleteDialog.cs b/osu.Game/Screens/Select/BeatmapDeleteDialog.cs index a605691ec5..96caf2f236 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 BeatmapStore store; + private BeatmapManager manager; [BackgroundDependencyLoader] - private void load(BeatmapStore beatmapStore) + private void load(BeatmapManager beatmapManager) { - store = beatmapStore; + manager = beatmapManager; } public BeatmapDeleteDialog(WorkingBeatmap beatmap) @@ -34,7 +34,7 @@ namespace osu.Game.Screens.Select Action = () => { beatmap.Dispose(); - store.Delete(beatmap.BeatmapSetInfo); + manager.Delete(beatmap.BeatmapSetInfo); }, }, new PopupDialogCancelButton diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index fb7653ed7d..bbd292870e 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<RulesetInfo> ruleset = new Bindable<RulesetInfo>(); - private BeatmapStore store; + private BeatmapManager manager; 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(BeatmapStore beatmaps, AudioManager audio, DialogOverlay dialog, OsuGame osu, OsuColour colours, UserInputManager input) + private void load(BeatmapManager 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 (store == null) - store = beatmaps; + if (manager == null) + manager = beatmaps; if (osu != null) ruleset.BindTo(osu.Ruleset); - store.BeatmapSetAdded += onBeatmapSetAdded; - store.BeatmapSetRemoved += onBeatmapSetRemoved; + manager.BeatmapSetAdded += onBeatmapSetAdded; + manager.BeatmapSetRemoved += onBeatmapSetRemoved; dialogOverlay = dialog; @@ -180,7 +180,7 @@ namespace osu.Game.Screens.Select initialAddSetsTask = new CancellationTokenSource(); - carousel.Beatmaps = store.GetAllUsableBeatmapSets(); + carousel.Beatmaps = manager.GetAllUsableBeatmapSets(); Beatmap.ValueChanged += beatmap_ValueChanged; @@ -230,7 +230,7 @@ namespace osu.Game.Screens.Select { bool preview = beatmap?.BeatmapSetInfoID != Beatmap.Value.BeatmapInfo.BeatmapSetInfoID; - Beatmap.Value = store.GetWorkingBeatmap(beatmap, Beatmap); + Beatmap.Value = manager.GetWorkingBeatmap(beatmap, Beatmap); ensurePlayingSelected(preview); } @@ -341,10 +341,10 @@ namespace osu.Game.Screens.Select { base.Dispose(isDisposing); - if (store != null) + if (manager != null) { - store.BeatmapSetAdded -= onBeatmapSetAdded; - store.BeatmapSetRemoved -= onBeatmapSetRemoved; + manager.BeatmapSetAdded -= onBeatmapSetAdded; + manager.BeatmapSetRemoved -= onBeatmapSetRemoved; } initialAddSetsTask?.Cancel(); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 13c4056bba..f8509314be 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -74,7 +74,7 @@ <ItemGroup> <Compile Include="Audio\SampleInfo.cs" /> <Compile Include="Audio\SampleInfoList.cs" /> - <Compile Include="Beatmaps\BeatmapDatabase.cs" /> + <Compile Include="Beatmaps\BeatmapStore.cs" /> <Compile Include="Beatmaps\BeatmapSetFileInfo.cs" /> <Compile Include="Beatmaps\Drawables\BeatmapBackgroundSprite.cs" /> <Compile Include="Beatmaps\DifficultyCalculator.cs" /> @@ -91,7 +91,7 @@ <Compile Include="Graphics\UserInterface\MenuItemType.cs" /> <Compile Include="Graphics\UserInterface\OsuContextMenu.cs" /> <Compile Include="Graphics\UserInterface\OsuContextMenuItem.cs" /> - <Compile Include="IO\FileDatabase.cs" /> + <Compile Include="IO\FileStore.cs" /> <Compile Include="IO\FileInfo.cs" /> <Compile Include="Online\API\Requests\GetUsersRequest.cs" /> <Compile Include="Online\API\Requests\PostMessageRequest.cs" /> @@ -131,9 +131,9 @@ <Compile Include="Beatmaps\Timing\BreakPeriod.cs" /> <Compile Include="Beatmaps\Timing\TimeSignatures.cs" /> <Compile Include="Beatmaps\BeatmapMetrics.cs" /> - <Compile Include="Database\DatabaseStore.cs" /> + <Compile Include="Database\DatabaseBackedStore.cs" /> <Compile Include="Rulesets\RulesetInfo.cs" /> - <Compile Include="Rulesets\Scoring\ScoreDatabase.cs" /> + <Compile Include="Rulesets\Scoring\ScoreStore.cs" /> <Compile Include="Graphics\Backgrounds\Triangles.cs" /> <Compile Include="Graphics\Cursor\CursorTrail.cs" /> <Compile Include="Graphics\Cursor\GameplayCursor.cs" /> @@ -220,7 +220,7 @@ <Compile Include="Rulesets\Objects\Legacy\ConvertHitObjectType.cs" /> <Compile Include="Rulesets\Replays\ReplayButtonState.cs" /> <Compile Include="Rulesets\Replays\ReplayFrame.cs" /> - <Compile Include="Rulesets\RulesetDatabase.cs" /> + <Compile Include="Rulesets\RulesetStore.cs" /> <Compile Include="Rulesets\Scoring\Score.cs" /> <Compile Include="Rulesets\Scoring\ScoreProcessor.cs" /> <Compile Include="Rulesets\Timing\SpeedAdjustmentContainer.cs" /> @@ -389,7 +389,7 @@ <Compile Include="Users\UpdateableAvatar.cs" /> <Compile Include="Users\User.cs" /> <Compile Include="Graphics\UserInterface\Volume\VolumeControl.cs" /> - <Compile Include="Beatmaps\BeatmapStore.cs" /> + <Compile Include="Beatmaps\BeatmapManager.cs" /> <Compile Include="Beatmaps\IO\ArchiveReader.cs" /> <Compile Include="Beatmaps\Formats\BeatmapDecoder.cs" /> <Compile Include="Beatmaps\Formats\OsuLegacyDecoder.cs" /> @@ -399,7 +399,6 @@ <Compile Include="Beatmaps\BeatmapMetadata.cs" /> <Compile Include="Beatmaps\BeatmapInfo.cs" /> <Compile Include="Beatmaps\BeatmapDifficulty.cs" /> - <Compile Include="Beatmaps\BeatmapStoreWorkingBeatmap.cs" /> <Compile Include="Graphics\UserInterface\OsuButton.cs" /> <Compile Include="Overlays\Settings\Sections\MaintenanceSection.cs" /> <Compile Include="Overlays\Settings\SettingsSection.cs" />