diff --git a/osu-framework b/osu-framework index 05a8b26376..5a9ca94fc3 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 05a8b2637623bd8ac6c1619b728ea9b4cca35b8a +Subproject commit 5a9ca94fc31bc796b45572eb3d0b27b46556c586 diff --git a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetails.cs b/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetails.cs index 9335938265..111bc03377 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetails.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetails.cs @@ -3,9 +3,9 @@ using osu.Framework.Graphics; using osu.Framework.Testing; -using osu.Game.Database; using osu.Game.Screens.Select; using System.Linq; +using osu.Game.Beatmaps; namespace osu.Desktop.VisualTests.Tests { diff --git a/osu.Desktop.VisualTests/Tests/TestCaseDirect.cs b/osu.Desktop.VisualTests/Tests/TestCaseDirect.cs index 2a20bca836..4a5ff1b576 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseDirect.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseDirect.cs @@ -4,8 +4,9 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Testing; -using osu.Game.Database; +using osu.Game.Beatmaps; using osu.Game.Overlays; +using osu.Game.Rulesets; namespace osu.Desktop.VisualTests.Tests { @@ -14,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() { @@ -28,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 4f4ef9bbb5..38cf03d60e 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs @@ -7,8 +7,9 @@ using osu.Framework.Testing; using osu.Game.Screens.Multiplayer; using osu.Game.Online.Multiplayer; using osu.Game.Users; -using osu.Game.Database; using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; namespace osu.Desktop.VisualTests.Tests { @@ -16,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() { @@ -124,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 0c5f21a185..0b08065241 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs @@ -8,7 +8,6 @@ using osu.Framework.MathUtils; using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Beatmaps; -using osu.Game.Database; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Objects; @@ -19,17 +18,18 @@ using System.Collections.Generic; using osu.Desktop.VisualTests.Beatmaps; using osu.Framework.Allocation; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets; 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 e626a70e5f..1604be603a 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseMods.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseMods.cs @@ -5,7 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Overlays.Mods; using osu.Framework.Testing; -using osu.Game.Database; +using osu.Game.Rulesets; using osu.Game.Screens.Play.HUD; using OpenTK; @@ -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 83a1436357..61e87a6621 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,31 +14,27 @@ namespace osu.Desktop.VisualTests.Tests { internal class TestCasePlaySongSelect : TestCase { - private readonly BeatmapDatabase db; + private readonly BeatmapManager manager; public override string Description => @"with fake data"; - private readonly RulesetDatabase rulesets; + private readonly RulesetStore rulesets; public TestCasePlaySongSelect() { PlaySongSelect songSelect; - if (db == null) + if (manager == null) { var storage = new TestStorage(@"TestCasePlaySongSelect"); var backingDatabase = storage.GetDatabase(@"client"); - rulesets = new RulesetDatabase(storage, backingDatabase); - db = new BeatmapDatabase(storage, backingDatabase, rulesets); - - var sets = new List(); + rulesets = new RulesetStore(backingDatabase); + manager = new BeatmapManager(storage, null, backingDatabase, rulesets); for (int i = 0; i < 100; i += 10) - sets.Add(createTestBeatmapSet(i)); - - db.Import(sets); + manager.Import(createTestBeatmapSet(i)); } Add(songSelect = new PlaySongSelect()); @@ -48,21 +45,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/TestCasePlayer.cs b/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs index 088ccb2aa3..f38cabd618 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs @@ -6,7 +6,6 @@ using osu.Framework.Allocation; using osu.Framework.Testing; using osu.Game.Beatmaps; using OpenTK; -using osu.Game.Database; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Play; @@ -14,18 +13,19 @@ using OpenTK.Graphics; using osu.Desktop.VisualTests.Beatmaps; using osu.Game.Rulesets.Osu.UI; using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets; 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 4a980068b9..288fff346f 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseResults.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseResults.cs @@ -3,11 +3,9 @@ using System; using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Testing; using osu.Game.Beatmaps; -using osu.Game.Database; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Ranking; using osu.Game.Users; @@ -16,14 +14,14 @@ namespace osu.Desktop.VisualTests.Tests { internal class TestCaseResults : TestCase { - private BeatmapDatabase db; + private BeatmapManager beatmaps; public override string Description => @"Results after playing."; [BackgroundDependencyLoader] - private void load(BeatmapDatabase db) + private void load(BeatmapManager beatmaps) { - this.db = db; + this.beatmaps = beatmaps; } private WorkingBeatmap beatmap; @@ -34,9 +32,9 @@ namespace osu.Desktop.VisualTests.Tests if (beatmap == null) { - var beatmapInfo = db.Query().FirstOrDefault(b => b.RulesetID == 0); + var beatmapInfo = beatmaps.QueryBeatmap(b => b.RulesetID == 0); if (beatmapInfo != null) - beatmap = db.GetWorkingBeatmap(beatmapInfo); + beatmap = beatmaps.GetWorkingBeatmap(beatmapInfo); } Add(new Results(new Score diff --git a/osu.Desktop.VisualTests/Tests/TestCaseRoomInspector.cs b/osu.Desktop.VisualTests/Tests/TestCaseRoomInspector.cs index 4d650afed5..043f072b25 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseRoomInspector.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseRoomInspector.cs @@ -4,10 +4,11 @@ using osu.Framework.Testing; using osu.Framework.Graphics; using osu.Game.Screens.Multiplayer; -using osu.Game.Database; using osu.Game.Online.Multiplayer; using osu.Game.Users; using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; namespace osu.Desktop.VisualTests.Tests { @@ -15,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() { @@ -135,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/Beatmaps/IO/LegacyFilesystemReader.cs b/osu.Desktop/Beatmaps/IO/LegacyFilesystemReader.cs deleted file mode 100644 index 8772fc9f28..0000000000 --- a/osu.Desktop/Beatmaps/IO/LegacyFilesystemReader.cs +++ /dev/null @@ -1,43 +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 System.Linq; -using osu.Game.Beatmaps.IO; - -namespace osu.Desktop.Beatmaps.IO -{ - /// - /// Reads an extracted legacy beatmap from disk. - /// - public class LegacyFilesystemReader : ArchiveReader - { - public static void Register() => AddReader((storage, path) => Directory.Exists(path)); - - private readonly string basePath; - - public LegacyFilesystemReader(string path) - { - basePath = path; - - BeatmapFilenames = Directory.GetFiles(basePath, @"*.osu").Select(Path.GetFileName).ToArray(); - - if (BeatmapFilenames.Length == 0) - throw new FileNotFoundException(@"This directory contains no beatmaps"); - - StoryboardFilename = Directory.GetFiles(basePath, @"*.osb").Select(Path.GetFileName).FirstOrDefault(); - } - - public override Stream GetStream(string name) - { - return File.OpenRead(Path.Combine(basePath, name)); - } - - public override void Dispose() - { - // no-op - } - - public override Stream GetUnderlyingStream() => null; - } -} diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index a2bfe6bf2e..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(() => BeatmapDatabase.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.Desktop/Program.cs b/osu.Desktop/Program.cs index 210f780078..3b63239525 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using osu.Desktop.Beatmaps.IO; using osu.Framework.Desktop; using osu.Framework.Desktop.Platform; using osu.Game.IPC; @@ -15,8 +14,6 @@ namespace osu.Desktop [STAThread] public static int Main(string[] args) { - LegacyFilesystemReader.Register(); - // Back up the cwd before DesktopGameHost changes it var cwd = Environment.CurrentDirectory; diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index e69603602c..82fbefec7a 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -228,7 +228,6 @@ - diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 0ed2a0ba6f..b55b9fdb37 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -10,7 +10,6 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Mania.Beatmaps.Patterns; using osu.Game.Rulesets.Mania.MathUtils; -using osu.Game.Database; using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy; using OpenTK; using osu.Game.Audio; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs index e6e3f1d07f..b72618c36c 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -4,7 +4,6 @@ using System; using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Database; using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Objects; using OpenTK; diff --git a/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs b/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs index 674d83f6f2..52b55a4ff5 100644 --- a/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs +++ b/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs @@ -1,7 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Database; +using osu.Game.Beatmaps; namespace osu.Game.Rulesets.Mania.Judgements { diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs index fa32d46a88..c3a29b39a8 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs @@ -2,8 +2,8 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Database; using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Mania.Objects diff --git a/osu.Game.Rulesets.Mania/Objects/Note.cs b/osu.Game.Rulesets.Mania/Objects/Note.cs index 6c0cacd277..3c4ff4216f 100644 --- a/osu.Game.Rulesets.Mania/Objects/Note.cs +++ b/osu.Game.Rulesets.Mania/Objects/Note.cs @@ -1,8 +1,8 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Database; using osu.Game.Rulesets.Mania.Judgements; namespace osu.Game.Rulesets.Mania.Objects diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index 798d4b8c5b..f575342486 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -4,7 +4,6 @@ using System; using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Database; using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects.Drawables; diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index e6fd82e6c8..7a311f1467 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -1,12 +1,12 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; using OpenTK; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using OpenTK.Graphics; -using osu.Game.Database; using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Osu.Objects diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 3b44e38d5e..4d8c030ede 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -6,9 +6,9 @@ using osu.Game.Rulesets.Objects.Types; using System; using System.Collections.Generic; using osu.Game.Rulesets.Objects; -using osu.Game.Database; using System.Linq; using osu.Game.Audio; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Osu.Objects diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index 6ba499739a..c4f5dfe97a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs @@ -1,8 +1,8 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects.Types; -using osu.Game.Database; using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Osu.Objects diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 5d44da78f9..44f61e2074 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -8,7 +8,6 @@ using osu.Game.Rulesets.Taiko.Objects; using System; using System.Collections.Generic; using System.Linq; -using osu.Game.Database; using osu.Game.IO.Serialization; using osu.Game.Audio; using osu.Game.Rulesets.Beatmaps; diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 18e3016fc3..fd9daa269c 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -5,8 +5,8 @@ using osu.Game.Rulesets.Objects.Types; using System; using System.Collections.Generic; using System.Linq; -using osu.Game.Database; using osu.Game.Audio; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Taiko.Objects diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs index f31472d0fd..03b9be4157 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Hit.cs @@ -1,8 +1,8 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Database; namespace osu.Game.Rulesets.Taiko.Objects { diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index 79bb901112..db368cb112 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs @@ -1,8 +1,8 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Database; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Taiko.UI; diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs index f5e2094cbf..70df3d8317 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs @@ -3,7 +3,6 @@ using System; using osu.Game.Beatmaps; -using osu.Game.Database; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs index 4814af984e..da3b448f74 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs @@ -16,12 +16,6 @@ namespace osu.Game.Tests.Beatmaps.Formats [TestFixture] public class OsuLegacyDecoderTest { - [OneTimeSetUpAttribute] - public void SetUp() - { - OsuLegacyDecoder.Register(); - } - [Test] public void TestDecodeMetadata() { diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 24970b8dab..ecaf0db096 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -10,9 +10,10 @@ using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Desktop.Platform; using osu.Framework.Platform; -using osu.Game.Database; using osu.Game.IPC; using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; namespace osu.Game.Tests.Beatmaps.IO { @@ -33,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); @@ -79,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); @@ -104,8 +105,8 @@ namespace osu.Game.Tests.Beatmaps.IO Thread.Sleep(1); //reset beatmap database (sqlite and storage backing) - osu.Dependencies.Get().Reset(); - osu.Dependencies.Get().Reset(); + osu.Dependencies.Get().Reset(); + osu.Dependencies.Get().Reset(); return osu; } @@ -114,10 +115,11 @@ namespace osu.Game.Tests.Beatmaps.IO { IEnumerable resultSets = null; + var store = osu.Dependencies.Get(); + Action waitAction = () => { - while (!(resultSets = osu.Dependencies.Get() - .Query().Where(s => s.OnlineBeatmapSetID == 241526)).Any()) + while (!(resultSets = store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526)).Any()) Thread.Sleep(50); }; @@ -133,15 +135,14 @@ 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 = store.QueryBeatmaps(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 = store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526).First(); Assert.IsTrue(set.Beatmaps.Count == resultBeatmaps.Count(), $@"Incorrect database beatmap count post-import ({resultBeatmaps.Count()} but should be {set.Beatmaps.Count})."); @@ -151,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 = store.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 = store.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 = store.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 = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 3))?.Beatmap; Assert.IsTrue(beatmap?.HitObjects.Count > 0); } } diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs index c9b3b1b922..7a7a8a58bc 100644 --- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs @@ -2,23 +2,18 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.IO; +using System.Linq; using NUnit.Framework; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.IO; using osu.Game.Tests.Resources; using osu.Game.Beatmaps.Formats; -using osu.Game.Database; namespace osu.Game.Tests.Beatmaps.IO { [TestFixture] public class OszArchiveReaderTest { - [OneTimeSetUpAttribute] - public void SetUp() - { - OszArchiveReader.Register(); - } - [Test] public void TestReadBeatmaps() { @@ -40,7 +35,7 @@ namespace osu.Game.Tests.Beatmaps.IO "Soleily - Renatus (MMzz) [Muzukashii].osu", "Soleily - Renatus (MMzz) [Oni].osu" }; - var maps = reader.BeatmapFilenames; + var maps = reader.Filenames.ToArray(); foreach (var map in expected) Assert.Contains(map, maps); } diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 0368455b92..8668b0c995 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -3,7 +3,6 @@ using OpenTK.Graphics; using osu.Game.Beatmaps.Timing; -using osu.Game.Database; using osu.Game.Rulesets.Objects; using System.Collections.Generic; using System.Linq; diff --git a/osu.Game/Database/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs similarity index 96% rename from osu.Game/Database/BeatmapDifficulty.cs rename to osu.Game/Beatmaps/BeatmapDifficulty.cs index 87c651aa88..7c2294cae9 100644 --- a/osu.Game/Database/BeatmapDifficulty.cs +++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs @@ -3,7 +3,7 @@ using SQLite.Net.Attributes; -namespace osu.Game.Database +namespace osu.Game.Beatmaps { public class BeatmapDifficulty { diff --git a/osu.Game/Database/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs similarity index 92% rename from osu.Game/Database/BeatmapInfo.cs rename to osu.Game/Beatmaps/BeatmapInfo.cs index 9f253f6055..6f4a4a8a69 100644 --- a/osu.Game/Database/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -1,14 +1,15 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using Newtonsoft.Json; -using osu.Game.IO.Serialization; -using SQLite.Net.Attributes; -using SQLiteNetExtensions.Attributes; using System; using System.Linq; +using Newtonsoft.Json; +using osu.Game.IO.Serialization; +using osu.Game.Rulesets; +using SQLite.Net.Attributes; +using SQLiteNetExtensions.Attributes; -namespace osu.Game.Database +namespace osu.Game.Beatmaps { public class BeatmapInfo : IEquatable, IJsonSerializable { @@ -60,7 +61,7 @@ namespace osu.Game.Database [ForeignKey(typeof(RulesetInfo))] public int RulesetID { get; set; } - [OneToOne(CascadeOperations = CascadeOperation.All)] + [OneToOne(CascadeOperations = CascadeOperation.CascadeRead)] public RulesetInfo Ruleset { get; set; } public bool LetterboxInBreaks { get; set; } @@ -94,11 +95,11 @@ namespace osu.Game.Database } public bool AudioEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null && - BeatmapSet.Path == other.BeatmapSet.Path && + BeatmapSet.Hash == other.BeatmapSet.Hash && (Metadata ?? BeatmapSet.Metadata).AudioFile == (other.Metadata ?? other.BeatmapSet.Metadata).AudioFile; public bool BackgroundEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null && - BeatmapSet.Path == other.BeatmapSet.Path && + BeatmapSet.Hash == other.BeatmapSet.Hash && (Metadata ?? BeatmapSet.Metadata).BackgroundFile == (other.Metadata ?? other.BeatmapSet.Metadata).BackgroundFile; } } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs new file mode 100644 index 0000000000..99117afe35 --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -0,0 +1,394 @@ +// 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 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 +{ + /// + /// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps. + /// + public class BeatmapManager + { + /// + /// Fired when a new becomes available in the database. + /// + public event Action BeatmapSetAdded; + + /// + /// Fired when a is removed from the database. + /// + public event Action BeatmapSetRemoved; + + /// + /// A default representation of a WorkingBeatmap to use when no beatmap is available. + /// + 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); + } + + /// + /// Import multiple from filesystem . + /// + /// Multiple locations on disk. + 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"); + } + } + } + + /// + /// Import a beatmap from an . + /// + /// The beatmap to be imported. + public BeatmapSetInfo Import(ArchiveReader archiveReader) + { + BeatmapSetInfo set = importToStorage(archiveReader); + Import(set); + return set; + } + + /// + /// Import a beatmap from a . + /// + /// The beatmap to be imported. + 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); + } + + /// + /// Delete a beatmap from the manager. + /// Is a no-op for already deleted beatmaps. + /// + /// The beatmap to delete. + public void Delete(BeatmapSetInfo beatmapSet) + { + if (!beatmaps.Delete(beatmapSet)) return; + + if (!beatmapSet.Protected) + files.Dereference(beatmapSet.Files); + } + + /// + /// Returns a to a usable state if it has previously been deleted but not yet purged. + /// Is a no-op for already usable beatmaps. + /// + /// The beatmap to restore. + public void Undelete(BeatmapSetInfo beatmapSet) + { + if (!beatmaps.Undelete(beatmapSet)) return; + + files.Reference(beatmapSet.Files); + } + + /// + /// Retrieve a instance for the provided + /// + /// The beatmap to lookup. + /// The currently loaded . Allows for optimisation where elements are shared with the new beatmap. + /// A instance correlating to the provided . + 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; + } + + /// + /// Reset the manager to an empty state. + /// + public void Reset() + { + beatmaps.Reset(); + } + + /// + /// Perform a lookup query on available s. + /// + /// The query. + /// The first result for the provided query, or null if no results were found. + public BeatmapSetInfo QueryBeatmapSet(Func query) + { + BeatmapSetInfo set = beatmaps.Query().FirstOrDefault(query); + + if (set != null) + beatmaps.Populate(set); + + return set; + } + + /// + /// Perform a lookup query on available s. + /// + /// The query. + /// Results from the provided query. + public List QueryBeatmapSets(Expression> query) => beatmaps.QueryAndPopulate(query); + + /// + /// Perform a lookup query on available s. + /// + /// The query. + /// The first result for the provided query, or null if no results were found. + public BeatmapInfo QueryBeatmap(Func query) + { + BeatmapInfo set = beatmaps.Query().FirstOrDefault(query); + + if (set != null) + beatmaps.Populate(set); + + return set; + } + + /// + /// Perform a lookup query on available s. + /// + /// The query. + /// Results from the provided query. + public List QueryBeatmaps(Expression> query) => beatmaps.QueryAndPopulate(query); + + /// + /// Creates an from a valid storage path. + /// + /// A file or folder path resolving the beatmap content. + /// A reader giving access to the beatmap's content. + private ArchiveReader getReaderFrom(string path) + { + if (ZipFile.IsZipFile(path)) + return new OszArchiveReader(storage.GetStream(path)); + else + return new LegacyFilesystemReader(path); + } + + /// + /// Import a beamap into our local storage. + /// If the beatmap is already imported, the existing instance will be returned. + /// + /// The beatmap archive to be read. + /// The imported beatmap, or an existing instance if it is already present. + 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.ComputeSHA2Hash(); + + // check if this beatmap has already been imported and exit early if so. + var beatmapSet = beatmaps.QueryAndPopulate().FirstOrDefault(b => b.Hash == hash); + if (beatmapSet != null) + { + Undelete(beatmapSet); + return beatmapSet; + } + + List fileInfos = new List(); + + // 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(), + 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.ComputeSHA2Hash(); + + // 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); + } + } + + return beatmapSet; + } + + /// + /// Returns a list of all usable s. + /// + /// Whether returned objects should be pre-populated with all data. + /// A list of available . + public List GetAllUsableBeatmapSets(bool populate = true) + { + if (populate) + return beatmaps.QueryAndPopulate(b => !b.DeletePending).ToList(); + else + return beatmaps.Query(b => !b.DeletePending).ToList(); + } + + protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap + { + private readonly IResourceStore store; + + public BeatmapManagerWorkingBeatmap(IResourceStore 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/Database/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs similarity index 94% rename from osu.Game/Database/BeatmapMetadata.cs rename to osu.Game/Beatmaps/BeatmapMetadata.cs index ccf1a99c2c..cc9a51b4e2 100644 --- a/osu.Game/Database/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -5,7 +5,7 @@ using System.Linq; using Newtonsoft.Json; using SQLite.Net.Attributes; -namespace osu.Game.Database +namespace osu.Game.Beatmaps { public class BeatmapMetadata { diff --git a/osu.Game/Database/BeatmapMetrics.cs b/osu.Game/Beatmaps/BeatmapMetrics.cs similarity index 94% rename from osu.Game/Database/BeatmapMetrics.cs rename to osu.Game/Beatmaps/BeatmapMetrics.cs index 25de0f0a8d..730cf635da 100644 --- a/osu.Game/Database/BeatmapMetrics.cs +++ b/osu.Game/Beatmaps/BeatmapMetrics.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using Newtonsoft.Json; -namespace osu.Game.Database +namespace osu.Game.Beatmaps { /// /// Beatmap metrics based on acculumated online data from community plays. diff --git a/osu.Game/Database/BeatmapOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapOnlineInfo.cs similarity index 93% rename from osu.Game/Database/BeatmapOnlineInfo.cs rename to osu.Game/Beatmaps/BeatmapOnlineInfo.cs index f4e70388f5..e8f40a7e07 100644 --- a/osu.Game/Database/BeatmapOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapOnlineInfo.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; -namespace osu.Game.Database +namespace osu.Game.Beatmaps { /// /// Beatmap info retrieved for previewing locally without having the beatmap downloaded. diff --git a/osu.Game/Beatmaps/BeatmapSetFileInfo.cs b/osu.Game/Beatmaps/BeatmapSetFileInfo.cs new file mode 100644 index 0000000000..d18b1e833b --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapSetFileInfo.cs @@ -0,0 +1,17 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.IO; +using SQLiteNetExtensions.Attributes; + +namespace osu.Game.Beatmaps +{ + public class BeatmapSetFileInfo + { + [ForeignKey(typeof(BeatmapSetInfo))] + public int BeatmapSetInfoID { get; set; } + + [ForeignKey(typeof(FileInfo))] + public int FileInfoID { get; set; } + } +} \ No newline at end of file diff --git a/osu.Game/Database/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs similarity index 73% rename from osu.Game/Database/BeatmapSetInfo.cs rename to osu.Game/Beatmaps/BeatmapSetInfo.cs index 017a489d9e..a99ba94e9a 100644 --- a/osu.Game/Database/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -3,10 +3,11 @@ using System.Collections.Generic; using System.Linq; +using osu.Game.IO; using SQLite.Net.Attributes; using SQLiteNetExtensions.Attributes; -namespace osu.Game.Database +namespace osu.Game.Beatmaps { public class BeatmapSetInfo { @@ -34,8 +35,11 @@ namespace osu.Game.Database public string Hash { get; set; } - public string Path { get; set; } + public string StoryboardFile => Files.FirstOrDefault(f => f.Filename.EndsWith(".osb"))?.Filename; - public string StoryboardFile { get; set; } + [ManyToMany(typeof(BeatmapSetFileInfo), CascadeOperations = CascadeOperation.CascadeRead)] + public List Files { get; set; } + + public bool Protected { get; set; } } } diff --git a/osu.Game/Database/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs similarity index 94% rename from osu.Game/Database/BeatmapSetOnlineInfo.cs rename to osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs index 61d408c94a..e5a1984f50 100644 --- a/osu.Game/Database/BeatmapSetOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; -namespace osu.Game.Database +namespace osu.Game.Beatmaps { /// /// Beatmap set info retrieved for previewing locally without having the set downloaded. diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs new file mode 100644 index 0000000000..4a7336535e --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -0,0 +1,116 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// 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 +{ + /// + /// Handles the storage and retrieval of Beatmaps/BeatmapSets to the database backing + /// + public class BeatmapStore : DatabaseBackedStore + { + public event Action BeatmapSetAdded; + public event Action BeatmapSetRemoved; + + public BeatmapStore(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(); + Connection.DropTable(); + Connection.DropTable(); + Connection.DropTable(); + Connection.DropTable(); + } + + Connection.CreateTable(); + Connection.CreateTable(); + Connection.CreateTable(); + Connection.CreateTable(); + Connection.CreateTable(); + + cleanupPendingDeletions(); + } + + /// + /// Add a to the database. + /// + /// The beatmap to add. + public void Add(BeatmapSetInfo beatmapSet) + { + Connection.InsertOrReplaceWithChildren(beatmapSet, true); + BeatmapSetAdded?.Invoke(beatmapSet); + } + + /// + /// Delete a to the database. + /// + /// The beatmap to delete. + /// Whether the beatmap's was changed. + public bool Delete(BeatmapSetInfo beatmapSet) + { + if (beatmapSet.DeletePending) return false; + + beatmapSet.DeletePending = true; + Connection.Update(beatmapSet); + + BeatmapSetRemoved?.Invoke(beatmapSet); + return true; + } + + /// + /// Restore a previously deleted . + /// + /// The beatmap to restore. + /// Whether the beatmap's was changed. + 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(b => b.DeletePending && !b.Protected)) + { + try + { + // many-to-many join table entries are not automatically tidied. + Connection.Table().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("UPDATE BeatmapSetInfo SET DeletePending = 0 WHERE DeletePending IS NULL"); + } + } +} diff --git a/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs b/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs index 11cc6122ac..ad9a0a787b 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, BeatmapManager manager) { BeatmapSet = beatmapSet; - WorkingBeatmap beatmap = database.GetWorkingBeatmap(BeatmapSet.Beatmaps.FirstOrDefault()); + WorkingBeatmap beatmap = manager.GetWorkingBeatmap(BeatmapSet.Beatmaps.FirstOrDefault()); Header = new BeatmapSetHeader(beatmap) { diff --git a/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs b/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs index 8e928391fb..429ecaf416 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs @@ -6,7 +6,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.UserInterface; diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs index 4e41424ae8..df7e0905d0 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs @@ -4,7 +4,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Game.Database; namespace osu.Game.Beatmaps.Drawables { diff --git a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs index e91b52565a..2614baa116 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs @@ -3,7 +3,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; -using osu.Game.Database; using osu.Game.Graphics; using OpenTK.Graphics; diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 14298c4172..f61380bfb8 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -3,7 +3,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Game.Database; using osu.Game.Graphics; using OpenTK; using OpenTK.Graphics; diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 970a3dbd35..0885fb05e5 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; -using osu.Game.Database; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; diff --git a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs index 21a3ab9842..1c3eadc91e 100644 --- a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.IO; using osu.Game.Rulesets.Objects; -using osu.Game.Database; namespace osu.Game.Beatmaps.Formats { @@ -13,6 +12,11 @@ namespace osu.Game.Beatmaps.Formats { private static readonly Dictionary decoders = new Dictionary(); + static BeatmapDecoder() + { + OsuLegacyDecoder.Register(); + } + public static BeatmapDecoder GetDecoder(StreamReader stream) { string line = stream.ReadLine()?.Trim(); diff --git a/osu.Game/Beatmaps/IO/ArchiveReader.cs b/osu.Game/Beatmaps/IO/ArchiveReader.cs index 7ff5668b6f..3af2a7ea2a 100644 --- a/osu.Game/Beatmaps/IO/ArchiveReader.cs +++ b/osu.Game/Beatmaps/IO/ArchiveReader.cs @@ -5,45 +5,11 @@ using System; using System.Collections.Generic; using System.IO; using osu.Framework.IO.Stores; -using osu.Framework.Platform; namespace osu.Game.Beatmaps.IO { public abstract class ArchiveReader : IDisposable, IResourceStore { - private class Reader - { - public Func Test; - public Type Type; - } - - private static readonly List readers = new List(); - - public static ArchiveReader GetReader(Storage storage, string path) - { - foreach (var reader in readers) - { - if (reader.Test(storage, path)) - return (ArchiveReader)Activator.CreateInstance(reader.Type, storage.GetStream(path)); - } - throw new IOException(@"Unknown file format"); - } - - protected static void AddReader(Func test) where T : ArchiveReader - { - readers.Add(new Reader { Test = test, Type = typeof(T) }); - } - - /// - /// Gets a list of beatmap file names. - /// - public string[] BeatmapFilenames { get; protected set; } - - /// - /// The storyboard filename. Null if no storyboard is present. - /// - public string StoryboardFilename { get; protected set; } - /// /// Opens a stream for reading a specific file from this archive. /// @@ -51,6 +17,8 @@ namespace osu.Game.Beatmaps.IO public abstract void Dispose(); + public abstract IEnumerable Filenames { get; } + public virtual byte[] Get(string name) { using (Stream input = GetStream(name)) diff --git a/osu.Game/Beatmaps/IO/LegacyFilesystemReader.cs b/osu.Game/Beatmaps/IO/LegacyFilesystemReader.cs new file mode 100644 index 0000000000..dc38181717 --- /dev/null +++ b/osu.Game/Beatmaps/IO/LegacyFilesystemReader.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace osu.Game.Beatmaps.IO +{ + /// + /// Reads an extracted legacy beatmap from disk. + /// + public class LegacyFilesystemReader : ArchiveReader + { + private readonly string path; + + public LegacyFilesystemReader(string path) + { + this.path = path; + } + + public override Stream GetStream(string name) => File.OpenRead(Path.Combine(path, name)); + + public override void Dispose() + { + // no-op + } + + public override IEnumerable Filenames => Directory.GetFiles(path).Select(Path.GetFileName).ToArray(); + + public override Stream GetUnderlyingStream() => null; + } +} diff --git a/osu.Game/Beatmaps/IO/OszArchiveReader.cs b/osu.Game/Beatmaps/IO/OszArchiveReader.cs index a22a5f4cce..4e0c40d28e 100644 --- a/osu.Game/Beatmaps/IO/OszArchiveReader.cs +++ b/osu.Game/Beatmaps/IO/OszArchiveReader.cs @@ -1,25 +1,15 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Collections.Generic; using System.IO; using System.Linq; using Ionic.Zip; -using osu.Game.Beatmaps.Formats; namespace osu.Game.Beatmaps.IO { public sealed class OszArchiveReader : ArchiveReader { - public static void Register() - { - AddReader((storage, path) => - { - using (var stream = storage.GetStream(path)) - return stream != null && ZipFile.IsZipFile(stream, false); - }); - OsuLegacyDecoder.Register(); - } - private readonly Stream archiveStream; private readonly ZipFile archive; @@ -27,13 +17,6 @@ namespace osu.Game.Beatmaps.IO { this.archiveStream = archiveStream; archive = ZipFile.Read(archiveStream); - - BeatmapFilenames = archive.Entries.Where(e => e.FileName.EndsWith(@".osu")).Select(e => e.FileName).ToArray(); - - if (BeatmapFilenames.Length == 0) - throw new FileNotFoundException(@"This directory contains no beatmaps"); - - StoryboardFilename = archive.Entries.Where(e => e.FileName.EndsWith(@".osb")).Select(e => e.FileName).FirstOrDefault(); } public override Stream GetStream(string name) @@ -41,7 +24,16 @@ namespace osu.Game.Beatmaps.IO ZipEntry entry = archive.Entries.SingleOrDefault(e => e.FileName == name); if (entry == null) throw new FileNotFoundException(); - return entry.OpenReader(); + + // allow seeking + MemoryStream copy = new MemoryStream(); + + using (Stream s = entry.OpenReader()) + s.CopyTo(copy); + + copy.Position = 0; + + return copy; } public override void Dispose() @@ -50,6 +42,8 @@ namespace osu.Game.Beatmaps.IO archiveStream.Dispose(); } + public override IEnumerable Filenames => archive.Entries.Select(e => e.FileName).ToArray(); + public override Stream GetUnderlyingStream() => archiveStream; } } \ No newline at end of file diff --git a/osu.Game/Database/RankStatus.cs b/osu.Game/Beatmaps/RankStatus.cs similarity index 91% rename from osu.Game/Database/RankStatus.cs rename to osu.Game/Beatmaps/RankStatus.cs index f2a7d67a40..17a4a4aa3f 100644 --- a/osu.Game/Database/RankStatus.cs +++ b/osu.Game/Beatmaps/RankStatus.cs @@ -3,7 +3,7 @@ using System.ComponentModel; -namespace osu.Game.Database +namespace osu.Game.Beatmaps { public enum RankStatus { diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 4dd624c14e..a6a3b23d5d 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -4,7 +4,6 @@ using osu.Framework.Audio.Track; using osu.Framework.Configuration; using osu.Framework.Graphics.Textures; -using osu.Game.Database; using osu.Game.Rulesets.Mods; using System; using System.Collections.Generic; diff --git a/osu.Game/Database/BeatmapDatabase.cs b/osu.Game/Database/BeatmapDatabase.cs deleted file mode 100644 index 84a7096da4..0000000000 --- a/osu.Game/Database/BeatmapDatabase.cs +++ /dev/null @@ -1,303 +0,0 @@ -// 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.Screens.Menu; -using SQLite.Net; -using SQLiteNetExtensions.Extensions; - -namespace osu.Game.Database -{ - public class BeatmapDatabase : Database - { - 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 BeatmapDatabase(Storage storage, SQLiteConnection connection, RulesetDatabase rulesets, IIpcHost importHost = null) : base(storage, connection) - { - this.rulesets = rulesets; - if (importHost != null) - ipc = new BeatmapIPCChannel(importHost, this); - } - - private void deletePending() - { - foreach (var b in GetAllWithChildren(b => b.DeletePending)) - { - if (b.Hash == Intro.MENU_MUSIC_BEATMAP_HASH) - // this is a bit hacky, but will do for now. - continue; - - try - { - Storage.Delete(b.Path); - - foreach (var i in b.Beatmaps) - { - if (i.Metadata != null) Connection.Delete(i.Metadata); - if (i.Difficulty != null) Connection.Delete(i.Difficulty); - - Connection.Delete(i); - } - - if (b.Metadata != null) Connection.Delete(b.Metadata); - Connection.Delete(b); - } - 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("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 DatabaseWorkingBeatmap(this, beatmapInfo); - - previous?.TransferTo(working); - - return working; - } - - public bool Exists(BeatmapSetInfo beatmapSet) => Storage.Exists(beatmapSet.Path); - } -} diff --git a/osu.Game/Database/Database.cs b/osu.Game/Database/Database.cs deleted file mode 100644 index a55c0f570b..0000000000 --- a/osu.Game/Database/Database.cs +++ /dev/null @@ -1,80 +0,0 @@ -// 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.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 Database - { - protected SQLiteConnection Connection { get; } - protected Storage Storage { get; } - - protected Database(Storage storage, SQLiteConnection connection) - { - Storage = storage; - Connection = connection; - - try - { - Prepare(); - } - catch (Exception e) - { - Logger.Error(e, $@"Failed to initialise the {GetType()}! Trying again with a clean database..."); - Prepare(true); - } - } - - /// - /// Prepare this database for use. - /// - protected abstract void Prepare(bool reset = false); - - /// - /// Reset this database to a default state. Undo all changes to database and storage backings. - /// - public void Reset() => Prepare(true); - - public TableQuery Query() where T : class - { - return Connection.Table(); - } - - /// - /// This is expensive. Use with caution. - /// - public List GetAllWithChildren(Expression> filter = null, bool recursive = true) - where T : class - { - return Connection.GetAllWithChildren(filter, recursive); - } - - public T GetChildren(T item, bool recursive = false) - { - if (item == null) return default(T); - - Connection.GetChildren(item, recursive); - return item; - } - - protected abstract Type[] ValidTypes { get; } - - public void Update(T record, bool cascade = true) where T : class - { - if (ValidTypes.All(t => t != typeof(T))) - throw new ArgumentException("Must be a type managed by BeatmapDatabase", nameof(T)); - if (cascade) - Connection.UpdateWithChildren(record); - else - Connection.Update(record); - } - } -} \ No newline at end of file diff --git a/osu.Game/Database/DatabaseBackedStore.cs b/osu.Game/Database/DatabaseBackedStore.cs new file mode 100644 index 0000000000..8366775483 --- /dev/null +++ b/osu.Game/Database/DatabaseBackedStore.cs @@ -0,0 +1,92 @@ +// 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.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 DatabaseBackedStore + { + protected readonly Storage Storage; + protected readonly SQLiteConnection Connection; + + protected DatabaseBackedStore(SQLiteConnection connection, Storage storage = null) + { + Storage = storage; + Connection = connection; + + try + { + Prepare(); + } + catch (Exception e) + { + Logger.Error(e, $@"Failed to initialise the {GetType()}! Trying again with a clean database..."); + Prepare(true); + } + } + + /// + /// Prepare this database for use. + /// + protected abstract void Prepare(bool reset = false); + + /// + /// Reset this database to a default state. Undo all changes to database and storage backings. + /// + public void Reset() => Prepare(true); + + + public TableQuery Query(Expression> filter = null) where T : class + { + checkType(typeof(T)); + + var query = Connection.Table(); + + if (filter != null) + query = query.Where(filter); + + return query; + } + + /// + /// Query and populate results. + /// + /// An optional filter to refine results. + /// + public List QueryAndPopulate(Expression> filter = null) + where T : class + { + checkType(typeof(T)); + + return Connection.GetAllWithChildren(filter, true); + } + + /// + /// Populate a database-backed item. + /// + /// + /// Whether population should recurse beyond a single level. + public void Populate(T item, bool recursive = true) + { + checkType(item.GetType()); + + Connection.GetChildren(item, recursive); + } + + 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(DatabaseBackedStore)}."); + } + + protected abstract Type[] ValidTypes { get; } + } +} diff --git a/osu.Game/Database/DatabaseWorkingBeatmap.cs b/osu.Game/Database/DatabaseWorkingBeatmap.cs deleted file mode 100644 index 25944faa42..0000000000 --- a/osu.Game/Database/DatabaseWorkingBeatmap.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 DatabaseWorkingBeatmap : WorkingBeatmap - { - private readonly BeatmapDatabase database; - - public DatabaseWorkingBeatmap(BeatmapDatabase database, BeatmapInfo beatmapInfo) - : base(beatmapInfo) - { - this.database = database; - } - - private ArchiveReader getReader() => database?.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/Graphics/Cursor/GameplayCursor.cs b/osu.Game/Graphics/Cursor/GameplayCursor.cs index e74222f136..9f771ae56d 100644 --- a/osu.Game/Graphics/Cursor/GameplayCursor.cs +++ b/osu.Game/Graphics/Cursor/GameplayCursor.cs @@ -13,7 +13,6 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Configuration; -using osu.Game.Database; namespace osu.Game.Graphics.Cursor { diff --git a/osu.Game/IO/FileInfo.cs b/osu.Game/IO/FileInfo.cs new file mode 100644 index 0000000000..6f4c4b26e8 --- /dev/null +++ b/osu.Game/IO/FileInfo.cs @@ -0,0 +1,24 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; +using SQLite.Net.Attributes; + +namespace osu.Game.IO +{ + public class FileInfo + { + [PrimaryKey, AutoIncrement] + public int ID { get; set; } + + public string Filename { get; set; } + + [Indexed(Unique = true)] + public string Hash { get; set; } + + public string StoragePath => Path.Combine(Hash.Remove(1), Hash.Remove(2), Hash); + + [Indexed] + public int ReferenceCount { get; set; } + } +} diff --git a/osu.Game/IO/FileStore.cs b/osu.Game/IO/FileStore.cs new file mode 100644 index 0000000000..e8cabafe17 --- /dev/null +++ b/osu.Game/IO/FileStore.cs @@ -0,0 +1,114 @@ +// 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.IO.Stores; +using osu.Framework.Logging; +using osu.Framework.Platform; +using osu.Game.Database; +using SQLite.Net; + +namespace osu.Game.IO +{ + /// + /// Handles the Store and retrieval of Files/FileSets to the database backing + /// + public class FileStore : DatabaseBackedStore + { + private const string prefix = "files"; + + public readonly ResourceStore Store; + + public FileStore(SQLiteConnection connection, Storage storage) : base(connection, storage) + { + Store = new NamespacedResourceStore(new StorageBackedResourceStore(storage), prefix); + } + + protected override Type[] ValidTypes => new[] { + typeof(FileInfo), + }; + + protected override void Prepare(bool reset = false) + { + if (reset) + Connection.DropTable(); + + Connection.CreateTable(); + + deletePending(); + } + + public FileInfo Add(Stream data, string filename = null) + { + string hash = data.ComputeSHA2Hash(); + + var info = new FileInfo + { + Filename = filename, + Hash = hash, + }; + + var existing = Connection.Table().FirstOrDefault(f => f.Hash == info.Hash); + + if (existing != null) + { + info = existing; + } + else + { + string path = Path.Combine(prefix, info.StoragePath); + + data.Seek(0, SeekOrigin.Begin); + + if (!Storage.Exists(path)) + using (var output = Storage.GetStream(path, FileAccess.Write)) + data.CopyTo(output); + + data.Seek(0, SeekOrigin.Begin); + + Connection.Insert(info); + } + + Reference(new[] { info }); + return info; + } + + public void Reference(IEnumerable files) + { + foreach (var f in files) + { + f.ReferenceCount++; + Connection.Update(f); + } + } + + public void Dereference(IEnumerable files) + { + foreach (var f in files) + { + f.ReferenceCount--; + Connection.Update(f); + } + } + + private void deletePending() + { + foreach (var f in QueryAndPopulate(f => f.ReferenceCount < 1)) + { + try + { + Connection.Delete(f); + Storage.Delete(Path.Combine(prefix, f.Hash)); + } + catch (Exception e) + { + Logger.Error(e, $@"Could not delete beatmap {f}"); + } + } + } + } +} \ No newline at end of file diff --git a/osu.Game/IPC/BeatmapIPCChannel.cs b/osu.Game/IPC/BeatmapIPCChannel.cs index 61e6cc76cc..6a9019251c 100644 --- a/osu.Game/IPC/BeatmapIPCChannel.cs +++ b/osu.Game/IPC/BeatmapIPCChannel.cs @@ -4,15 +4,15 @@ using System.Diagnostics; using System.Threading.Tasks; using osu.Framework.Platform; -using osu.Game.Database; +using osu.Game.Beatmaps; namespace osu.Game.IPC { public class BeatmapIPCChannel : IpcChannel { - private readonly BeatmapDatabase beatmaps; + private readonly BeatmapManager beatmaps; - public BeatmapIPCChannel(IIpcHost host, BeatmapDatabase 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 7a509ee0e8..ae44250e8d 100644 --- a/osu.Game/IPC/ScoreIPCChannel.cs +++ b/osu.Game/IPC/ScoreIPCChannel.cs @@ -4,15 +4,15 @@ using System.Diagnostics; using System.Threading.Tasks; using osu.Framework.Platform; -using osu.Game.Database; +using osu.Game.Rulesets.Scoring; namespace osu.Game.IPC { public class ScoreIPCChannel : IpcChannel { - 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/GetBeatmapDetailsRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapDetailsRequest.cs index a529dde592..15e20a3d55 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapDetailsRequest.cs +++ b/osu.Game/Online/API/Requests/GetBeatmapDetailsRequest.cs @@ -2,7 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using Newtonsoft.Json; -using osu.Game.Database; +using osu.Game.Beatmaps; namespace osu.Game.Online.API.Requests { diff --git a/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs index f6f9bf69fd..ca984d3511 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs @@ -4,9 +4,10 @@ using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; -using osu.Game.Database; +using osu.Game.Beatmaps; using osu.Game.Overlays; using osu.Game.Overlays.Direct; +using osu.Game.Rulesets; namespace osu.Game.Online.API.Requests { @@ -48,7 +49,7 @@ namespace osu.Game.Online.API.Requests [JsonProperty(@"beatmaps")] private IEnumerable beatmaps { get; set; } - public BeatmapSetInfo ToBeatmapSet(RulesetDatabase rulesets) + public BeatmapSetInfo ToBeatmapSet(RulesetStore rulesets) { return new BeatmapSetInfo { @@ -78,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/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 5e6bf1ea9f..966049429e 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using Newtonsoft.Json; using osu.Framework.IO.Network; -using osu.Game.Database; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Scoring; namespace osu.Game.Online.API.Requests diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs index 26b9dbc88a..e808f2c3ad 100644 --- a/osu.Game/Online/Multiplayer/Room.cs +++ b/osu.Game/Online/Multiplayer/Room.cs @@ -2,7 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Configuration; -using osu.Game.Database; +using osu.Game.Beatmaps; using osu.Game.Users; namespace osu.Game.Online.Multiplayer diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 2cfd1f8214..4f4c2e2883 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -21,10 +21,10 @@ using OpenTK; using System.Linq; using System.Threading.Tasks; using osu.Framework.Threading; -using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Rulesets.Scoring; using osu.Game.Overlays.Notifications; +using osu.Game.Rulesets; using osu.Game.Screens.Play; namespace osu.Game @@ -99,13 +99,13 @@ namespace osu.Game if (args?.Length > 0) { var paths = args.Where(a => !a.StartsWith(@"-")); - Task.Run(() => BeatmapDatabase.Import(paths.ToArray())); + Task.Run(() => BeatmapManager.Import(paths.ToArray())); } dependencies.Cache(this); configRuleset = LocalConfig.GetBindable(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 = BeatmapDatabase.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 27ad4f20f9..b507aa2315 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -11,15 +11,16 @@ using osu.Framework.Graphics.Containers; using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.IO; using osu.Game.Configuration; -using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Processing; using osu.Game.Online.API; using SQLite.Net; using osu.Framework.Graphics.Performance; +using osu.Game.IO; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Scoring; namespace osu.Game { @@ -27,11 +28,13 @@ namespace osu.Game { protected OsuConfigManager LocalConfig; - protected BeatmapDatabase BeatmapDatabase; + protected BeatmapManager BeatmapManager; - protected RulesetDatabase RulesetDatabase; + protected RulesetStore RulesetStore; - protected ScoreDatabase ScoreDatabase; + protected FileStore FileStore; + + protected ScoreStore ScoreStore; protected override string MainResourceFile => @"osu.Game.Resources.dll"; @@ -94,9 +97,10 @@ 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(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. @@ -128,9 +132,7 @@ namespace osu.Game var defaultBeatmap = new DummyWorkingBeatmap(this); Beatmap = new NonNullableBindable(defaultBeatmap); - BeatmapDatabase.DefaultBeatmap = defaultBeatmap; - - OszArchiveReader.Register(); + BeatmapManager.DefaultBeatmap = defaultBeatmap; dependencies.Cache(API = new APIAccess { diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs index b7631d5794..98ab5e88f8 100644 --- a/osu.Game/Overlays/Direct/DirectGridPanel.cs +++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs @@ -8,10 +8,10 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; -using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; namespace osu.Game.Overlays.Direct { diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs index 0a445e49d4..b9063a5c82 100644 --- a/osu.Game/Overlays/Direct/DirectListPanel.cs +++ b/osu.Game/Overlays/Direct/DirectListPanel.cs @@ -9,11 +9,11 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Colour; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Database; using osu.Framework.Allocation; using osu.Framework.Localisation; using osu.Framework.Input; using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Direct diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 09b634de81..75619d9ba4 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -6,8 +6,8 @@ using OpenTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; -using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs index e947895fc2..4f815f220c 100644 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ b/osu.Game/Overlays/Direct/FilterControl.cs @@ -7,10 +7,11 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Database; +using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Overlays.SearchableList; +using osu.Game.Rulesets; namespace osu.Game.Overlays.Direct { @@ -33,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 f10d542830..b1c7dab778 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -9,13 +9,14 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Threading; -using osu.Game.Database; +using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Direct; using osu.Game.Overlays.SearchableList; +using osu.Game.Rulesets; using OpenTK.Graphics; namespace osu.Game.Overlays @@ -25,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; @@ -160,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 703deea382..eb643f390f 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -15,8 +15,8 @@ using osu.Game.Rulesets.Mods; using System; using System.Collections.Generic; using System.Linq; -using osu.Game.Database; using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets; namespace osu.Game.Overlays.Mods { @@ -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/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index 128dddbce5..1e3e48b17a 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; -using osu.Game.Database; +using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs index cd28737adb..88f499f9a6 100644 --- a/osu.Game/Overlays/Music/PlaylistList.cs +++ b/osu.Game/Overlays/Music/PlaylistList.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Database; +using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Music diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index bd822f8145..31fe755d2b 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -9,7 +9,6 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; -using osu.Game.Database; using osu.Game.Graphics; using OpenTK; using OpenTK.Graphics; @@ -27,7 +26,7 @@ namespace osu.Game.Overlays.Music private FilterControl filter; private PlaylistList list; - private BeatmapDatabase beatmaps; + private BeatmapManager beatmaps; private readonly Bindable beatmapBacking = new Bindable(); @@ -35,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, BeatmapManager beatmaps, OsuColour colours, UserInputManager inputManager) { this.inputManager = inputManager; this.beatmaps = beatmaps; @@ -78,8 +77,9 @@ namespace osu.Game.Overlays.Music }, }; - list.BeatmapSets = BeatmapSets = beatmaps.GetAllWithChildren(b => !b.DeletePending).ToList(); + list.BeatmapSets = BeatmapSets = beatmaps.GetAllUsableBeatmapSets(); + // todo: these should probably be above the query. beatmaps.BeatmapSetAdded += s => list.AddBeatmapSet(s); beatmaps.BeatmapSetRemoved += s => list.RemoveBeatmapSet(s); diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index b435c505c9..2807a02543 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -18,7 +18,6 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; using osu.Framework.Localisation; using osu.Game.Beatmaps; -using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Framework.Threading; diff --git a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs index c20519a9b5..326cb582e2 100644 --- a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs +++ b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs @@ -3,7 +3,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Overlays.Settings.Sections.Gameplay; using osu.Game.Rulesets; @@ -26,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 be08e61c1a..6c25b146a1 100644 --- a/osu.Game/Overlays/Settings/SettingsFooter.cs +++ b/osu.Game/Overlays/Settings/SettingsFooter.cs @@ -5,9 +5,9 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets; using OpenTK; using OpenTK.Graphics; @@ -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/ToolbarModeButton.cs b/osu.Game/Overlays/Toolbar/ToolbarModeButton.cs index 6a11f68572..2c50897e1f 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarModeButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarModeButton.cs @@ -2,7 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics.Containers; -using osu.Game.Database; +using osu.Game.Rulesets; using OpenTK.Graphics; namespace osu.Game.Overlays.Toolbar diff --git a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs index f48cb681a8..60c1261190 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs @@ -6,11 +6,11 @@ using osu.Framework.Allocation; using osu.Framework.Caching; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Database; using OpenTK; using OpenTK.Graphics; using osu.Framework.Configuration; using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets; namespace osu.Game.Overlays.Toolbar { @@ -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/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index b282965db8..c343cdaf33 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -2,8 +2,8 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Audio; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Database; using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Objects diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index 8c2aead5ff..ffe40e4f2e 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -6,8 +6,8 @@ using System; using System.Collections.Generic; using OpenTK; using osu.Game.Audio; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Database; namespace osu.Game.Rulesets.Objects.Legacy { diff --git a/osu.Game/Database/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs similarity index 89% rename from osu.Game/Database/RulesetInfo.cs rename to osu.Game/Rulesets/RulesetInfo.cs index 3990f9e8ae..84ecb7718e 100644 --- a/osu.Game/Database/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/Database/RulesetDatabase.cs b/osu.Game/Rulesets/RulesetStore.cs similarity index 87% rename from osu.Game/Database/RulesetDatabase.cs rename to osu.Game/Rulesets/RulesetStore.cs index b78ca5ffc6..88aee2bffc 100644 --- a/osu.Game/Database/RulesetDatabase.cs +++ b/osu.Game/Rulesets/RulesetStore.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. + /// Todo: All of this needs to be moved to a RulesetStore. /// - public class RulesetDatabase : Database + public class RulesetStore : DatabaseBackedStore { public IEnumerable AllRulesets => Query().Where(r => r.Available); - public RulesetDatabase(Storage storage, SQLiteConnection connection) - : base(storage, connection) + public RulesetStore(SQLiteConnection connection) : base(connection) { } diff --git a/osu.Game/Rulesets/Scoring/Score.cs b/osu.Game/Rulesets/Scoring/Score.cs index 2cca0f54af..6169bb7380 100644 --- a/osu.Game/Rulesets/Scoring/Score.cs +++ b/osu.Game/Rulesets/Scoring/Score.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using Newtonsoft.Json; -using osu.Game.Database; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; using osu.Game.Users; using osu.Game.Rulesets.Replays; diff --git a/osu.Game/Database/ScoreDatabase.cs b/osu.Game/Rulesets/Scoring/ScoreStore.cs similarity index 87% rename from osu.Game/Database/ScoreDatabase.cs rename to osu.Game/Rulesets/Scoring/ScoreStore.cs index adca76d2ac..e69fec4b54 100644 --- a/osu.Game/Database/ScoreDatabase.cs +++ b/osu.Game/Rulesets/Scoring/ScoreStore.cs @@ -4,30 +4,30 @@ using System; 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 : Database + public class ScoreStore : DatabaseBackedStore { private readonly Storage storage; - private readonly BeatmapDatabase 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, BeatmapDatabase beatmaps = null, RulesetDatabase rulesets = null) : base(storage, connection) + public ScoreStore(Storage storage, SQLiteConnection connection, IIpcHost importHost = null, BeatmapManager beatmaps = null, RulesetStore rulesets = null) : base(connection) { this.storage = storage; this.beatmaps = beatmaps; @@ -53,7 +53,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.QueryBeatmap(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 7b16e7e7ad..a0cf6eec66 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -9,9 +9,9 @@ using osu.Framework.Configuration; using osu.Framework.Screens; using osu.Framework.Graphics; using osu.Framework.MathUtils; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.IO; using osu.Game.Configuration; -using osu.Game.Database; using osu.Game.Graphics.Containers; using osu.Game.Screens.Backgrounds; using OpenTK.Graphics; @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Menu { private readonly OsuLogo logo; - public const string MENU_MUSIC_BEATMAP_HASH = "21c1271b91234385978b5418881fdd88"; + private const string menu_music_beatmap_hash = "715a09144f885d746644c1983e285044"; /// /// Whether we have loaded the menu previously. @@ -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, BeatmapManager beatmaps, Framework.Game game) { menuVoice = config.GetBindable(OsuSetting.MenuVoice); menuMusic = config.GetBindable(OsuSetting.MenuMusic); @@ -76,31 +76,25 @@ namespace osu.Game.Screens.Menu if (!menuMusic) { - var query = beatmaps.Query().Where(b => !b.DeletePending); - int count = query.Count(); - if (count > 0) - setInfo = query.ElementAt(RNG.Next(0, count - 1)); + var sets = beatmaps.GetAllUsableBeatmapSets(false); + if (sets.Count > 0) + setInfo = beatmaps.QueryBeatmapSet(s => s.ID == sets[RNG.Next(0, sets.Count - 1)].ID); } if (setInfo == null) { - var query = beatmaps.Query().Where(b => b.Hash == MENU_MUSIC_BEATMAP_HASH); - - setInfo = query.FirstOrDefault(); + setInfo = beatmaps.QueryBeatmapSet(b => b.Hash == menu_music_beatmap_hash); if (setInfo == null) { // we need to import the default menu background beatmap - beatmaps.Import(new OszArchiveReader(game.Resources.GetStream(@"Tracks/circles.osz"))); + setInfo = beatmaps.Import(new OszArchiveReader(game.Resources.GetStream(@"Tracks/circles.osz"))); - setInfo = query.First(); - - setInfo.DeletePending = true; - beatmaps.Update(setInfo, false); + setInfo.Protected = true; + beatmaps.Delete(setInfo); } } - beatmaps.GetChildren(setInfo); Beatmap.Value = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); track = Beatmap.Value.Track; diff --git a/osu.Game/Screens/Multiplayer/DrawableRoom.cs b/osu.Game/Screens/Multiplayer/DrawableRoom.cs index 8e2d9e0200..d2f88224c2 100644 --- a/osu.Game/Screens/Multiplayer/DrawableRoom.cs +++ b/osu.Game/Screens/Multiplayer/DrawableRoom.cs @@ -10,8 +10,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; -using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; diff --git a/osu.Game/Screens/Multiplayer/ModeTypeInfo.cs b/osu.Game/Screens/Multiplayer/ModeTypeInfo.cs index fff40aeed5..40d385418a 100644 --- a/osu.Game/Screens/Multiplayer/ModeTypeInfo.cs +++ b/osu.Game/Screens/Multiplayer/ModeTypeInfo.cs @@ -4,8 +4,8 @@ using OpenTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; -using osu.Game.Database; using osu.Game.Online.Multiplayer; namespace osu.Game.Screens.Multiplayer diff --git a/osu.Game/Screens/Multiplayer/RoomInspector.cs b/osu.Game/Screens/Multiplayer/RoomInspector.cs index 18fad53b51..66ce51b428 100644 --- a/osu.Game/Screens/Multiplayer/RoomInspector.cs +++ b/osu.Game/Screens/Multiplayer/RoomInspector.cs @@ -13,8 +13,8 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; -using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index d916614abd..d215385ece 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -6,11 +6,11 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Screens; using osu.Game.Beatmaps; -using osu.Game.Database; using osu.Game.Graphics.Containers; using OpenTK; using osu.Framework.Audio.Sample; using osu.Framework.Audio; +using osu.Game.Rulesets; namespace osu.Game.Screens { diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index cda4713575..84261d509e 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -13,7 +13,6 @@ using osu.Framework.Logging; using osu.Framework.Screens; using osu.Framework.Timing; using osu.Game.Configuration; -using osu.Game.Database; using osu.Game.Rulesets; using osu.Game.Rulesets.UI; using osu.Game.Screens.Backgrounds; diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index d9bdb50ef0..1cac1bd2ad 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Screens; using osu.Game.Beatmaps; -using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Screens.Backgrounds; diff --git a/osu.Game/Screens/Ranking/ResultsPageScore.cs b/osu.Game/Screens/Ranking/ResultsPageScore.cs index 00cc031ec6..bf406ff912 100644 --- a/osu.Game/Screens/Ranking/ResultsPageScore.cs +++ b/osu.Game/Screens/Ranking/ResultsPageScore.cs @@ -15,7 +15,6 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Localisation; using osu.Game.Beatmaps; -using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 1940cdef6e..743a0a0f63 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -4,7 +4,6 @@ using OpenTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Database; using System; using System.Collections.Generic; using System.Linq; @@ -18,6 +17,7 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Threading; using osu.Framework.Configuration; +using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; namespace osu.Game.Screens.Select @@ -68,7 +68,7 @@ namespace osu.Game.Screens.Select /// /// Required for now unfortunately. /// - private BeatmapDatabase database; + private BeatmapManager manager; 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, 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(BeatmapDatabase database, OsuConfigManager config) + private void load(BeatmapManager manager, OsuConfigManager config) { - this.database = database; + this.manager = manager; randomType = config.GetBindable(OsuSetting.SelectionRandomType); } diff --git a/osu.Game/Screens/Select/BeatmapDeleteDialog.cs b/osu.Game/Screens/Select/BeatmapDeleteDialog.cs index 0890625eb9..96caf2f236 100644 --- a/osu.Game/Screens/Select/BeatmapDeleteDialog.cs +++ b/osu.Game/Screens/Select/BeatmapDeleteDialog.cs @@ -4,7 +4,6 @@ using System; using osu.Framework.Allocation; using osu.Game.Beatmaps; -using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Overlays.Dialog; @@ -12,12 +11,12 @@ namespace osu.Game.Screens.Select { public class BeatmapDeleteDialog : PopupDialog { - private BeatmapDatabase database; + private BeatmapManager manager; [BackgroundDependencyLoader] - private void load(BeatmapDatabase beatmapDatabase) + private void load(BeatmapManager beatmapManager) { - database = beatmapDatabase; + manager = beatmapManager; } public BeatmapDeleteDialog(WorkingBeatmap beatmap) @@ -35,7 +34,7 @@ namespace osu.Game.Screens.Select Action = () => { beatmap.Dispose(); - database.Delete(beatmap.BeatmapSetInfo); + manager.Delete(beatmap.BeatmapSetInfo); }, }, new PopupDialogCancelButton diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index dd5ba7b8ae..a5486efa96 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -6,7 +6,6 @@ using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -16,6 +15,7 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Framework.Threading; using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; namespace osu.Game.Screens.Select { diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 8aa2796189..385492980f 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -14,7 +14,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.MathUtils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; -using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 35fee9e32d..c406e7c44d 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -14,8 +14,8 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Select.Filter; using Container = osu.Framework.Graphics.Containers.Container; using osu.Framework.Input; -using osu.Game.Database; using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets; namespace osu.Game.Screens.Select { diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index 3634e739cf..a1fea4a41d 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps.Drawables; -using osu.Game.Database; +using osu.Game.Rulesets; using osu.Game.Screens.Select.Filter; namespace osu.Game.Screens.Select diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 57c333138f..5a375e55d4 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -11,7 +11,7 @@ using osu.Framework.Graphics.Containers; using System; using osu.Framework.Allocation; using osu.Framework.Threading; -using osu.Game.Database; +using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Scoring; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index b2311d6561..bbd292870e 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -16,10 +16,10 @@ using osu.Framework.Input; using osu.Framework.Screens; using osu.Framework.Threading; using osu.Game.Beatmaps; -using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Overlays; +using osu.Game.Rulesets; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Select.Options; @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Select public abstract class SongSelect : OsuScreen { private readonly Bindable ruleset = new Bindable(); - private BeatmapDatabase database; + 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(BeatmapDatabase 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 (database == null) - database = beatmaps; + if (manager == null) + manager = beatmaps; if (osu != null) ruleset.BindTo(osu.Ruleset); - database.BeatmapSetAdded += onBeatmapSetAdded; - database.BeatmapSetRemoved += onBeatmapSetRemoved; + manager.BeatmapSetAdded += onBeatmapSetAdded; + manager.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 = 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 = database.GetWorkingBeatmap(beatmap, Beatmap); + Beatmap.Value = manager.GetWorkingBeatmap(beatmap, Beatmap); ensurePlayingSelected(preview); } @@ -341,10 +341,10 @@ namespace osu.Game.Screens.Select { base.Dispose(isDisposing); - if (database != null) + if (manager != null) { - database.BeatmapSetAdded -= onBeatmapSetAdded; - database.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 6031304b26..f8509314be 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -74,9 +74,12 @@ + + + @@ -88,6 +91,8 @@ + + @@ -125,10 +130,10 @@ - - - - + + + + @@ -215,7 +220,7 @@ - + @@ -384,17 +389,16 @@ - + - - - - - + + + + @@ -488,10 +492,10 @@ - + - - + + diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index f6a6e4bf9b..e3eae96ca8 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -189,7 +189,9 @@ IP IPC LTRB + MD5 RNG + SHA SRGB TK SS