From 449f04c07b16ed6584fdb74e40b990ded0953d18 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 18 Oct 2016 13:35:01 -0400 Subject: [PATCH 1/6] Refactor the database code --- .vscode/launch.json | 2 +- osu-framework | 2 +- .../Beatmaps/IO/LegacyFilesystemReader.cs | 9 +- .../Beatmaps/Formats/OsuLegacyDecoderTest.cs | 38 ++++---- osu.Game/Beatmaps/Beatmap.cs | 54 +---------- osu.Game/Beatmaps/BeatmapSet.cs | 13 +-- osu.Game/Beatmaps/Formats/BeatmapDecoder.cs | 5 +- osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs | 90 ++++++++++--------- osu.Game/Beatmaps/IO/ArchiveReader.cs | 1 + osu.Game/Beatmaps/IO/OszArchiveReader.cs | 9 +- .../{Beatmaps => Database}/BaseDifficulty.cs | 4 +- osu.Game/Database/BeatmapDatabase.cs | 54 +++++------ osu.Game/Database/BeatmapInfo.cs | 63 +++++++++++++ .../{Beatmaps => Database}/BeatmapMetadata.cs | 4 +- osu.Game/Database/BeatmapSetInfo.cs | 19 ++++ osu.Game/osu.Game.csproj | 30 ++++--- osu.Game/packages.config | 9 +- 17 files changed, 208 insertions(+), 198 deletions(-) rename osu.Game/{Beatmaps => Database}/BaseDifficulty.cs (85%) create mode 100644 osu.Game/Database/BeatmapInfo.cs rename osu.Game/{Beatmaps => Database}/BeatmapMetadata.cs (90%) create mode 100644 osu.Game/Database/BeatmapSetInfo.cs diff --git a/.vscode/launch.json b/.vscode/launch.json index f1682a2ce2..d9a6ae12ff 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,7 +5,7 @@ "name": "Launch", "type": "mono", "request": "launch", - "program": "${workspaceRoot}/osu.Desktop.VisualTests/bin/Debug/osu!.exe", + "program": "${workspaceRoot}/osu.Desktop/bin/Debug/osu!.exe", "args": [], "cwd": "${workspaceRoot}", "preLaunchTask": "", diff --git a/osu-framework b/osu-framework index cda05b3a64..f7be41642b 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit cda05b3a64162eb9d8e591ffe7f79d959c39eebc +Subproject commit f7be41642b23e5b262505896d834e5c813a1aac3 diff --git a/osu.Desktop/Beatmaps/IO/LegacyFilesystemReader.cs b/osu.Desktop/Beatmaps/IO/LegacyFilesystemReader.cs index 8282d8556a..c57498f5dd 100644 --- a/osu.Desktop/Beatmaps/IO/LegacyFilesystemReader.cs +++ b/osu.Desktop/Beatmaps/IO/LegacyFilesystemReader.cs @@ -5,6 +5,7 @@ using System.Linq; using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.IO; using osu.Game.Beatmaps; +using osu.Game.Database; namespace osu.Desktop.Beatmaps.IO { @@ -31,8 +32,7 @@ namespace osu.Desktop.Beatmaps.IO using (var stream = new StreamReader(ReadFile(beatmaps[0]))) { var decoder = BeatmapDecoder.GetDecoder(stream); - firstMap = new Beatmap(); - decoder.Decode(stream, firstMap); + firstMap = decoder.Decode(stream); } } @@ -48,11 +48,12 @@ namespace osu.Desktop.Beatmaps.IO public override BeatmapMetadata ReadMetadata() { - return firstMap.Metadata; + return firstMap.BeatmapInfo.Metadata; } public override void Dispose() { // no-op - } } + } + } } \ No newline at end of file diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs index adb203b483..d48019c446 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs @@ -26,9 +26,8 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new OsuLegacyDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { - Beatmap beatmap = new Beatmap(); - decoder.Decode(new StreamReader(stream), beatmap); - var meta = beatmap.Metadata; + var beatmap = decoder.Decode(new StreamReader(stream)); + var meta = beatmap.BeatmapInfo.Metadata; Assert.AreEqual(241526, meta.BeatmapSetID); Assert.AreEqual("Soleily", meta.Artist); Assert.AreEqual("Soleily", meta.ArtistUnicode); @@ -49,16 +48,15 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new OsuLegacyDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { - Beatmap beatmap = new Beatmap(); - decoder.Decode(new StreamReader(stream), beatmap); - Assert.AreEqual(0, beatmap.AudioLeadIn); - Assert.AreEqual(false, beatmap.Countdown); - Assert.AreEqual(SampleSet.Soft, beatmap.SampleSet); - Assert.AreEqual(0.7f, beatmap.StackLeniency); - Assert.AreEqual(false, beatmap.SpecialStyle); - Assert.AreEqual(PlayMode.Osu, beatmap.Mode); - Assert.AreEqual(false, beatmap.LetterboxInBreaks); - Assert.AreEqual(false, beatmap.WidescreenStoryboard); + var beatmapInfo = decoder.Decode(new StreamReader(stream)).BeatmapInfo; + Assert.AreEqual(0, beatmapInfo.AudioLeadIn); + Assert.AreEqual(false, beatmapInfo.Countdown); + Assert.AreEqual(SampleSet.Soft, beatmapInfo.SampleSet); + Assert.AreEqual(0.7f, beatmapInfo.StackLeniency); + Assert.AreEqual(false, beatmapInfo.SpecialStyle); + Assert.AreEqual(PlayMode.Osu, beatmapInfo.Mode); + Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks); + Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard); } } @@ -68,8 +66,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new OsuLegacyDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { - Beatmap beatmap = new Beatmap(); - decoder.Decode(new StreamReader(stream), beatmap); + var beatmap = decoder.Decode(new StreamReader(stream)).BeatmapInfo; int[] expectedBookmarks = { 11505, 22054, 32604, 43153, 53703, 64252, 74802, 85351, @@ -92,9 +89,8 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new OsuLegacyDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { - Beatmap beatmap = new Beatmap(); - decoder.Decode(new StreamReader(stream), beatmap); - var difficulty = beatmap.BaseDifficulty; + var beatmap = decoder.Decode(new StreamReader(stream)); + var difficulty = beatmap.BeatmapInfo.BaseDifficulty; Assert.AreEqual(6.5f, difficulty.DrainRate); Assert.AreEqual(4, difficulty.CircleSize); Assert.AreEqual(8, difficulty.OverallDifficulty); @@ -110,8 +106,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new OsuLegacyDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { - Beatmap beatmap = new Beatmap(); - decoder.Decode(new StreamReader(stream), beatmap); + var beatmap = decoder.Decode(new StreamReader(stream)); Color4[] expected = { new Color4(142, 199, 255, 255), @@ -132,8 +127,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new OsuLegacyDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { - Beatmap beatmap = new Beatmap(); - decoder.Decode(new StreamReader(stream), beatmap); + var beatmap = decoder.Decode(new StreamReader(stream)); var slider = beatmap.HitObjects[0] as Slider; Assert.IsNotNull(slider); Assert.AreEqual(new Vector2(192, 168), slider.Position); diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 99199e421c..9b6028cac5 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -2,68 +2,18 @@ //Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; -using System.Linq; using OpenTK.Graphics; using osu.Game.Beatmaps.Objects; -using osu.Game.Beatmaps.Samples; using osu.Game.Beatmaps.Timing; -using osu.Game.GameModes.Play; -using osu.Game.Users; -using SQLite; +using osu.Game.Database; namespace osu.Game.Beatmaps { public class Beatmap { - [PrimaryKey] - public int BeatmapID { get; set; } - [NotNull, Indexed] - public int BeatmapSetID { get; set; } - [Indexed] - public int BeatmapMetadataID { get; set; } - public int BaseDifficultyID { get; set; } - [Ignore] + public BeatmapInfo BeatmapInfo { get; set; } public List HitObjects { get; set; } - [Ignore] public List ControlPoints { get; set; } - [Ignore] - public BeatmapMetadata Metadata { get; set; } - [Ignore] - public BaseDifficulty BaseDifficulty { get; set; } - [Ignore] public List ComboColors { get; set; } - - // General - public int AudioLeadIn { get; set; } - public bool Countdown { get; set; } - public SampleSet SampleSet { get; set; } - public float StackLeniency { get; set; } - public bool SpecialStyle { get; set; } - public PlayMode Mode { get; set; } - public bool LetterboxInBreaks { get; set; } - public bool WidescreenStoryboard { get; set; } - - // Editor - // This bookmarks stuff is necessary because DB doesn't know how to store int[] - public string StoredBookmarks { get; internal set; } - [Ignore] - public int[] Bookmarks - { - get - { - return StoredBookmarks.Split(',').Select(b => int.Parse(b)).ToArray(); - } - set - { - StoredBookmarks = string.Join(",", value); - } - } - public double DistanceSpacing { get; set; } - public int BeatDivisor { get; set; } - public int GridSize { get; set; } - public double TimelineZoom { get; set; } - - // Metadata - public string Version { get; set; } } } \ No newline at end of file diff --git a/osu.Game/Beatmaps/BeatmapSet.cs b/osu.Game/Beatmaps/BeatmapSet.cs index 11f82329f9..55c888384f 100644 --- a/osu.Game/Beatmaps/BeatmapSet.cs +++ b/osu.Game/Beatmaps/BeatmapSet.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.Database; using osu.Game.Users; -using SQLite; namespace osu.Game.Beatmaps { @@ -12,17 +12,8 @@ namespace osu.Game.Beatmaps /// public class BeatmapSet { - [PrimaryKey] - public int BeatmapSetID { get; set; } - [NotNull, Indexed] - public int BeatmapMetadataID { get; set; } - [Ignore] + public BeatmapSetInfo BeatmapSetInfo { get; set; } public List Beatmaps { get; protected set; } = new List(); - [Ignore] - public BeatmapMetadata Metadata { get; set; } - [Ignore] public User Creator { get; set; } - public string Hash { get; set; } - public string Path { get; set; } } } diff --git a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs index 0c6bbb02e1..92f2452484 100644 --- a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs @@ -15,11 +15,12 @@ namespace osu.Game.Beatmaps.Formats throw new IOException(@"Unknown file format"); return (BeatmapDecoder)Activator.CreateInstance(decoders[line]); } - protected static void AddDecoder(string magic) where T : BeatmapDecoder + + protected static void AddDecoder(string magic) where T : BeatmapDecoder { decoders[magic] = typeof(T); } - public abstract void Decode(TextReader stream, Beatmap beatmap); + public abstract Beatmap Decode(TextReader stream); } } \ No newline at end of file diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs index 4250e9a7c2..9cbddc853f 100644 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using OpenTK.Graphics; +using osu.Game.Database; using osu.Game.Beatmaps.Events; using osu.Game.Beatmaps.Objects; using osu.Game.Beatmaps.Samples; @@ -38,37 +39,38 @@ namespace osu.Game.Beatmaps.Formats private void handleGeneral(Beatmap beatmap, string key, string val) { + var metadata = beatmap.BeatmapInfo.Metadata; switch (key) { case @"AudioFilename": - beatmap.Metadata.AudioFile = val; + metadata.AudioFile = val; break; case @"AudioLeadIn": - beatmap.AudioLeadIn = int.Parse(val); + beatmap.BeatmapInfo.AudioLeadIn = int.Parse(val); break; case @"PreviewTime": - beatmap.Metadata.PreviewTime = int.Parse(val); + metadata.PreviewTime = int.Parse(val); break; case @"Countdown": - beatmap.Countdown = int.Parse(val) == 1; + beatmap.BeatmapInfo.Countdown = int.Parse(val) == 1; break; case @"SampleSet": - beatmap.SampleSet = (SampleSet)Enum.Parse(typeof(SampleSet), val); + beatmap.BeatmapInfo.SampleSet = (SampleSet)Enum.Parse(typeof(SampleSet), val); break; case @"StackLeniency": - beatmap.StackLeniency = float.Parse(val, NumberFormatInfo.InvariantInfo); + beatmap.BeatmapInfo.StackLeniency = float.Parse(val, NumberFormatInfo.InvariantInfo); break; case @"Mode": - beatmap.Mode = (PlayMode)int.Parse(val); + beatmap.BeatmapInfo.Mode = (PlayMode)int.Parse(val); break; case @"LetterboxInBreaks": - beatmap.LetterboxInBreaks = int.Parse(val) == 1; + beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(val) == 1; break; case @"SpecialStyle": - beatmap.SpecialStyle = int.Parse(val) == 1; + beatmap.BeatmapInfo.SpecialStyle = int.Parse(val) == 1; break; case @"WidescreenStoryboard": - beatmap.WidescreenStoryboard = int.Parse(val) == 1; + beatmap.BeatmapInfo.WidescreenStoryboard = int.Parse(val) == 1; break; } } @@ -78,82 +80,84 @@ namespace osu.Game.Beatmaps.Formats switch (key) { case @"Bookmarks": - beatmap.StoredBookmarks = val; + beatmap.BeatmapInfo.StoredBookmarks = val; break; case @"DistanceSpacing": - beatmap.DistanceSpacing = double.Parse(val, NumberFormatInfo.InvariantInfo); + beatmap.BeatmapInfo.DistanceSpacing = double.Parse(val, NumberFormatInfo.InvariantInfo); break; case @"BeatDivisor": - beatmap.BeatDivisor = int.Parse(val); + beatmap.BeatmapInfo.BeatDivisor = int.Parse(val); break; case @"GridSize": - beatmap.GridSize = int.Parse(val); + beatmap.BeatmapInfo.GridSize = int.Parse(val); break; case @"TimelineZoom": - beatmap.TimelineZoom = double.Parse(val, NumberFormatInfo.InvariantInfo); + beatmap.BeatmapInfo.TimelineZoom = double.Parse(val, NumberFormatInfo.InvariantInfo); break; } } private void handleMetadata(Beatmap beatmap, string key, string val) { + var metadata = beatmap.BeatmapInfo.Metadata; switch (key) { case @"Title": - beatmap.Metadata.Title = val; + metadata.Title = val; break; case @"TitleUnicode": - beatmap.Metadata.TitleUnicode = val; + metadata.TitleUnicode = val; break; case @"Artist": - beatmap.Metadata.Artist = val; + metadata.Artist = val; break; case @"ArtistUnicode": - beatmap.Metadata.ArtistUnicode = val; + metadata.ArtistUnicode = val; break; case @"Creator": - beatmap.Metadata.Author = val; + metadata.Author = val; break; case @"Version": - beatmap.Version = val; + beatmap.BeatmapInfo.Version = val; break; case @"Source": - beatmap.Metadata.Source = val; + beatmap.BeatmapInfo.Metadata.Source = val; break; case @"Tags": - beatmap.Metadata.Tags = val; + beatmap.BeatmapInfo.Metadata.Tags = val; break; case @"BeatmapID": - beatmap.BeatmapID = int.Parse(val); + beatmap.BeatmapInfo.BeatmapID = int.Parse(val); break; case @"BeatmapSetID": - beatmap.BeatmapSetID = int.Parse(val); - beatmap.Metadata.BeatmapSetID = int.Parse(val); + beatmap.BeatmapInfo.BeatmapSetID = int.Parse(val); + metadata.BeatmapSetID = int.Parse(val); break; } } private void handleDifficulty(Beatmap beatmap, string key, string val) { + var difficulty = beatmap.BeatmapInfo.BaseDifficulty; switch (key) { case @"HPDrainRate": - beatmap.BaseDifficulty.DrainRate = float.Parse(val, NumberFormatInfo.InvariantInfo); + difficulty.DrainRate = float.Parse(val, NumberFormatInfo.InvariantInfo); break; case @"CircleSize": - beatmap.BaseDifficulty.CircleSize = float.Parse(val, NumberFormatInfo.InvariantInfo); + difficulty.CircleSize = float.Parse(val, NumberFormatInfo.InvariantInfo); break; case @"OverallDifficulty": - beatmap.BaseDifficulty.OverallDifficulty = float.Parse(val, NumberFormatInfo.InvariantInfo); + difficulty.OverallDifficulty = float.Parse(val, NumberFormatInfo.InvariantInfo); break; case @"ApproachRate": - beatmap.BaseDifficulty.ApproachRate = float.Parse(val, NumberFormatInfo.InvariantInfo); + difficulty.ApproachRate = float.Parse(val, NumberFormatInfo.InvariantInfo); break; case @"SliderMultiplier": - beatmap.BaseDifficulty.SliderMultiplier = float.Parse(val, NumberFormatInfo.InvariantInfo); + difficulty.SliderMultiplier = float.Parse(val, NumberFormatInfo.InvariantInfo); break; case @"SliderTickRate": - beatmap.BaseDifficulty.SliderTickRate = float.Parse(val, NumberFormatInfo.InvariantInfo); + difficulty.SliderTickRate = float.Parse(val, NumberFormatInfo.InvariantInfo); break; } } @@ -176,7 +180,7 @@ namespace osu.Game.Beatmaps.Formats type = (EventType)_type; // TODO: Parse and store the rest of the event if (type == EventType.Background) - beatmap.Metadata.BackgroundFile = split[2].Trim('"'); + beatmap.BeatmapInfo.Metadata.BackgroundFile = split[2].Trim('"'); } private void handleTimingPoints(Beatmap beatmap, string val) @@ -202,15 +206,15 @@ namespace osu.Game.Beatmaps.Formats }); } - public override void Decode(TextReader stream, Beatmap beatmap) + public override Beatmap Decode(TextReader stream) { - // We don't overwrite these two because they're DB bound - if (beatmap.Metadata == null) beatmap.Metadata = new BeatmapMetadata(); - if (beatmap.BaseDifficulty == null) beatmap.BaseDifficulty = new BaseDifficulty(); - // These are fine though - beatmap.HitObjects = new List(); - beatmap.ControlPoints = new List(); - beatmap.ComboColors = new List(); + var beatmap = new Beatmap + { + HitObjects = new List(), + ControlPoints = new List(), + ComboColors = new List(), + BeatmapInfo = new BeatmapInfo(), + }; var section = Section.None; string line; @@ -262,10 +266,12 @@ namespace osu.Game.Beatmaps.Formats handleColours(beatmap, key, val); break; case Section.HitObjects: - beatmap.HitObjects.Add(HitObject.Parse(beatmap.Mode, val)); + beatmap.HitObjects.Add(HitObject.Parse(beatmap.BeatmapInfo.Mode, val)); break; } } + + return beatmap; } } } \ No newline at end of file diff --git a/osu.Game/Beatmaps/IO/ArchiveReader.cs b/osu.Game/Beatmaps/IO/ArchiveReader.cs index 89adf26989..5f1afe3a00 100644 --- a/osu.Game/Beatmaps/IO/ArchiveReader.cs +++ b/osu.Game/Beatmaps/IO/ArchiveReader.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using osu.Framework.Platform; +using osu.Game.Database; namespace osu.Game.Beatmaps.IO { diff --git a/osu.Game/Beatmaps/IO/OszArchiveReader.cs b/osu.Game/Beatmaps/IO/OszArchiveReader.cs index 69776d0095..8b677d0145 100644 --- a/osu.Game/Beatmaps/IO/OszArchiveReader.cs +++ b/osu.Game/Beatmaps/IO/OszArchiveReader.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Security.Cryptography; using Ionic.Zip; using osu.Game.Beatmaps.Formats; +using osu.Game.Database; namespace osu.Game.Beatmaps.IO { @@ -33,8 +34,7 @@ namespace osu.Game.Beatmaps.IO using (var stream = new StreamReader(ReadFile(beatmaps[0]))) { var decoder = BeatmapDecoder.GetDecoder(stream); - firstMap = new Beatmap(); - decoder.Decode(stream, firstMap); + firstMap = decoder.Decode(stream); } } @@ -53,9 +53,10 @@ namespace osu.Game.Beatmaps.IO public override BeatmapMetadata ReadMetadata() { - return firstMap.Metadata; + return firstMap.BeatmapInfo.Metadata; } - public override void Dispose() + + public override void Dispose() { archive.Dispose(); } diff --git a/osu.Game/Beatmaps/BaseDifficulty.cs b/osu.Game/Database/BaseDifficulty.cs similarity index 85% rename from osu.Game/Beatmaps/BaseDifficulty.cs rename to osu.Game/Database/BaseDifficulty.cs index ce9e3bc624..265f933045 100644 --- a/osu.Game/Beatmaps/BaseDifficulty.cs +++ b/osu.Game/Database/BaseDifficulty.cs @@ -1,7 +1,7 @@ using System; -using SQLite; +using SQLite.Net.Attributes; -namespace osu.Game.Beatmaps +namespace osu.Game.Database { public class BaseDifficulty { diff --git a/osu.Game/Database/BeatmapDatabase.cs b/osu.Game/Database/BeatmapDatabase.cs index 7f8cf86c14..324e8a6a24 100644 --- a/osu.Game/Database/BeatmapDatabase.cs +++ b/osu.Game/Database/BeatmapDatabase.cs @@ -6,7 +6,8 @@ using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.IO; -using SQLite; +using SQLite.Net; +using SQLiteNetExtensions.Extensions; namespace osu.Game.Database { @@ -23,11 +24,12 @@ namespace osu.Game.Database connection = storage.GetDatabase(@"beatmaps"); connection.CreateTable(); connection.CreateTable(); - connection.CreateTable(); - connection.CreateTable(); + connection.CreateTable(); + connection.CreateTable(); } } - public void AddBeatmap(string path) + + public void AddBeatmap(string path) { string hash = null; ArchiveReader reader; @@ -47,54 +49,38 @@ namespace osu.Game.Database else reader = ArchiveReader.GetReader(storage, path); var metadata = reader.ReadMetadata(); - if (connection.Table().Count(b => b.BeatmapSetID == metadata.BeatmapSetID) != 0) + if (connection.Table().Count(b => b.BeatmapSetID == metadata.BeatmapSetID) != 0) return; // TODO: Update this beatmap instead string[] mapNames = reader.ReadBeatmaps(); - var beatmapSet = new BeatmapSet + var beatmapSet = new BeatmapSetInfo { BeatmapSetID = metadata.BeatmapSetID, Path = path, Hash = hash, }; - var maps = new List(); + var maps = new List(); foreach (var name in mapNames) { using (var stream = new StreamReader(reader.ReadFile(name))) { var decoder = BeatmapDecoder.GetDecoder(stream); - Beatmap beatmap = new Beatmap(); - decoder.Decode(stream, beatmap); - maps.Add(beatmap); - beatmap.BaseDifficultyID = connection.Insert(beatmap.BaseDifficulty); + Beatmap beatmap = decoder.Decode(stream); + // TODO: Diff beatmap metadata with set metadata and insert if necessary + beatmap.BeatmapInfo.Metadata = null; + maps.Add(beatmap.BeatmapInfo); + connection.Insert(beatmap.BeatmapInfo.BaseDifficulty); + connection.Insert(beatmap.BeatmapInfo); + connection.UpdateWithChildren(beatmap.BeatmapInfo); } } - beatmapSet.BeatmapMetadataID = connection.Insert(metadata); connection.Insert(beatmapSet); - connection.InsertAll(maps); + beatmapSet.BeatmapMetadataID = connection.Insert(metadata); + connection.UpdateWithChildren(beatmapSet); } - public ArchiveReader GetReader(BeatmapSet beatmapSet) + + public ArchiveReader GetReader(BeatmapSetInfo beatmapSet) { return ArchiveReader.GetReader(storage, beatmapSet.Path); } - - /// - /// Given a BeatmapSet pulled from the database, loads the rest of its data from disk. - /// public void PopulateBeatmap(BeatmapSet beatmapSet) - { - using (var reader = GetReader(beatmapSet)) - { - string[] mapNames = reader.ReadBeatmaps(); - foreach (var name in mapNames) - { - using (var stream = new StreamReader(reader.ReadFile(name))) - { - var decoder = BeatmapDecoder.GetDecoder(stream); - Beatmap beatmap = new Beatmap(); - decoder.Decode(stream, beatmap); - beatmapSet.Beatmaps.Add(beatmap); - } - } - } - } } } \ No newline at end of file diff --git a/osu.Game/Database/BeatmapInfo.cs b/osu.Game/Database/BeatmapInfo.cs new file mode 100644 index 0000000000..c03130baa0 --- /dev/null +++ b/osu.Game/Database/BeatmapInfo.cs @@ -0,0 +1,63 @@ +using System; +using System.Linq; +using osu.Game.Beatmaps.Samples; +using osu.Game.GameModes.Play; +using SQLite.Net.Attributes; +using SQLiteNetExtensions.Attributes; + +namespace osu.Game.Database +{ + public class BeatmapInfo + { + public BeatmapInfo() + { + BaseDifficulty = new BaseDifficulty(); + } + + [PrimaryKey] + public int BeatmapID { get; set; } + [NotNull, Indexed] + public int BeatmapSetID { get; set; } + [ForeignKey(typeof(BeatmapMetadata))] + public int BeatmapMetadataID { get; set; } + [ForeignKey(typeof(BaseDifficulty)), NotNull] + public int BaseDifficultyID { get; set; } + [OneToOne] + public BeatmapMetadata Metadata { get; set; } + [OneToOne] + public BaseDifficulty BaseDifficulty { get; set; } + + // General + public int AudioLeadIn { get; set; } + public bool Countdown { get; set; } + public SampleSet SampleSet { get; set; } + public float StackLeniency { get; set; } + public bool SpecialStyle { get; set; } + public PlayMode Mode { get; set; } + public bool LetterboxInBreaks { get; set; } + public bool WidescreenStoryboard { get; set; } + + // Editor + // This bookmarks stuff is necessary because DB doesn't know how to store int[] + public string StoredBookmarks { get; internal set; } + [Ignore] + public int[] Bookmarks + { + get + { + return StoredBookmarks.Split(',').Select(b => int.Parse(b)).ToArray(); + } + set + { + StoredBookmarks = string.Join(",", value); + } + } + public double DistanceSpacing { get; set; } + public int BeatDivisor { get; set; } + public int GridSize { get; set; } + public double TimelineZoom { get; set; } + + // Metadata + public string Version { get; set; } + } +} \ No newline at end of file diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Database/BeatmapMetadata.cs similarity index 90% rename from osu.Game/Beatmaps/BeatmapMetadata.cs rename to osu.Game/Database/BeatmapMetadata.cs index 1499dde323..531779922a 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Database/BeatmapMetadata.cs @@ -2,9 +2,9 @@ //Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.GameModes.Play; -using SQLite; +using SQLite.Net.Attributes; -namespace osu.Game.Beatmaps +namespace osu.Game.Database { public class BeatmapMetadata { diff --git a/osu.Game/Database/BeatmapSetInfo.cs b/osu.Game/Database/BeatmapSetInfo.cs new file mode 100644 index 0000000000..97b3359510 --- /dev/null +++ b/osu.Game/Database/BeatmapSetInfo.cs @@ -0,0 +1,19 @@ +using System; +using SQLite.Net.Attributes; +using SQLiteNetExtensions.Attributes; + +namespace osu.Game.Database +{ + public class BeatmapSetInfo + { + [PrimaryKey] + public int BeatmapSetID { get; set; } + [OneToOne] + public BeatmapMetadata Metadata { get; set; } + [NotNull, ForeignKey(typeof(BeatmapMetadata))] + public int BeatmapMetadataID { get; set; } + public string Hash { get; set; } + public string Path { get; set; } + } +} + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 05e73fc4f1..c42f535852 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -43,26 +43,25 @@ - - $(SolutionDir)\packages\SQLitePCLRaw.core.1.0.1\lib\net45\SQLitePCLRaw.core.dll - - - $(SolutionDir)\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.0.1\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll - - - $(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.0.1\lib\net45\SQLitePCLRaw.batteries_green.dll - - - $(SolutionDir)\packages\sqlite-net-pcl.1.2.0\lib\portable-net45+wp8+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10\SQLite-net.dll - $(SolutionDir)\packages\DotNetZip.1.10.1\lib\net20\DotNetZip.dll + + ..\packages\SQLite.Net-PCL.3.0.5\lib\net4\SQLite.Net.Platform.Win32.dll + + + ..\packages\SQLite.Net-PCL.3.0.5\lib\net40\SQLite.Net.dll + + + ..\packages\SQLite.Net-PCL.3.0.5\lib\net40\SQLite.Net.Platform.Generic.dll + + + ..\packages\SQLiteNetExtensions.1.3.0\lib\portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\SQLiteNetExtensions.dll + - @@ -168,9 +167,12 @@ - + + + + diff --git a/osu.Game/packages.config b/osu.Game/packages.config index 1f14b0038d..988b02e510 100644 --- a/osu.Game/packages.config +++ b/osu.Game/packages.config @@ -7,11 +7,6 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste - - - - - - - + + From 8ca4a2067e5447924ff9628012d861032181e60e Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 18 Oct 2016 13:58:24 -0400 Subject: [PATCH 2/6] $(SolutionDir) --- osu-framework | 2 +- osu.Game/osu.Game.csproj | 13 +++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/osu-framework b/osu-framework index f7be41642b..fc9d4413b4 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit f7be41642b23e5b262505896d834e5c813a1aac3 +Subproject commit fc9d4413b45a7b010ee7b516bab2cc4d75d0d6e2 diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c42f535852..14b61c218f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -47,16 +47,16 @@ $(SolutionDir)\packages\DotNetZip.1.10.1\lib\net20\DotNetZip.dll - ..\packages\SQLite.Net-PCL.3.0.5\lib\net4\SQLite.Net.Platform.Win32.dll + $(SolutionDir)\packages\SQLite.Net-PCL.3.0.5\lib\net4\SQLite.Net.Platform.Win32.dll - ..\packages\SQLite.Net-PCL.3.0.5\lib\net40\SQLite.Net.dll + $(SolutionDir)\packages\SQLite.Net-PCL.3.0.5\lib\net40\SQLite.Net.dll - ..\packages\SQLite.Net-PCL.3.0.5\lib\net40\SQLite.Net.Platform.Generic.dll + $(SolutionDir)\packages\SQLite.Net-PCL.3.0.5\lib\net40\SQLite.Net.Platform.Generic.dll - ..\packages\SQLiteNetExtensions.1.3.0\lib\portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\SQLiteNetExtensions.dll + $(SolutionDir)\packages\SQLiteNetExtensions.1.3.0\lib\portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\SQLiteNetExtensions.dll @@ -200,7 +200,4 @@ --> - - - - \ No newline at end of file + From ad14462369f83698a48c2bd8a62e603717be9dec Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 18 Oct 2016 15:38:59 -0400 Subject: [PATCH 3/6] Add lifecycle management to BeatmapDatabase --- osu.Game/Database/BeatmapDatabase.cs | 40 ++++++++++++++++++++++++++++ osu.Game/Database/BeatmapInfo.cs | 4 ++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/BeatmapDatabase.cs b/osu.Game/Database/BeatmapDatabase.cs index 324e8a6a24..45f835c514 100644 --- a/osu.Game/Database/BeatmapDatabase.cs +++ b/osu.Game/Database/BeatmapDatabase.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Security.Cryptography; using osu.Framework.Platform; using osu.Game.Beatmaps; @@ -65,6 +66,7 @@ namespace osu.Game.Database { var decoder = BeatmapDecoder.GetDecoder(stream); Beatmap beatmap = decoder.Decode(stream); + beatmap.BeatmapInfo.Path = name; // TODO: Diff beatmap metadata with set metadata and insert if necessary beatmap.BeatmapInfo.Metadata = null; maps.Add(beatmap.BeatmapInfo); @@ -82,5 +84,43 @@ namespace osu.Game.Database { return ArchiveReader.GetReader(storage, beatmapSet.Path); } + + public Beatmap GetBeatmap(BeatmapInfo beatmapInfo) + { + var beatmapSet = Query() + .Where(s => s.BeatmapSetID == beatmapInfo.BeatmapSetID).FirstOrDefault(); + if (beatmapSet == null) + throw new InvalidOperationException( + $@"Beatmap set {beatmapInfo.BeatmapSetID} is not in the local database."); + using (var reader = GetReader(beatmapSet)) + using (var stream = new StreamReader(reader.ReadFile(beatmapInfo.Path))) + { + var decoder = BeatmapDecoder.GetDecoder(stream); + return decoder.Decode(stream); + } + } + + public TableQuery Query() where T : class + { + return connection.Table(); + } + + readonly Type[] validTypes = new[] + { + typeof(BeatmapSetInfo), + typeof(BeatmapInfo), + typeof(BeatmapMetadata), + typeof(BaseDifficulty), + }; + + public void Update(T record, bool cascade = true) where T : class + { + if (!validTypes.Any(t => t == typeof(T))) + throw new ArgumentException(nameof(T), "Must be a type managed by BeatmapDatabase"); + if (cascade) + connection.UpdateWithChildren(record); + else + connection.Update(record); + } } } \ No newline at end of file diff --git a/osu.Game/Database/BeatmapInfo.cs b/osu.Game/Database/BeatmapInfo.cs index c03130baa0..4487485def 100644 --- a/osu.Game/Database/BeatmapInfo.cs +++ b/osu.Game/Database/BeatmapInfo.cs @@ -2,7 +2,7 @@ using System.Linq; using osu.Game.Beatmaps.Samples; using osu.Game.GameModes.Play; -using SQLite.Net.Attributes; +using SQLite.Net.Attributes; using SQLiteNetExtensions.Attributes; namespace osu.Game.Database @@ -27,6 +27,8 @@ namespace osu.Game.Database [OneToOne] public BaseDifficulty BaseDifficulty { get; set; } + public string Path { get; set; } + // General public int AudioLeadIn { get; set; } public bool Countdown { get; set; } From 105bba6178d7c2298eac8d91232102870ce29c15 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 18 Oct 2016 15:41:46 -0400 Subject: [PATCH 4/6] Rework duplicate detection in AddBeatmap --- osu.Game/Beatmaps/Beatmap.cs | 1 + osu.Game/Database/BeatmapDatabase.cs | 11 ++++------- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 9b6028cac5..438a5a2b89 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -1,6 +1,7 @@ //Copyright (c) 2007-2016 ppy Pty Ltd . //Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.Generic; using OpenTK.Graphics; using osu.Game.Beatmaps.Objects; diff --git a/osu.Game/Database/BeatmapDatabase.cs b/osu.Game/Database/BeatmapDatabase.cs index 45f835c514..bf3ab9dec9 100644 --- a/osu.Game/Database/BeatmapDatabase.cs +++ b/osu.Game/Database/BeatmapDatabase.cs @@ -33,7 +33,10 @@ namespace osu.Game.Database public void AddBeatmap(string path) { string hash = null; - ArchiveReader reader; + var reader = ArchiveReader.GetReader(storage, path); + var metadata = reader.ReadMetadata(); + if (connection.Table().Count(b => b.BeatmapSetID == metadata.BeatmapSetID) != 0) + return; // TODO: Update this beatmap instead if (File.Exists(path)) // Not always the case, i.e. for LegacyFilesystemReader { using (var md5 = MD5.Create()) @@ -44,14 +47,8 @@ namespace osu.Game.Database var outputPath = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash); using (var output = storage.GetStream(outputPath, FileAccess.Write)) input.CopyTo(output); - reader = ArchiveReader.GetReader(storage, path = outputPath); } } - else - reader = ArchiveReader.GetReader(storage, path); - var metadata = reader.ReadMetadata(); - if (connection.Table().Count(b => b.BeatmapSetID == metadata.BeatmapSetID) != 0) - return; // TODO: Update this beatmap instead string[] mapNames = reader.ReadBeatmaps(); var beatmapSet = new BeatmapSetInfo { From 768dd38fa09c6b45ceec0478fbf7e9e73530d0f9 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 18 Oct 2016 15:42:07 -0400 Subject: [PATCH 5/6] s/AddBeatmap/ImportBeatmap/g --- osu.Game/Database/BeatmapDatabase.cs | 2 +- osu.Game/OsuGame.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Database/BeatmapDatabase.cs b/osu.Game/Database/BeatmapDatabase.cs index bf3ab9dec9..7b3b741b50 100644 --- a/osu.Game/Database/BeatmapDatabase.cs +++ b/osu.Game/Database/BeatmapDatabase.cs @@ -30,7 +30,7 @@ namespace osu.Game.Database } } - public void AddBeatmap(string path) + public void ImportBeatmap(string path) { string hash = null; var reader = ArchiveReader.GetReader(storage, path); diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 684cc42d5a..7a77ef5b0a 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -74,7 +74,7 @@ namespace osu.Game { try { - Beatmaps.AddBeatmap(message.Path); + Beatmaps.ImportBeatmap(message.Path); // TODO: Switch to beatmap list and select the new song } catch (Exception ex) From 7bdf1fe1bf980fecf32ff37dd44ee9f7f0bc0284 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 18 Oct 2016 15:48:24 -0400 Subject: [PATCH 6/6] Drop BeatmapSet, only BeatmapSetInfo is necessary --- osu.Game/Beatmaps/BeatmapSet.cs | 19 ------------------- osu.Game/Database/BeatmapDatabase.cs | 5 +++++ osu.Game/osu.Game.csproj | 1 - 3 files changed, 5 insertions(+), 20 deletions(-) delete mode 100644 osu.Game/Beatmaps/BeatmapSet.cs diff --git a/osu.Game/Beatmaps/BeatmapSet.cs b/osu.Game/Beatmaps/BeatmapSet.cs deleted file mode 100644 index 55c888384f..0000000000 --- a/osu.Game/Beatmaps/BeatmapSet.cs +++ /dev/null @@ -1,19 +0,0 @@ -//Copyright (c) 2007-2016 ppy Pty Ltd . -//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Game.Database; -using osu.Game.Users; - -namespace osu.Game.Beatmaps -{ - /// - /// A beatmap set contains multiple beatmap (difficulties). - /// - public class BeatmapSet - { - public BeatmapSetInfo BeatmapSetInfo { get; set; } - public List Beatmaps { get; protected set; } = new List(); - public User Creator { get; set; } - } -} diff --git a/osu.Game/Database/BeatmapDatabase.cs b/osu.Game/Database/BeatmapDatabase.cs index 7b3b741b50..73809c6fc2 100644 --- a/osu.Game/Database/BeatmapDatabase.cs +++ b/osu.Game/Database/BeatmapDatabase.cs @@ -82,6 +82,11 @@ namespace osu.Game.Database return ArchiveReader.GetReader(storage, beatmapSet.Path); } + public BeatmapSetInfo GetBeatmapSet(int id) + { + return Query().Where(s => s.BeatmapSetID == id).FirstOrDefault(); + } + public Beatmap GetBeatmap(BeatmapInfo beatmapInfo) { var beatmapSet = Query() diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 14b61c218f..8f029070ca 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -61,7 +61,6 @@ -