From 6a4198d0d67b77294916ebd0767a1817bf30e2d4 Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Wed, 4 Oct 2017 22:52:12 +0300 Subject: [PATCH 01/90] Initial EF Core commit --- .../Beatmaps/ManiaBeatmapConverter.cs | 2 +- .../Legacy/DistanceObjectPatternGenerator.cs | 2 +- .../Patterns/Legacy/PatternGenerator.cs | 2 +- .../ManiaDifficultyCalculator.cs | 2 +- .../Scoring/ManiaScoreProcessor.cs | 2 +- .../UI/ManiaRulesetContainer.cs | 10 +- .../Scoring/OsuScoreProcessor.cs | 2 +- .../UI/Cursor/GameplayCursor.cs | 2 +- .../Beatmaps/TaikoBeatmapConverter.cs | 12 +-- .../Scoring/TaikoScoreProcessor.cs | 4 +- .../Tests/TestCaseTaikoPlayfield.cs | 4 +- .../UI/TaikoRulesetContainer.cs | 2 +- .../Beatmaps/Formats/OsuLegacyDecoderTest.cs | 8 +- .../Beatmaps/IO/ImportBeatmapTest.cs | 16 +-- .../Beatmaps/IO/OszArchiveReaderTest.cs | 2 +- osu.Game.Tests/app.config | 4 + osu.Game.Tests/osu.Game.Tests.csproj | 9 -- osu.Game.Tests/packages.config | 2 - osu.Game/Beatmaps/Beatmap.cs | 2 +- osu.Game/Beatmaps/BeatmapDifficulty.cs | 7 +- osu.Game/Beatmaps/BeatmapInfo.cs | 67 +++++------- osu.Game/Beatmaps/BeatmapManager.cs | 63 +++++------ osu.Game/Beatmaps/BeatmapMetadata.cs | 9 +- osu.Game/Beatmaps/BeatmapOnlineInfo.cs | 4 + osu.Game/Beatmaps/BeatmapSetFileInfo.cs | 21 ++-- osu.Game/Beatmaps/BeatmapSetInfo.cs | 23 ++-- osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs | 16 ++- osu.Game/Beatmaps/BeatmapStore.cs | 102 +++++++----------- osu.Game/Beatmaps/DifficultyCalculator.cs | 2 +- osu.Game/Beatmaps/Drawables/BeatmapPanel.cs | 2 +- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 2 +- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 8 +- osu.Game/Beatmaps/Formats/BeatmapDecoder.cs | 4 +- osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs | 24 ++--- osu.Game/Beatmaps/WorkingBeatmap.cs | 4 +- osu.Game/Database/DatabaseBackedStore.cs | 79 +------------- osu.Game/Database/DbContextBase.cs | 8 ++ osu.Game/Database/OsuDbContext.cs | 55 ++++++++++ osu.Game/Database/StoreVersion.cs | 15 --- osu.Game/IO/FileInfo.cs | 9 +- osu.Game/IO/FileStore.cs | 91 +++++----------- .../Input/Bindings/DatabasedKeyBinding.cs | 17 ++- .../DatabasedKeyBindingInputManager.cs | 2 +- osu.Game/Input/KeyBindingStore.cs | 55 ++++------ .../API/Requests/DownloadBeatmapSetRequest.cs | 2 +- .../API/Requests/GetBeatmapDetailsRequest.cs | 2 +- .../API/Requests/GetBeatmapSetsRequest.cs | 10 +- .../Online/API/Requests/GetScoresRequest.cs | 8 +- osu.Game/OsuGame.cs | 2 +- osu.Game/OsuGameBase.cs | 42 +++++--- osu.Game/Overlays/BeatmapSet/Header.cs | 4 +- osu.Game/Overlays/BeatmapSet/Info.cs | 4 +- osu.Game/Overlays/Direct/DirectGridPanel.cs | 10 +- osu.Game/Overlays/Direct/DirectListPanel.cs | 10 +- osu.Game/Overlays/Direct/FilterControl.cs | 2 +- osu.Game/Overlays/DirectOverlay.cs | 12 +-- .../KeyBinding/KeyBindingsSubsection.cs | 2 +- osu.Game/Overlays/Music/PlaylistItem.cs | 2 +- osu.Game/Overlays/Music/PlaylistList.cs | 4 +- osu.Game/Overlays/Music/PlaylistOverlay.cs | 2 +- osu.Game/Overlays/MusicController.cs | 4 +- .../Overlays/Toolbar/ToolbarModeSelector.cs | 2 +- osu.Game/Rulesets/RulesetInfo.cs | 11 +- osu.Game/Rulesets/RulesetStore.cs | 91 +++++++++------- osu.Game/Rulesets/Scoring/ScoreStore.cs | 3 +- osu.Game/Rulesets/UI/RulesetContainer.cs | 4 +- osu.Game/Screens/Menu/Intro.cs | 2 +- osu.Game/Screens/Multiplayer/DrawableRoom.cs | 6 +- osu.Game/Screens/Multiplayer/RoomInspector.cs | 8 +- osu.Game/Screens/Play/Player.cs | 6 +- osu.Game/Screens/Play/PlayerLoader.cs | 2 +- osu.Game/Screens/Ranking/ResultsPageScore.cs | 6 +- osu.Game/Screens/Select/BeatmapCarousel.cs | 12 +-- .../Screens/Select/BeatmapDeleteDialog.cs | 2 +- osu.Game/Screens/Select/BeatmapDetails.cs | 6 +- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 4 +- .../Screens/Select/Details/AdvancedStats.cs | 12 +-- osu.Game/Screens/Select/FilterCriteria.cs | 10 +- .../Select/Leaderboards/Leaderboard.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 4 +- osu.Game/Storyboards/Storyboard.cs | 2 +- osu.Game/Tests/Platform/TestStorage.cs | 17 +-- .../Tests/Visual/TestCaseBeatmapDetails.cs | 16 +-- .../Tests/Visual/TestCaseBeatmapSetOverlay.cs | 44 ++++---- osu.Game/Tests/Visual/TestCaseDirect.cs | 52 ++++----- osu.Game/Tests/Visual/TestCaseDrawableRoom.cs | 12 +-- .../Tests/Visual/TestCasePlaySongSelect.cs | 34 +++--- osu.Game/Tests/Visual/TestCasePlayer.cs | 7 +- osu.Game/Tests/Visual/TestCaseResults.cs | 2 +- .../Tests/Visual/TestCaseRoomInspector.cs | 12 +-- .../Visual/TestCaseScrollingPlayfield.cs | 4 +- osu.Game/osu.Game.csproj | 100 ++++++++++++++--- osu.Game/packages.config | 43 +++++++- 93 files changed, 730 insertions(+), 708 deletions(-) create mode 100644 osu.Game/Database/DbContextBase.cs create mode 100644 osu.Game/Database/OsuDbContext.cs delete mode 100644 osu.Game/Database/StoreVersion.cs diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index d7c86e1f89..713ee6bb44 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps { beatmap = original; - BeatmapDifficulty difficulty = original.BeatmapInfo.Difficulty; + BeatmapDifficulty difficulty = original.BeatmapInfo.BeatmapDifficulty; int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate); random = new FastRandom(seed); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 20966a75f7..07a7c47f82 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy // The true distance, accounting for any repeats double distance = (distanceData?.Distance ?? 0) * repeatCount; // The velocity of the osu! hit object - calculated as the velocity of a slider - double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / timingPoint.BeatLength; + double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BeatmapDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / timingPoint.BeatLength; // The duration of the osu! hit object double osuDuration = distance / osuVelocity; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs index a3173f9784..d3b743062d 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy if (drainTime == 0) drainTime = 10000; - BeatmapDifficulty difficulty = Beatmap.BeatmapInfo.Difficulty; + BeatmapDifficulty difficulty = Beatmap.BeatmapInfo.BeatmapDifficulty; conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + Beatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15; conversionDifficulty = Math.Min(conversionDifficulty.Value, 12); diff --git a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs index b98802db69..6a96bc0a7d 100644 --- a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs @@ -21,6 +21,6 @@ namespace osu.Game.Rulesets.Mania return 0; } - protected override BeatmapConverter CreateBeatmapConverter() => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize))); + protected override BeatmapConverter CreateBeatmapConverter() => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(Beatmap.BeatmapInfo.BeatmapDifficulty.CircleSize))); } } diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index a200ba31e2..d6c076803d 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Mania.Scoring protected override void SimulateAutoplay(Beatmap beatmap) { - BeatmapDifficulty difficulty = beatmap.BeatmapInfo.Difficulty; + BeatmapDifficulty difficulty = beatmap.BeatmapInfo.BeatmapDifficulty; hpMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max); hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max); diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs index 3b49d81674..3858c60367 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs @@ -88,18 +88,18 @@ namespace osu.Game.Rulesets.Mania.UI protected override BeatmapConverter CreateBeatmapConverter() { if (IsForCurrentRuleset) - AvailableColumns = (int)Math.Max(1, Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.CircleSize)); + AvailableColumns = (int)Math.Max(1, Math.Round(WorkingBeatmap.BeatmapInfo.BeatmapDifficulty.CircleSize)); else { float percentSliderOrSpinner = (float)WorkingBeatmap.Beatmap.HitObjects.Count(h => h is IHasEndTime) / WorkingBeatmap.Beatmap.HitObjects.Count; if (percentSliderOrSpinner < 0.2) AvailableColumns = 7; - else if (percentSliderOrSpinner < 0.3 || Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.CircleSize) >= 5) - AvailableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) > 5 ? 7 : 6; + else if (percentSliderOrSpinner < 0.3 || Math.Round(WorkingBeatmap.BeatmapInfo.BeatmapDifficulty.CircleSize) >= 5) + AvailableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.BeatmapDifficulty.OverallDifficulty) > 5 ? 7 : 6; else if (percentSliderOrSpinner > 0.6) - AvailableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) > 4 ? 5 : 4; + AvailableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.BeatmapDifficulty.OverallDifficulty) > 4 ? 5 : 4; else - AvailableColumns = Math.Max(4, Math.Min((int)Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) + 1, 7)); + AvailableColumns = Math.Max(4, Math.Min((int)Math.Round(WorkingBeatmap.BeatmapInfo.BeatmapDifficulty.OverallDifficulty) + 1, 7)); } return new ManiaBeatmapConverter(IsForCurrentRuleset, AvailableColumns); diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index 50239bf16c..b320940e67 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Scoring protected override void SimulateAutoplay(Beatmap beatmap) { - hpDrainRate = beatmap.BeatmapInfo.Difficulty.DrainRate; + hpDrainRate = beatmap.BeatmapInfo.BeatmapDifficulty.DrainRate; foreach (var obj in beatmap.HitObjects) { diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs index adfc946f86..724a5f2b7a 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs @@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor if (autoCursorScale && beatmap.Value != null) { // if we have a beatmap available, let's get its circle size to figure out an automatic cursor scale modifier. - scale *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.Difficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY); + scale *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.BeatmapDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY); } cursorContainer.Scale = new Vector2(scale); diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 4f2707ff88..6a8f046800 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps { // Rewrite the beatmap info to add the slider velocity multiplier BeatmapInfo info = original.BeatmapInfo.DeepClone(); - info.Difficulty.SliderMultiplier *= legacy_velocity_multiplier; + info.BeatmapDifficulty.SliderMultiplier *= legacy_velocity_multiplier; Beatmap converted = base.ConvertBeatmap(original); @@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps double distance = distanceData.Distance * repeats * legacy_velocity_multiplier; // The velocity of the taiko hit object - calculated as the velocity of a drum roll - double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength; + double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.BeatmapDifficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength; // The duration of the taiko hit object double taikoDuration = distance / taikoVelocity; @@ -103,12 +103,12 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps speedAdjustedBeatLength *= speedAdjustment; // The velocity of the osu! hit object - calculated as the velocity of a slider - double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength; + double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BeatmapDifficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength; // The duration of the osu! hit object double osuDuration = distance / osuVelocity; // If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat - double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.Difficulty.SliderTickRate, taikoDuration / repeats); + double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.BeatmapDifficulty.SliderTickRate, taikoDuration / repeats); if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength) { @@ -151,13 +151,13 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps Samples = obj.Samples, IsStrong = strong, Duration = taikoDuration, - TickRate = beatmap.BeatmapInfo.Difficulty.SliderTickRate == 3 ? 3 : 4, + TickRate = beatmap.BeatmapInfo.BeatmapDifficulty.SliderTickRate == 3 ? 3 : 4, }; } } else if (endTimeData != null) { - double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier; + double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BeatmapDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier; yield return new Swell { diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs index abdda9676f..9b208813dc 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs @@ -71,12 +71,12 @@ namespace osu.Game.Rulesets.Taiko.Scoring protected override void SimulateAutoplay(Beatmap beatmap) { - double hpMultiplierNormal = 1 / (hp_hit_great * beatmap.HitObjects.FindAll(o => o is Hit).Count * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.DrainRate, 0.5, 0.75, 0.98)); + double hpMultiplierNormal = 1 / (hp_hit_great * beatmap.HitObjects.FindAll(o => o is Hit).Count * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BeatmapDifficulty.DrainRate, 0.5, 0.75, 0.98)); hpIncreaseTick = hp_hit_tick; hpIncreaseGreat = hpMultiplierNormal * hp_hit_great; hpIncreaseGood = hpMultiplierNormal * hp_hit_good; - hpIncreaseMiss = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.DrainRate, hp_miss_min, hp_miss_mid, hp_miss_max); + hpIncreaseMiss = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BeatmapDifficulty.DrainRate, hp_miss_min, hp_miss_mid, hp_miss_max); foreach (var obj in beatmap.HitObjects) { diff --git a/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs index 79ee2945ad..8e93d52492 100644 --- a/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs @@ -67,8 +67,8 @@ namespace osu.Game.Rulesets.Taiko.Tests HitObjects = new List { new CentreHit() }, BeatmapInfo = new BeatmapInfo { - Difficulty = new BeatmapDifficulty(), - Metadata = new BeatmapMetadata + BeatmapDifficulty = new BeatmapDifficulty(), + BeatmapMetadata = new BeatmapMetadata { Artist = @"Unknown", Title = @"Sample Beatmap", diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs index f0853aef0e..ca0a7e058f 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Taiko.UI StartTime = time, }; - barLine.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.Difficulty); + barLine.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BeatmapDifficulty); bool isMajor = currentBeat % (int)currentPoint.TimeSignature == 0; Playfield.Add(isMajor ? new DrawableBarLineMajor(barLine) : new DrawableBarLine(barLine)); diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs index da3b448f74..4c71601eed 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs @@ -23,8 +23,8 @@ namespace osu.Game.Tests.Beatmaps.Formats using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { var beatmap = decoder.Decode(new StreamReader(stream)); - var meta = beatmap.BeatmapInfo.Metadata; - Assert.AreEqual(241526, meta.OnlineBeatmapSetID); + var meta = beatmap.BeatmapInfo.BeatmapMetadata; + Assert.AreEqual(241526, meta.BeatmapSetOnlineInfoId); Assert.AreEqual("Soleily", meta.Artist); Assert.AreEqual("Soleily", meta.ArtistUnicode); Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile); @@ -49,7 +49,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(false, beatmapInfo.Countdown); Assert.AreEqual(0.7f, beatmapInfo.StackLeniency); Assert.AreEqual(false, beatmapInfo.SpecialStyle); - Assert.IsTrue(beatmapInfo.RulesetID == 0); + Assert.IsTrue(beatmapInfo.RulesetInfoId == 0); Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks); Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard); } @@ -85,7 +85,7 @@ namespace osu.Game.Tests.Beatmaps.Formats using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { var beatmap = decoder.Decode(new StreamReader(stream)); - var difficulty = beatmap.BeatmapInfo.Difficulty; + var difficulty = beatmap.BeatmapInfo.BeatmapDifficulty; Assert.AreEqual(6.5f, difficulty.DrainRate); Assert.AreEqual(4, difficulty.CircleSize); Assert.AreEqual(8, difficulty.OverallDifficulty); diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 35bebf2d4f..4b704fe242 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -111,7 +111,7 @@ namespace osu.Game.Tests.Beatmaps.IO var store = osu.Dependencies.Get(); - waitForOrAssert(() => (resultSets = store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526)).Any(), + waitForOrAssert(() => (resultSets = store.QueryBeatmapSets(s => s.BeatmapSetOnlineInfoId == 241526)).Any(), @"BeatmapSet did not import to the database in allocated time.", timeout); //ensure we were stored to beatmap database backing... @@ -120,29 +120,29 @@ namespace osu.Game.Tests.Beatmaps.IO IEnumerable resultBeatmaps = null; //if we don't re-check here, the set will be inserted but the beatmaps won't be present yet. - waitForOrAssert(() => (resultBeatmaps = store.QueryBeatmaps(s => s.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0)).Count() == 12, + waitForOrAssert(() => (resultBeatmaps = store.QueryBeatmaps(s => s.BeatmapSetOnlineInfoId == 241526 && s.BeatmapDifficultyId > 0)).Count() == 12, @"Beatmaps did not import to the database in allocated time", timeout); - var set = store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526).First(); + var set = store.QueryBeatmapSets(s => s.BeatmapSetOnlineInfoId == 241526).First(); Assert.IsTrue(set.Beatmaps.Count == resultBeatmaps.Count(), $@"Incorrect database beatmap count post-import ({resultBeatmaps.Count()} but should be {set.Beatmaps.Count})."); foreach (BeatmapInfo b in resultBeatmaps) - Assert.IsTrue(set.Beatmaps.Any(c => c.OnlineBeatmapID == b.OnlineBeatmapID)); + Assert.IsTrue(set.Beatmaps.Any(c => c.BeatmapOnlineInfoId == b.BeatmapOnlineInfoId)); Assert.IsTrue(set.Beatmaps.Count > 0); - var beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 0))?.Beatmap; + var beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetInfoId == 0))?.Beatmap; Assert.IsTrue(beatmap?.HitObjects.Count > 0); - beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 1))?.Beatmap; + beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetInfoId == 1))?.Beatmap; Assert.IsTrue(beatmap?.HitObjects.Count > 0); - beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 2))?.Beatmap; + beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetInfoId == 2))?.Beatmap; Assert.IsTrue(beatmap?.HitObjects.Count > 0); - beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 3))?.Beatmap; + beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetInfoId == 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 7a7a8a58bc..9ca3089944 100644 --- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs @@ -52,7 +52,7 @@ namespace osu.Game.Tests.Beatmaps.IO using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu"))) meta = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata; - Assert.AreEqual(241526, meta.OnlineBeatmapSetID); + Assert.AreEqual(241526, meta.BeatmapSetOnlineInfoId); Assert.AreEqual("Soleily", meta.Artist); Assert.AreEqual("Soleily", meta.ArtistUnicode); Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile); diff --git a/osu.Game.Tests/app.config b/osu.Game.Tests/app.config index faeaf001de..11af32e2cf 100644 --- a/osu.Game.Tests/app.config +++ b/osu.Game.Tests/app.config @@ -6,6 +6,10 @@ + + + + \ No newline at end of file diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 8ebd38022e..e188c82e79 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -39,15 +39,6 @@ True - - $(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll - - - $(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net4\SQLite.Net.Platform.Win32.dll - - - $(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net40\SQLite.Net.Platform.Generic.dll - diff --git a/osu.Game.Tests/packages.config b/osu.Game.Tests/packages.config index af47f642e3..b94c0c6e2d 100644 --- a/osu.Game.Tests/packages.config +++ b/osu.Game.Tests/packages.config @@ -6,6 +6,4 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste - - \ No newline at end of file diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 458c2304f2..0d6cd6f49a 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -29,7 +29,7 @@ namespace osu.Game.Beatmaps new Color4(121, 9, 13, 255) }; - public BeatmapMetadata Metadata => BeatmapInfo?.Metadata ?? BeatmapInfo?.BeatmapSet?.Metadata; + public BeatmapMetadata Metadata => BeatmapInfo?.BeatmapMetadata ?? BeatmapInfo?.BeatmapSetInfo?.BeatmapMetadata; /// /// The HitObjects this Beatmap contains. diff --git a/osu.Game/Beatmaps/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs index 7c2294cae9..e354f3703e 100644 --- a/osu.Game/Beatmaps/BeatmapDifficulty.cs +++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs @@ -1,7 +1,8 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using SQLite.Net.Attributes; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; namespace osu.Game.Beatmaps { @@ -12,8 +13,8 @@ namespace osu.Game.Beatmaps /// public const float DEFAULT_DIFFICULTY = 5; - [PrimaryKey, AutoIncrement] - public int ID { get; set; } + [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } public float DrainRate { get; set; } = DEFAULT_DIFFICULTY; public float CircleSize { get; set; } = DEFAULT_DIFFICULTY; public float OverallDifficulty { get; set; } = DEFAULT_DIFFICULTY; diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 5e4e122fb5..565e1b97d6 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -2,49 +2,43 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; 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.Beatmaps { public class BeatmapInfo : IEquatable, IJsonSerializable { - [PrimaryKey, AutoIncrement] - public int ID { get; set; } + [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } //TODO: should be in database public int BeatmapVersion; - public int? OnlineBeatmapID { get; set; } + public int? BeatmapOnlineInfoId { get; set; } - public int? OnlineBeatmapSetID { get; set; } + public int? BeatmapSetOnlineInfoId { get; set; } - [ForeignKey(typeof(BeatmapSetInfo))] - public int BeatmapSetInfoID { get; set; } + [ForeignKey(nameof(BeatmapSetInfo))] + public int BeatmapSetInfoId { get; set; } + public BeatmapSetInfo BeatmapSetInfo { get; set; } - [ManyToOne] - public BeatmapSetInfo BeatmapSet { get; set; } + [ForeignKey(nameof(BeatmapMetadata))] + public int BeatmapMetadataId { get; set; } + public BeatmapMetadata BeatmapMetadata { get; set; } - [ForeignKey(typeof(BeatmapMetadata))] - public int BeatmapMetadataID { get; set; } + [ForeignKey(nameof(BeatmapDifficulty))] + public int BeatmapDifficultyId { get; set; } + public BeatmapDifficulty BeatmapDifficulty { get; set; } - [OneToOne(CascadeOperations = CascadeOperation.All)] - public BeatmapMetadata Metadata { get; set; } - - [ForeignKey(typeof(BeatmapDifficulty)), NotNull] - public int BaseDifficultyID { get; set; } - - [OneToOne(CascadeOperations = CascadeOperation.All)] - public BeatmapDifficulty Difficulty { get; set; } - - [Ignore] + [NotMapped] public BeatmapMetrics Metrics { get; set; } - [Ignore] + [NotMapped] public BeatmapOnlineInfo OnlineInfo { get; set; } public string Path { get; set; } @@ -57,7 +51,6 @@ namespace osu.Game.Beatmaps /// /// MD5 is kept for legacy support (matching against replays, osu-web-10 etc.). /// - [Indexed] [JsonProperty("file_md5")] public string MD5Hash { get; set; } @@ -67,11 +60,9 @@ namespace osu.Game.Beatmaps public float StackLeniency { get; set; } public bool SpecialStyle { get; set; } - [ForeignKey(typeof(RulesetInfo))] - public int RulesetID { get; set; } - - [OneToOne(CascadeOperations = CascadeOperation.CascadeRead)] - public RulesetInfo Ruleset { get; set; } + [ForeignKey(nameof(RulesetInfo))] + public int RulesetInfoId { get; set; } + public RulesetInfo RulesetInfo { get; set; } public bool LetterboxInBreaks { get; set; } public bool WidescreenStoryboard { get; set; } @@ -99,7 +90,7 @@ namespace osu.Game.Beatmaps } } - [Ignore] + [NotMapped] public int[] Bookmarks { get; set; } = new int[0]; public double DistanceSpacing { get; set; } @@ -114,20 +105,20 @@ namespace osu.Game.Beatmaps public bool Equals(BeatmapInfo other) { - if (ID == 0 || other?.ID == 0) + if (Id == 0 || other?.Id == 0) // one of the two BeatmapInfos we are comparing isn't sourced from a database. // fall back to reference equality. return ReferenceEquals(this, other); - return ID == other?.ID; + return Id == other?.Id; } - public bool AudioEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null && - BeatmapSet.Hash == other.BeatmapSet.Hash && - (Metadata ?? BeatmapSet.Metadata).AudioFile == (other.Metadata ?? other.BeatmapSet.Metadata).AudioFile; + public bool AudioEquals(BeatmapInfo other) => other != null && BeatmapSetInfo != null && other.BeatmapSetInfo != null && + BeatmapSetInfo.Hash == other.BeatmapSetInfo.Hash && + (BeatmapMetadata ?? BeatmapSetInfo.BeatmapMetadata).AudioFile == (other.BeatmapMetadata ?? other.BeatmapSetInfo.BeatmapMetadata).AudioFile; - public bool BackgroundEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null && - BeatmapSet.Hash == other.BeatmapSet.Hash && - (Metadata ?? BeatmapSet.Metadata).BackgroundFile == (other.Metadata ?? other.BeatmapSet.Metadata).BackgroundFile; + public bool BackgroundEquals(BeatmapInfo other) => other != null && BeatmapSetInfo != null && other.BeatmapSetInfo != null && + BeatmapSetInfo.Hash == other.BeatmapSetInfo.Hash && + (BeatmapMetadata ?? BeatmapSetInfo.BeatmapMetadata).BackgroundFile == (other.BeatmapMetadata ?? other.BeatmapSetInfo.BeatmapMetadata).BackgroundFile; } } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index a1b678392b..3b023e34e4 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -19,9 +19,9 @@ using osu.Game.IO; using osu.Game.IPC; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; -using SQLite.Net; using osu.Game.Online.API.Requests; using System.Threading.Tasks; +using osu.Game.Database; using osu.Game.Online.API; namespace osu.Game.Beatmaps @@ -60,7 +60,7 @@ namespace osu.Game.Beatmaps private readonly FileStore files; - private readonly SQLiteConnection connection; + private readonly OsuDbContext connection; private readonly RulesetStore rulesets; @@ -83,7 +83,7 @@ namespace osu.Game.Beatmaps /// public Func GetStableStorage { private get; set; } - public BeatmapManager(Storage storage, FileStore files, SQLiteConnection connection, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null) + public BeatmapManager(Storage storage, FileStore files, OsuDbContext connection, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null) { beatmaps = new BeatmapStore(connection); beatmaps.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s); @@ -168,7 +168,7 @@ namespace osu.Game.Beatmaps // let's only allow one concurrent import at a time for now. lock (importLock) - connection.RunInTransaction(() => Import(set = importToStorage(archiveReader))); + Import(set = importToStorage(archiveReader)); return set; } @@ -180,7 +180,7 @@ namespace osu.Game.Beatmaps public void Import(BeatmapSetInfo beatmapSetInfo) { // If we have an ID then we already exist in the database. - if (beatmapSetInfo.ID != 0) return; + if (beatmapSetInfo.Id != 0) return; beatmaps.Add(beatmapSetInfo); } @@ -200,7 +200,7 @@ namespace osu.Game.Beatmaps ProgressNotification downloadNotification = new ProgressNotification { - Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}", + Text = $"Downloading {beatmapSetInfo.BeatmapMetadata.Artist} - {beatmapSetInfo.BeatmapMetadata.Title}", }; var request = new DownloadBeatmapSetRequest(beatmapSetInfo); @@ -251,7 +251,7 @@ namespace osu.Game.Beatmaps /// /// The whose download request is wanted. /// The object if it exists, or null. - public DownloadBeatmapSetRequest GetExistingDownload(BeatmapSetInfo beatmap) => currentDownloads.Find(d => d.BeatmapSet.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID); + public DownloadBeatmapSetRequest GetExistingDownload(BeatmapSetInfo beatmap) => currentDownloads.Find(d => d.BeatmapSet.BeatmapSetOnlineInfoId == beatmap.BeatmapSetOnlineInfoId); /// /// Delete a beatmap from the manager. @@ -302,14 +302,15 @@ namespace osu.Game.Beatmaps if (beatmapInfo == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) return DefaultBeatmap; - lock (beatmaps) - beatmaps.Populate(beatmapInfo); + // TODO Include() + //lock (beatmaps) + // beatmaps.Populate(beatmapInfo); - if (beatmapInfo.BeatmapSet == null) - throw new InvalidOperationException($@"Beatmap set {beatmapInfo.BeatmapSetInfoID} is not in the local database."); + if (beatmapInfo.BeatmapSetInfo == null) + throw new InvalidOperationException($@"Beatmap set {beatmapInfo.BeatmapSetInfoId} is not in the local database."); - if (beatmapInfo.Metadata == null) - beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata; + if (beatmapInfo.BeatmapMetadata == null) + beatmapInfo.BeatmapMetadata = beatmapInfo.BeatmapSetInfo.BeatmapMetadata; WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(files.Store, beatmapInfo); @@ -336,10 +337,11 @@ namespace osu.Game.Beatmaps { lock (beatmaps) { - BeatmapSetInfo set = beatmaps.Query().FirstOrDefault(query); + BeatmapSetInfo set = beatmaps.QueryBeatmapSet(query); - if (set != null) - beatmaps.Populate(set); + // TODO Include() + //if (set != null) + // beatmaps.Populate(set); return set; } @@ -350,7 +352,7 @@ namespace osu.Game.Beatmaps /// /// A stale instance. /// A fresh instance. - public BeatmapSetInfo Refresh(BeatmapSetInfo beatmapSet) => QueryBeatmapSet(s => s.ID == beatmapSet.ID); + public BeatmapSetInfo Refresh(BeatmapSetInfo beatmapSet) => QueryBeatmapSet(s => s.Id == beatmapSet.Id); /// /// Perform a lookup query on available s. @@ -359,7 +361,7 @@ namespace osu.Game.Beatmaps /// Results from the provided query. public List QueryBeatmapSets(Expression> query) { - return beatmaps.QueryAndPopulate(query); + return beatmaps.QueryBeatmapSets(query); } /// @@ -369,10 +371,11 @@ namespace osu.Game.Beatmaps /// 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); + BeatmapInfo set = beatmaps.QueryBeatmap(query); - if (set != null) - beatmaps.Populate(set); + // TODO Include() + //if (set != null) + // beatmaps.Populate(set); return set; } @@ -384,7 +387,7 @@ namespace osu.Game.Beatmaps /// Results from the provided query. public List QueryBeatmaps(Expression> query) { - lock (beatmaps) return beatmaps.QueryAndPopulate(query); + lock (beatmaps) return beatmaps.QueryBeatmaps(query); } /// @@ -424,7 +427,7 @@ namespace osu.Game.Beatmaps // check if this beatmap has already been imported and exit early if so. BeatmapSetInfo beatmapSet; lock (beatmaps) - beatmapSet = beatmaps.QueryAndPopulate(b => b.Hash == hash).FirstOrDefault(); + beatmapSet = beatmaps.QueryBeatmapSet(b => b.Hash == hash); if (beatmapSet != null) { @@ -459,11 +462,11 @@ namespace osu.Game.Beatmaps beatmapSet = new BeatmapSetInfo { - OnlineBeatmapSetID = metadata.OnlineBeatmapSetID, + BeatmapSetOnlineInfoId = metadata.BeatmapSetOnlineInfoId, Beatmaps = new List(), Hash = hash, Files = fileInfos, - Metadata = metadata + BeatmapMetadata = metadata }; var mapNames = reader.Filenames.Where(f => f.EndsWith(".osu")); @@ -485,11 +488,11 @@ namespace osu.Game.Beatmaps beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash(); // TODO: Diff beatmap metadata with set metadata and leave it here if necessary - beatmap.BeatmapInfo.Metadata = null; + beatmap.BeatmapInfo.BeatmapMetadata = 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) + beatmap.BeatmapInfo.RulesetInfo = rulesets.QueryRulesetInfo(r => r.Id == beatmap.BeatmapInfo.RulesetInfoId); + beatmap.BeatmapInfo.StarDifficulty = rulesets.QueryRulesetInfo(r => r.Id == beatmap.BeatmapInfo.RulesetInfoId)?.CreateInstance()?.CreateDifficultyCalculator(beatmap) .Calculate() ?? 0; beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo); @@ -509,9 +512,9 @@ namespace osu.Game.Beatmaps lock (beatmaps) { if (populate) - return beatmaps.QueryAndPopulate(b => !b.DeletePending).ToList(); + return beatmaps.QueryBeatmapSets(b => !b.DeletePending); else - return beatmaps.Query(b => !b.DeletePending).ToList(); + return beatmaps.QueryBeatmapSets(b => !b.DeletePending); } } diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index cc9a51b4e2..8266dc9161 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -1,18 +1,19 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using Newtonsoft.Json; -using SQLite.Net.Attributes; namespace osu.Game.Beatmaps { public class BeatmapMetadata { - [PrimaryKey, AutoIncrement] - public int ID { get; set; } + [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } - public int? OnlineBeatmapSetID { get; set; } + public int? BeatmapSetOnlineInfoId { get; set; } public string Title { get; set; } public string TitleUnicode { get; set; } diff --git a/osu.Game/Beatmaps/BeatmapOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapOnlineInfo.cs index 399cabda99..2264a4fc1a 100644 --- a/osu.Game/Beatmaps/BeatmapOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapOnlineInfo.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.ComponentModel.DataAnnotations.Schema; using Newtonsoft.Json; namespace osu.Game.Beatmaps @@ -10,6 +11,9 @@ namespace osu.Game.Beatmaps /// public class BeatmapOnlineInfo { + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } + /// /// The length in milliseconds of this beatmap's song. /// diff --git a/osu.Game/Beatmaps/BeatmapSetFileInfo.cs b/osu.Game/Beatmaps/BeatmapSetFileInfo.cs index a05362b32d..aec87296a0 100644 --- a/osu.Game/Beatmaps/BeatmapSetFileInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetFileInfo.cs @@ -1,27 +1,26 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; using osu.Game.IO; -using SQLite.Net.Attributes; -using SQLiteNetExtensions.Attributes; namespace osu.Game.Beatmaps { public class BeatmapSetFileInfo { - [PrimaryKey, AutoIncrement] + [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { get; set; } - [ForeignKey(typeof(BeatmapSetInfo)), NotNull] - public int BeatmapSetInfoID { get; set; } + [ForeignKey(nameof(BeatmapSetInfo))] + public int BeatmapSetInfoId { get; set; } + public BeatmapSetInfo BeatmapSetInfo { get; set; } - [ForeignKey(typeof(FileInfo)), NotNull] - public int FileInfoID { get; set; } - - [OneToOne(CascadeOperations = CascadeOperation.CascadeRead)] + [ForeignKey(nameof(FileInfo))] + public int FileInfoId { get; set; } public FileInfo FileInfo { get; set; } - [NotNull] + [Required] public string Filename { get; set; } } -} \ No newline at end of file +} diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index f47affcab8..9be00152c3 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -2,41 +2,36 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; using System.Linq; -using SQLite.Net.Attributes; -using SQLiteNetExtensions.Attributes; namespace osu.Game.Beatmaps { public class BeatmapSetInfo { - [PrimaryKey, AutoIncrement] - public int ID { get; set; } + [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } - public int? OnlineBeatmapSetID { get; set; } + public int? BeatmapSetOnlineInfoId { get; set; } - [OneToOne(CascadeOperations = CascadeOperation.All)] - public BeatmapMetadata Metadata { get; set; } + [ForeignKey(nameof(BeatmapMetadata))] + public int BeatmapMetadataId { get; set; } + public BeatmapMetadata BeatmapMetadata { get; set; } - [NotNull, ForeignKey(typeof(BeatmapMetadata))] - public int BeatmapMetadataID { get; set; } - - [OneToMany(CascadeOperations = CascadeOperation.All)] public List Beatmaps { get; set; } - [Ignore] + [NotMapped] public BeatmapSetOnlineInfo OnlineInfo { get; set; } public double MaxStarDifficulty => Beatmaps.Max(b => b.StarDifficulty); - [Indexed] public bool DeletePending { get; set; } public string Hash { get; set; } public string StoryboardFile => Files.FirstOrDefault(f => f.Filename.EndsWith(".osb"))?.Filename; - [OneToMany(CascadeOperations = CascadeOperation.All)] public List Files { get; set; } public bool Protected { get; set; } diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs index 6b59f0f298..c33ee66dac 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs @@ -2,6 +2,9 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; using Newtonsoft.Json; using osu.Game.Users; @@ -12,11 +15,19 @@ namespace osu.Game.Beatmaps /// public class BeatmapSetOnlineInfo { + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } + /// /// The author of the beatmaps in this set. /// public User Author; + public int BeatmapSetInfoId { get; set; } + public BeatmapSetInfo BeatmapSetInfo { get; set; } + + public List Beatmaps { get; set; } + /// /// The date this beatmap set was submitted to the online listing. /// @@ -35,7 +46,7 @@ namespace osu.Game.Beatmaps /// /// The different sizes of cover art for this beatmap set. /// - [JsonProperty(@"covers")] + [Required, JsonProperty(@"covers")] public BeatmapSetOnlineCovers Covers { get; set; } /// @@ -64,6 +75,9 @@ namespace osu.Game.Beatmaps public class BeatmapSetOnlineCovers { + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } + public string CoverLowRes { get; set; } [JsonProperty(@"cover@2x")] diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 0f2d8cffa6..ece006a818 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -2,9 +2,11 @@ // 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 Microsoft.EntityFrameworkCore; using osu.Game.Database; -using SQLite.Net; -using SQLiteNetExtensions.Extensions; namespace osu.Game.Beatmaps { @@ -19,13 +21,7 @@ namespace osu.Game.Beatmaps public event Action BeatmapHidden; public event Action BeatmapRestored; - /// - /// The current version of this store. Used for migrations (see ). - /// The initial version is 1. - /// - protected override int StoreVersion => 4; - - public BeatmapStore(SQLiteConnection connection) + public BeatmapStore(OsuDbContext connection) : base(connection) { } @@ -42,18 +38,13 @@ namespace osu.Game.Beatmaps { if (reset) { - Connection.DropTable(); - Connection.DropTable(); - Connection.DropTable(); - Connection.DropTable(); - Connection.DropTable(); + // https://stackoverflow.com/a/10450893 + Connection.Database.ExecuteSqlCommand("DELETE FROM BeatmapMetadata"); + Connection.Database.ExecuteSqlCommand("DELETE FROM BeatmapDifficulty"); + Connection.Database.ExecuteSqlCommand("DELETE FROM BeatmapSetInfo"); + Connection.Database.ExecuteSqlCommand("DELETE FROM BeatmapSetFileInfo"); + Connection.Database.ExecuteSqlCommand("DELETE FROM BeatmapInfo"); } - - Connection.CreateTable(); - Connection.CreateTable(); - Connection.CreateTable(); - Connection.CreateTable(); - Connection.CreateTable(); } protected override void StartupTasks() @@ -62,46 +53,14 @@ namespace osu.Game.Beatmaps cleanupPendingDeletions(); } - /// - /// Perform migrations between two store versions. - /// - /// The current store version. This will be zero on a fresh database initialisation. - /// The target version which we are migrating to (equal to the current ). - protected override void PerformMigration(int currentVersion, int targetVersion) - { - base.PerformMigration(currentVersion, targetVersion); - - while (currentVersion++ < targetVersion) - { - switch (currentVersion) - { - case 1: - case 2: - // cannot migrate; breaking underlying changes. - Reset(); - break; - case 3: - // Added MD5Hash column to BeatmapInfo - Connection.MigrateTable(); - break; - case 4: - // Added Hidden column to BeatmapInfo - Connection.MigrateTable(); - break; - } - } - } - /// /// Add a to the database. /// /// The beatmap to add. public void Add(BeatmapSetInfo beatmapSet) { - Connection.RunInTransaction(() => - { - Connection.InsertOrReplaceWithChildren(beatmapSet, true); - }); + Connection.BeatmapSetInfo.Update(beatmapSet); + Connection.SaveChanges(); BeatmapSetAdded?.Invoke(beatmapSet); } @@ -116,7 +75,8 @@ namespace osu.Game.Beatmaps if (beatmapSet.DeletePending) return false; beatmapSet.DeletePending = true; - Connection.Update(beatmapSet); + Connection.BeatmapSetInfo.Remove(beatmapSet); + Connection.SaveChanges(); BeatmapSetRemoved?.Invoke(beatmapSet); return true; @@ -132,7 +92,7 @@ namespace osu.Game.Beatmaps if (!beatmapSet.DeletePending) return false; beatmapSet.DeletePending = false; - Connection.Update(beatmapSet); + Connection.BeatmapSetInfo.Update(beatmapSet); BeatmapSetAdded?.Invoke(beatmapSet); return true; @@ -148,7 +108,7 @@ namespace osu.Game.Beatmaps if (beatmap.Hidden) return false; beatmap.Hidden = true; - Connection.Update(beatmap); + Connection.BeatmapInfo.Update(beatmap); BeatmapHidden?.Invoke(beatmap); return true; @@ -164,7 +124,7 @@ namespace osu.Game.Beatmaps if (!beatmap.Hidden) return false; beatmap.Hidden = false; - Connection.Update(beatmap); + Connection.BeatmapInfo.Update(beatmap); BeatmapRestored?.Invoke(beatmap); return true; @@ -172,11 +132,27 @@ namespace osu.Game.Beatmaps private void cleanupPendingDeletions() { - Connection.RunInTransaction(() => - { - foreach (var b in QueryAndPopulate(b => b.DeletePending && !b.Protected)) - Connection.Delete(b, true); - }); + Connection.BeatmapSetInfo.RemoveRange(Connection.BeatmapSetInfo.Where(b => b.DeletePending && !b.Protected)); + } + + public BeatmapSetInfo QueryBeatmapSet(Func query) + { + return Connection.BeatmapSetInfo.FirstOrDefault(query); + } + + public List QueryBeatmapSets(Expression> query) + { + return Connection.BeatmapSetInfo.Where(query).ToList(); + } + + public BeatmapInfo QueryBeatmap(Func query) + { + return Connection.BeatmapInfo.FirstOrDefault(query); + } + + public List QueryBeatmaps(Expression> query) + { + return Connection.BeatmapInfo.Where(query).ToList(); } } } diff --git a/osu.Game/Beatmaps/DifficultyCalculator.cs b/osu.Game/Beatmaps/DifficultyCalculator.cs index 60cbf0ac61..1cd0516b0e 100644 --- a/osu.Game/Beatmaps/DifficultyCalculator.cs +++ b/osu.Game/Beatmaps/DifficultyCalculator.cs @@ -40,7 +40,7 @@ namespace osu.Game.Beatmaps Objects = CreateBeatmapConverter().Convert(beatmap).HitObjects; foreach (var h in Objects) - h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.Difficulty); + h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BeatmapDifficulty); PreprocessHitObjects(); } diff --git a/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs b/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs index e216f1b83e..dfff949fe9 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs @@ -135,7 +135,7 @@ namespace osu.Game.Beatmaps.Drawables new OsuSpriteText { Font = @"Exo2.0-MediumItalic", - Text = $"{(beatmap.Metadata ?? beatmap.BeatmapSet.Metadata).Author}", + Text = $"{(beatmap.BeatmapMetadata ?? beatmap.BeatmapSetInfo.BeatmapMetadata).Author}", TextSize = 16, Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 42db025a40..2981b3b053 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -35,7 +35,7 @@ namespace osu.Game.Beatmaps.Drawables new ConstrainedIconContainer { RelativeSizeAxes = Axes.Both, - Icon = beatmap.Ruleset.CreateInstance().CreateIcon() + Icon = beatmap.RulesetInfo.CreateInstance().CreateIcon() } }; } diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index d8cd58d939..3746a1fabe 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -18,14 +18,14 @@ namespace osu.Game.Beatmaps public DummyWorkingBeatmap(OsuGameBase game) : base(new BeatmapInfo { - Metadata = new BeatmapMetadata + BeatmapMetadata = new BeatmapMetadata { Artist = "please load a beatmap!", Title = "no beatmaps available!", Author = "no one", }, - BeatmapSet = new BeatmapSetInfo(), - Difficulty = new BeatmapDifficulty + BeatmapSetInfo = new BeatmapSetInfo(), + BeatmapDifficulty = new BeatmapDifficulty { DrainRate = 0, CircleSize = 0, @@ -34,7 +34,7 @@ namespace osu.Game.Beatmaps SliderMultiplier = 0, SliderTickRate = 0, }, - Ruleset = new DummyRulesetInfo() + RulesetInfo = new DummyRulesetInfo() }) { this.game = game; diff --git a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs index 81695c3b5a..9d97ca8602 100644 --- a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs @@ -48,8 +48,8 @@ namespace osu.Game.Beatmaps.Formats { BeatmapInfo = new BeatmapInfo { - Metadata = new BeatmapMetadata(), - Difficulty = new BeatmapDifficulty(), + BeatmapMetadata = new BeatmapMetadata(), + BeatmapDifficulty = new BeatmapDifficulty(), }, }; diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs index 21fee0f465..9d6586ef26 100644 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs @@ -72,7 +72,7 @@ namespace osu.Game.Beatmaps.Formats { var pair = splitKeyVal(line, ':'); - var metadata = beatmap.BeatmapInfo.Metadata; + var metadata = beatmap.BeatmapInfo.BeatmapMetadata; switch (pair.Key) { case @"AudioFilename": @@ -97,9 +97,9 @@ namespace osu.Game.Beatmaps.Formats beatmap.BeatmapInfo.StackLeniency = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; case @"Mode": - beatmap.BeatmapInfo.RulesetID = int.Parse(pair.Value); + beatmap.BeatmapInfo.RulesetInfoId = int.Parse(pair.Value); - switch (beatmap.BeatmapInfo.RulesetID) + switch (beatmap.BeatmapInfo.RulesetInfoId) { case 0: parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); @@ -155,7 +155,7 @@ namespace osu.Game.Beatmaps.Formats { var pair = splitKeyVal(line, ':'); - var metadata = beatmap.BeatmapInfo.Metadata; + var metadata = beatmap.BeatmapInfo.BeatmapMetadata; switch (pair.Key) { case @"Title": @@ -177,17 +177,17 @@ namespace osu.Game.Beatmaps.Formats beatmap.BeatmapInfo.Version = pair.Value; break; case @"Source": - beatmap.BeatmapInfo.Metadata.Source = pair.Value; + beatmap.BeatmapInfo.BeatmapMetadata.Source = pair.Value; break; case @"Tags": - beatmap.BeatmapInfo.Metadata.Tags = pair.Value; + beatmap.BeatmapInfo.BeatmapMetadata.Tags = pair.Value; break; case @"BeatmapID": - beatmap.BeatmapInfo.OnlineBeatmapID = int.Parse(pair.Value); + beatmap.BeatmapInfo.BeatmapOnlineInfoId = int.Parse(pair.Value); break; case @"BeatmapSetID": - beatmap.BeatmapInfo.OnlineBeatmapSetID = int.Parse(pair.Value); - metadata.OnlineBeatmapSetID = int.Parse(pair.Value); + beatmap.BeatmapInfo.BeatmapSetOnlineInfoId = int.Parse(pair.Value); + metadata.BeatmapSetOnlineInfoId = int.Parse(pair.Value); break; } } @@ -196,7 +196,7 @@ namespace osu.Game.Beatmaps.Formats { var pair = splitKeyVal(line, ':'); - var difficulty = beatmap.BeatmapInfo.Difficulty; + var difficulty = beatmap.BeatmapInfo.BeatmapDifficulty; switch (pair.Key) { case @"HPDrainRate": @@ -270,7 +270,7 @@ namespace osu.Game.Beatmaps.Formats string filename = split[2].Trim('"'); if (type == EventType.Background) - beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; + beatmap.BeatmapInfo.BeatmapMetadata.BackgroundFile = filename; break; case EventType.Break: @@ -674,7 +674,7 @@ namespace osu.Game.Beatmaps.Formats } foreach (var hitObject in beatmap.HitObjects) - hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.Difficulty); + hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BeatmapDifficulty); } private KeyValuePair splitKeyVal(string line, char separator) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 277846ee80..4b50e50efe 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -24,8 +24,8 @@ namespace osu.Game.Beatmaps protected WorkingBeatmap(BeatmapInfo beatmapInfo) { BeatmapInfo = beatmapInfo; - BeatmapSetInfo = beatmapInfo.BeatmapSet; - Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); + BeatmapSetInfo = beatmapInfo.BeatmapSetInfo; + Metadata = beatmapInfo.BeatmapMetadata ?? BeatmapSetInfo?.BeatmapMetadata ?? new BeatmapMetadata(); Mods.ValueChanged += mods => applyRateAdjustments(); } diff --git a/osu.Game/Database/DatabaseBackedStore.cs b/osu.Game/Database/DatabaseBackedStore.cs index d8e2e35bd7..f4b6a866dc 100644 --- a/osu.Game/Database/DatabaseBackedStore.cs +++ b/osu.Game/Database/DatabaseBackedStore.cs @@ -2,27 +2,23 @@ // 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 Microsoft.EntityFrameworkCore; 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 readonly OsuDbContext Connection; - protected virtual int StoreVersion => 1; - - protected DatabaseBackedStore(SQLiteConnection connection, Storage storage = null) + protected DatabaseBackedStore(OsuDbContext connection, Storage storage = null) { Storage = storage; Connection = connection; + Connection.Database.SetCommandTimeout(new TimeSpan(TimeSpan.TicksPerSecond * 10)); try { @@ -33,36 +29,6 @@ namespace osu.Game.Database Logger.Error(e, $@"Failed to initialise the {GetType()}! Trying again with a clean database..."); Prepare(true); } - - checkMigrations(); - } - - private void checkMigrations() - { - var storeName = GetType().Name; - - var reportedVersion = Connection.Table().Where(s => s.StoreName == storeName).FirstOrDefault() ?? new StoreVersion - { - StoreName = storeName, - Version = 0 - }; - - if (reportedVersion.Version != StoreVersion) - PerformMigration(reportedVersion.Version, reportedVersion.Version = StoreVersion); - - Connection.InsertOrReplace(reportedVersion); - - StartupTasks(); - } - - /// - /// Called when the database version of this store doesn't match the local version. - /// Any manual migration operations should be performed in this. - /// - /// The current store version. This will be zero on a fresh database initialisation. - /// The target version which we are migrating to (equal to the current ). - protected virtual void PerformMigration(int currentVersion, int targetVersion) - { } /// @@ -83,43 +49,6 @@ namespace osu.Game.Database /// 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 filter to refine results. - /// - public List QueryAndPopulate(Expression> filter) - 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)) diff --git a/osu.Game/Database/DbContextBase.cs b/osu.Game/Database/DbContextBase.cs new file mode 100644 index 0000000000..93f4ac4c23 --- /dev/null +++ b/osu.Game/Database/DbContextBase.cs @@ -0,0 +1,8 @@ +using Microsoft.EntityFrameworkCore; + +namespace osu.Game.Database +{ + public abstract class DbContextBase:DbContext + { + } +} diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs new file mode 100644 index 0000000000..41ae0711f4 --- /dev/null +++ b/osu.Game/Database/OsuDbContext.cs @@ -0,0 +1,55 @@ +using Microsoft.EntityFrameworkCore; +using osu.Game.Beatmaps; +using osu.Game.Input.Bindings; +using osu.Game.IO; +using osu.Game.Rulesets; + +namespace osu.Game.Database +{ + public class OsuDbContext : DbContext + { + private readonly string connectionString; + + public OsuDbContext() + { + connectionString = "DataSource=:memory:"; + } + + public OsuDbContext(string connectionString) + { + this.connectionString = connectionString; + } + + public DbSet BeatmapMetadata { get; set; } + public DbSet BeatmapDifficulty { get; set; } + public DbSet BeatmapInfo { get; set; } + public DbSet BeatmapSetInfo { get; set; } + public DbSet BeatmapSetFileInfo { get; set; } + public DbSet DatabasedKeyBinding { get; set; } + public DbSet FileInfo { get; set; } + public DbSet RulesetInfo { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + base.OnConfiguring(optionsBuilder); + optionsBuilder.UseSqlite(connectionString); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + modelBuilder.Entity().HasIndex(b => b.MD5Hash); + modelBuilder.Entity().HasIndex(b => b.DeletePending); + modelBuilder.Entity().HasIndex(b => b.Variant); + modelBuilder.Entity().HasIndex(b => b.IntAction); + modelBuilder.Entity().HasIndex(b => b.Hash).IsUnique(); + modelBuilder.Entity().HasIndex(b => b.ReferenceCount); + modelBuilder.Entity().HasIndex(b => b.Name).IsUnique(); + modelBuilder.Entity().HasIndex(b => b.InstantiationInfo).IsUnique(); + modelBuilder.Entity().HasIndex(b => b.Available); + //modelBuilder.Entity().HasOne(b => b.BeatmapSetOnlineInfo).WithOne(bs => bs.BeatmapMetadata).OnDelete(DeleteBehavior.SetNull); + //modelBuilder.Entity().HasOne(b => b.BeatmapSetOnlineInfo).WithOne(bs => bs.BeatmapSetInfo).OnDelete(DeleteBehavior.SetNull); + //modelBuilder.Entity().HasOne(b => b.BeatmapSetOnlineInfo).WithMany(bs => bs.Beatmaps).OnDelete(DeleteBehavior.SetNull); + } + } +} diff --git a/osu.Game/Database/StoreVersion.cs b/osu.Game/Database/StoreVersion.cs deleted file mode 100644 index 00314875a6..0000000000 --- a/osu.Game/Database/StoreVersion.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using SQLite.Net.Attributes; - -namespace osu.Game.Database -{ - public class StoreVersion - { - [PrimaryKey] - public string StoreName { get; set; } - - public int Version { get; set; } - } -} diff --git a/osu.Game/IO/FileInfo.cs b/osu.Game/IO/FileInfo.cs index 367fd68f7b..7ba75eeb0c 100644 --- a/osu.Game/IO/FileInfo.cs +++ b/osu.Game/IO/FileInfo.cs @@ -1,22 +1,21 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; using System.IO; -using SQLite.Net.Attributes; namespace osu.Game.IO { public class FileInfo { - [PrimaryKey, AutoIncrement] - public int ID { get; set; } + [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { 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 index c3d8c1df46..416b6c2989 100644 --- a/osu.Game/IO/FileStore.cs +++ b/osu.Game/IO/FileStore.cs @@ -4,12 +4,12 @@ using System; using System.IO; using System.Linq; +using Microsoft.EntityFrameworkCore; 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 { @@ -22,9 +22,7 @@ namespace osu.Game.IO public readonly ResourceStore Store; - protected override int StoreVersion => 2; - - public FileStore(SQLiteConnection connection, Storage storage) : base(connection, storage) + public FileStore(OsuDbContext connection, Storage storage) : base(connection, storage) { Store = new NamespacedResourceStore(new StorageBackedResourceStore(storage), prefix); } @@ -44,10 +42,8 @@ namespace osu.Game.IO if (Storage.ExistsDirectory(prefix)) Storage.DeleteDirectory(prefix); - Connection.DropTable(); + Connection.Database.ExecuteSqlCommand("DELETE FROM FileInfo"); } - - Connection.CreateTable(); } protected override void StartupTasks() @@ -56,33 +52,11 @@ namespace osu.Game.IO deletePending(); } - /// - /// Perform migrations between two store versions. - /// - /// The current store version. This will be zero on a fresh database initialisation. - /// The target version which we are migrating to (equal to the current ). - protected override void PerformMigration(int currentVersion, int targetVersion) - { - base.PerformMigration(currentVersion, targetVersion); - - while (currentVersion++ < targetVersion) - { - switch (currentVersion) - { - case 1: - case 2: - // cannot migrate; breaking underlying changes. - Reset(); - break; - } - } - } - public FileInfo Add(Stream data, bool reference = true) { string hash = data.ComputeSHA2Hash(); - var existing = Connection.Table().Where(f => f.Hash == hash).FirstOrDefault(); + var existing = Connection.FileInfo.Where(f => f.Hash == hash).FirstOrDefault(); var info = existing ?? new FileInfo { Hash = hash }; @@ -100,61 +74,54 @@ namespace osu.Game.IO } if (existing == null) - Connection.Insert(info); + Connection.FileInfo.Add(info); if (reference || existing == null) Reference(info); + Connection.SaveChanges(); return info; } public void Reference(params FileInfo[] files) { - Connection.RunInTransaction(() => + var incrementedFiles = files.GroupBy(f => f.Id).Select(f => { - var incrementedFiles = files.GroupBy(f => f.ID).Select(f => - { - var accurateRefCount = Connection.Get(f.First().ID); - accurateRefCount.ReferenceCount += f.Count(); - return accurateRefCount; - }); - - Connection.UpdateAll(incrementedFiles); + var accurateRefCount = Connection.Find(f.First().Id); + accurateRefCount.ReferenceCount += f.Count(); + return accurateRefCount; }); + //Connection.FileInfo.UpdateRange(incrementedFiles); + Connection.SaveChanges(); } public void Dereference(params FileInfo[] files) { - Connection.RunInTransaction(() => + var incrementedFiles = files.GroupBy(f => f.Id).Select(f => { - var incrementedFiles = files.GroupBy(f => f.ID).Select(f => - { - var accurateRefCount = Connection.Get(f.First().ID); - accurateRefCount.ReferenceCount -= f.Count(); - return accurateRefCount; - }); - - Connection.UpdateAll(incrementedFiles); + var accurateRefCount = Connection.Find(f.First().Id); + accurateRefCount.ReferenceCount -= f.Count(); + return accurateRefCount; }); + + //Connection.FileInfo.UpdateRange(incrementedFiles); + Connection.SaveChanges(); } private void deletePending() { - Connection.RunInTransaction(() => + foreach (var f in Connection.FileInfo.Where(f => f.ReferenceCount < 1)) { - foreach (var f in Query(f => f.ReferenceCount < 1)) + try { - try - { - Storage.Delete(Path.Combine(prefix, f.StoragePath)); - Connection.Delete(f); - } - catch (Exception e) - { - Logger.Error(e, $@"Could not delete beatmap {f}"); - } + Storage.Delete(Path.Combine(prefix, f.StoragePath)); + Connection.FileInfo.Remove(f); } - }); + catch (Exception e) + { + Logger.Error(e, $@"Could not delete beatmap {f}"); + } + } } } -} \ No newline at end of file +} diff --git a/osu.Game/Input/Bindings/DatabasedKeyBinding.cs b/osu.Game/Input/Bindings/DatabasedKeyBinding.cs index cbf74d6984..73a8e117d3 100644 --- a/osu.Game/Input/Bindings/DatabasedKeyBinding.cs +++ b/osu.Game/Input/Bindings/DatabasedKeyBinding.cs @@ -1,23 +1,23 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; using osu.Framework.Input.Bindings; using osu.Game.Rulesets; -using SQLite.Net.Attributes; -using SQLiteNetExtensions.Attributes; namespace osu.Game.Input.Bindings { [Table("KeyBinding")] public class DatabasedKeyBinding : KeyBinding { - [PrimaryKey, AutoIncrement] - public int ID { get; set; } + [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } - [ForeignKey(typeof(RulesetInfo))] - public int? RulesetID { get; set; } + [ForeignKey(nameof(RulesetInfo))] + public int? RulesetInfoId { get; set; } + public RulesetInfo RulesetInfo; - [Indexed] public int? Variant { get; set; } [Column("Keys")] @@ -27,7 +27,6 @@ namespace osu.Game.Input.Bindings private set { KeyCombination = value; } } - [Indexed] [Column("Action")] public int IntAction { @@ -35,4 +34,4 @@ namespace osu.Game.Input.Bindings set { Action = value; } } } -} \ No newline at end of file +} diff --git a/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs b/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs index 0a4fcf4389..41a2f54277 100644 --- a/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs +++ b/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs @@ -48,7 +48,7 @@ namespace osu.Game.Input.Bindings protected override void ReloadMappings() { - KeyBindings = store.Query(ruleset?.ID, variant); + KeyBindings = store.Query(ruleset?.Id, variant); } } } \ No newline at end of file diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index c5ba1683dd..3157bee6a7 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -4,78 +4,54 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.EntityFrameworkCore; using osu.Framework.Input.Bindings; using osu.Framework.Platform; using osu.Game.Database; using osu.Game.Input.Bindings; using osu.Game.Rulesets; -using SQLite.Net; namespace osu.Game.Input { public class KeyBindingStore : DatabaseBackedStore { - public KeyBindingStore(SQLiteConnection connection, RulesetStore rulesets, Storage storage = null) + public KeyBindingStore(OsuDbContext connection, RulesetStore rulesets, Storage storage = null) : base(connection, storage) { foreach (var info in rulesets.AllRulesets) { var ruleset = info.CreateInstance(); foreach (var variant in ruleset.AvailableVariants) - insertDefaults(ruleset.GetDefaultKeyBindings(variant), info.ID, variant); + insertDefaults(ruleset.GetDefaultKeyBindings(variant), info.Id, variant); } } public void Register(KeyBindingInputManager manager) => insertDefaults(manager.DefaultKeyBindings); - protected override int StoreVersion => 3; - - protected override void PerformMigration(int currentVersion, int targetVersion) - { - base.PerformMigration(currentVersion, targetVersion); - - while (currentVersion++ < targetVersion) - { - switch (currentVersion) - { - case 1: - case 2: - case 3: - // cannot migrate; breaking underlying changes. - Reset(); - break; - } - } - } - protected override void Prepare(bool reset = false) { - if (reset) - Connection.DropTable(); - - Connection.CreateTable(); + Connection.Database.ExecuteSqlCommand("DELETE FROM KeyBinding"); } private void insertDefaults(IEnumerable defaults, int? rulesetId = null, int? variant = null) { - var query = Query(rulesetId, variant); - // compare counts in database vs defaults foreach (var group in defaults.GroupBy(k => k.Action)) { int count; - while (group.Count() > (count = query.Count(k => (int)k.Action == (int)group.Key))) + while (group.Count() > (count = Query(rulesetId, variant).Count(k => (int)k.Action == (int)group.Key))) { var insertable = group.Skip(count).First(); // insert any defaults which are missing. - Connection.Insert(new DatabasedKeyBinding + Connection.DatabasedKeyBinding.Add(new DatabasedKeyBinding { KeyCombination = insertable.KeyCombination, Action = insertable.Action, - RulesetID = rulesetId, + RulesetInfoId = rulesetId, Variant = variant }); + Connection.SaveChanges(); } } } @@ -85,9 +61,18 @@ namespace osu.Game.Input typeof(DatabasedKeyBinding) }; - public IEnumerable Query(int? rulesetId = null, int? variant = null) => - Query(b => b.RulesetID == rulesetId && b.Variant == variant); + public List Query(int? rulesetId = null, int? variant = null) => + new List(Connection.DatabasedKeyBinding.Where(b => b.RulesetInfoId == rulesetId && b.Variant == variant)); - public void Update(KeyBinding keyBinding) => Connection.Update(keyBinding); + public void Update(KeyBinding keyBinding) + { + var dbKeyBinding = Connection.DatabasedKeyBinding.FirstOrDefault(kb => kb.ToString() == keyBinding.ToString()); + if (dbKeyBinding!=null) + { + dbKeyBinding.KeyCombination = keyBinding.KeyCombination; + dbKeyBinding.Action = keyBinding.Action; + } + Connection.SaveChanges(); + } } } diff --git a/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs b/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs index 5a9f609bca..f8e20d1e9f 100644 --- a/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs +++ b/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs @@ -19,6 +19,6 @@ namespace osu.Game.Online.API.Requests Progress += (current, total) => DownloadProgressed?.Invoke((float) current / total); } - protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download"; + protected override string Target => $@"beatmapsets/{BeatmapSet.BeatmapSetOnlineInfoId}/download"; } } diff --git a/osu.Game/Online/API/Requests/GetBeatmapDetailsRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapDetailsRequest.cs index 934ef7ffa2..233e16beb1 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapDetailsRequest.cs +++ b/osu.Game/Online/API/Requests/GetBeatmapDetailsRequest.cs @@ -10,7 +10,7 @@ namespace osu.Game.Online.API.Requests { private readonly BeatmapInfo beatmap; - private string lookupString => beatmap.OnlineBeatmapID > 0 ? beatmap.OnlineBeatmapID.ToString() : $@"lookup?checksum={beatmap.Hash}&filename={System.Uri.EscapeUriString(beatmap.Path)}"; + private string lookupString => beatmap.BeatmapOnlineInfoId > 0 ? beatmap.BeatmapOnlineInfoId.ToString() : $@"lookup?checksum={beatmap.Hash}&filename={System.Uri.EscapeUriString(beatmap.Path)}"; public GetBeatmapDetailsRequest(BeatmapInfo beatmap) { diff --git a/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs index 470e13ea7b..37d7a5e2d6 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs @@ -30,7 +30,7 @@ namespace osu.Game.Online.API.Requests this.direction = direction; } - protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.ID ?? 0}&s={(int)rankStatus}&sort={sortCriteria.ToString().ToLower()}_{directionString}"; + protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.Id ?? 0}&s={(int)rankStatus}&sort={sortCriteria.ToString().ToLower()}_{directionString}"; } public class GetBeatmapSetsResponse : BeatmapMetadata @@ -63,8 +63,8 @@ namespace osu.Game.Online.API.Requests { return new BeatmapSetInfo { - OnlineBeatmapSetID = onlineId, - Metadata = this, + BeatmapSetOnlineInfoId = onlineId, + BeatmapMetadata = this, OnlineInfo = new BeatmapSetOnlineInfo { Author = new User @@ -99,8 +99,8 @@ namespace osu.Game.Online.API.Requests { return new BeatmapInfo { - Metadata = this, - Ruleset = rulesets.GetRuleset(ruleset), + BeatmapMetadata = this, + RulesetInfo = rulesets.GetRuleset(ruleset), StarDifficulty = starDifficulty, OnlineInfo = new BeatmapOnlineInfo { diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 537fce2548..6bb654f403 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -18,8 +18,8 @@ namespace osu.Game.Online.API.Requests public GetScoresRequest(BeatmapInfo beatmap) { - if (!beatmap.OnlineBeatmapID.HasValue) - throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}."); + if (!beatmap.BeatmapOnlineInfoId.HasValue) + throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.BeatmapOnlineInfoId)}."); this.beatmap = beatmap; @@ -32,7 +32,7 @@ namespace osu.Game.Online.API.Requests score.ApplyBeatmap(beatmap); } - protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores"; + protected override string Target => $@"beatmaps/{beatmap.BeatmapOnlineInfoId}/scores"; } public class GetScoresResponse @@ -116,7 +116,7 @@ namespace osu.Game.Online.API.Requests public void ApplyBeatmap(BeatmapInfo beatmap) { Beatmap = beatmap; - Ruleset = beatmap.Ruleset; + Ruleset = beatmap.RulesetInfo; // Evaluate the mod string Mods = Ruleset.CreateInstance().GetAllMods().Where(mod => modStrings.Contains(mod.ShortenedName)).ToArray(); diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index c137b8f6f5..6dacef6e8d 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -110,7 +110,7 @@ namespace osu.Game configRuleset = LocalConfig.GetBindable(OsuSetting.Ruleset); Ruleset.Value = RulesetStore.GetRuleset(configRuleset.Value); - Ruleset.ValueChanged += r => configRuleset.Value = r.ID ?? 0; + Ruleset.ValueChanged += r => configRuleset.Value = r.Id ?? 0; } private ScheduledDelegate scoreLoad; diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 8e7bfa8a76..f2ee6effb9 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -3,7 +3,9 @@ using System; using System.Diagnostics; +using System.Linq; using System.Reflection; +using Microsoft.EntityFrameworkCore; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Development; @@ -17,7 +19,6 @@ 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.Database; using osu.Game.Input; @@ -82,14 +83,21 @@ namespace osu.Game protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); - private SQLiteConnection createConnection() - { - var conn = Host.Storage.GetDatabase(@"client"); - conn.BusyTimeout = new TimeSpan(TimeSpan.TicksPerSecond * 10); - return conn; - } + //private OsuDbContext dbContext; - private SQLiteConnection connection; + private OsuDbContext createDbContext() + { + var connectionString = Host.Storage.GetDatabaseConnectionString(@"client"); + var context = new OsuDbContext(connectionString); + var connection = context.Database.GetDbConnection(); + connection.Open(); + using (var command = connection.CreateCommand()) + { + command.CommandText = "PRAGMA journal_mode=WAL;"; + command.ExecuteNonQuery(); + } + return context; + } [BackgroundDependencyLoader] private void load() @@ -97,8 +105,10 @@ namespace osu.Game dependencies.Cache(this); dependencies.Cache(LocalConfig); - connection = createConnection(); - connection.CreateTable(); + + using (var dbContext = createDbContext()) + if (dbContext.Database.GetPendingMigrations().Any()) + dbContext.Database.Migrate(); dependencies.Cache(API = new APIAccess { @@ -106,11 +116,11 @@ namespace osu.Game Token = LocalConfig.Get(OsuSetting.Token) }); - dependencies.Cache(RulesetStore = new RulesetStore(connection)); - dependencies.Cache(FileStore = new FileStore(connection, Host.Storage)); - dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, FileStore, connection, RulesetStore, API, Host)); - dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, connection, Host, BeatmapManager, RulesetStore)); - dependencies.Cache(KeyBindingStore = new KeyBindingStore(connection, RulesetStore)); + dependencies.Cache(RulesetStore = new RulesetStore(createDbContext())); + dependencies.Cache(FileStore = new FileStore(createDbContext(), Host.Storage)); + dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, FileStore, createDbContext(), RulesetStore, API, Host)); + dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, createDbContext(), Host, BeatmapManager, RulesetStore)); + dependencies.Cache(KeyBindingStore = new KeyBindingStore(createDbContext(), RulesetStore)); dependencies.Cache(new OsuColour()); //this completely overrides the framework default. will need to change once we make a proper FontStore. @@ -237,7 +247,7 @@ namespace osu.Game LocalConfig.Save(); } - connection?.Dispose(); + //dbContext?.Dispose(); base.Dispose(isDisposing); } diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index a93ccbf704..25e598ffb9 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -43,8 +43,8 @@ namespace osu.Game.Overlays.BeatmapSet beatmapSet = value; Picker.BeatmapSet = author.BeatmapSet = details.BeatmapSet = BeatmapSet; - title.Text = BeatmapSet.Metadata.Title; - artist.Text = BeatmapSet.Metadata.Artist; + title.Text = BeatmapSet.BeatmapMetadata.Title; + artist.Text = BeatmapSet.BeatmapMetadata.Artist; cover?.FadeOut(400, Easing.Out); coverContainer.Add(cover = new DelayedLoadWrapper(new BeatmapSetCover(BeatmapSet) diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs index 4a59591a72..16cb19dbd7 100644 --- a/osu.Game/Overlays/BeatmapSet/Info.cs +++ b/osu.Game/Overlays/BeatmapSet/Info.cs @@ -33,8 +33,8 @@ namespace osu.Game.Overlays.BeatmapSet if (value == beatmapSet) return; beatmapSet = value; - source.Text = BeatmapSet.Metadata.Source; - tags.Text = BeatmapSet.Metadata.Tags; + source.Text = BeatmapSet.BeatmapMetadata.Source; + tags.Text = BeatmapSet.BeatmapMetadata.Tags; } } diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs index 1675a2f663..8d3d91fcd2 100644 --- a/osu.Game/Overlays/Direct/DirectGridPanel.cs +++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs @@ -66,13 +66,13 @@ namespace osu.Game.Overlays.Direct { new OsuSpriteText { - Text = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title), + Text = localisation.GetUnicodePreference(SetInfo.BeatmapMetadata.TitleUnicode, SetInfo.BeatmapMetadata.Title), TextSize = 18, Font = @"Exo2.0-BoldItalic", }, new OsuSpriteText { - Text = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), + Text = localisation.GetUnicodePreference(SetInfo.BeatmapMetadata.ArtistUnicode, SetInfo.BeatmapMetadata.Artist), Font = @"Exo2.0-BoldItalic", }, }, @@ -116,7 +116,7 @@ namespace osu.Game.Overlays.Direct }, new OsuSpriteText { - Text = SetInfo.Metadata.Author, + Text = SetInfo.BeatmapMetadata.Author, TextSize = 14, Font = @"Exo2.0-SemiBoldItalic", Shadow = false, @@ -132,11 +132,11 @@ namespace osu.Game.Overlays.Direct { new OsuSpriteText { - Text = $"from {SetInfo.Metadata.Source}", + Text = $"from {SetInfo.BeatmapMetadata.Source}", TextSize = 14, Shadow = false, Colour = colours.Gray5, - Alpha = string.IsNullOrEmpty(SetInfo.Metadata.Source) ? 0f : 1f, + Alpha = string.IsNullOrEmpty(SetInfo.BeatmapMetadata.Source) ? 0f : 1f, }, }, }, diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs index 6702b7394c..b46ce78654 100644 --- a/osu.Game/Overlays/Direct/DirectListPanel.cs +++ b/osu.Game/Overlays/Direct/DirectListPanel.cs @@ -54,13 +54,13 @@ namespace osu.Game.Overlays.Direct { new OsuSpriteText { - Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title), + Current = localisation.GetUnicodePreference(SetInfo.BeatmapMetadata.TitleUnicode, SetInfo.BeatmapMetadata.Title), TextSize = 18, Font = @"Exo2.0-BoldItalic", }, new OsuSpriteText { - Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), + Current = localisation.GetUnicodePreference(SetInfo.BeatmapMetadata.ArtistUnicode, SetInfo.BeatmapMetadata.Artist), Font = @"Exo2.0-BoldItalic", }, new FillFlowContainer @@ -101,7 +101,7 @@ namespace osu.Game.Overlays.Direct }, new OsuSpriteText { - Text = SetInfo.Metadata.Author, + Text = SetInfo.BeatmapMetadata.Author, TextSize = 14, Font = @"Exo2.0-SemiBoldItalic", }, @@ -109,11 +109,11 @@ namespace osu.Game.Overlays.Direct }, new OsuSpriteText { - Text = $"from {SetInfo.Metadata.Source}", + Text = $"from {SetInfo.BeatmapMetadata.Source}", Anchor = Anchor.TopRight, Origin = Anchor.TopRight, TextSize = 14, - Alpha = string.IsNullOrEmpty(SetInfo.Metadata.Source) ? 0f : 1f, + Alpha = string.IsNullOrEmpty(SetInfo.BeatmapMetadata.Source) ? 0f : 1f, }, }, }, diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs index 28d26d0641..1500723680 100644 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ b/osu.Game/Overlays/Direct/FilterControl.cs @@ -70,7 +70,7 @@ namespace osu.Game.Overlays.Direct private void Bindable_ValueChanged(RulesetInfo obj) { - iconContainer.FadeTo(Ruleset.ID == obj?.ID ? 1f : 0.5f, 100); + iconContainer.FadeTo(Ruleset.Id == obj?.Id ? 1f : 0.5f, 100); } public RulesetToggleButton(Bindable bindable, RulesetInfo ruleset) diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 5b5003b30f..df08400ee9 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -57,9 +57,9 @@ namespace osu.Game.Overlays var tags = new List(); foreach (var s in beatmapSets) { - artists.Add(s.Metadata.Artist); - songs.Add(s.Metadata.Title); - tags.AddRange(s.Metadata.Tags.Split(' ')); + artists.Add(s.BeatmapMetadata.Artist); + songs.Add(s.BeatmapMetadata.Title); + tags.AddRange(s.BeatmapMetadata.Tags.Split(' ')); } ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags)); @@ -175,8 +175,8 @@ namespace osu.Game.Overlays private void setAdded(BeatmapSetInfo set) { // if a new map was imported, we should remove it from search results (download completed etc.) - panels?.FirstOrDefault(p => p.SetInfo.OnlineBeatmapSetID == set.OnlineBeatmapSetID)?.FadeOut(400).Expire(); - BeatmapSets = BeatmapSets?.Where(b => b.OnlineBeatmapSetID != set.OnlineBeatmapSetID); + panels?.FirstOrDefault(p => p.SetInfo.BeatmapSetOnlineInfoId == set.BeatmapSetOnlineInfoId)?.FadeOut(400).Expire(); + BeatmapSets = BeatmapSets?.Where(b => b.BeatmapSetOnlineInfoId != set.BeatmapSetOnlineInfoId); } private void updateResultCounts() @@ -260,7 +260,7 @@ namespace osu.Game.Overlays { BeatmapSets = r?. Select(response => response.ToBeatmapSet(rulesets)). - Where(b => beatmaps.QueryBeatmapSet(q => q.OnlineBeatmapSetID == b.OnlineBeatmapSetID) == null); + Where(b => beatmaps.QueryBeatmapSet(q => q.BeatmapSetOnlineInfoId == b.BeatmapSetOnlineInfoId) == null); recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value); }; diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs index bd69403831..8442203f14 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.KeyBinding [BackgroundDependencyLoader] private void load(KeyBindingStore store) { - var bindings = store.Query(Ruleset?.ID, variant); + var bindings = store.Query(Ruleset?.Id, variant); foreach (var defaultGroup in Defaults.GroupBy(d => d.Action)) { diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index 723b3f4e96..6a8476dddd 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -66,7 +66,7 @@ namespace osu.Game.Overlays.Music hoverColour = colours.Yellow; artistColour = colours.Gray9; - var metadata = BeatmapSetInfo.Metadata; + var metadata = BeatmapSetInfo.BeatmapMetadata; FilterTerms = metadata.SearchableTerms; Children = new Drawable[] diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs index 6f1eaded7f..b989b1cb1c 100644 --- a/osu.Game/Overlays/Music/PlaylistList.cs +++ b/osu.Game/Overlays/Music/PlaylistList.cs @@ -105,7 +105,7 @@ namespace osu.Game.Overlays.Music public bool RemoveBeatmapSet(BeatmapSetInfo beatmapSet) { - var itemToRemove = items.FirstOrDefault(i => i.BeatmapSetInfo.ID == beatmapSet.ID); + var itemToRemove = items.FirstOrDefault(i => i.BeatmapSetInfo.Id == beatmapSet.Id); if (itemToRemove == null) return false; return items.Remove(itemToRemove); @@ -117,7 +117,7 @@ namespace osu.Game.Overlays.Music set { foreach (PlaylistItem s in items.Children) - s.Selected = s.BeatmapSetInfo.ID == value?.ID; + s.Selected = s.BeatmapSetInfo.Id == value?.Id; } } diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index d05ad85726..e78e3d44be 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -115,7 +115,7 @@ namespace osu.Game.Overlays.Music private void itemSelected(BeatmapSetInfo set) { - if (set.ID == (beatmapBacking.Value?.BeatmapSetInfo?.ID ?? -1)) + if (set.Id == (beatmapBacking.Value?.BeatmapSetInfo?.Id ?? -1)) { beatmapBacking.Value?.Track?.Seek(0); return; diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 64d0d628f0..4efd395212 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -302,8 +302,8 @@ namespace osu.Game.Overlays else { //figure out the best direction based on order in playlist. - var last = playlist.BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count(); - var next = beatmap == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != beatmap.BeatmapSetInfo?.ID).Count(); + var last = playlist.BeatmapSets.TakeWhile(b => b.Id != current.BeatmapSetInfo?.Id).Count(); + var next = beatmap == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.Id != beatmap.BeatmapSetInfo?.Id).Count(); direction = last > next ? TransformDirection.Prev : TransformDirection.Next; } diff --git a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs index 60c1261190..cd18980e32 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs @@ -98,7 +98,7 @@ namespace osu.Game.Overlays.Toolbar { foreach (ToolbarModeButton m in modeButtons.Children.Cast()) { - bool isActive = m.Ruleset.ID == ruleset.ID; + bool isActive = m.Ruleset.Id == ruleset.Id; m.Active = isActive; if (isActive) activeButton = m; diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs index 740369b1b6..9c51e1e66a 100644 --- a/osu.Game/Rulesets/RulesetInfo.cs +++ b/osu.Game/Rulesets/RulesetInfo.cs @@ -2,26 +2,23 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using SQLite.Net.Attributes; +using System.ComponentModel.DataAnnotations; namespace osu.Game.Rulesets { public class RulesetInfo : IEquatable { - [PrimaryKey, AutoIncrement] - public int? ID { get; set; } + [Key] + public int? Id { get; set; } - [Indexed(Unique = true)] public string Name { get; set; } - [Indexed(Unique = true)] public string InstantiationInfo { get; set; } - [Indexed] public bool Available { get; set; } public virtual Ruleset CreateInstance() => (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo), this); - public bool Equals(RulesetInfo other) => other != null && ID == other.ID && Available == other.Available && Name == other.Name && InstantiationInfo == other.InstantiationInfo; + public bool Equals(RulesetInfo other) => other != null && Id == other.Id && Available == other.Available && Name == other.Name && InstantiationInfo == other.InstantiationInfo; } } diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 5eef4a8470..f29929a6aa 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -6,8 +6,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; +using Microsoft.EntityFrameworkCore; using osu.Game.Database; -using SQLite.Net; namespace osu.Game.Rulesets { @@ -18,9 +18,9 @@ namespace osu.Game.Rulesets { private static readonly Dictionary loaded_assemblies = new Dictionary(); - public IEnumerable AllRulesets => Query().Where(r => r.Available); + public IEnumerable AllRulesets => Connection.RulesetInfo.Where(r => r.Available); - public RulesetStore(SQLiteConnection connection) : base(connection) + public RulesetStore(OsuDbContext connection) : base(connection) { } @@ -38,54 +38,50 @@ namespace osu.Game.Rulesets protected override void Prepare(bool reset = false) { - - Connection.CreateTable(); - if (reset) { - Connection.DeleteAll(); + Connection.Database.ExecuteSqlCommand("DELETE FROM RulesetInfo"); } - + var instances = loaded_assemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r, new RulesetInfo())); - Connection.RunInTransaction(() => + //add all legacy modes in correct order + foreach (var r in instances.Where(r => r.LegacyID >= 0).OrderBy(r => r.LegacyID)) { - //add all legacy modes in correct order - foreach (var r in instances.Where(r => r.LegacyID >= 0).OrderBy(r => r.LegacyID)) + var rulesetInfo = createRulesetInfo(r); + if (Connection.RulesetInfo.SingleOrDefault(rsi=>rsi.Id==rulesetInfo.Id)==null) { - Connection.InsertOrReplace(createRulesetInfo(r)); + Connection.RulesetInfo.Add(rulesetInfo); } + } - //add any other modes - foreach (var r in instances.Where(r => r.LegacyID < 0)) - { - var us = createRulesetInfo(r); - - var existing = Query().Where(ri => ri.InstantiationInfo == us.InstantiationInfo).FirstOrDefault(); - - if (existing == null) - Connection.Insert(us); - } - }); - - Connection.RunInTransaction(() => + //add any other modes + foreach (var r in instances.Where(r => r.LegacyID < 0)) { - //perform a consistency check - foreach (var r in Query()) - { - try - { - r.CreateInstance(); - r.Available = true; - } - catch - { - r.Available = false; - } + var us = createRulesetInfo(r); - Connection.Update(r); + var existing = Connection.RulesetInfo.Where(ri => ri.InstantiationInfo == us.InstantiationInfo).FirstOrDefault(); + + if (existing == null) + Connection.RulesetInfo.Add(us); + } + + //perform a consistency check + foreach (var r in Connection.RulesetInfo) + { + try + { + r.CreateInstance(); + r.Available = true; } - }); + catch + { + r.Available = false; + } + + //Connection.RulesetInfo.Update(r); + } + Connection.SaveChanges(); } private static void loadRulesetFromFile(string file) @@ -107,11 +103,24 @@ namespace osu.Game.Rulesets { Name = ruleset.Description, InstantiationInfo = ruleset.GetType().AssemblyQualifiedName, - ID = ruleset.LegacyID + Id = ruleset.LegacyID }; protected override Type[] ValidTypes => new[] { typeof(RulesetInfo) }; - public RulesetInfo GetRuleset(int id) => Query().First(r => r.ID == id); + public RulesetInfo GetRuleset(int id) => Connection.RulesetInfo.First(r => r.Id == id); + + public RulesetInfo QueryRulesetInfo(Func query) + { + return Connection.RulesetInfo.FirstOrDefault(query); + } + + public List QueryRulesets(Func query = null) + { + var rulesets = Connection.RulesetInfo; + if (query != null) + return rulesets.Where(query).ToList(); + return rulesets.ToList(); + } } } diff --git a/osu.Game/Rulesets/Scoring/ScoreStore.cs b/osu.Game/Rulesets/Scoring/ScoreStore.cs index c06d31e38f..1604e8cb1c 100644 --- a/osu.Game/Rulesets/Scoring/ScoreStore.cs +++ b/osu.Game/Rulesets/Scoring/ScoreStore.cs @@ -11,7 +11,6 @@ using osu.Game.IO.Legacy; using osu.Game.IPC; using osu.Game.Rulesets.Replays; using SharpCompress.Compressors.LZMA; -using SQLite.Net; namespace osu.Game.Rulesets.Scoring { @@ -27,7 +26,7 @@ namespace osu.Game.Rulesets.Scoring // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) private ScoreIPCChannel ipc; - public ScoreStore(Storage storage, SQLiteConnection connection, IIpcHost importHost = null, BeatmapManager beatmaps = null, RulesetStore rulesets = null) : base(connection) + public ScoreStore(Storage storage, OsuDbContext connection, IIpcHost importHost = null, BeatmapManager beatmaps = null, RulesetStore rulesets = null) : base(connection) { this.storage = storage; this.beatmaps = beatmaps; diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 729df02ffd..0e784c06af 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -172,11 +172,11 @@ namespace osu.Game.Rulesets.UI // Apply difficulty adjustments from mods before using Difficulty. foreach (var mod in Mods.OfType()) - mod.ApplyToDifficulty(Beatmap.BeatmapInfo.Difficulty); + mod.ApplyToDifficulty(Beatmap.BeatmapInfo.BeatmapDifficulty); // Apply defaults foreach (var h in Beatmap.HitObjects) - h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.Difficulty); + h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BeatmapDifficulty); // Post-process the beatmap processor.PostProcess(Beatmap); diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index 3aeef4bbc9..66e6da3af9 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -78,7 +78,7 @@ namespace osu.Game.Screens.Menu { var sets = beatmaps.GetAllUsableBeatmapSets(false); if (sets.Count > 0) - setInfo = beatmaps.QueryBeatmapSet(s => s.ID == sets[RNG.Next(0, sets.Count - 1)].ID); + setInfo = beatmaps.QueryBeatmapSet(s => s.Id == sets[RNG.Next(0, sets.Count - 1)].Id); } if (setInfo == null) diff --git a/osu.Game/Screens/Multiplayer/DrawableRoom.cs b/osu.Game/Screens/Multiplayer/DrawableRoom.cs index d2f88224c2..f3d648e07b 100644 --- a/osu.Game/Screens/Multiplayer/DrawableRoom.cs +++ b/osu.Game/Screens/Multiplayer/DrawableRoom.cs @@ -230,7 +230,7 @@ namespace osu.Game.Screens.Multiplayer coverContainer.FadeIn(transition_duration); coverContainer.Children = new[] { - new AsyncLoadWrapper(new BeatmapSetCover(value.BeatmapSet) + new AsyncLoadWrapper(new BeatmapSetCover(value.BeatmapSetInfo) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -239,9 +239,9 @@ namespace osu.Game.Screens.Multiplayer }) { RelativeSizeAxes = Axes.Both }, }; - beatmapTitle.Current = localisation.GetUnicodePreference(value.Metadata.TitleUnicode, value.Metadata.Title); + beatmapTitle.Current = localisation.GetUnicodePreference(value.BeatmapMetadata.TitleUnicode, value.BeatmapMetadata.Title); beatmapDash.Text = @" - "; - beatmapArtist.Current = localisation.GetUnicodePreference(value.Metadata.ArtistUnicode, value.Metadata.Artist); + beatmapArtist.Current = localisation.GetUnicodePreference(value.BeatmapMetadata.ArtistUnicode, value.BeatmapMetadata.Artist); } else { diff --git a/osu.Game/Screens/Multiplayer/RoomInspector.cs b/osu.Game/Screens/Multiplayer/RoomInspector.cs index 66ce51b428..aabbd2be5f 100644 --- a/osu.Game/Screens/Multiplayer/RoomInspector.cs +++ b/osu.Game/Screens/Multiplayer/RoomInspector.cs @@ -331,7 +331,7 @@ namespace osu.Game.Screens.Multiplayer coverContainer.FadeIn(transition_duration); coverContainer.Children = new[] { - new AsyncLoadWrapper(new BeatmapSetCover(value.BeatmapSet) + new AsyncLoadWrapper(new BeatmapSetCover(value.BeatmapSetInfo) { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, @@ -341,10 +341,10 @@ namespace osu.Game.Screens.Multiplayer }) { RelativeSizeAxes = Axes.Both }, }; - beatmapTitle.Current = localisation.GetUnicodePreference(value.Metadata.TitleUnicode, value.Metadata.Title); + beatmapTitle.Current = localisation.GetUnicodePreference(value.BeatmapMetadata.TitleUnicode, value.BeatmapMetadata.Title); beatmapDash.Text = @" - "; - beatmapArtist.Current = localisation.GetUnicodePreference(value.Metadata.ArtistUnicode, value.Metadata.Artist); - beatmapAuthor.Text = $"mapped by {value.Metadata.Author}"; + beatmapArtist.Current = localisation.GetUnicodePreference(value.BeatmapMetadata.ArtistUnicode, value.BeatmapMetadata.Artist); + beatmapAuthor.Text = $"mapped by {value.BeatmapMetadata.Author}"; } else { diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index e120c7f193..9b0172a1b2 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -99,18 +99,18 @@ namespace osu.Game.Screens.Play if (beatmap == null) throw new InvalidOperationException("Beatmap was not loaded"); - ruleset = Ruleset.Value ?? beatmap.BeatmapInfo.Ruleset; + ruleset = Ruleset.Value ?? beatmap.BeatmapInfo.RulesetInfo; var rulesetInstance = ruleset.CreateInstance(); try { - RulesetContainer = rulesetInstance.CreateRulesetContainerWith(working, ruleset.ID == beatmap.BeatmapInfo.Ruleset.ID); + RulesetContainer = rulesetInstance.CreateRulesetContainerWith(working, ruleset.Id == beatmap.BeatmapInfo.RulesetInfo.Id); } catch (BeatmapInvalidForRulesetException) { // we may fail to create a RulesetContainer if the beatmap cannot be loaded with the user's preferred ruleset // let's try again forcing the beatmap's ruleset. - ruleset = beatmap.BeatmapInfo.Ruleset; + ruleset = beatmap.BeatmapInfo.RulesetInfo; rulesetInstance = ruleset.CreateInstance(); RulesetContainer = rulesetInstance.CreateRulesetContainerWith(Beatmap, true); } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 9366797f47..92369c4f29 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -177,7 +177,7 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader] private void load(LocalisationEngine localisation) { - var metadata = beatmap?.BeatmapInfo?.Metadata ?? new BeatmapMetadata(); + var metadata = beatmap?.BeatmapInfo?.BeatmapMetadata ?? new BeatmapMetadata(); AutoSizeAxes = Axes.Both; Children = new Drawable[] diff --git a/osu.Game/Screens/Ranking/ResultsPageScore.cs b/osu.Game/Screens/Ranking/ResultsPageScore.cs index b01410cff5..694391a5ff 100644 --- a/osu.Game/Screens/Ranking/ResultsPageScore.cs +++ b/osu.Game/Screens/Ranking/ResultsPageScore.cs @@ -324,9 +324,9 @@ namespace osu.Game.Screens.Ranking title.Colour = artist.Colour = colours.BlueDarker; versionMapper.Colour = colours.Gray8; - versionMapper.Text = $"{beatmap.Version} - mapped by {beatmap.Metadata.Author}"; - title.Current = localisation.GetUnicodePreference(beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title); - artist.Current = localisation.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist); + versionMapper.Text = $"{beatmap.Version} - mapped by {beatmap.BeatmapMetadata.Author}"; + title.Current = localisation.GetUnicodePreference(beatmap.BeatmapMetadata.TitleUnicode, beatmap.BeatmapMetadata.Title); + artist.Current = localisation.GetUnicodePreference(beatmap.BeatmapMetadata.ArtistUnicode, beatmap.BeatmapMetadata.Artist); } } diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index abd288baf2..0c31f51c2b 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -113,15 +113,15 @@ namespace osu.Game.Screens.Select }); } - public void RemoveBeatmap(BeatmapSetInfo beatmapSet) => removeGroup(groups.Find(b => b.BeatmapSet.ID == beatmapSet.ID)); + public void RemoveBeatmap(BeatmapSetInfo beatmapSet) => removeGroup(groups.Find(b => b.BeatmapSet.Id == beatmapSet.Id)); internal void UpdateBeatmap(BeatmapInfo beatmap) { // todo: this method should not run more than once for the same BeatmapSetInfo. - var set = manager.Refresh(beatmap.BeatmapSet); + var set = manager.Refresh(beatmap.BeatmapSetInfo); // todo: this method should be smarter as to not recreate panels that haven't changed, etc. - var group = groups.Find(b => b.BeatmapSet.ID == set.ID); + var group = groups.Find(b => b.BeatmapSet.Id == set.Id); if (group == null) return; @@ -141,7 +141,7 @@ namespace osu.Game.Screens.Select if (selectedGroup == group && newGroup.BeatmapPanels.Count > 0) { var newSelection = - newGroup.BeatmapPanels.Find(p => p.Beatmap.ID == selectedPanel?.Beatmap.ID) ?? + newGroup.BeatmapPanels.Find(p => p.Beatmap.Id == selectedPanel?.Beatmap.Id) ?? newGroup.BeatmapPanels[Math.Min(newGroup.BeatmapPanels.Count - 1, group.BeatmapPanels.IndexOf(selectedPanel))]; selectGroup(newGroup, newSelection); @@ -337,8 +337,8 @@ namespace osu.Game.Screens.Select { foreach (var b in beatmapSet.Beatmaps) { - if (b.Metadata == null) - b.Metadata = beatmapSet.Metadata; + if (b.BeatmapMetadata == null) + b.BeatmapMetadata = beatmapSet.BeatmapMetadata; } return new BeatmapGroup(beatmapSet, manager) diff --git a/osu.Game/Screens/Select/BeatmapDeleteDialog.cs b/osu.Game/Screens/Select/BeatmapDeleteDialog.cs index aa37705cdf..7bd50e1fea 100644 --- a/osu.Game/Screens/Select/BeatmapDeleteDialog.cs +++ b/osu.Game/Screens/Select/BeatmapDeleteDialog.cs @@ -20,7 +20,7 @@ namespace osu.Game.Screens.Select public BeatmapDeleteDialog(BeatmapSetInfo beatmap) { - BodyText = $@"{beatmap.Metadata?.Artist} - {beatmap.Metadata?.Title}"; + BodyText = $@"{beatmap.BeatmapMetadata?.Artist} - {beatmap.BeatmapMetadata?.Title}"; Icon = FontAwesome.fa_trash_o; HeaderText = @"Confirm deletion of"; diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index a98362e89c..9d8c76c1fe 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -188,8 +188,8 @@ namespace osu.Game.Screens.Select ratingsContainer.FadeIn(transition_duration); advanced.Beatmap = Beatmap; description.Text = Beatmap.Version; - source.Text = Beatmap.Metadata.Source; - tags.Text = Beatmap.Metadata.Tags; + source.Text = Beatmap.BeatmapMetadata.Source; + tags.Text = Beatmap.BeatmapMetadata.Tags; var requestedBeatmap = Beatmap; if (requestedBeatmap.Metrics == null) @@ -264,7 +264,7 @@ namespace osu.Game.Screens.Select advanced.Beatmap = new BeatmapInfo { StarDifficulty = 0, - Difficulty = new BeatmapDifficulty + BeatmapDifficulty = new BeatmapDifficulty { CircleSize = 0, DrainRate = 0, diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 76c384b84c..a940c49b07 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -90,7 +90,7 @@ namespace osu.Game.Screens.Select public BufferedWedgeInfo(WorkingBeatmap beatmap) { BeatmapInfo beatmapInfo = beatmap.BeatmapInfo; - BeatmapMetadata metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); + BeatmapMetadata metadata = beatmapInfo.BeatmapMetadata ?? beatmap.BeatmapSetInfo?.BeatmapMetadata ?? new BeatmapMetadata(); List labels = new List(); @@ -114,7 +114,7 @@ namespace osu.Game.Screens.Select })); //get statistics from the current ruleset. - labels.AddRange(beatmapInfo.Ruleset.CreateInstance().GetBeatmapStatistics(beatmap).Select(s => new InfoLabel(s))); + labels.AddRange(beatmapInfo.RulesetInfo.CreateInstance().GetBeatmapStatistics(beatmap).Select(s => new InfoLabel(s))); } PixelSnapping = true; diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index f1215ab33d..551f67c51b 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -29,20 +29,20 @@ namespace osu.Game.Screens.Select.Details beatmap = value; //mania specific - if ((Beatmap?.Ruleset?.ID ?? 0) == 3) + if ((Beatmap?.RulesetInfo?.Id ?? 0) == 3) { firstValue.Title = "Key Amount"; - firstValue.Value = (int)Math.Round(Beatmap?.Difficulty?.CircleSize ?? 0); + firstValue.Value = (int)Math.Round(Beatmap?.BeatmapDifficulty?.CircleSize ?? 0); } else { firstValue.Title = "Circle Size"; - firstValue.Value = Beatmap?.Difficulty?.CircleSize ?? 0; + firstValue.Value = Beatmap?.BeatmapDifficulty?.CircleSize ?? 0; } - hpDrain.Value = beatmap.Difficulty?.DrainRate ?? 0; - accuracy.Value = beatmap.Difficulty?.OverallDifficulty ?? 0; - approachRate.Value = beatmap.Difficulty?.ApproachRate ?? 0; + hpDrain.Value = beatmap.BeatmapDifficulty?.DrainRate ?? 0; + accuracy.Value = beatmap.BeatmapDifficulty?.OverallDifficulty ?? 0; + approachRate.Value = beatmap.BeatmapDifficulty?.ApproachRate ?? 0; starDifficulty.Value = (float)beatmap.StarDifficulty; } } diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index a1fea4a41d..f0bf6c94e7 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -23,12 +23,12 @@ namespace osu.Game.Screens.Select { var set = g.BeatmapSet; - bool hasCurrentMode = set.Beatmaps.Any(bm => bm.RulesetID == (Ruleset?.ID ?? 0)); + bool hasCurrentMode = set.Beatmaps.Any(bm => bm.RulesetInfoId == (Ruleset?.Id ?? 0)); bool match = hasCurrentMode; if (!string.IsNullOrEmpty(SearchText)) - match &= set.Metadata.SearchableTerms.Any(term => term.IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) >= 0); + match &= set.BeatmapMetadata.SearchableTerms.Any(term => term.IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) >= 0); switch (g.State) { @@ -45,13 +45,13 @@ namespace osu.Game.Screens.Select { default: case SortMode.Artist: - groups.Sort((x, y) => string.Compare(x.BeatmapSet.Metadata.Artist, y.BeatmapSet.Metadata.Artist, StringComparison.InvariantCultureIgnoreCase)); + groups.Sort((x, y) => string.Compare(x.BeatmapSet.BeatmapMetadata.Artist, y.BeatmapSet.BeatmapMetadata.Artist, StringComparison.InvariantCultureIgnoreCase)); break; case SortMode.Title: - groups.Sort((x, y) => string.Compare(x.BeatmapSet.Metadata.Title, y.BeatmapSet.Metadata.Title, StringComparison.InvariantCultureIgnoreCase)); + groups.Sort((x, y) => string.Compare(x.BeatmapSet.BeatmapMetadata.Title, y.BeatmapSet.BeatmapMetadata.Title, StringComparison.InvariantCultureIgnoreCase)); break; case SortMode.Author: - groups.Sort((x, y) => string.Compare(x.BeatmapSet.Metadata.Author, y.BeatmapSet.Metadata.Author, StringComparison.InvariantCultureIgnoreCase)); + groups.Sort((x, y) => string.Compare(x.BeatmapSet.BeatmapMetadata.Author, y.BeatmapSet.BeatmapMetadata.Author, StringComparison.InvariantCultureIgnoreCase)); break; case SortMode.Difficulty: groups.Sort((x, y) => x.BeatmapSet.MaxStarDifficulty.CompareTo(y.BeatmapSet.MaxStarDifficulty)); diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 7d65b8b648..df08d4e6f6 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -130,7 +130,7 @@ namespace osu.Game.Screens.Select.Leaderboards Scores = null; getScoresRequest?.Cancel(); - if (api == null || Beatmap?.OnlineBeatmapID == null) return; + if (api == null || Beatmap?.BeatmapOnlineInfoId == null) return; loading.Show(); diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 836ed465c3..6b0d9f1eb7 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -238,7 +238,7 @@ namespace osu.Game.Screens.Select // In these cases, the other component has already loaded the beatmap, so we don't need to do so again. if (beatmap?.Equals(Beatmap.Value.BeatmapInfo) != true) { - bool preview = beatmap?.BeatmapSetInfoID != Beatmap.Value.BeatmapInfo.BeatmapSetInfoID; + bool preview = beatmap?.BeatmapSetInfoId != Beatmap.Value.BeatmapInfo.BeatmapSetInfoId; Beatmap.Value = manager.GetWorkingBeatmap(beatmap, Beatmap); ensurePlayingSelected(preview); @@ -261,7 +261,7 @@ namespace osu.Game.Screens.Select } else { - if (beatmap.BeatmapSetInfoID == beatmapNoDebounce?.BeatmapSetInfoID) + if (beatmap.BeatmapSetInfoId == beatmapNoDebounce?.BeatmapSetInfoId) sampleChangeDifficulty.Play(); else sampleChangeBeatmap.Play(); diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index 59cbe74650..bad31ee55a 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -37,7 +37,7 @@ namespace osu.Game.Storyboards /// public bool ReplacesBackground(BeatmapInfo beatmapInfo) { - var backgroundPath = beatmapInfo.BeatmapSet?.Metadata?.BackgroundFile?.ToLowerInvariant(); + var backgroundPath = beatmapInfo.BeatmapSetInfo?.BeatmapMetadata?.BackgroundFile?.ToLowerInvariant(); if (backgroundPath == null) return false; diff --git a/osu.Game/Tests/Platform/TestStorage.cs b/osu.Game/Tests/Platform/TestStorage.cs index 9f76df2a58..be893f7e9c 100644 --- a/osu.Game/Tests/Platform/TestStorage.cs +++ b/osu.Game/Tests/Platform/TestStorage.cs @@ -1,12 +1,8 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework; +using Microsoft.Data.Sqlite; using osu.Framework.Platform; -using SQLite.Net; -using SQLite.Net.Interop; -using SQLite.Net.Platform.Generic; -using SQLite.Net.Platform.Win32; namespace osu.Game.Tests.Platform { @@ -16,14 +12,9 @@ namespace osu.Game.Tests.Platform { } - public override SQLiteConnection GetDatabase(string name) + public override string GetDatabaseConnectionString(string name) { - ISQLitePlatform platform; - if (RuntimeInfo.IsWindows) - platform = new SQLitePlatformWin32(); - else - platform = new SQLitePlatformGeneric(); - return new SQLiteConnection(platform, @":memory:"); + return "DataSource=:memory:"; } } -} \ No newline at end of file +} diff --git a/osu.Game/Tests/Visual/TestCaseBeatmapDetails.cs b/osu.Game/Tests/Visual/TestCaseBeatmapDetails.cs index cd4d97425b..e126980edf 100644 --- a/osu.Game/Tests/Visual/TestCaseBeatmapDetails.cs +++ b/osu.Game/Tests/Visual/TestCaseBeatmapDetails.cs @@ -24,12 +24,12 @@ namespace osu.Game.Tests.Visual AddStep("beatmap all metrics", () => details.Beatmap = new BeatmapInfo { Version = "All Metrics", - Metadata = new BeatmapMetadata + BeatmapMetadata = new BeatmapMetadata { Source = "osu!lazer", Tags = "this beatmap has all the metrics", }, - Difficulty = new BeatmapDifficulty + BeatmapDifficulty = new BeatmapDifficulty { CircleSize = 7, DrainRate = 1, @@ -48,12 +48,12 @@ namespace osu.Game.Tests.Visual AddStep("beatmap ratings", () => details.Beatmap = new BeatmapInfo { Version = "Only Ratings", - Metadata = new BeatmapMetadata + BeatmapMetadata = new BeatmapMetadata { Source = "osu!lazer", Tags = "this beatmap has ratings metrics but not retries or fails", }, - Difficulty = new BeatmapDifficulty + BeatmapDifficulty = new BeatmapDifficulty { CircleSize = 6, DrainRate = 9, @@ -70,12 +70,12 @@ namespace osu.Game.Tests.Visual AddStep("beatmap fails retries", () => details.Beatmap = new BeatmapInfo { Version = "Only Retries and Fails", - Metadata = new BeatmapMetadata + BeatmapMetadata = new BeatmapMetadata { Source = "osu!lazer", Tags = "this beatmap has retries and fails but no ratings", }, - Difficulty = new BeatmapDifficulty + BeatmapDifficulty = new BeatmapDifficulty { CircleSize = 3.7f, DrainRate = 6, @@ -93,12 +93,12 @@ namespace osu.Game.Tests.Visual AddStep("beatmap no metrics", () => details.Beatmap = new BeatmapInfo { Version = "No Metrics", - Metadata = new BeatmapMetadata + BeatmapMetadata = new BeatmapMetadata { Source = "osu!lazer", Tags = "this beatmap has no metrics", }, - Difficulty = new BeatmapDifficulty + BeatmapDifficulty = new BeatmapDifficulty { CircleSize = 5, DrainRate = 5, diff --git a/osu.Game/Tests/Visual/TestCaseBeatmapSetOverlay.cs b/osu.Game/Tests/Visual/TestCaseBeatmapSetOverlay.cs index 76ed9979ca..e8027978b4 100644 --- a/osu.Game/Tests/Visual/TestCaseBeatmapSetOverlay.cs +++ b/osu.Game/Tests/Visual/TestCaseBeatmapSetOverlay.cs @@ -33,7 +33,7 @@ namespace osu.Game.Tests.Visual { overlay.ShowBeatmapSet(new BeatmapSetInfo { - Metadata = new BeatmapMetadata + BeatmapMetadata = new BeatmapMetadata { Title = @"Lachryma ", Artist = @"Kaneko Chiharu", @@ -64,8 +64,8 @@ namespace osu.Game.Tests.Visual { StarDifficulty = 1.36, Version = @"BASIC", - Ruleset = mania, - Difficulty = new BeatmapDifficulty + RulesetInfo = mania, + BeatmapDifficulty = new BeatmapDifficulty { CircleSize = 4, DrainRate = 6.5f, @@ -92,8 +92,8 @@ namespace osu.Game.Tests.Visual { StarDifficulty = 2.22, Version = @"NOVICE", - Ruleset = mania, - Difficulty = new BeatmapDifficulty + RulesetInfo = mania, + BeatmapDifficulty = new BeatmapDifficulty { CircleSize = 4, DrainRate = 7, @@ -120,8 +120,8 @@ namespace osu.Game.Tests.Visual { StarDifficulty = 3.49, Version = @"ADVANCED", - Ruleset = mania, - Difficulty = new BeatmapDifficulty + RulesetInfo = mania, + BeatmapDifficulty = new BeatmapDifficulty { CircleSize = 4, DrainRate = 7.5f, @@ -148,8 +148,8 @@ namespace osu.Game.Tests.Visual { StarDifficulty = 4.24, Version = @"EXHAUST", - Ruleset = mania, - Difficulty = new BeatmapDifficulty + RulesetInfo = mania, + BeatmapDifficulty = new BeatmapDifficulty { CircleSize = 4, DrainRate = 8, @@ -176,8 +176,8 @@ namespace osu.Game.Tests.Visual { StarDifficulty = 5.26, Version = @"GRAVITY", - Ruleset = mania, - Difficulty = new BeatmapDifficulty + RulesetInfo = mania, + BeatmapDifficulty = new BeatmapDifficulty { CircleSize = 4, DrainRate = 8.5f, @@ -208,7 +208,7 @@ namespace osu.Game.Tests.Visual { overlay.ShowBeatmapSet(new BeatmapSetInfo { - Metadata = new BeatmapMetadata + BeatmapMetadata = new BeatmapMetadata { Title = @"Soumatou Labyrinth", Artist = @"Yunomi with Momobako&miko", @@ -238,8 +238,8 @@ namespace osu.Game.Tests.Visual { StarDifficulty = 1.40, Version = @"yzrin's Kantan", - Ruleset = taiko, - Difficulty = new BeatmapDifficulty + RulesetInfo = taiko, + BeatmapDifficulty = new BeatmapDifficulty { CircleSize = 2, DrainRate = 7, @@ -266,8 +266,8 @@ namespace osu.Game.Tests.Visual { StarDifficulty = 2.23, Version = @"Futsuu", - Ruleset = taiko, - Difficulty = new BeatmapDifficulty + RulesetInfo = taiko, + BeatmapDifficulty = new BeatmapDifficulty { CircleSize = 2, DrainRate = 6, @@ -294,8 +294,8 @@ namespace osu.Game.Tests.Visual { StarDifficulty = 3.19, Version = @"Muzukashii", - Ruleset = taiko, - Difficulty = new BeatmapDifficulty + RulesetInfo = taiko, + BeatmapDifficulty = new BeatmapDifficulty { CircleSize = 2, DrainRate = 6, @@ -322,8 +322,8 @@ namespace osu.Game.Tests.Visual { StarDifficulty = 3.97, Version = @"Charlotte's Oni", - Ruleset = taiko, - Difficulty = new BeatmapDifficulty + RulesetInfo = taiko, + BeatmapDifficulty = new BeatmapDifficulty { CircleSize = 5, DrainRate = 6, @@ -350,8 +350,8 @@ namespace osu.Game.Tests.Visual { StarDifficulty = 5.08, Version = @"Labyrinth Oni", - Ruleset = taiko, - Difficulty = new BeatmapDifficulty + RulesetInfo = taiko, + BeatmapDifficulty = new BeatmapDifficulty { CircleSize = 5, DrainRate = 5, diff --git a/osu.Game/Tests/Visual/TestCaseDirect.cs b/osu.Game/Tests/Visual/TestCaseDirect.cs index 1fb9dbea8f..cb34d28578 100644 --- a/osu.Game/Tests/Visual/TestCaseDirect.cs +++ b/osu.Game/Tests/Visual/TestCaseDirect.cs @@ -41,8 +41,8 @@ namespace osu.Game.Tests.Visual { new BeatmapSetInfo { - OnlineBeatmapSetID = 578332, - Metadata = new BeatmapMetadata + BeatmapSetOnlineInfoId = 578332, + BeatmapMetadata = new BeatmapMetadata { Title = @"OrVid", Artist = @"An", @@ -65,16 +65,16 @@ namespace osu.Game.Tests.Visual { new BeatmapInfo { - Ruleset = ruleset, + RulesetInfo = ruleset, StarDifficulty = 5.35f, - Metadata = new BeatmapMetadata(), + BeatmapMetadata = new BeatmapMetadata(), }, }, }, new BeatmapSetInfo { - OnlineBeatmapSetID = 599627, - Metadata = new BeatmapMetadata + BeatmapSetOnlineInfoId = 599627, + BeatmapMetadata = new BeatmapMetadata { Title = @"tiny lamp", Artist = @"fhana", @@ -97,16 +97,16 @@ namespace osu.Game.Tests.Visual { new BeatmapInfo { - Ruleset = ruleset, + RulesetInfo = ruleset, StarDifficulty = 5.81f, - Metadata = new BeatmapMetadata(), + BeatmapMetadata = new BeatmapMetadata(), }, }, }, new BeatmapSetInfo { - OnlineBeatmapSetID = 513268, - Metadata = new BeatmapMetadata + BeatmapSetOnlineInfoId = 513268, + BeatmapMetadata = new BeatmapMetadata { Title = @"At Gwanghwamun", Artist = @"KYUHYUN", @@ -129,31 +129,31 @@ namespace osu.Game.Tests.Visual { new BeatmapInfo { - Ruleset = ruleset, + RulesetInfo = ruleset, StarDifficulty = 0.9f, - Metadata = new BeatmapMetadata(), + BeatmapMetadata = new BeatmapMetadata(), }, new BeatmapInfo { - Ruleset = ruleset, + RulesetInfo = ruleset, StarDifficulty = 1.1f, }, new BeatmapInfo { - Ruleset = ruleset, + RulesetInfo = ruleset, StarDifficulty = 2.02f, }, new BeatmapInfo { - Ruleset = ruleset, + RulesetInfo = ruleset, StarDifficulty = 3.49f, }, }, }, new BeatmapSetInfo { - OnlineBeatmapSetID = 586841, - Metadata = new BeatmapMetadata + BeatmapSetOnlineInfoId = 586841, + BeatmapMetadata = new BeatmapMetadata { Title = @"RHAPSODY OF BLUE SKY", Artist = @"fhana", @@ -176,43 +176,43 @@ namespace osu.Game.Tests.Visual { new BeatmapInfo { - Ruleset = ruleset, + RulesetInfo = ruleset, StarDifficulty = 1.26f, - Metadata = new BeatmapMetadata(), + BeatmapMetadata = new BeatmapMetadata(), }, new BeatmapInfo { - Ruleset = ruleset, + RulesetInfo = ruleset, StarDifficulty = 2.01f, }, new BeatmapInfo { - Ruleset = ruleset, + RulesetInfo = ruleset, StarDifficulty = 2.87f, }, new BeatmapInfo { - Ruleset = ruleset, + RulesetInfo = ruleset, StarDifficulty = 3.76f, }, new BeatmapInfo { - Ruleset = ruleset, + RulesetInfo = ruleset, StarDifficulty = 3.93f, }, new BeatmapInfo { - Ruleset = ruleset, + RulesetInfo = ruleset, StarDifficulty = 4.37f, }, new BeatmapInfo { - Ruleset = ruleset, + RulesetInfo = ruleset, StarDifficulty = 5.13f, }, new BeatmapInfo { - Ruleset = ruleset, + RulesetInfo = ruleset, StarDifficulty = 5.42f, }, }, diff --git a/osu.Game/Tests/Visual/TestCaseDrawableRoom.cs b/osu.Game/Tests/Visual/TestCaseDrawableRoom.cs index 7113bcbff5..a57bf64c9a 100644 --- a/osu.Game/Tests/Visual/TestCaseDrawableRoom.cs +++ b/osu.Game/Tests/Visual/TestCaseDrawableRoom.cs @@ -43,13 +43,13 @@ namespace osu.Game.Tests.Visual Value = new BeatmapInfo { StarDifficulty = 4.65, - Ruleset = rulesets.GetRuleset(3), - Metadata = new BeatmapMetadata + RulesetInfo = rulesets.GetRuleset(3), + BeatmapMetadata = new BeatmapMetadata { Title = @"Critical Crystal", Artist = @"Seiryu", }, - BeatmapSet = new BeatmapSetInfo + BeatmapSetInfo = new BeatmapSetInfo { OnlineInfo = new BeatmapSetOnlineInfo { @@ -81,13 +81,13 @@ namespace osu.Game.Tests.Visual Value = new BeatmapInfo { StarDifficulty = 1.96, - Ruleset = rulesets.GetRuleset(0), - Metadata = new BeatmapMetadata + RulesetInfo = rulesets.GetRuleset(0), + BeatmapMetadata = new BeatmapMetadata { Title = @"Serendipity", Artist = @"ZAQ", }, - BeatmapSet = new BeatmapSetInfo + BeatmapSetInfo = new BeatmapSetInfo { OnlineInfo = new BeatmapSetOnlineInfo { diff --git a/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs index feff7497d8..12ac7240d6 100644 --- a/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs +++ b/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.MathUtils; using osu.Game.Beatmaps; @@ -37,12 +38,11 @@ namespace osu.Game.Tests.Visual { var storage = new TestStorage(@"TestCasePlaySongSelect"); - var backingDatabase = storage.GetDatabase(@"client"); - backingDatabase.CreateTable(); + var dbConnectionString = storage.GetDatabaseConnectionString(@"client"); - dependencies.Cache(rulesets = new RulesetStore(backingDatabase)); - dependencies.Cache(files = new FileStore(backingDatabase, storage)); - dependencies.Cache(manager = new BeatmapManager(storage, files, backingDatabase, rulesets, null)); + dependencies.Cache(rulesets = new RulesetStore(new OsuDbContext(dbConnectionString))); + dependencies.Cache(files = new FileStore(new OsuDbContext(dbConnectionString), storage)); + dependencies.Cache(manager = new BeatmapManager(storage, files, new OsuDbContext(dbConnectionString), rulesets, null)); for (int i = 0; i < 100; i += 10) manager.Import(createTestBeatmapSet(i)); @@ -60,11 +60,11 @@ namespace osu.Game.Tests.Visual { return new BeatmapSetInfo { - OnlineBeatmapSetID = 1234 + i, + BeatmapSetOnlineInfoId = 1234 + i, Hash = "d8e8fca2dc0f896fd7cb4cb0031ba249", - Metadata = new BeatmapMetadata + BeatmapMetadata = new BeatmapMetadata { - OnlineBeatmapSetID = 1234 + i, + BeatmapSetOnlineInfoId = 1234 + i, // Create random metadata, then we can check if sorting works based on these Artist = "MONACA " + RNG.Next(0, 9), Title = "Black Song " + RNG.Next(0, 9), @@ -74,33 +74,33 @@ namespace osu.Game.Tests.Visual { new BeatmapInfo { - OnlineBeatmapID = 1234 + i, - Ruleset = rulesets.Query().First(), + BeatmapOnlineInfoId = 1234 + i, + RulesetInfo = rulesets.QueryRulesets().First(), Path = "normal.osu", Version = "Normal", - Difficulty = new BeatmapDifficulty + BeatmapDifficulty = new BeatmapDifficulty { OverallDifficulty = 3.5f, } }, new BeatmapInfo { - OnlineBeatmapID = 1235 + i, - Ruleset = rulesets.Query().First(), + BeatmapOnlineInfoId = 1235 + i, + RulesetInfo = rulesets.QueryRulesets().First(), Path = "hard.osu", Version = "Hard", - Difficulty = new BeatmapDifficulty + BeatmapDifficulty = new BeatmapDifficulty { OverallDifficulty = 5, } }, new BeatmapInfo { - OnlineBeatmapID = 1236 + i, - Ruleset = rulesets.Query().First(), + BeatmapOnlineInfoId = 1236 + i, + RulesetInfo = rulesets.QueryRulesets().First(), Path = "insane.osu", Version = "Insane", - Difficulty = new BeatmapDifficulty + BeatmapDifficulty = new BeatmapDifficulty { OverallDifficulty = 7, } diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index 4a25a52e36..98c573f62c 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -39,10 +39,11 @@ namespace osu.Game.Tests.Visual Colour = Color4.Black, }); - foreach (var r in rulesets.Query()) + var queryRulesets = rulesets.QueryRulesets(r=>true); + foreach (var r in queryRulesets) AddStep(r.Name, () => loadPlayerFor(r)); - loadPlayerFor(rulesets.Query().First()); + loadPlayerFor(queryRulesets.First()); } protected virtual Beatmap CreateBeatmap() @@ -60,7 +61,7 @@ namespace osu.Game.Tests.Visual { var beatmap = CreateBeatmap(); - beatmap.BeatmapInfo.Ruleset = r; + beatmap.BeatmapInfo.RulesetInfo = r; var instance = r.CreateInstance(); diff --git a/osu.Game/Tests/Visual/TestCaseResults.cs b/osu.Game/Tests/Visual/TestCaseResults.cs index 62154a535a..a0146de7d0 100644 --- a/osu.Game/Tests/Visual/TestCaseResults.cs +++ b/osu.Game/Tests/Visual/TestCaseResults.cs @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual if (beatmap == null) { - var beatmapInfo = beatmaps.QueryBeatmap(b => b.RulesetID == 0); + var beatmapInfo = beatmaps.QueryBeatmap(b => b.RulesetInfoId == 0); if (beatmapInfo != null) beatmap = beatmaps.GetWorkingBeatmap(beatmapInfo); } diff --git a/osu.Game/Tests/Visual/TestCaseRoomInspector.cs b/osu.Game/Tests/Visual/TestCaseRoomInspector.cs index dd773b361a..41b722841c 100644 --- a/osu.Game/Tests/Visual/TestCaseRoomInspector.cs +++ b/osu.Game/Tests/Visual/TestCaseRoomInspector.cs @@ -32,14 +32,14 @@ namespace osu.Game.Tests.Visual Value = new BeatmapInfo { StarDifficulty = 3.7, - Ruleset = rulesets.GetRuleset(3), - Metadata = new BeatmapMetadata + RulesetInfo = rulesets.GetRuleset(3), + BeatmapMetadata = new BeatmapMetadata { Title = @"Platina", Artist = @"Maaya Sakamoto", Author = @"uwutm8", }, - BeatmapSet = new BeatmapSetInfo + BeatmapSetInfo = new BeatmapSetInfo { OnlineInfo = new BeatmapSetOnlineInfo { @@ -99,14 +99,14 @@ namespace osu.Game.Tests.Visual Value = new BeatmapInfo { StarDifficulty = 7.07, - Ruleset = rulesets.GetRuleset(0), - Metadata = new BeatmapMetadata + RulesetInfo = rulesets.GetRuleset(0), + BeatmapMetadata = new BeatmapMetadata { Title = @"FREEDOM DIVE", Artist = @"xi", Author = @"Nakagawa-Kanon", }, - BeatmapSet = new BeatmapSetInfo + BeatmapSetInfo = new BeatmapSetInfo { OnlineInfo = new BeatmapSetOnlineInfo { diff --git a/osu.Game/Tests/Visual/TestCaseScrollingPlayfield.cs b/osu.Game/Tests/Visual/TestCaseScrollingPlayfield.cs index d0761e5841..29623fb05f 100644 --- a/osu.Game/Tests/Visual/TestCaseScrollingPlayfield.cs +++ b/osu.Game/Tests/Visual/TestCaseScrollingPlayfield.cs @@ -48,8 +48,8 @@ namespace osu.Game.Tests.Visual HitObjects = objects, BeatmapInfo = new BeatmapInfo { - Difficulty = new BeatmapDifficulty(), - Metadata = new BeatmapMetadata() + BeatmapDifficulty = new BeatmapDifficulty(), + BeatmapMetadata = new BeatmapMetadata() } }; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index bdca48ccdf..afccbe9dc1 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -124,6 +124,49 @@ $(SolutionDir)\packages\DotNetZip.1.10.1\lib\net20\DotNetZip.dll True + + + ..\packages\Microsoft.Data.Sqlite.Core.2.0.0\lib\netstandard2.0\Microsoft.Data.Sqlite.dll + + + ..\packages\Microsoft.EntityFrameworkCore.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.dll + + + ..\packages\Microsoft.EntityFrameworkCore.Design.2.0.0\lib\net461\Microsoft.EntityFrameworkCore.Design.dll + + + ..\packages\Microsoft.EntityFrameworkCore.Relational.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.Relational.dll + + + ..\packages\Microsoft.EntityFrameworkCore.Sqlite.Core.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.Sqlite.dll + + + ..\packages\Microsoft.Extensions.Caching.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Abstractions.dll + + + ..\packages\Microsoft.Extensions.Caching.Memory.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Memory.dll + + + ..\packages\Microsoft.Extensions.Configuration.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Configuration.Abstractions.dll + + + ..\packages\Microsoft.Extensions.DependencyInjection.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.dll + + + ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll + + + ..\packages\Microsoft.Extensions.Logging.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.dll + + + ..\packages\Microsoft.Extensions.Logging.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll + + + ..\packages\Microsoft.Extensions.Options.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Options.dll + + + ..\packages\Microsoft.Extensions.Primitives.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll + $(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll True @@ -157,6 +200,9 @@ $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll True + + ..\packages\Remotion.Linq.2.1.2\lib\net45\Remotion.Linq.dll + $(SolutionDir)\packages\SharpCompress.0.18.1\lib\net45\SharpCompress.dll True @@ -165,29 +211,43 @@ $(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll True - - $(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll - True + + ..\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_green.dll - - $(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net40\SQLite.Net.Platform.Generic.dll - True + + ..\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_v2.dll - - $(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net4\SQLite.Net.Platform.Win32.dll - True + + ..\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll - - $(SolutionDir)\packages\SQLiteNetExtensions.1.3.0\lib\portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\SQLiteNetExtensions.dll - True + + ..\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.8\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll $(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\Squirrel.dll True + + ..\packages\System.Collections.Immutable.1.4.0\lib\netstandard2.0\System.Collections.Immutable.dll + + + ..\packages\System.ComponentModel.Annotations.4.4.0\lib\net461\System.ComponentModel.Annotations.dll + + + + + + ..\packages\System.Diagnostics.DiagnosticSource.4.4.1\lib\net46\System.Diagnostics.DiagnosticSource.dll + + + ..\packages\System.Interactive.Async.3.1.1\lib\net46\System.Interactive.Async.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.4.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + @@ -287,7 +347,7 @@ - + @@ -800,6 +860,9 @@ + + + - - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 4923c399c5..2dd159188f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -202,7 +202,8 @@ True - ..\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll + ../packages/System.ValueTuple.4.4.0/lib/net461/System.ValueTuple.dll + True From 5b6c331434faf50fea6663f58ff36ad272f2e67c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Oct 2017 14:12:08 +0900 Subject: [PATCH 39/90] Fix all keybindings being reset every startup --- osu.Game/Input/KeyBindingStore.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index e2b6c1dc87..f9578d150b 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -30,7 +30,8 @@ namespace osu.Game.Input protected override void Prepare(bool reset = false) { - Connection.Database.ExecuteSqlCommand("DELETE FROM KeyBinding"); + if (reset) + Connection.Database.ExecuteSqlCommand("DELETE FROM KeyBinding"); } private void insertDefaults(IEnumerable defaults, int? rulesetId = null, int? variant = null) From 1a16784db9f8857ef69f20a0a82333ac7411147d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Oct 2017 14:18:01 +0900 Subject: [PATCH 40/90] Add BindingRedirects rule to fix dependency when running via nunit See https://github.com/ErikEJ/EntityFramework.SqlServerCompact/issues/463 --- osu.Game/osu.Game.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 2dd159188f..6c447f560e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,6 +23,7 @@ LocalIntranet v4.6.1 true + true publish\ true Disk From 7049a73490cf17a75c7938cbecd55fe4925f39bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Oct 2017 14:47:33 +0900 Subject: [PATCH 41/90] Remove ValidTypes from databased stored; explicitly expose query methods instead --- osu.Game/Beatmaps/BeatmapStore.cs | 8 -------- osu.Game/Database/DatabaseBackedStore.cs | 9 --------- osu.Game/IO/FileStore.cs | 4 ---- osu.Game/Input/KeyBindingStore.cs | 6 ------ osu.Game/Rulesets/RulesetStore.cs | 2 -- osu.Game/Rulesets/Scoring/ScoreStore.cs | 3 --- 6 files changed, 32 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 9c5a1a9dfc..817b46041f 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -26,14 +26,6 @@ namespace osu.Game.Beatmaps { } - protected override Type[] ValidTypes => new[] - { - typeof(BeatmapSetInfo), - typeof(BeatmapInfo), - typeof(BeatmapMetadata), - typeof(BeatmapDifficulty), - }; - protected override void Prepare(bool reset = false) { if (reset) diff --git a/osu.Game/Database/DatabaseBackedStore.cs b/osu.Game/Database/DatabaseBackedStore.cs index 6b3fe232b2..46fc596584 100644 --- a/osu.Game/Database/DatabaseBackedStore.cs +++ b/osu.Game/Database/DatabaseBackedStore.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Linq; using Microsoft.EntityFrameworkCore; using osu.Framework.Logging; using osu.Framework.Platform; @@ -47,13 +46,5 @@ namespace osu.Game.Database /// Reset this database to a default state. Undo all changes to database and storage backings. /// public void Reset() => Prepare(true); - - 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/IO/FileStore.cs b/osu.Game/IO/FileStore.cs index c15ebd264e..55b00b51d9 100644 --- a/osu.Game/IO/FileStore.cs +++ b/osu.Game/IO/FileStore.cs @@ -27,10 +27,6 @@ namespace osu.Game.IO Store = new NamespacedResourceStore(new StorageBackedResourceStore(storage), prefix); } - protected override Type[] ValidTypes => new[] { - typeof(FileInfo), - }; - protected override void Prepare(bool reset = false) { if (reset) diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index f9578d150b..088e3bd75b 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -1,7 +1,6 @@ // 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 Microsoft.EntityFrameworkCore; @@ -57,11 +56,6 @@ namespace osu.Game.Input } } - protected override Type[] ValidTypes => new[] - { - typeof(DatabasedKeyBinding) - }; - public List Query(int? rulesetId = null, int? variant = null) => new List(Connection.DatabasedKeyBinding.Where(b => b.RulesetID == rulesetId && b.Variant == variant)); diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 068997e732..fe9dfd7a11 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -109,8 +109,6 @@ namespace osu.Game.Rulesets ID = ruleset.LegacyID }; - protected override Type[] ValidTypes => new[] { typeof(RulesetInfo) }; - public RulesetInfo GetRuleset(int id) => Connection.RulesetInfo.First(r => r.ID == id); public RulesetInfo QueryRulesetInfo(Func query) diff --git a/osu.Game/Rulesets/Scoring/ScoreStore.cs b/osu.Game/Rulesets/Scoring/ScoreStore.cs index 1604e8cb1c..66fcfb5d67 100644 --- a/osu.Game/Rulesets/Scoring/ScoreStore.cs +++ b/osu.Game/Rulesets/Scoring/ScoreStore.cs @@ -1,7 +1,6 @@ // 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 osu.Framework.Platform; @@ -147,7 +146,5 @@ namespace osu.Game.Rulesets.Scoring protected override void Prepare(bool reset = false) { } - - protected override Type[] ValidTypes => new[] { typeof(Score) }; } } From 1f4a943f74dfeb8340296e0187c4ca865866ecd1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Oct 2017 15:09:43 +0900 Subject: [PATCH 42/90] Fix test case runs not being correctly isolated on mono --- osu.Game/Tests/Visual/OsuTestCase.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs index d722f7d711..90c6e427c4 100644 --- a/osu.Game/Tests/Visual/OsuTestCase.cs +++ b/osu.Game/Tests/Visual/OsuTestCase.cs @@ -11,8 +11,15 @@ namespace osu.Game.Tests.Visual { public override void RunTest() { - using (var host = new HeadlessGameHost(AppDomain.CurrentDomain.FriendlyName.Replace(' ', '-'), realtime: false)) + Storage storage; + using (var host = new HeadlessGameHost($"test-{Guid.NewGuid()}", realtime: false)) + { + storage = host.Storage; host.Run(new OsuTestCaseTestRunner(this)); + } + + // clean up after each run + storage.DeleteDirectory(string.Empty); } public class OsuTestCaseTestRunner : OsuGameBase From ec51314e37a52d7880d2353df55eae151940bdf8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Oct 2017 16:11:34 +0900 Subject: [PATCH 43/90] Remove duplicate command --- osu.Game/Database/DatabaseBackedStore.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Database/DatabaseBackedStore.cs b/osu.Game/Database/DatabaseBackedStore.cs index 46fc596584..adddad6122 100644 --- a/osu.Game/Database/DatabaseBackedStore.cs +++ b/osu.Game/Database/DatabaseBackedStore.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using Microsoft.EntityFrameworkCore; using osu.Framework.Logging; using osu.Framework.Platform; @@ -17,7 +16,6 @@ namespace osu.Game.Database { Storage = storage; Connection = connection; - Connection.Database.SetCommandTimeout(new TimeSpan(TimeSpan.TicksPerSecond * 10)); try { From acc299c7b97b24dc234e27fae049a421c609ab1b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Oct 2017 17:02:31 +0900 Subject: [PATCH 44/90] Correct and simplify RulesetStore --- osu.Game/Beatmaps/BeatmapManager.cs | 7 +-- osu.Game/Input/KeyBindingStore.cs | 2 +- osu.Game/Overlays/Direct/FilterControl.cs | 2 +- osu.Game/Overlays/KeyBindingOverlay.cs | 2 +- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- .../Settings/Sections/GameplaySection.cs | 2 +- osu.Game/Overlays/Settings/SettingsFooter.cs | 2 +- .../Overlays/Toolbar/ToolbarModeSelector.cs | 2 +- osu.Game/Rulesets/RulesetStore.cs | 44 +++++++++---------- osu.Game/Tests/Visual/TestCaseMods.cs | 2 +- .../Tests/Visual/TestCasePlaySongSelect.cs | 6 +-- osu.Game/Tests/Visual/TestCasePlayer.cs | 2 +- 12 files changed, 37 insertions(+), 38 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index e4c53cec94..985d2c7a15 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -474,10 +474,11 @@ namespace osu.Game.Beatmaps // TODO: Diff beatmap metadata with set metadata and leave it here if necessary beatmap.BeatmapInfo.Metadata = null; + RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID); + // TODO: this should be done in a better place once we actually need to dynamically update it. - beatmap.BeatmapInfo.Ruleset = rulesets.QueryRulesetInfo(r => r.ID == beatmap.BeatmapInfo.RulesetID); - beatmap.BeatmapInfo.StarDifficulty = rulesets.QueryRulesetInfo(r => r.ID == beatmap.BeatmapInfo.RulesetID)?.CreateInstance()?.CreateDifficultyCalculator(beatmap) - .Calculate() ?? 0; + beatmap.BeatmapInfo.Ruleset = ruleset; + beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance()?.CreateDifficultyCalculator(beatmap).Calculate() ?? 0; beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo); } diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index 088e3bd75b..5cc018ff2c 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -17,7 +17,7 @@ namespace osu.Game.Input public KeyBindingStore(OsuDbContext connection, RulesetStore rulesets, Storage storage = null) : base(connection, storage) { - foreach (var info in rulesets.AllRulesets) + foreach (var info in rulesets.AvailableRulesets) { var ruleset = info.CreateInstance(); foreach (var variant in ruleset.AvailableVariants) diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs index 28d26d0641..9b52cfd367 100644 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ b/osu.Game/Overlays/Direct/FilterControl.cs @@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Direct DisplayStyleControl.Dropdown.AccentColour = colours.BlueDark; Ruleset.BindTo(game?.Ruleset ?? new Bindable { Value = rulesets.GetRuleset(0) }); - foreach (var r in rulesets.AllRulesets) + foreach (var r in rulesets.AvailableRulesets) { modeButtons.Add(new RulesetToggleButton(Ruleset, r)); } diff --git a/osu.Game/Overlays/KeyBindingOverlay.cs b/osu.Game/Overlays/KeyBindingOverlay.cs index 72c653030c..4394d0fec0 100644 --- a/osu.Game/Overlays/KeyBindingOverlay.cs +++ b/osu.Game/Overlays/KeyBindingOverlay.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays { AddSection(new GlobalKeyBindingsSection(global)); - foreach (var ruleset in rulesets.AllRulesets) + foreach (var ruleset in rulesets.AvailableRulesets) AddSection(new RulesetBindingsSection(ruleset)); } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index eb643f390f..9ff21dfdd4 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -56,7 +56,7 @@ namespace osu.Game.Overlays.Mods if (osu != null) Ruleset.BindTo(osu.Ruleset); else - Ruleset.Value = rulesets.AllRulesets.First(); + Ruleset.Value = rulesets.AvailableRulesets.First(); Ruleset.ValueChanged += rulesetChanged; Ruleset.TriggerChange(); diff --git a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs index 326cb582e2..035a3c7a13 100644 --- a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs +++ b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections [BackgroundDependencyLoader] private void load(RulesetStore rulesets) { - foreach(Ruleset ruleset in rulesets.AllRulesets.Select(info => info.CreateInstance())) + foreach(Ruleset ruleset in rulesets.AvailableRulesets.Select(info => info.CreateInstance())) { SettingsSubsection section = ruleset.CreateSettings(); if (section != null) diff --git a/osu.Game/Overlays/Settings/SettingsFooter.cs b/osu.Game/Overlays/Settings/SettingsFooter.cs index cb1c861ee7..bf417a2fac 100644 --- a/osu.Game/Overlays/Settings/SettingsFooter.cs +++ b/osu.Game/Overlays/Settings/SettingsFooter.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings var modes = new List(); - foreach (var ruleset in rulesets.AllRulesets) + foreach (var ruleset in rulesets.AvailableRulesets) { var icon = new ConstrainedIconContainer { diff --git a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs index 60c1261190..da72ae0347 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs @@ -67,7 +67,7 @@ namespace osu.Game.Overlays.Toolbar [BackgroundDependencyLoader] private void load(RulesetStore rulesets, OsuGame game) { - foreach (var r in rulesets.AllRulesets) + foreach (var r in rulesets.AvailableRulesets) { modeButtons.Add(new ToolbarModeButton { diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index fe9dfd7a11..c39312205c 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -18,13 +18,6 @@ namespace osu.Game.Rulesets { private static readonly Dictionary loaded_assemblies = new Dictionary(); - public IEnumerable AllRulesets => Connection.RulesetInfo.Where(r => r.Available); - - public RulesetStore(OsuDbContext connection) - : base(connection) - { - } - static RulesetStore() { AppDomain.CurrentDomain.AssemblyResolve += currentDomain_AssemblyResolve; @@ -33,6 +26,23 @@ namespace osu.Game.Rulesets loadRulesetFromFile(file); } + public RulesetStore(OsuDbContext connection) + : base(connection) + { + } + + /// + /// Retrieve a ruleset using a known ID. + /// + /// The ruleset's internal ID. + /// A ruleset, if available, else null. + public RulesetInfo GetRuleset(int id) => AvailableRulesets.FirstOrDefault(r => r.ID == id); + + /// + /// All available rulesets. + /// + public IEnumerable AvailableRulesets => Connection.RulesetInfo.Where(r => r.Available); + private static Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args) => loaded_assemblies.Keys.FirstOrDefault(a => a.FullName == args.Name); private const string ruleset_library_prefix = "osu.Game.Rulesets"; @@ -44,7 +54,7 @@ namespace osu.Game.Rulesets Connection.Database.ExecuteSqlCommand("DELETE FROM RulesetInfo"); } - var instances = loaded_assemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r, new RulesetInfo())); + var instances = loaded_assemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r, new RulesetInfo())).ToList(); //add all legacy modes in correct order foreach (var r in instances.Where(r => r.LegacyID >= 0).OrderBy(r => r.LegacyID)) @@ -55,6 +65,7 @@ namespace osu.Game.Rulesets Connection.RulesetInfo.Add(rulesetInfo); } } + Connection.SaveChanges(); //add any other modes @@ -67,6 +78,7 @@ namespace osu.Game.Rulesets if (existing == null) Connection.RulesetInfo.Add(us); } + Connection.SaveChanges(); //perform a consistency check @@ -82,6 +94,7 @@ namespace osu.Game.Rulesets r.Available = false; } } + Connection.SaveChanges(); } @@ -108,20 +121,5 @@ namespace osu.Game.Rulesets InstantiationInfo = ruleset.GetType().AssemblyQualifiedName, ID = ruleset.LegacyID }; - - public RulesetInfo GetRuleset(int id) => Connection.RulesetInfo.First(r => r.ID == id); - - public RulesetInfo QueryRulesetInfo(Func query) - { - return Connection.RulesetInfo.FirstOrDefault(query); - } - - public List QueryRulesets(Func query = null) - { - var rulesets = Connection.RulesetInfo; - if (query != null) - return rulesets.Where(query).ToList(); - return rulesets.ToList(); - } } } diff --git a/osu.Game/Tests/Visual/TestCaseMods.cs b/osu.Game/Tests/Visual/TestCaseMods.cs index ef250edcc3..0447d6582d 100644 --- a/osu.Game/Tests/Visual/TestCaseMods.cs +++ b/osu.Game/Tests/Visual/TestCaseMods.cs @@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual AddStep("Toggle", modSelect.ToggleVisibility); - foreach (var ruleset in rulesets.AllRulesets) + foreach (var ruleset in rulesets.AvailableRulesets) AddStep(ruleset.CreateInstance().Description, () => modSelect.Ruleset.Value = ruleset); } } diff --git a/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs index 1b7f2def4d..a8a00e9a0d 100644 --- a/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs +++ b/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs @@ -75,7 +75,7 @@ namespace osu.Game.Tests.Visual new BeatmapInfo { OnlineBeatmapID = 1234 + i, - Ruleset = rulesets.QueryRulesets().First(), + Ruleset = rulesets.AvailableRulesets.First(), Path = "normal.osu", Version = "Normal", Difficulty = new BeatmapDifficulty @@ -86,7 +86,7 @@ namespace osu.Game.Tests.Visual new BeatmapInfo { OnlineBeatmapID = 1235 + i, - Ruleset = rulesets.QueryRulesets().First(), + Ruleset = rulesets.AvailableRulesets.First(), Path = "hard.osu", Version = "Hard", Difficulty = new BeatmapDifficulty @@ -97,7 +97,7 @@ namespace osu.Game.Tests.Visual new BeatmapInfo { OnlineBeatmapID = 1236 + i, - Ruleset = rulesets.QueryRulesets().First(), + Ruleset = rulesets.AvailableRulesets.First(), Path = "insane.osu", Version = "Insane", Difficulty = new BeatmapDifficulty diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index 811e240cee..dfbdaf1d9d 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual string instantiation = ruleset?.AssemblyQualifiedName; - foreach (var r in rulesets.QueryRulesets(rs => rs.Available && (instantiation == null || rs.InstantiationInfo == instantiation))) + foreach (var r in rulesets.AvailableRulesets.Where(rs => instantiation == null || rs.InstantiationInfo == instantiation)) AddStep(r.Name, () => loadPlayerFor(r)); } From 9ee6d1e3f9d9274cf7d37ab6f17b9eea8c93438c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Oct 2017 17:58:17 +0900 Subject: [PATCH 45/90] Correct and simplify KeyBindingStore --- osu.Game/Input/KeyBindingStore.cs | 30 ++++++++++--------- .../KeyBinding/KeyBindingsSubsection.cs | 4 ++- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index 5cc018ff2c..9edab896b3 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -38,11 +38,13 @@ namespace osu.Game.Input // compare counts in database vs defaults foreach (var group in defaults.GroupBy(k => k.Action)) { - int count; - while (group.Count() > (count = Query(rulesetId, variant).Count(k => (int)k.Action == (int)group.Key))) - { - var insertable = group.Skip(count).First(); + int count = Query(rulesetId, variant).Count(k => (int)k.Action == (int)group.Key); + int aimCount = group.Count(); + if (aimCount <= count) + continue; + + foreach (var insertable in group.Skip(count).Take(aimCount - count)) // insert any defaults which are missing. Connection.DatabasedKeyBinding.Add(new DatabasedKeyBinding { @@ -51,22 +53,22 @@ namespace osu.Game.Input RulesetID = rulesetId, Variant = variant }); - Connection.SaveChanges(); - } } + + Connection.SaveChanges(); } - public List Query(int? rulesetId = null, int? variant = null) => - new List(Connection.DatabasedKeyBinding.Where(b => b.RulesetID == rulesetId && b.Variant == variant)); + /// + /// Retrieve s for a specified ruleset/variant content. + /// + /// The ruleset's internal ID. + /// An optional variant. + /// + public IEnumerable Query(int? rulesetId = null, int? variant = null) => Connection.DatabasedKeyBinding.Where(b => b.RulesetID == rulesetId && b.Variant == variant); public void Update(KeyBinding keyBinding) { - var dbKeyBinding = Connection.DatabasedKeyBinding.FirstOrDefault(kb => kb.ToString() == keyBinding.ToString()); - if (dbKeyBinding != null) - { - dbKeyBinding.KeyCombination = keyBinding.KeyCombination; - dbKeyBinding.Action = keyBinding.Action; - } + Connection.Update(keyBinding); Connection.SaveChanges(); } } diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs index bd69403831..128b5e2f09 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs @@ -37,8 +37,10 @@ namespace osu.Game.Overlays.KeyBinding foreach (var defaultGroup in Defaults.GroupBy(d => d.Action)) { + int intKey = (int)defaultGroup.Key; + // one row per valid action. - Add(new KeyBindingRow(defaultGroup.Key, bindings.Where(b => b.Action.Equals((int)defaultGroup.Key))) + Add(new KeyBindingRow(defaultGroup.Key, bindings.Where(b => ((int)b.Action).Equals(intKey))) { AllowMainMouseButtons = Ruleset != null, Defaults = defaultGroup.Select(d => d.KeyCombination) From e378d0685db0847a33d4de796a26f6ba8879b7df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Oct 2017 20:28:49 +0900 Subject: [PATCH 46/90] Remove weird additions --- osu.Game.Tests/app.config | 4 ---- osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs | 3 +-- osu.Game/osu.Game.csproj | 13 ------------- 3 files changed, 1 insertion(+), 19 deletions(-) diff --git a/osu.Game.Tests/app.config b/osu.Game.Tests/app.config index 11af32e2cf..faeaf001de 100644 --- a/osu.Game.Tests/app.config +++ b/osu.Game.Tests/app.config @@ -6,10 +6,6 @@ - - - - \ No newline at end of file diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs index 2568af6ce1..27d1f057ca 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.ComponentModel.DataAnnotations; using Newtonsoft.Json; namespace osu.Game.Beatmaps @@ -30,7 +29,7 @@ namespace osu.Game.Beatmaps /// /// The different sizes of cover art for this beatmap set. /// - [Required, JsonProperty(@"covers")] + [JsonProperty(@"covers")] public BeatmapSetOnlineCovers Covers { get; set; } /// diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 6c447f560e..ced7ca7318 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,6 @@ LocalIntranet v4.6.1 true - true publish\ true Disk @@ -202,10 +201,6 @@ ..\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll True - - ../packages/System.ValueTuple.4.4.0/lib/net461/System.ValueTuple.dll - True - @@ -862,14 +857,6 @@ - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - From eff1c20e38cd9fd8583bc6637bea61c53e5476d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Oct 2017 20:50:34 +0900 Subject: [PATCH 47/90] CI fixing --- osu.Desktop/app.config | 4 ++++ osu.Desktop/osu.Desktop.csproj | 4 ++++ osu.Desktop/packages.config | 1 + osu.Game.Tests/osu.Game.Tests.csproj | 4 ++++ osu.Game.Tests/packages.config | 17 +++++++++-------- osu.Game/app.config | 4 ++++ osu.Game/osu.Game.csproj | 8 ++++++++ 7 files changed, 34 insertions(+), 8 deletions(-) diff --git a/osu.Desktop/app.config b/osu.Desktop/app.config index 824430b24a..0841541f3d 100644 --- a/osu.Desktop/app.config +++ b/osu.Desktop/app.config @@ -11,6 +11,10 @@ + + + + diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index fad297fa0a..7b2ec3b24c 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -158,6 +158,10 @@ + + ../packages/System.ValueTuple.4.4.0/lib/net461/System.ValueTuple.dll + True + diff --git a/osu.Desktop/packages.config b/osu.Desktop/packages.config index 9cb31a6e25..f47fe8ac62 100644 --- a/osu.Desktop/packages.config +++ b/osu.Desktop/packages.config @@ -11,4 +11,5 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste + \ No newline at end of file diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index e188c82e79..7a3bbab176 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -39,6 +39,10 @@ True + + ../packages/System.ValueTuple.4.4.0/lib/net461/System.ValueTuple.dll + True + diff --git a/osu.Game.Tests/packages.config b/osu.Game.Tests/packages.config index b94c0c6e2d..ecc44f0c70 100644 --- a/osu.Game.Tests/packages.config +++ b/osu.Game.Tests/packages.config @@ -1,9 +1,10 @@ - - - - - + + + + + + \ No newline at end of file diff --git a/osu.Game/app.config b/osu.Game/app.config index a704cc3750..7f2ad68041 100644 --- a/osu.Game/app.config +++ b/osu.Game/app.config @@ -10,6 +10,10 @@ + + + + \ No newline at end of file diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ced7ca7318..a442d1cef6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -201,6 +201,10 @@ ..\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll True + + ../packages/System.ValueTuple.4.4.0/lib/net461/System.ValueTuple.dll + True + @@ -857,6 +861,10 @@ + + true + true + From 81476ebe7543a921b962c70f8e7b065596a9e629 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Oct 2017 22:04:22 +0900 Subject: [PATCH 48/90] Correct and simplify BeatmapStore and BeatmapManager --- osu.Game/Beatmaps/BeatmapManager.cs | 64 ++++++++++++++++++----------- osu.Game/Beatmaps/BeatmapStore.cs | 59 ++++++++------------------ 2 files changed, 57 insertions(+), 66 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 985d2c7a15..af88f22e40 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Linq.Expressions; using System.Threading.Tasks; using Ionic.Zip; using osu.Framework.Audio.Track; @@ -179,7 +178,8 @@ namespace osu.Game.Beatmaps // If we have an ID then we already exist in the database. if (beatmapSetInfo.ID != 0) return; - beatmaps.Add(beatmapSetInfo); + lock (beatmaps) + beatmaps.Add(beatmapSetInfo); } /// @@ -257,7 +257,8 @@ namespace osu.Game.Beatmaps /// The beatmap set to delete. public void Delete(BeatmapSetInfo beatmapSet) { - if (!beatmaps.Delete(beatmapSet)) return; + lock (beatmaps) + if (!beatmaps.Delete(beatmapSet)) return; if (!beatmapSet.Protected) files.Dereference(beatmapSet.Files.Select(f => f.FileInfo).ToArray()); @@ -267,13 +268,21 @@ namespace osu.Game.Beatmaps /// Delete a beatmap difficulty. /// /// The beatmap difficulty to hide. - public void Hide(BeatmapInfo beatmap) => beatmaps.Hide(beatmap); + public void Hide(BeatmapInfo beatmap) + { + lock (beatmaps) + beatmaps.Hide(beatmap); + } /// /// Restore a beatmap difficulty. /// /// The beatmap difficulty to restore. - public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap); + public void Restore(BeatmapInfo beatmap) + { + lock (beatmaps) + beatmaps.Restore(beatmap); + } /// /// Returns a to a usable state if it has previously been deleted but not yet purged. @@ -282,7 +291,8 @@ namespace osu.Game.Beatmaps /// The beatmap to restore. public void Undelete(BeatmapSetInfo beatmapSet) { - if (!beatmaps.Undelete(beatmapSet)) return; + lock (beatmaps) + if (!beatmaps.Undelete(beatmapSet)) return; if (!beatmapSet.Protected) files.Reference(beatmapSet.Files.Select(f => f.FileInfo).ToArray()); @@ -329,11 +339,7 @@ namespace osu.Game.Beatmaps public BeatmapSetInfo QueryBeatmapSet(Func query) { lock (beatmaps) - { - BeatmapSetInfo set = beatmaps.QueryBeatmapSet(query); - - return set; - } + return beatmaps.BeatmapSets.FirstOrDefault(query); } /// @@ -348,9 +354,10 @@ namespace osu.Game.Beatmaps /// /// The query. /// Results from the provided query. - public List QueryBeatmapSets(Expression> query) + public List QueryBeatmapSets(Func query) { - return beatmaps.QueryBeatmapSets(query); + lock (beatmaps) + return beatmaps.BeatmapSets.Where(query).ToList(); } /// @@ -360,9 +367,8 @@ namespace osu.Game.Beatmaps /// The first result for the provided query, or null if no results were found. public BeatmapInfo QueryBeatmap(Func query) { - BeatmapInfo set = beatmaps.QueryBeatmap(query); - - return set; + lock (beatmaps) + return beatmaps.Beatmaps.FirstOrDefault(query); } /// @@ -370,9 +376,10 @@ namespace osu.Game.Beatmaps /// /// The query. /// Results from the provided query. - public List QueryBeatmaps(Expression> query) + public List QueryBeatmaps(Func query) { - lock (beatmaps) return beatmaps.QueryBeatmaps(query); + lock (beatmaps) + return beatmaps.Beatmaps.Where(query).ToList(); } /// @@ -411,7 +418,7 @@ namespace osu.Game.Beatmaps // check if this beatmap has already been imported and exit early if so. BeatmapSetInfo beatmapSet; lock (beatmaps) - beatmapSet = beatmaps.QueryBeatmapSet(b => b.Hash == hash); + beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.Hash == hash); if (beatmapSet != null) { @@ -494,9 +501,7 @@ namespace osu.Game.Beatmaps public List GetAllUsableBeatmapSets() { lock (beatmaps) - { - return beatmaps.QueryBeatmapSets(b => !b.DeletePending); - } + return beatmaps.BeatmapSets.ToList(); } protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap @@ -531,7 +536,10 @@ namespace osu.Game.Beatmaps return beatmap; } - catch { return null; } + catch + { + return null; + } } private string getPathForFile(string filename) => BeatmapSetInfo.Files.First(f => string.Equals(f.Filename, filename, StringComparison.InvariantCultureIgnoreCase)).FileInfo.StoragePath; @@ -545,7 +553,10 @@ namespace osu.Game.Beatmaps { return new TextureStore(new RawTextureLoaderStore(store), false).Get(getPathForFile(Metadata.BackgroundFile)); } - catch { return null; } + catch + { + return null; + } } protected override Track GetTrack() @@ -555,7 +566,10 @@ namespace osu.Game.Beatmaps var trackData = store.GetStream(getPathForFile(Metadata.AudioFile)); return trackData == null ? null : new TrackBass(trackData); } - catch { return new TrackVirtual(); } + catch + { + return new TrackVirtual(); + } } protected override Waveform GetWaveform() => new Waveform(store.GetStream(getPathForFile(Metadata.AudioFile))); diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 817b46041f..6464db86d4 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Linq.Expressions; using Microsoft.EntityFrameworkCore; using osu.Game.Database; @@ -51,7 +50,7 @@ namespace osu.Game.Beatmaps /// The beatmap to add. public void Add(BeatmapSetInfo beatmapSet) { - Connection.BeatmapSetInfo.Update(beatmapSet); + Connection.BeatmapSetInfo.Add(beatmapSet); Connection.SaveChanges(); BeatmapSetAdded?.Invoke(beatmapSet); @@ -67,7 +66,7 @@ namespace osu.Game.Beatmaps if (beatmapSet.DeletePending) return false; beatmapSet.DeletePending = true; - Connection.BeatmapSetInfo.Remove(beatmapSet); + Connection.BeatmapSetInfo.Update(beatmapSet); Connection.SaveChanges(); BeatmapSetRemoved?.Invoke(beatmapSet); @@ -85,6 +84,7 @@ namespace osu.Game.Beatmaps beatmapSet.DeletePending = false; Connection.BeatmapSetInfo.Update(beatmapSet); + Connection.SaveChanges(); BeatmapSetAdded?.Invoke(beatmapSet); return true; @@ -101,6 +101,7 @@ namespace osu.Game.Beatmaps beatmap.Hidden = true; Connection.BeatmapInfo.Update(beatmap); + Connection.SaveChanges(); BeatmapHidden?.Invoke(beatmap); return true; @@ -117,6 +118,7 @@ namespace osu.Game.Beatmaps beatmap.Hidden = false; Connection.BeatmapInfo.Update(beatmap); + Connection.SaveChanges(); BeatmapRestored?.Invoke(beatmap); return true; @@ -125,46 +127,21 @@ namespace osu.Game.Beatmaps private void cleanupPendingDeletions() { Connection.BeatmapSetInfo.RemoveRange(Connection.BeatmapSetInfo.Where(b => b.DeletePending && !b.Protected)); + Connection.SaveChanges(); } - public BeatmapSetInfo QueryBeatmapSet(Func query) - { - return Connection.BeatmapSetInfo - .Include(b => b.Metadata) - .Include(b => b.Beatmaps).ThenInclude(b => b.Ruleset) - .Include(b => b.Beatmaps).ThenInclude(b => b.Difficulty) - .Include(b => b.Files).ThenInclude(f => f.FileInfo) - .FirstOrDefault(query); - } + public IEnumerable BeatmapSets => Connection.BeatmapSetInfo + .Include(s => s.Metadata) + .Include(s => s.Beatmaps).ThenInclude(s => s.Ruleset) + .Include(s => s.Beatmaps).ThenInclude(b => b.Difficulty) + .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata) + .Include(s => s.Files).ThenInclude(f => f.FileInfo) + .Where(s => !s.DeletePending); - public List QueryBeatmapSets(Expression> query) - { - return Connection.BeatmapSetInfo - .Include(b => b.Metadata) - .Include(b => b.Beatmaps).ThenInclude(b => b.Ruleset) - .Include(b => b.Beatmaps).ThenInclude(b => b.Difficulty) - .Include(b => b.Files).ThenInclude(f => f.FileInfo) - .Where(query).ToList(); - } - - public BeatmapInfo QueryBeatmap(Func query) - { - return Connection.BeatmapInfo - .Include(b => b.BeatmapSet) - .Include(b => b.Metadata) - .Include(b => b.Ruleset) - .Include(b => b.Difficulty) - .FirstOrDefault(query); - } - - public List QueryBeatmaps(Expression> query) - { - return Connection.BeatmapInfo - .Include(b => b.BeatmapSet) - .Include(b => b.Metadata) - .Include(b => b.Ruleset) - .Include(b => b.Difficulty) - .Where(query).ToList(); - } + public IEnumerable Beatmaps => Connection.BeatmapInfo + .Include(b => b.BeatmapSet).ThenInclude(s => s.Metadata) + .Include(b => b.Metadata) + .Include(b => b.Ruleset) + .Include(b => b.Difficulty); } } From 799f51021700d394d5e19a5f329816ca018bd9a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Oct 2017 10:25:18 +0900 Subject: [PATCH 49/90] FileStore logic fixes --- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 2 -- osu.Game/IO/FileStore.cs | 16 +++++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index cd9e765e7f..087fb54b5f 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -95,8 +95,6 @@ namespace osu.Game.Tests.Beatmaps.IO private OsuGameBase loadOsu(GameHost host) { - host.Storage.DeleteDatabase(@"client"); - var osu = new OsuGameBase(); Task.Run(() => host.Run(osu)); diff --git a/osu.Game/IO/FileStore.cs b/osu.Game/IO/FileStore.cs index 55b00b51d9..ae0cfb30c8 100644 --- a/osu.Game/IO/FileStore.cs +++ b/osu.Game/IO/FileStore.cs @@ -31,10 +31,6 @@ namespace osu.Game.IO { if (reset) { - // in earlier versions we stored beatmaps as solid archives, but not any more. - if (Storage.ExistsDirectory("beatmaps")) - Storage.DeleteDirectory("beatmaps"); - if (Storage.ExistsDirectory(prefix)) Storage.DeleteDirectory(prefix); @@ -70,12 +66,14 @@ namespace osu.Game.IO } if (existing == null) + { Connection.FileInfo.Add(info); + Connection.SaveChanges(); + } if (reference || existing == null) Reference(info); - Connection.SaveChanges(); return info; } @@ -85,6 +83,7 @@ namespace osu.Game.IO { var refetch = Connection.Find(f.First().ID); refetch.ReferenceCount += f.Count(); + Connection.Update(refetch); } Connection.SaveChanges(); @@ -94,8 +93,9 @@ namespace osu.Game.IO { foreach (var f in files.GroupBy(f => f.ID)) { - var accurateRefCount = Connection.Find(f.First().ID); - accurateRefCount.ReferenceCount -= f.Count(); + var refetch = Connection.Find(f.First().ID); + refetch.ReferenceCount -= f.Count(); + Connection.Update(refetch); } Connection.SaveChanges(); @@ -115,6 +115,8 @@ namespace osu.Game.IO Logger.Error(e, $@"Could not delete beatmap {f}"); } } + + Connection.SaveChanges(); } } } From ef10bb73db05785a42def26ca2820e1d567a1b68 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Oct 2017 10:25:25 +0900 Subject: [PATCH 50/90] osu.Game csproj fixes --- osu.Game/osu.Game.csproj | 62 +++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index a442d1cef6..731e065a48 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -105,46 +105,46 @@ - ..\packages\Microsoft.Data.Sqlite.Core.2.0.0\lib\netstandard2.0\Microsoft.Data.Sqlite.dll + $(SolutionDir)\packages\Microsoft.Data.Sqlite.Core.2.0.0\lib\netstandard2.0\Microsoft.Data.Sqlite.dll - ..\packages\Microsoft.EntityFrameworkCore.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.dll + $(SolutionDir)\packages\Microsoft.EntityFrameworkCore.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.dll - ..\packages\Microsoft.EntityFrameworkCore.Design.2.0.0\lib\net461\Microsoft.EntityFrameworkCore.Design.dll + $(SolutionDir)\packages\Microsoft.EntityFrameworkCore.Design.2.0.0\lib\net461\Microsoft.EntityFrameworkCore.Design.dll - ..\packages\Microsoft.EntityFrameworkCore.Relational.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.Relational.dll + $(SolutionDir)\packages\Microsoft.EntityFrameworkCore.Relational.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.Relational.dll - ..\packages\Microsoft.EntityFrameworkCore.Sqlite.Core.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.Sqlite.dll + $(SolutionDir)\packages\Microsoft.EntityFrameworkCore.Sqlite.Core.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.Sqlite.dll - ..\packages\Microsoft.Extensions.Caching.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Abstractions.dll + $(SolutionDir)\packages\Microsoft.Extensions.Caching.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Abstractions.dll - ..\packages\Microsoft.Extensions.Caching.Memory.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Memory.dll + $(SolutionDir)\packages\Microsoft.Extensions.Caching.Memory.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Memory.dll - ..\packages\Microsoft.Extensions.Configuration.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Configuration.Abstractions.dll + $(SolutionDir)\packages\Microsoft.Extensions.Configuration.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Configuration.Abstractions.dll - ..\packages\Microsoft.Extensions.DependencyInjection.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.dll + $(SolutionDir)\packages\Microsoft.Extensions.DependencyInjection.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.dll - ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll + $(SolutionDir)\packages\Microsoft.Extensions.DependencyInjection.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll - ..\packages\Microsoft.Extensions.Logging.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.dll + $(SolutionDir)\packages\Microsoft.Extensions.Logging.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.dll - ..\packages\Microsoft.Extensions.Logging.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll + $(SolutionDir)\packages\Microsoft.Extensions.Logging.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll - ..\packages\Microsoft.Extensions.Options.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Options.dll + $(SolutionDir)\packages\Microsoft.Extensions.Options.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Options.dll - ..\packages\Microsoft.Extensions.Primitives.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll + $(SolutionDir)\packages\Microsoft.Extensions.Primitives.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll @@ -160,49 +160,53 @@ True - ..\packages\Remotion.Linq.2.1.2\lib\net45\Remotion.Linq.dll + $(SolutionDir)\packages\Remotion.Linq.2.1.2\lib\net45\Remotion.Linq.dll $(SolutionDir)\packages\SharpCompress.0.18.1\lib\net45\SharpCompress.dll True - ..\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_green.dll + $(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_green.dll + True - ..\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_v2.dll + $(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_v2.dll + True - ..\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll + $(SolutionDir)\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll + True - ..\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.8\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll + $(SolutionDir)\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.8\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll + True - ..\packages\System.Collections.Immutable.1.4.0\lib\netstandard2.0\System.Collections.Immutable.dll + $(SolutionDir)\packages\System.Collections.Immutable.1.4.0\lib\netstandard2.0\System.Collections.Immutable.dll - ..\packages\System.ComponentModel.Annotations.4.4.0\lib\net461\System.ComponentModel.Annotations.dll + $(SolutionDir)\packages\System.ComponentModel.Annotations.4.4.0\lib\net461\System.ComponentModel.Annotations.dll - ..\packages\System.Diagnostics.DiagnosticSource.4.4.1\lib\net46\System.Diagnostics.DiagnosticSource.dll + $(SolutionDir)\packages\System.Diagnostics.DiagnosticSource.4.4.1\lib\net46\System.Diagnostics.DiagnosticSource.dll - ..\packages\System.Interactive.Async.3.1.1\lib\net46\System.Interactive.Async.dll + $(SolutionDir)\packages\System.Interactive.Async.3.1.1\lib\net46\System.Interactive.Async.dll - ..\packages\System.Runtime.CompilerServices.Unsafe.4.4.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + $(SolutionDir)\packages\System.Runtime.CompilerServices.Unsafe.4.4.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll - ..\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll + $(SolutionDir)\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll True - ../packages/System.ValueTuple.4.4.0/lib/net461/System.ValueTuple.dll + $(SolutionDir)\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll True @@ -865,7 +869,7 @@ true true - - - + + + \ No newline at end of file From 31dc5c97f2e0399795767332bddab53a5959c24f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Oct 2017 11:10:55 +0900 Subject: [PATCH 51/90] Fix intro and duplicate inserts --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- osu.Game/Beatmaps/BeatmapStore.cs | 5 ++-- osu.Game/IO/FileStore.cs | 4 +--- osu.Game/Screens/Menu/Intro.cs | 1 - osu.Game/Screens/Select/SongSelect.cs | 34 +++++++++++++-------------- 5 files changed, 21 insertions(+), 25 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index af88f22e40..f546172bfe 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -501,7 +501,7 @@ namespace osu.Game.Beatmaps public List GetAllUsableBeatmapSets() { lock (beatmaps) - return beatmaps.BeatmapSets.ToList(); + return beatmaps.BeatmapSets.Where(s => !s.DeletePending).ToList(); } protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 6464db86d4..ea5c5f7155 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -50,7 +50,7 @@ namespace osu.Game.Beatmaps /// The beatmap to add. public void Add(BeatmapSetInfo beatmapSet) { - Connection.BeatmapSetInfo.Add(beatmapSet); + Connection.BeatmapSetInfo.Attach(beatmapSet); Connection.SaveChanges(); BeatmapSetAdded?.Invoke(beatmapSet); @@ -135,8 +135,7 @@ namespace osu.Game.Beatmaps .Include(s => s.Beatmaps).ThenInclude(s => s.Ruleset) .Include(s => s.Beatmaps).ThenInclude(b => b.Difficulty) .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata) - .Include(s => s.Files).ThenInclude(f => f.FileInfo) - .Where(s => !s.DeletePending); + .Include(s => s.Files).ThenInclude(f => f.FileInfo); public IEnumerable Beatmaps => Connection.BeatmapInfo .Include(b => b.BeatmapSet).ThenInclude(s => s.Metadata) diff --git a/osu.Game/IO/FileStore.cs b/osu.Game/IO/FileStore.cs index ae0cfb30c8..19237c6063 100644 --- a/osu.Game/IO/FileStore.cs +++ b/osu.Game/IO/FileStore.cs @@ -66,10 +66,8 @@ namespace osu.Game.IO } if (existing == null) - { + // SaveChanges is performed in Reference. Connection.FileInfo.Add(info); - Connection.SaveChanges(); - } if (reference || existing == null) Reference(info); diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index b597fc11e4..ee84cf2d30 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -89,7 +89,6 @@ namespace osu.Game.Screens.Menu { // we need to import the default menu background beatmap setInfo = beatmaps.Import(new OszArchiveReader(game.Resources.GetStream(@"Tracks/circles.osz"))); - setInfo.Protected = true; } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index b11613634a..e11eed7040 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -26,7 +26,7 @@ namespace osu.Game.Screens.Select { public abstract class SongSelect : OsuScreen { - private BeatmapManager manager; + private BeatmapManager beatmaps; protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(); private readonly BeatmapCarousel carousel; @@ -108,9 +108,9 @@ namespace osu.Game.Screens.Select SelectionChanged = carouselSelectionChanged, BeatmapsChanged = carouselBeatmapsLoaded, DeleteRequested = promptDelete, - RestoreRequested = s => { foreach (var b in s.Beatmaps) manager.Restore(b); }, + RestoreRequested = s => { foreach (var b in s.Beatmaps) beatmaps.Restore(b); }, EditRequested = editRequested, - HideDifficultyRequested = b => manager.Hide(b), + HideDifficultyRequested = b => beatmaps.Hide(b), StartRequested = () => carouselRaisedStart(), }); Add(FilterControl = new FilterControl @@ -171,16 +171,16 @@ namespace osu.Game.Screens.Select BeatmapOptions.AddButton(@"Delete", @"Beatmap", FontAwesome.fa_trash, colours.Pink, () => promptDelete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue); } - if (manager == null) - manager = beatmaps; + if (this.beatmaps == null) + this.beatmaps = beatmaps; if (osu != null) Ruleset.BindTo(osu.Ruleset); - manager.BeatmapSetAdded += onBeatmapSetAdded; - manager.BeatmapSetRemoved += onBeatmapSetRemoved; - manager.BeatmapHidden += onBeatmapHidden; - manager.BeatmapRestored += onBeatmapRestored; + this.beatmaps.BeatmapSetAdded += onBeatmapSetAdded; + this.beatmaps.BeatmapSetRemoved += onBeatmapSetRemoved; + this.beatmaps.BeatmapHidden += onBeatmapHidden; + this.beatmaps.BeatmapRestored += onBeatmapRestored; dialogOverlay = dialog; @@ -189,7 +189,7 @@ namespace osu.Game.Screens.Select initialAddSetsTask = new CancellationTokenSource(); - carousel.Beatmaps = manager.GetAllUsableBeatmapSets(); + carousel.Beatmaps = this.beatmaps.GetAllUsableBeatmapSets(); Beatmap.ValueChanged += beatmap_ValueChanged; @@ -199,7 +199,7 @@ namespace osu.Game.Screens.Select private void editRequested(BeatmapInfo beatmap) { - Beatmap.Value = manager.GetWorkingBeatmap(beatmap, Beatmap); + Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, Beatmap); Push(new Editor()); } @@ -248,7 +248,7 @@ namespace osu.Game.Screens.Select { bool preview = beatmap?.BeatmapSetInfoID != Beatmap.Value.BeatmapInfo.BeatmapSetInfoID; - Beatmap.Value = manager.GetWorkingBeatmap(beatmap, Beatmap); + Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, Beatmap); ensurePlayingSelected(preview); } @@ -357,12 +357,12 @@ namespace osu.Game.Screens.Select { base.Dispose(isDisposing); - if (manager != null) + if (beatmaps != null) { - manager.BeatmapSetAdded -= onBeatmapSetAdded; - manager.BeatmapSetRemoved -= onBeatmapSetRemoved; - manager.BeatmapHidden -= onBeatmapHidden; - manager.BeatmapRestored -= onBeatmapRestored; + beatmaps.BeatmapSetAdded -= onBeatmapSetAdded; + beatmaps.BeatmapSetRemoved -= onBeatmapSetRemoved; + beatmaps.BeatmapHidden -= onBeatmapHidden; + beatmaps.BeatmapRestored -= onBeatmapRestored; } initialAddSetsTask?.Cancel(); From 0df474accb7a10ec10aa9c2fa17b71c1e1bdd4f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Oct 2017 11:22:50 +0900 Subject: [PATCH 52/90] Simplify file storing --- osu.Game/IO/FileStore.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game/IO/FileStore.cs b/osu.Game/IO/FileStore.cs index 19237c6063..ede85ad42f 100644 --- a/osu.Game/IO/FileStore.cs +++ b/osu.Game/IO/FileStore.cs @@ -65,10 +65,6 @@ namespace osu.Game.IO data.Seek(0, SeekOrigin.Begin); } - if (existing == null) - // SaveChanges is performed in Reference. - Connection.FileInfo.Add(info); - if (reference || existing == null) Reference(info); @@ -79,9 +75,9 @@ namespace osu.Game.IO { foreach (var f in files.GroupBy(f => f.ID)) { - var refetch = Connection.Find(f.First().ID); + var refetch = Connection.Find(f.First().ID) ?? f.First(); refetch.ReferenceCount += f.Count(); - Connection.Update(refetch); + Connection.FileInfo.Update(refetch); } Connection.SaveChanges(); From c92e0e2dc19f56fe770ddfbf75e658f67feac35f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Oct 2017 11:24:59 +0900 Subject: [PATCH 53/90] Fix username display on beatmap panels --- osu.Game/Beatmaps/Drawables/BeatmapPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs b/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs index e216f1b83e..c0705d8f61 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs @@ -135,7 +135,7 @@ namespace osu.Game.Beatmaps.Drawables new OsuSpriteText { Font = @"Exo2.0-MediumItalic", - Text = $"{(beatmap.Metadata ?? beatmap.BeatmapSet.Metadata).Author}", + Text = $"{(beatmap.Metadata ?? beatmap.BeatmapSet.Metadata).Author.Username}", TextSize = 16, Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft From 66894d11ea1c1b8358fbe81dc01752a6fea67361 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Oct 2017 11:39:47 +0900 Subject: [PATCH 54/90] Connection -> context --- osu.Game/Beatmaps/BeatmapManager.cs | 4 +-- osu.Game/Beatmaps/BeatmapStore.cs | 42 ++++++++++++------------ osu.Game/Database/DatabaseBackedStore.cs | 6 ++-- osu.Game/IO/FileStore.cs | 24 +++++++------- osu.Game/Input/KeyBindingStore.cs | 16 ++++----- osu.Game/Rulesets/RulesetStore.cs | 24 +++++++------- osu.Game/Rulesets/Scoring/ScoreStore.cs | 2 +- 7 files changed, 59 insertions(+), 59 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f546172bfe..7d174e6a28 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -80,9 +80,9 @@ namespace osu.Game.Beatmaps /// public Func GetStableStorage { private get; set; } - public BeatmapManager(Storage storage, FileStore files, OsuDbContext connection, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null) + public BeatmapManager(Storage storage, FileStore files, OsuDbContext context, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null) { - beatmaps = new BeatmapStore(connection); + beatmaps = new BeatmapStore(context); beatmaps.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s); beatmaps.BeatmapSetRemoved += s => BeatmapSetRemoved?.Invoke(s); beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b); diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index ea5c5f7155..134059fd07 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -20,8 +20,8 @@ namespace osu.Game.Beatmaps public event Action BeatmapHidden; public event Action BeatmapRestored; - public BeatmapStore(OsuDbContext connection) - : base(connection) + public BeatmapStore(OsuDbContext context) + : base(context) { } @@ -30,11 +30,11 @@ namespace osu.Game.Beatmaps if (reset) { // https://stackoverflow.com/a/10450893 - Connection.Database.ExecuteSqlCommand("DELETE FROM BeatmapMetadata"); - Connection.Database.ExecuteSqlCommand("DELETE FROM BeatmapDifficulty"); - Connection.Database.ExecuteSqlCommand("DELETE FROM BeatmapSetInfo"); - Connection.Database.ExecuteSqlCommand("DELETE FROM BeatmapSetFileInfo"); - Connection.Database.ExecuteSqlCommand("DELETE FROM BeatmapInfo"); + Context.Database.ExecuteSqlCommand("DELETE FROM BeatmapMetadata"); + Context.Database.ExecuteSqlCommand("DELETE FROM BeatmapDifficulty"); + Context.Database.ExecuteSqlCommand("DELETE FROM BeatmapSetInfo"); + Context.Database.ExecuteSqlCommand("DELETE FROM BeatmapSetFileInfo"); + Context.Database.ExecuteSqlCommand("DELETE FROM BeatmapInfo"); } } @@ -50,8 +50,8 @@ namespace osu.Game.Beatmaps /// The beatmap to add. public void Add(BeatmapSetInfo beatmapSet) { - Connection.BeatmapSetInfo.Attach(beatmapSet); - Connection.SaveChanges(); + Context.BeatmapSetInfo.Attach(beatmapSet); + Context.SaveChanges(); BeatmapSetAdded?.Invoke(beatmapSet); } @@ -66,8 +66,8 @@ namespace osu.Game.Beatmaps if (beatmapSet.DeletePending) return false; beatmapSet.DeletePending = true; - Connection.BeatmapSetInfo.Update(beatmapSet); - Connection.SaveChanges(); + Context.BeatmapSetInfo.Update(beatmapSet); + Context.SaveChanges(); BeatmapSetRemoved?.Invoke(beatmapSet); return true; @@ -83,8 +83,8 @@ namespace osu.Game.Beatmaps if (!beatmapSet.DeletePending) return false; beatmapSet.DeletePending = false; - Connection.BeatmapSetInfo.Update(beatmapSet); - Connection.SaveChanges(); + Context.BeatmapSetInfo.Update(beatmapSet); + Context.SaveChanges(); BeatmapSetAdded?.Invoke(beatmapSet); return true; @@ -100,8 +100,8 @@ namespace osu.Game.Beatmaps if (beatmap.Hidden) return false; beatmap.Hidden = true; - Connection.BeatmapInfo.Update(beatmap); - Connection.SaveChanges(); + Context.BeatmapInfo.Update(beatmap); + Context.SaveChanges(); BeatmapHidden?.Invoke(beatmap); return true; @@ -117,8 +117,8 @@ namespace osu.Game.Beatmaps if (!beatmap.Hidden) return false; beatmap.Hidden = false; - Connection.BeatmapInfo.Update(beatmap); - Connection.SaveChanges(); + Context.BeatmapInfo.Update(beatmap); + Context.SaveChanges(); BeatmapRestored?.Invoke(beatmap); return true; @@ -126,18 +126,18 @@ namespace osu.Game.Beatmaps private void cleanupPendingDeletions() { - Connection.BeatmapSetInfo.RemoveRange(Connection.BeatmapSetInfo.Where(b => b.DeletePending && !b.Protected)); - Connection.SaveChanges(); + Context.BeatmapSetInfo.RemoveRange(Context.BeatmapSetInfo.Where(b => b.DeletePending && !b.Protected)); + Context.SaveChanges(); } - public IEnumerable BeatmapSets => Connection.BeatmapSetInfo + public IEnumerable BeatmapSets => Context.BeatmapSetInfo .Include(s => s.Metadata) .Include(s => s.Beatmaps).ThenInclude(s => s.Ruleset) .Include(s => s.Beatmaps).ThenInclude(b => b.Difficulty) .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata) .Include(s => s.Files).ThenInclude(f => f.FileInfo); - public IEnumerable Beatmaps => Connection.BeatmapInfo + public IEnumerable Beatmaps => Context.BeatmapInfo .Include(b => b.BeatmapSet).ThenInclude(s => s.Metadata) .Include(b => b.Metadata) .Include(b => b.Ruleset) diff --git a/osu.Game/Database/DatabaseBackedStore.cs b/osu.Game/Database/DatabaseBackedStore.cs index adddad6122..0cfe2adb44 100644 --- a/osu.Game/Database/DatabaseBackedStore.cs +++ b/osu.Game/Database/DatabaseBackedStore.cs @@ -10,12 +10,12 @@ namespace osu.Game.Database public abstract class DatabaseBackedStore { protected readonly Storage Storage; - protected readonly OsuDbContext Connection; + protected readonly OsuDbContext Context; - protected DatabaseBackedStore(OsuDbContext connection, Storage storage = null) + protected DatabaseBackedStore(OsuDbContext context, Storage storage = null) { Storage = storage; - Connection = connection; + Context = context; try { diff --git a/osu.Game/IO/FileStore.cs b/osu.Game/IO/FileStore.cs index ede85ad42f..ed295c76b2 100644 --- a/osu.Game/IO/FileStore.cs +++ b/osu.Game/IO/FileStore.cs @@ -22,7 +22,7 @@ namespace osu.Game.IO public readonly ResourceStore Store; - public FileStore(OsuDbContext connection, Storage storage) : base(connection, storage) + public FileStore(OsuDbContext context, Storage storage) : base(context, storage) { Store = new NamespacedResourceStore(new StorageBackedResourceStore(storage), prefix); } @@ -34,7 +34,7 @@ namespace osu.Game.IO if (Storage.ExistsDirectory(prefix)) Storage.DeleteDirectory(prefix); - Connection.Database.ExecuteSqlCommand("DELETE FROM FileInfo"); + Context.Database.ExecuteSqlCommand("DELETE FROM FileInfo"); } } @@ -48,7 +48,7 @@ namespace osu.Game.IO { string hash = data.ComputeSHA2Hash(); - var existing = Connection.FileInfo.FirstOrDefault(f => f.Hash == hash); + var existing = Context.FileInfo.FirstOrDefault(f => f.Hash == hash); var info = existing ?? new FileInfo { Hash = hash }; @@ -75,34 +75,34 @@ namespace osu.Game.IO { foreach (var f in files.GroupBy(f => f.ID)) { - var refetch = Connection.Find(f.First().ID) ?? f.First(); + var refetch = Context.Find(f.First().ID) ?? f.First(); refetch.ReferenceCount += f.Count(); - Connection.FileInfo.Update(refetch); + Context.FileInfo.Update(refetch); } - Connection.SaveChanges(); + Context.SaveChanges(); } public void Dereference(params FileInfo[] files) { foreach (var f in files.GroupBy(f => f.ID)) { - var refetch = Connection.Find(f.First().ID); + var refetch = Context.Find(f.First().ID); refetch.ReferenceCount -= f.Count(); - Connection.Update(refetch); + Context.Update(refetch); } - Connection.SaveChanges(); + Context.SaveChanges(); } private void deletePending() { - foreach (var f in Connection.FileInfo.Where(f => f.ReferenceCount < 1)) + foreach (var f in Context.FileInfo.Where(f => f.ReferenceCount < 1)) { try { Storage.Delete(Path.Combine(prefix, f.StoragePath)); - Connection.FileInfo.Remove(f); + Context.FileInfo.Remove(f); } catch (Exception e) { @@ -110,7 +110,7 @@ namespace osu.Game.IO } } - Connection.SaveChanges(); + Context.SaveChanges(); } } } diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index 9edab896b3..2d0aabdd7f 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -14,8 +14,8 @@ namespace osu.Game.Input { public class KeyBindingStore : DatabaseBackedStore { - public KeyBindingStore(OsuDbContext connection, RulesetStore rulesets, Storage storage = null) - : base(connection, storage) + public KeyBindingStore(OsuDbContext context, RulesetStore rulesets, Storage storage = null) + : base(context, storage) { foreach (var info in rulesets.AvailableRulesets) { @@ -30,7 +30,7 @@ namespace osu.Game.Input protected override void Prepare(bool reset = false) { if (reset) - Connection.Database.ExecuteSqlCommand("DELETE FROM KeyBinding"); + Context.Database.ExecuteSqlCommand("DELETE FROM KeyBinding"); } private void insertDefaults(IEnumerable defaults, int? rulesetId = null, int? variant = null) @@ -46,7 +46,7 @@ namespace osu.Game.Input foreach (var insertable in group.Skip(count).Take(aimCount - count)) // insert any defaults which are missing. - Connection.DatabasedKeyBinding.Add(new DatabasedKeyBinding + Context.DatabasedKeyBinding.Add(new DatabasedKeyBinding { KeyCombination = insertable.KeyCombination, Action = insertable.Action, @@ -55,7 +55,7 @@ namespace osu.Game.Input }); } - Connection.SaveChanges(); + Context.SaveChanges(); } /// @@ -64,12 +64,12 @@ namespace osu.Game.Input /// The ruleset's internal ID. /// An optional variant. /// - public IEnumerable Query(int? rulesetId = null, int? variant = null) => Connection.DatabasedKeyBinding.Where(b => b.RulesetID == rulesetId && b.Variant == variant); + public IEnumerable Query(int? rulesetId = null, int? variant = null) => Context.DatabasedKeyBinding.Where(b => b.RulesetID == rulesetId && b.Variant == variant); public void Update(KeyBinding keyBinding) { - Connection.Update(keyBinding); - Connection.SaveChanges(); + Context.Update(keyBinding); + Context.SaveChanges(); } } } diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index c39312205c..82252b76fa 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -26,8 +26,8 @@ namespace osu.Game.Rulesets loadRulesetFromFile(file); } - public RulesetStore(OsuDbContext connection) - : base(connection) + public RulesetStore(OsuDbContext context) + : base(context) { } @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets /// /// All available rulesets. /// - public IEnumerable AvailableRulesets => Connection.RulesetInfo.Where(r => r.Available); + public IEnumerable AvailableRulesets => Context.RulesetInfo.Where(r => r.Available); private static Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args) => loaded_assemblies.Keys.FirstOrDefault(a => a.FullName == args.Name); @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets { if (reset) { - Connection.Database.ExecuteSqlCommand("DELETE FROM RulesetInfo"); + Context.Database.ExecuteSqlCommand("DELETE FROM RulesetInfo"); } var instances = loaded_assemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r, new RulesetInfo())).ToList(); @@ -60,29 +60,29 @@ namespace osu.Game.Rulesets foreach (var r in instances.Where(r => r.LegacyID >= 0).OrderBy(r => r.LegacyID)) { var rulesetInfo = createRulesetInfo(r); - if (Connection.RulesetInfo.SingleOrDefault(rsi => rsi.ID == rulesetInfo.ID) == null) + if (Context.RulesetInfo.SingleOrDefault(rsi => rsi.ID == rulesetInfo.ID) == null) { - Connection.RulesetInfo.Add(rulesetInfo); + Context.RulesetInfo.Add(rulesetInfo); } } - Connection.SaveChanges(); + Context.SaveChanges(); //add any other modes foreach (var r in instances.Where(r => r.LegacyID < 0)) { var us = createRulesetInfo(r); - var existing = Connection.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo == us.InstantiationInfo); + var existing = Context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo == us.InstantiationInfo); if (existing == null) - Connection.RulesetInfo.Add(us); + Context.RulesetInfo.Add(us); } - Connection.SaveChanges(); + Context.SaveChanges(); //perform a consistency check - foreach (var r in Connection.RulesetInfo) + foreach (var r in Context.RulesetInfo) { try { @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets } } - Connection.SaveChanges(); + Context.SaveChanges(); } private static void loadRulesetFromFile(string file) diff --git a/osu.Game/Rulesets/Scoring/ScoreStore.cs b/osu.Game/Rulesets/Scoring/ScoreStore.cs index 66fcfb5d67..02dd5c40ac 100644 --- a/osu.Game/Rulesets/Scoring/ScoreStore.cs +++ b/osu.Game/Rulesets/Scoring/ScoreStore.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Scoring // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) private ScoreIPCChannel ipc; - public ScoreStore(Storage storage, OsuDbContext connection, IIpcHost importHost = null, BeatmapManager beatmaps = null, RulesetStore rulesets = null) : base(connection) + public ScoreStore(Storage storage, OsuDbContext context, IIpcHost importHost = null, BeatmapManager beatmaps = null, RulesetStore rulesets = null) : base(context) { this.storage = storage; this.beatmaps = beatmaps; From fe44a28d48ead86d187ee2ee83aaf3b43ba1d4fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Oct 2017 12:46:38 +0900 Subject: [PATCH 55/90] Add back startup tasks runner --- osu.Game/Database/DatabaseBackedStore.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Database/DatabaseBackedStore.cs b/osu.Game/Database/DatabaseBackedStore.cs index 0cfe2adb44..cba334d58a 100644 --- a/osu.Game/Database/DatabaseBackedStore.cs +++ b/osu.Game/Database/DatabaseBackedStore.cs @@ -26,6 +26,8 @@ namespace osu.Game.Database Logger.Error(e, $@"Failed to initialise the {GetType()}! Trying again with a clean database..."); Prepare(true); } + + StartupTasks(); } /// From cd41862e3bcfd579b40eea94c84cb00c8d1d62ff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Oct 2017 15:00:27 +0900 Subject: [PATCH 56/90] Add back transaction support for beatmap importing --- osu.Game/Beatmaps/BeatmapManager.cs | 56 +++++++++++-------- osu.Game/Beatmaps/BeatmapStore.cs | 4 +- osu.Game/Database/DatabaseBackedStore.cs | 9 ++- osu.Game/Database/DatabaseContextFactory.cs | 19 +++++++ osu.Game/IO/FileStore.cs | 2 +- osu.Game/Input/KeyBindingStore.cs | 5 +- osu.Game/OsuGameBase.cs | 18 +++--- osu.Game/Rulesets/RulesetStore.cs | 4 +- osu.Game/Rulesets/Scoring/ScoreStore.cs | 3 +- .../Tests/Visual/TestCasePlaySongSelect.cs | 11 ++-- osu.Game/osu.Game.csproj | 1 + 11 files changed, 84 insertions(+), 48 deletions(-) create mode 100644 osu.Game/Database/DatabaseContextFactory.cs diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 7d174e6a28..1423e9138f 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -57,6 +57,18 @@ namespace osu.Game.Beatmaps private readonly Storage storage; + private BeatmapStore createBeatmapStore(Func context) + { + var store = new BeatmapStore(context); + store.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s); + store.BeatmapSetRemoved += s => BeatmapSetRemoved?.Invoke(s); + store.BeatmapHidden += b => BeatmapHidden?.Invoke(b); + store.BeatmapRestored += b => BeatmapRestored?.Invoke(b); + return store; + } + + private readonly Func createContext; + private readonly FileStore files; private readonly RulesetStore rulesets; @@ -80,16 +92,13 @@ namespace osu.Game.Beatmaps /// public Func GetStableStorage { private get; set; } - public BeatmapManager(Storage storage, FileStore files, OsuDbContext context, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null) + public BeatmapManager(Storage storage, Func context, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null) { - beatmaps = new BeatmapStore(context); - beatmaps.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s); - beatmaps.BeatmapSetRemoved += s => BeatmapSetRemoved?.Invoke(s); - beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b); - beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b); + createContext = context; + beatmaps = createBeatmapStore(context); + files = new FileStore(context, storage); this.storage = storage; - this.files = files; this.rulesets = rulesets; this.api = api; @@ -160,13 +169,24 @@ namespace osu.Game.Beatmaps /// The beatmap to be imported. public BeatmapSetInfo Import(ArchiveReader archiveReader) { - BeatmapSetInfo set; - // let's only allow one concurrent import at a time for now. lock (importLock) - Import(set = importToStorage(archiveReader)); + { + var context = createContext(); - return set; + using (var transaction = context.Database.BeginTransaction()) + { + // create local stores so we can isolate and thread safely, and share a context/transaction. + var filesForImport = new FileStore(() => context, storage); + var beatmapsForImport = createBeatmapStore(() => context); + + BeatmapSetInfo set = importToStorage(filesForImport, archiveReader); + beatmapsForImport.Add(set); + context.SaveChanges(); + transaction.Commit(); + return set; + } + } } /// @@ -178,8 +198,7 @@ namespace osu.Game.Beatmaps // If we have an ID then we already exist in the database. if (beatmapSetInfo.ID != 0) return; - lock (beatmaps) - beatmaps.Add(beatmapSetInfo); + createBeatmapStore(createContext).Add(beatmapSetInfo); } /// @@ -322,15 +341,6 @@ namespace osu.Game.Beatmaps return working; } - /// - /// Reset the manager to an empty state. - /// - public void Reset() - { - lock (beatmaps) - beatmaps.Reset(); - } - /// /// Perform a lookup query on available s. /// @@ -400,7 +410,7 @@ namespace osu.Game.Beatmaps /// /// The beatmap archive to be read. /// The imported beatmap, or an existing instance if it is already present. - private BeatmapSetInfo importToStorage(ArchiveReader reader) + private BeatmapSetInfo importToStorage(FileStore files, ArchiveReader reader) { // let's make sure there are actually .osu files to import. string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu")); diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 134059fd07..f3d3caeb0f 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -20,8 +20,8 @@ namespace osu.Game.Beatmaps public event Action BeatmapHidden; public event Action BeatmapRestored; - public BeatmapStore(OsuDbContext context) - : base(context) + public BeatmapStore(Func factory) + : base(factory) { } diff --git a/osu.Game/Database/DatabaseBackedStore.cs b/osu.Game/Database/DatabaseBackedStore.cs index cba334d58a..9d3d020250 100644 --- a/osu.Game/Database/DatabaseBackedStore.cs +++ b/osu.Game/Database/DatabaseBackedStore.cs @@ -10,12 +10,15 @@ namespace osu.Game.Database public abstract class DatabaseBackedStore { protected readonly Storage Storage; - protected readonly OsuDbContext Context; - protected DatabaseBackedStore(OsuDbContext context, Storage storage = null) + private readonly Func contextSource; + + protected OsuDbContext Context => contextSource(); + + protected DatabaseBackedStore(Func contextSource, Storage storage = null) { Storage = storage; - Context = context; + this.contextSource = contextSource; try { diff --git a/osu.Game/Database/DatabaseContextFactory.cs b/osu.Game/Database/DatabaseContextFactory.cs new file mode 100644 index 0000000000..3fc5141880 --- /dev/null +++ b/osu.Game/Database/DatabaseContextFactory.cs @@ -0,0 +1,19 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE + +using osu.Framework.Platform; + +namespace osu.Game.Database +{ + public class DatabaseContextFactory + { + private readonly GameHost host; + + public DatabaseContextFactory(GameHost host) + { + this.host = host; + } + + public OsuDbContext GetContext() => new OsuDbContext(host.Storage.GetDatabaseConnectionString(@"client")); + } +} diff --git a/osu.Game/IO/FileStore.cs b/osu.Game/IO/FileStore.cs index ed295c76b2..d715ccd0a7 100644 --- a/osu.Game/IO/FileStore.cs +++ b/osu.Game/IO/FileStore.cs @@ -22,7 +22,7 @@ namespace osu.Game.IO public readonly ResourceStore Store; - public FileStore(OsuDbContext context, Storage storage) : base(context, storage) + public FileStore(Func contextSource, Storage storage) : base(contextSource, storage) { Store = new NamespacedResourceStore(new StorageBackedResourceStore(storage), prefix); } diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index 2d0aabdd7f..5c41179418 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -1,6 +1,7 @@ // 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 Microsoft.EntityFrameworkCore; @@ -14,8 +15,8 @@ namespace osu.Game.Input { public class KeyBindingStore : DatabaseBackedStore { - public KeyBindingStore(OsuDbContext context, RulesetStore rulesets, Storage storage = null) - : base(context, storage) + public KeyBindingStore(Func contextSource, RulesetStore rulesets, Storage storage = null) + : base(contextSource, storage) { foreach (var info in rulesets.AvailableRulesets) { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index d1e684499c..5ecc7279da 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -81,16 +81,18 @@ namespace osu.Game protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); - private OsuDbContext createDbContext() => new OsuDbContext(Host.Storage.GetDatabaseConnectionString(@"client")); + private DatabaseContextFactory contextFactory; [BackgroundDependencyLoader] private void load() { + dependencies.Cache(contextFactory = new DatabaseContextFactory(Host)); + dependencies.Cache(this); dependencies.Cache(LocalConfig); - using (var dbContext = createDbContext()) - dbContext.Database.Migrate(); + using (var context = contextFactory.GetContext()) + context.Database.Migrate(); dependencies.Cache(API = new APIAccess { @@ -98,11 +100,11 @@ namespace osu.Game Token = LocalConfig.Get(OsuSetting.Token) }); - dependencies.Cache(RulesetStore = new RulesetStore(createDbContext())); - dependencies.Cache(FileStore = new FileStore(createDbContext(), Host.Storage)); - dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, FileStore, createDbContext(), RulesetStore, API, Host)); - dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, createDbContext(), Host, BeatmapManager, RulesetStore)); - dependencies.Cache(KeyBindingStore = new KeyBindingStore(createDbContext(), RulesetStore)); + dependencies.Cache(RulesetStore = new RulesetStore(contextFactory.GetContext)); + dependencies.Cache(FileStore = new FileStore(contextFactory.GetContext, Host.Storage)); + dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory.GetContext, RulesetStore, API, Host)); + dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, contextFactory.GetContext, Host, BeatmapManager, RulesetStore)); + dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory.GetContext, RulesetStore)); dependencies.Cache(new OsuColour()); //this completely overrides the framework default. will need to change once we make a proper FontStore. diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 82252b76fa..bd3c22fc42 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -26,8 +26,8 @@ namespace osu.Game.Rulesets loadRulesetFromFile(file); } - public RulesetStore(OsuDbContext context) - : base(context) + public RulesetStore(Func factory) + : base(factory) { } diff --git a/osu.Game/Rulesets/Scoring/ScoreStore.cs b/osu.Game/Rulesets/Scoring/ScoreStore.cs index 02dd5c40ac..67a8e5372e 100644 --- a/osu.Game/Rulesets/Scoring/ScoreStore.cs +++ b/osu.Game/Rulesets/Scoring/ScoreStore.cs @@ -1,6 +1,7 @@ // 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 osu.Framework.Platform; @@ -25,7 +26,7 @@ namespace osu.Game.Rulesets.Scoring // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) private ScoreIPCChannel ipc; - public ScoreStore(Storage storage, OsuDbContext context, IIpcHost importHost = null, BeatmapManager beatmaps = null, RulesetStore rulesets = null) : base(context) + public ScoreStore(Storage storage, Func factory, IIpcHost importHost = null, BeatmapManager beatmaps = null, RulesetStore rulesets = null) : base(factory) { this.storage = storage; this.beatmaps = beatmaps; diff --git a/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs index a8a00e9a0d..9ca6aa2fb5 100644 --- a/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs +++ b/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs @@ -1,13 +1,13 @@ // 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 osu.Framework.Allocation; using osu.Framework.MathUtils; using osu.Game.Beatmaps; using osu.Game.Database; -using osu.Game.IO; using osu.Game.Rulesets; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Filter; @@ -25,8 +25,6 @@ namespace osu.Game.Tests.Visual private DependencyContainer dependencies; - private FileStore files; - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent); [BackgroundDependencyLoader] @@ -40,9 +38,10 @@ namespace osu.Game.Tests.Visual var dbConnectionString = storage.GetDatabaseConnectionString(@"client"); - dependencies.Cache(rulesets = new RulesetStore(new OsuDbContext(dbConnectionString))); - dependencies.Cache(files = new FileStore(new OsuDbContext(dbConnectionString), storage)); - dependencies.Cache(manager = new BeatmapManager(storage, files, new OsuDbContext(dbConnectionString), rulesets, null)); + Func contextFactory = () => new OsuDbContext(dbConnectionString); + + dependencies.Cache(rulesets = new RulesetStore(contextFactory)); + dependencies.Cache(manager = new BeatmapManager(storage, contextFactory, rulesets, null)); for (int i = 0; i < 100; i += 10) manager.Import(createTestBeatmapSet(i)); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 731e065a48..d3ac2d6189 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -285,6 +285,7 @@ + 20171014052545_Init.cs From e487b6f82a18543592c4f209f5d428881a774d72 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Oct 2017 15:50:42 +0900 Subject: [PATCH 57/90] Standardise context retrieval --- osu.Game/Beatmaps/BeatmapStore.cs | 70 ++++++++++++++---------- osu.Game/Database/DatabaseBackedStore.cs | 8 +-- osu.Game/IO/FileStore.cs | 36 +++++++----- osu.Game/Input/KeyBindingStore.cs | 31 ++++++++--- osu.Game/Rulesets/RulesetStore.cs | 22 ++++---- 5 files changed, 101 insertions(+), 66 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index f3d3caeb0f..69aadb470e 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -29,12 +29,14 @@ namespace osu.Game.Beatmaps { if (reset) { + var context = GetContext(); + // https://stackoverflow.com/a/10450893 - Context.Database.ExecuteSqlCommand("DELETE FROM BeatmapMetadata"); - Context.Database.ExecuteSqlCommand("DELETE FROM BeatmapDifficulty"); - Context.Database.ExecuteSqlCommand("DELETE FROM BeatmapSetInfo"); - Context.Database.ExecuteSqlCommand("DELETE FROM BeatmapSetFileInfo"); - Context.Database.ExecuteSqlCommand("DELETE FROM BeatmapInfo"); + context.Database.ExecuteSqlCommand("DELETE FROM BeatmapMetadata"); + context.Database.ExecuteSqlCommand("DELETE FROM BeatmapDifficulty"); + context.Database.ExecuteSqlCommand("DELETE FROM BeatmapSetInfo"); + context.Database.ExecuteSqlCommand("DELETE FROM BeatmapSetFileInfo"); + context.Database.ExecuteSqlCommand("DELETE FROM BeatmapInfo"); } } @@ -50,8 +52,10 @@ namespace osu.Game.Beatmaps /// The beatmap to add. public void Add(BeatmapSetInfo beatmapSet) { - Context.BeatmapSetInfo.Attach(beatmapSet); - Context.SaveChanges(); + var context = GetContext(); + + context.BeatmapSetInfo.Attach(beatmapSet); + context.SaveChanges(); BeatmapSetAdded?.Invoke(beatmapSet); } @@ -63,11 +67,13 @@ namespace osu.Game.Beatmaps /// Whether the beatmap's was changed. public bool Delete(BeatmapSetInfo beatmapSet) { + var context = GetContext(); + if (beatmapSet.DeletePending) return false; beatmapSet.DeletePending = true; - Context.BeatmapSetInfo.Update(beatmapSet); - Context.SaveChanges(); + context.BeatmapSetInfo.Update(beatmapSet); + context.SaveChanges(); BeatmapSetRemoved?.Invoke(beatmapSet); return true; @@ -80,11 +86,13 @@ namespace osu.Game.Beatmaps /// Whether the beatmap's was changed. public bool Undelete(BeatmapSetInfo beatmapSet) { + var context = GetContext(); + if (!beatmapSet.DeletePending) return false; beatmapSet.DeletePending = false; - Context.BeatmapSetInfo.Update(beatmapSet); - Context.SaveChanges(); + context.BeatmapSetInfo.Update(beatmapSet); + context.SaveChanges(); BeatmapSetAdded?.Invoke(beatmapSet); return true; @@ -97,11 +105,13 @@ namespace osu.Game.Beatmaps /// Whether the beatmap's was changed. public bool Hide(BeatmapInfo beatmap) { + var context = GetContext(); + if (beatmap.Hidden) return false; beatmap.Hidden = true; - Context.BeatmapInfo.Update(beatmap); - Context.SaveChanges(); + context.BeatmapInfo.Update(beatmap); + context.SaveChanges(); BeatmapHidden?.Invoke(beatmap); return true; @@ -114,11 +124,13 @@ namespace osu.Game.Beatmaps /// Whether the beatmap's was changed. public bool Restore(BeatmapInfo beatmap) { + var context = GetContext(); + if (!beatmap.Hidden) return false; beatmap.Hidden = false; - Context.BeatmapInfo.Update(beatmap); - Context.SaveChanges(); + context.BeatmapInfo.Update(beatmap); + context.SaveChanges(); BeatmapRestored?.Invoke(beatmap); return true; @@ -126,21 +138,23 @@ namespace osu.Game.Beatmaps private void cleanupPendingDeletions() { - Context.BeatmapSetInfo.RemoveRange(Context.BeatmapSetInfo.Where(b => b.DeletePending && !b.Protected)); - Context.SaveChanges(); + var context = GetContext(); + + context.BeatmapSetInfo.RemoveRange(context.BeatmapSetInfo.Where(b => b.DeletePending && !b.Protected)); + context.SaveChanges(); } - public IEnumerable BeatmapSets => Context.BeatmapSetInfo - .Include(s => s.Metadata) - .Include(s => s.Beatmaps).ThenInclude(s => s.Ruleset) - .Include(s => s.Beatmaps).ThenInclude(b => b.Difficulty) - .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata) - .Include(s => s.Files).ThenInclude(f => f.FileInfo); + public IEnumerable BeatmapSets => GetContext().BeatmapSetInfo + .Include(s => s.Metadata) + .Include(s => s.Beatmaps).ThenInclude(s => s.Ruleset) + .Include(s => s.Beatmaps).ThenInclude(b => b.Difficulty) + .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata) + .Include(s => s.Files).ThenInclude(f => f.FileInfo); - public IEnumerable Beatmaps => Context.BeatmapInfo - .Include(b => b.BeatmapSet).ThenInclude(s => s.Metadata) - .Include(b => b.Metadata) - .Include(b => b.Ruleset) - .Include(b => b.Difficulty); + public IEnumerable Beatmaps => GetContext().BeatmapInfo + .Include(b => b.BeatmapSet).ThenInclude(s => s.Metadata) + .Include(b => b.Metadata) + .Include(b => b.Ruleset) + .Include(b => b.Difficulty); } } diff --git a/osu.Game/Database/DatabaseBackedStore.cs b/osu.Game/Database/DatabaseBackedStore.cs index 9d3d020250..79aea7863a 100644 --- a/osu.Game/Database/DatabaseBackedStore.cs +++ b/osu.Game/Database/DatabaseBackedStore.cs @@ -11,14 +11,12 @@ namespace osu.Game.Database { protected readonly Storage Storage; - private readonly Func contextSource; + protected readonly Func GetContext; - protected OsuDbContext Context => contextSource(); - - protected DatabaseBackedStore(Func contextSource, Storage storage = null) + protected DatabaseBackedStore(Func getContext, Storage storage = null) { Storage = storage; - this.contextSource = contextSource; + GetContext = getContext; try { diff --git a/osu.Game/IO/FileStore.cs b/osu.Game/IO/FileStore.cs index d715ccd0a7..b60d82d61c 100644 --- a/osu.Game/IO/FileStore.cs +++ b/osu.Game/IO/FileStore.cs @@ -22,7 +22,7 @@ namespace osu.Game.IO public readonly ResourceStore Store; - public FileStore(Func contextSource, Storage storage) : base(contextSource, storage) + public FileStore(Func getContext, Storage storage) : base(getContext, storage) { Store = new NamespacedResourceStore(new StorageBackedResourceStore(storage), prefix); } @@ -34,7 +34,7 @@ namespace osu.Game.IO if (Storage.ExistsDirectory(prefix)) Storage.DeleteDirectory(prefix); - Context.Database.ExecuteSqlCommand("DELETE FROM FileInfo"); + GetContext().Database.ExecuteSqlCommand("DELETE FROM FileInfo"); } } @@ -46,9 +46,11 @@ namespace osu.Game.IO public FileInfo Add(Stream data, bool reference = true) { + var context = GetContext(); + string hash = data.ComputeSHA2Hash(); - var existing = Context.FileInfo.FirstOrDefault(f => f.Hash == hash); + var existing = context.FileInfo.FirstOrDefault(f => f.Hash == hash); var info = existing ?? new FileInfo { Hash = hash }; @@ -71,38 +73,44 @@ namespace osu.Game.IO return info; } - public void Reference(params FileInfo[] files) + public void Reference(params FileInfo[] files) => reference(GetContext(), files); + + private void reference(OsuDbContext context, FileInfo[] files) { foreach (var f in files.GroupBy(f => f.ID)) { - var refetch = Context.Find(f.First().ID) ?? f.First(); + var refetch = context.Find(f.First().ID) ?? f.First(); refetch.ReferenceCount += f.Count(); - Context.FileInfo.Update(refetch); + context.FileInfo.Update(refetch); } - Context.SaveChanges(); + context.SaveChanges(); } - public void Dereference(params FileInfo[] files) + public void Dereference(params FileInfo[] files) => dereference(GetContext(), files); + + private void dereference(OsuDbContext context, FileInfo[] files) { foreach (var f in files.GroupBy(f => f.ID)) { - var refetch = Context.Find(f.First().ID); + var refetch = context.Find(f.First().ID); refetch.ReferenceCount -= f.Count(); - Context.Update(refetch); + context.Update(refetch); } - Context.SaveChanges(); + context.SaveChanges(); } private void deletePending() { - foreach (var f in Context.FileInfo.Where(f => f.ReferenceCount < 1)) + var context = GetContext(); + + foreach (var f in context.FileInfo.Where(f => f.ReferenceCount < 1)) { try { Storage.Delete(Path.Combine(prefix, f.StoragePath)); - Context.FileInfo.Remove(f); + context.FileInfo.Remove(f); } catch (Exception e) { @@ -110,7 +118,7 @@ namespace osu.Game.IO } } - Context.SaveChanges(); + context.SaveChanges(); } } } diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index 5c41179418..1e9a2aa22f 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -15,9 +15,16 @@ namespace osu.Game.Input { public class KeyBindingStore : DatabaseBackedStore { - public KeyBindingStore(Func contextSource, RulesetStore rulesets, Storage storage = null) - : base(contextSource, storage) + /// + /// As we do a lot of lookups, let's share a context between them to hopefully improve performance. + /// + private readonly OsuDbContext queryContext; + + public KeyBindingStore(Func getContext, RulesetStore rulesets, Storage storage = null) + : base(getContext, storage) { + queryContext = GetContext(); + foreach (var info in rulesets.AvailableRulesets) { var ruleset = info.CreateInstance(); @@ -31,15 +38,17 @@ namespace osu.Game.Input protected override void Prepare(bool reset = false) { if (reset) - Context.Database.ExecuteSqlCommand("DELETE FROM KeyBinding"); + GetContext().Database.ExecuteSqlCommand("DELETE FROM KeyBinding"); } private void insertDefaults(IEnumerable defaults, int? rulesetId = null, int? variant = null) { + var context = GetContext(); + // compare counts in database vs defaults foreach (var group in defaults.GroupBy(k => k.Action)) { - int count = Query(rulesetId, variant).Count(k => (int)k.Action == (int)group.Key); + int count = query(context, rulesetId, variant).Count(k => (int)k.Action == (int)group.Key); int aimCount = group.Count(); if (aimCount <= count) @@ -47,7 +56,7 @@ namespace osu.Game.Input foreach (var insertable in group.Skip(count).Take(aimCount - count)) // insert any defaults which are missing. - Context.DatabasedKeyBinding.Add(new DatabasedKeyBinding + context.DatabasedKeyBinding.Add(new DatabasedKeyBinding { KeyCombination = insertable.KeyCombination, Action = insertable.Action, @@ -56,7 +65,7 @@ namespace osu.Game.Input }); } - Context.SaveChanges(); + context.SaveChanges(); } /// @@ -65,12 +74,16 @@ namespace osu.Game.Input /// The ruleset's internal ID. /// An optional variant. /// - public IEnumerable Query(int? rulesetId = null, int? variant = null) => Context.DatabasedKeyBinding.Where(b => b.RulesetID == rulesetId && b.Variant == variant); + public IEnumerable Query(int? rulesetId = null, int? variant = null) => query(queryContext, rulesetId, variant); + + private IEnumerable query(OsuDbContext context, int? rulesetId = null, int? variant = null) => + context.DatabasedKeyBinding.Where(b => b.RulesetID == rulesetId && b.Variant == variant); public void Update(KeyBinding keyBinding) { - Context.Update(keyBinding); - Context.SaveChanges(); + var context = GetContext(); + context.Update(keyBinding); + context.SaveChanges(); } } } diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index bd3c22fc42..7d982eb39e 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets /// /// All available rulesets. /// - public IEnumerable AvailableRulesets => Context.RulesetInfo.Where(r => r.Available); + public IEnumerable AvailableRulesets => GetContext().RulesetInfo.Where(r => r.Available); private static Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args) => loaded_assemblies.Keys.FirstOrDefault(a => a.FullName == args.Name); @@ -49,9 +49,11 @@ namespace osu.Game.Rulesets protected override void Prepare(bool reset = false) { + var context = GetContext(); + if (reset) { - Context.Database.ExecuteSqlCommand("DELETE FROM RulesetInfo"); + context.Database.ExecuteSqlCommand("DELETE FROM RulesetInfo"); } var instances = loaded_assemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r, new RulesetInfo())).ToList(); @@ -60,29 +62,29 @@ namespace osu.Game.Rulesets foreach (var r in instances.Where(r => r.LegacyID >= 0).OrderBy(r => r.LegacyID)) { var rulesetInfo = createRulesetInfo(r); - if (Context.RulesetInfo.SingleOrDefault(rsi => rsi.ID == rulesetInfo.ID) == null) + if (context.RulesetInfo.SingleOrDefault(rsi => rsi.ID == rulesetInfo.ID) == null) { - Context.RulesetInfo.Add(rulesetInfo); + context.RulesetInfo.Add(rulesetInfo); } } - Context.SaveChanges(); + context.SaveChanges(); //add any other modes foreach (var r in instances.Where(r => r.LegacyID < 0)) { var us = createRulesetInfo(r); - var existing = Context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo == us.InstantiationInfo); + var existing = context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo == us.InstantiationInfo); if (existing == null) - Context.RulesetInfo.Add(us); + context.RulesetInfo.Add(us); } - Context.SaveChanges(); + context.SaveChanges(); //perform a consistency check - foreach (var r in Context.RulesetInfo) + foreach (var r in context.RulesetInfo) { try { @@ -95,7 +97,7 @@ namespace osu.Game.Rulesets } } - Context.SaveChanges(); + context.SaveChanges(); } private static void loadRulesetFromFile(string file) From cf3881b18c4848e16188d5b96dd7b65e87ce996b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Oct 2017 15:50:52 +0900 Subject: [PATCH 58/90] Fix not being able to restore hidden beatmaps via context menu --- osu.Game/Beatmaps/Drawables/BeatmapGroup.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs b/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs index d1682a392d..6e5af29799 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs @@ -83,8 +83,7 @@ namespace osu.Game.Beatmaps.Drawables RelativeSizeAxes = Axes.X, }; - BeatmapSet.Beatmaps = BeatmapSet.Beatmaps.Where(b => !b.Hidden).OrderBy(b => b.StarDifficulty).ToList(); - BeatmapPanels = BeatmapSet.Beatmaps.Select(b => new BeatmapPanel(b) + BeatmapPanels = BeatmapSet.Beatmaps.Where(b => !b.Hidden).OrderBy(b => b.StarDifficulty).Select(b => new BeatmapPanel(b) { Alpha = 0, GainedSelection = panelGainedSelection, From ad54ca92688be0e4f7ebf8ccc4f548b6d360d2be Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Oct 2017 16:02:13 +0900 Subject: [PATCH 59/90] Fix TestCasePlaySongSelect --- osu.Game/Database/DatabaseContextFactory.cs | 2 +- osu.Game/Tests/Visual/TestCasePlaySongSelect.cs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/DatabaseContextFactory.cs b/osu.Game/Database/DatabaseContextFactory.cs index 3fc5141880..359188b4e2 100644 --- a/osu.Game/Database/DatabaseContextFactory.cs +++ b/osu.Game/Database/DatabaseContextFactory.cs @@ -1,5 +1,5 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Platform; diff --git a/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs index 9ca6aa2fb5..a46542760b 100644 --- a/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs +++ b/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.EntityFrameworkCore; using osu.Framework.Allocation; using osu.Framework.MathUtils; using osu.Game.Beatmaps; @@ -36,9 +37,11 @@ namespace osu.Game.Tests.Visual { var storage = new TestStorage(@"TestCasePlaySongSelect"); - var dbConnectionString = storage.GetDatabaseConnectionString(@"client"); + // this is by no means clean. should be replacing inside of OsuGameBase somehow. + var context = new OsuDbContext(storage.GetDatabaseConnectionString(@"client")); + context.Database.Migrate(); - Func contextFactory = () => new OsuDbContext(dbConnectionString); + Func contextFactory = () => context; dependencies.Cache(rulesets = new RulesetStore(contextFactory)); dependencies.Cache(manager = new BeatmapManager(storage, contextFactory, rulesets, null)); From e02640637a1c52df860876f1f9ab94998d412c43 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Oct 2017 17:08:01 +0900 Subject: [PATCH 60/90] Fix KeyBindingStore regression --- osu.Game/Input/KeyBindingStore.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index 1e9a2aa22f..54cf48bc2a 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -15,16 +15,9 @@ namespace osu.Game.Input { public class KeyBindingStore : DatabaseBackedStore { - /// - /// As we do a lot of lookups, let's share a context between them to hopefully improve performance. - /// - private readonly OsuDbContext queryContext; - public KeyBindingStore(Func getContext, RulesetStore rulesets, Storage storage = null) : base(getContext, storage) { - queryContext = GetContext(); - foreach (var info in rulesets.AvailableRulesets) { var ruleset = info.CreateInstance(); @@ -74,7 +67,7 @@ namespace osu.Game.Input /// The ruleset's internal ID. /// An optional variant. /// - public IEnumerable Query(int? rulesetId = null, int? variant = null) => query(queryContext, rulesetId, variant); + public IEnumerable Query(int? rulesetId = null, int? variant = null) => query(GetContext(), rulesetId, variant); private IEnumerable query(OsuDbContext context, int? rulesetId = null, int? variant = null) => context.DatabasedKeyBinding.Where(b => b.RulesetID == rulesetId && b.Variant == variant); From 64dfce258ff1b561120bc0e6c23f5e2872e93db3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Oct 2017 17:08:19 +0900 Subject: [PATCH 61/90] Fix file prefix not being read when calling storage.Exists --- osu.Game/IO/FileStore.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/IO/FileStore.cs b/osu.Game/IO/FileStore.cs index b60d82d61c..db4f5fd2a2 100644 --- a/osu.Game/IO/FileStore.cs +++ b/osu.Game/IO/FileStore.cs @@ -18,21 +18,21 @@ namespace osu.Game.IO /// public class FileStore : DatabaseBackedStore { - private const string prefix = "files"; + public readonly IResourceStore Store; - public readonly ResourceStore Store; + public Storage Storage => base.Storage; - public FileStore(Func getContext, Storage storage) : base(getContext, storage) + public FileStore(Func getContext, Storage storage) : base(getContext, storage.GetStorageForDirectory(@"files")) { - Store = new NamespacedResourceStore(new StorageBackedResourceStore(storage), prefix); + Store = new StorageBackedResourceStore(Storage); } protected override void Prepare(bool reset = false) { if (reset) { - if (Storage.ExistsDirectory(prefix)) - Storage.DeleteDirectory(prefix); + if (Storage.ExistsDirectory(string.Empty)) + Storage.DeleteDirectory(string.Empty); GetContext().Database.ExecuteSqlCommand("DELETE FROM FileInfo"); } @@ -54,7 +54,7 @@ namespace osu.Game.IO var info = existing ?? new FileInfo { Hash = hash }; - string path = Path.Combine(prefix, info.StoragePath); + string path = info.StoragePath; // we may be re-adding a file to fix missing store entries. if (!Storage.Exists(path)) @@ -109,7 +109,7 @@ namespace osu.Game.IO { try { - Storage.Delete(Path.Combine(prefix, f.StoragePath)); + Storage.Delete(f.StoragePath); context.FileInfo.Remove(f); } catch (Exception e) From 7a18d373ec9a4b96151280c5d475ce7d1a64e474 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Oct 2017 17:08:42 +0900 Subject: [PATCH 62/90] Improve performance of beatmap imports (still needs revision) --- osu.Game/Beatmaps/BeatmapManager.cs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 1423e9138f..bf71033f36 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -98,7 +98,7 @@ namespace osu.Game.Beatmaps beatmaps = createBeatmapStore(context); files = new FileStore(context, storage); - this.storage = storage; + this.storage = files.Storage; this.rulesets = rulesets; this.api = api; @@ -174,16 +174,23 @@ namespace osu.Game.Beatmaps { var context = createContext(); + context.Database.AutoTransactionsEnabled = false; + using (var transaction = context.Database.BeginTransaction()) { // create local stores so we can isolate and thread safely, and share a context/transaction. - var filesForImport = new FileStore(() => context, storage); - var beatmapsForImport = createBeatmapStore(() => context); + var iFiles = new FileStore(() => context, storage); + var iBeatmaps = createBeatmapStore(() => context); + + BeatmapSetInfo set = importToStorage(iFiles, iBeatmaps, archiveReader); + + if (set.ID == 0) + { + iBeatmaps.Add(set); + context.SaveChanges(); + transaction.Commit(); + } - BeatmapSetInfo set = importToStorage(filesForImport, archiveReader); - beatmapsForImport.Add(set); - context.SaveChanges(); - transaction.Commit(); return set; } } @@ -308,7 +315,7 @@ namespace osu.Game.Beatmaps /// Is a no-op for already usable beatmaps. /// /// The beatmap to restore. - public void Undelete(BeatmapSetInfo beatmapSet) + private void undelete(BeatmapStore beatmaps, FileStore files, BeatmapSetInfo beatmapSet) { lock (beatmaps) if (!beatmaps.Undelete(beatmapSet)) return; @@ -410,7 +417,7 @@ namespace osu.Game.Beatmaps /// /// The beatmap archive to be read. /// The imported beatmap, or an existing instance if it is already present. - private BeatmapSetInfo importToStorage(FileStore files, ArchiveReader reader) + private BeatmapSetInfo importToStorage(FileStore files, BeatmapStore beatmaps, ArchiveReader reader) { // let's make sure there are actually .osu files to import. string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu")); @@ -432,7 +439,7 @@ namespace osu.Game.Beatmaps if (beatmapSet != null) { - Undelete(beatmapSet); + undelete(beatmaps, files, beatmapSet); // ensure all files are present and accessible foreach (var f in beatmapSet.Files) @@ -442,6 +449,8 @@ namespace osu.Game.Beatmaps files.Add(s, false); } + // todo: delete any files which shouldn't exist any more. + return beatmapSet; } From 0177fcbe5f9ec6c849a084ca3559311437bc4d27 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Oct 2017 17:08:47 +0900 Subject: [PATCH 63/90] Fix xmldoc --- osu.Game/Database/DatabaseBackedStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/DatabaseBackedStore.cs b/osu.Game/Database/DatabaseBackedStore.cs index 79aea7863a..90a43a47c5 100644 --- a/osu.Game/Database/DatabaseBackedStore.cs +++ b/osu.Game/Database/DatabaseBackedStore.cs @@ -32,7 +32,7 @@ namespace osu.Game.Database } /// - /// Perform any common startup tasks. Runs after and . + /// Perform any common startup tasks. Runs after . /// protected virtual void StartupTasks() { From 3e415e326908ba50ecb42c78dc1c70d7fc4433f7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Oct 2017 17:52:02 +0900 Subject: [PATCH 64/90] Fix tooling failures --- osu.Game/Database/OsuDbContext.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index 5f4e42ed6b..bd288621e0 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -28,11 +28,19 @@ namespace osu.Game.Database SQLitePCL.Batteries_V2.Init(); } + /// + /// Create a new in-memory OsuDbContext instance. + /// + public OsuDbContext() : this("DataSource=:memory:") + { + // required for tooling (see https://wildermuth.com/2017/07/06/Program-cs-in-ASP-NET-Core-2-0). + } + /// /// Create a new OsuDbContext instance. /// - /// A valid SQLite connection string. If not provided, an in-memory instance will be created. - public OsuDbContext(string connectionString = "DataSource=:memory:") + /// A valid SQLite connection string. + public OsuDbContext(string connectionString) { this.connectionString = connectionString; @@ -82,7 +90,7 @@ namespace osu.Game.Database public void AddProvider(ILoggerProvider provider) { - throw new NotImplementedException(); + // no-op. called by tooling. } private class OsuDbLoggerProvider : ILoggerProvider From 12639c68191d65e8e703a0d313caf640aa6325b4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Oct 2017 17:52:20 +0900 Subject: [PATCH 65/90] Use a different database name for now to avoid conflicts when switching versions --- osu.Game/Database/DatabaseContextFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/DatabaseContextFactory.cs b/osu.Game/Database/DatabaseContextFactory.cs index 359188b4e2..e22301adfe 100644 --- a/osu.Game/Database/DatabaseContextFactory.cs +++ b/osu.Game/Database/DatabaseContextFactory.cs @@ -14,6 +14,6 @@ namespace osu.Game.Database this.host = host; } - public OsuDbContext GetContext() => new OsuDbContext(host.Storage.GetDatabaseConnectionString(@"client")); + public OsuDbContext GetContext() => new OsuDbContext(host.Storage.GetDatabaseConnectionString(@"client-ef")); } } From b9d0fb96ed0fd636a18fd385887f334b7cb10a3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Oct 2017 18:26:28 +0900 Subject: [PATCH 66/90] Fix cascade deletions --- osu.Game/Beatmaps/BeatmapDifficulty.cs | 3 + osu.Game/Beatmaps/BeatmapInfo.cs | 1 + osu.Game/Beatmaps/BeatmapMetadata.cs | 8 + osu.Game/Database/OsuDbContext.cs | 3 + ... 20171017092037_InitialCreate.Designer.cs} | 76 ++--- ...nit.cs => 20171017092037_InitialCreate.cs} | 280 +++++++++--------- .../Migrations/OsuDbContextModelSnapshot.cs | 76 ++--- osu.Game/osu.Game.csproj | 6 +- 8 files changed, 243 insertions(+), 210 deletions(-) rename osu.Game/Migrations/{20171014052545_Init.designer.cs => 20171017092037_InitialCreate.Designer.cs} (80%) rename osu.Game/Migrations/{20171014052545_Init.cs => 20171017092037_InitialCreate.cs} (86%) diff --git a/osu.Game/Beatmaps/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs index 25e212f3c5..e310c4a646 100644 --- a/osu.Game/Beatmaps/BeatmapDifficulty.cs +++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs @@ -14,6 +14,9 @@ namespace osu.Game.Beatmaps [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { get; set; } + + public int BeatmapInfoID { get; set; } + public float DrainRate { get; set; } = DEFAULT_DIFFICULTY; public float CircleSize { get; set; } = DEFAULT_DIFFICULTY; public float OverallDifficulty { get; set; } = DEFAULT_DIFFICULTY; diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index c1516f17c9..d2ed5d692c 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -31,6 +31,7 @@ namespace osu.Game.Beatmaps [Required] public BeatmapSetInfo BeatmapSet { get; set; } + public BeatmapMetadata Metadata { get; set; } public int BaseDifficultyID { get; set; } diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index 5e47d6c13d..ca8702d222 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -16,6 +16,14 @@ namespace osu.Game.Beatmaps [NotMapped] public int? OnlineBeatmapSetID { get; set; } + public int? BeatmapSetInfoID { get; set; } + + public BeatmapSetInfo BeatmapSetInfo { get; set; } + + public int? BeatmapInfoID { get; set; } + + public BeatmapInfo BeatmapInfo { get; set; } + public string Title { get; set; } public string TitleUnicode { get; set; } public string Artist { get; set; } diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index bd288621e0..e360f11ba3 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -74,6 +74,9 @@ namespace osu.Game.Database modelBuilder.Entity().HasIndex(b => b.Name).IsUnique(); modelBuilder.Entity().HasIndex(b => b.InstantiationInfo).IsUnique(); modelBuilder.Entity().HasIndex(b => b.Available); + + modelBuilder.Entity().HasOne(m => m.BeatmapSetInfo).WithOne(s => s.Metadata).OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity().HasOne(m => m.BeatmapInfo).WithOne(b => b.Metadata).OnDelete(DeleteBehavior.Cascade); } private class OsuDbLoggerFactory : ILoggerFactory diff --git a/osu.Game/Migrations/20171014052545_Init.designer.cs b/osu.Game/Migrations/20171017092037_InitialCreate.Designer.cs similarity index 80% rename from osu.Game/Migrations/20171014052545_Init.designer.cs rename to osu.Game/Migrations/20171017092037_InitialCreate.Designer.cs index f151131882..422c1612d2 100644 --- a/osu.Game/Migrations/20171014052545_Init.designer.cs +++ b/osu.Game/Migrations/20171017092037_InitialCreate.Designer.cs @@ -1,7 +1,4 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -// +// using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; @@ -13,8 +10,8 @@ using System; namespace osu.Game.Migrations { [DbContext(typeof(OsuDbContext))] - [Migration("20171014052545_Init")] - partial class Init + [Migration("20171017092037_InitialCreate")] + partial class InitialCreate { protected override void BuildTargetModel(ModelBuilder modelBuilder) { @@ -29,6 +26,8 @@ namespace osu.Game.Migrations b.Property("ApproachRate"); + b.Property("BeatmapInfoID"); + b.Property("CircleSize"); b.Property("DrainRate"); @@ -41,6 +40,9 @@ namespace osu.Game.Migrations b.HasKey("ID"); + b.HasIndex("BeatmapInfoID") + .IsUnique(); + b.ToTable("BeatmapDifficulty"); }); @@ -59,8 +61,6 @@ namespace osu.Game.Migrations b.Property("Countdown"); - b.Property("DifficultyID"); - b.Property("DistanceSpacing"); b.Property("GridSize"); @@ -73,8 +73,6 @@ namespace osu.Game.Migrations b.Property("MD5Hash"); - b.Property("MetadataID"); - b.Property("Path"); b.Property("RulesetID"); @@ -97,12 +95,8 @@ namespace osu.Game.Migrations b.HasIndex("BeatmapSetInfoID"); - b.HasIndex("DifficultyID"); - b.HasIndex("MD5Hash"); - b.HasIndex("MetadataID"); - b.HasIndex("RulesetID"); b.ToTable("BeatmapInfo"); @@ -119,10 +113,15 @@ namespace osu.Game.Migrations b.Property("AudioFile"); - b.Property("Author"); + b.Property("AuthorString") + .HasColumnName("Author"); b.Property("BackgroundFile"); + b.Property("BeatmapInfoID"); + + b.Property("BeatmapSetInfoID"); + b.Property("PreviewTime"); b.Property("Source"); @@ -135,6 +134,12 @@ namespace osu.Game.Migrations b.HasKey("ID"); + b.HasIndex("BeatmapInfoID") + .IsUnique(); + + b.HasIndex("BeatmapSetInfoID") + .IsUnique(); + b.ToTable("BeatmapMetadata"); }); @@ -168,16 +173,12 @@ namespace osu.Game.Migrations b.Property("Hash"); - b.Property("MetadataID"); - b.Property("Protected"); b.HasKey("ID"); b.HasIndex("DeletePending"); - b.HasIndex("MetadataID"); - b.ToTable("BeatmapSetInfo"); }); @@ -248,6 +249,14 @@ namespace osu.Game.Migrations b.ToTable("RulesetInfo"); }); + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapInfo") + .WithOne("Difficulty") + .HasForeignKey("osu.Game.Beatmaps.BeatmapDifficulty", "BeatmapInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => { b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") @@ -255,21 +264,25 @@ namespace osu.Game.Migrations .HasForeignKey("BeatmapSetInfoID") .OnDelete(DeleteBehavior.Cascade); - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "Difficulty") - .WithMany() - .HasForeignKey("DifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany() - .HasForeignKey("MetadataID"); - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") .WithMany() .HasForeignKey("RulesetID") .OnDelete(DeleteBehavior.Cascade); }); + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "BeatmapInfo") + .WithOne("Metadata") + .HasForeignKey("osu.Game.Beatmaps.BeatmapMetadata", "BeatmapInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSetInfo") + .WithOne("Metadata") + .HasForeignKey("osu.Game.Beatmaps.BeatmapMetadata", "BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => { b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") @@ -282,13 +295,6 @@ namespace osu.Game.Migrations .HasForeignKey("FileInfoID") .OnDelete(DeleteBehavior.Cascade); }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany() - .HasForeignKey("MetadataID"); - }); #pragma warning restore 612, 618 } } diff --git a/osu.Game/Migrations/20171014052545_Init.cs b/osu.Game/Migrations/20171017092037_InitialCreate.cs similarity index 86% rename from osu.Game/Migrations/20171014052545_Init.cs rename to osu.Game/Migrations/20171017092037_InitialCreate.cs index 6792f79e3d..2626e2ea74 100644 --- a/osu.Game/Migrations/20171014052545_Init.cs +++ b/osu.Game/Migrations/20171017092037_InitialCreate.cs @@ -1,52 +1,26 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; namespace osu.Game.Migrations { - public partial class Init : Migration + public partial class InitialCreate : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( - name: "BeatmapDifficulty", + name: "BeatmapSetInfo", columns: table => new { ID = table.Column(type: "INTEGER", nullable: false) .Annotation("Sqlite:Autoincrement", true), - ApproachRate = table.Column(type: "REAL", nullable: false), - CircleSize = table.Column(type: "REAL", nullable: false), - DrainRate = table.Column(type: "REAL", nullable: false), - OverallDifficulty = table.Column(type: "REAL", nullable: false), - SliderMultiplier = table.Column(type: "REAL", nullable: false), - SliderTickRate = table.Column(type: "REAL", nullable: false) + DeletePending = table.Column(type: "INTEGER", nullable: false), + Hash = table.Column(type: "TEXT", nullable: true), + Protected = table.Column(type: "INTEGER", nullable: false) }, constraints: table => { - table.PrimaryKey("PK_BeatmapDifficulty", x => x.ID); - }); - - migrationBuilder.CreateTable( - name: "BeatmapMetadata", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Artist = table.Column(type: "TEXT", nullable: true), - ArtistUnicode = table.Column(type: "TEXT", nullable: true), - AudioFile = table.Column(type: "TEXT", nullable: true), - Author = table.Column(type: "TEXT", nullable: true), - BackgroundFile = table.Column(type: "TEXT", nullable: true), - PreviewTime = table.Column(type: "INTEGER", nullable: false), - Source = table.Column(type: "TEXT", nullable: true), - Tags = table.Column(type: "TEXT", nullable: true), - Title = table.Column(type: "TEXT", nullable: true), - TitleUnicode = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_BeatmapMetadata", x => x.ID); + table.PrimaryKey("PK_BeatmapSetInfo", x => x.ID); }); migrationBuilder.CreateTable( @@ -94,86 +68,6 @@ namespace osu.Game.Migrations table.PrimaryKey("PK_RulesetInfo", x => x.ID); }); - migrationBuilder.CreateTable( - name: "BeatmapSetInfo", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - DeletePending = table.Column(type: "INTEGER", nullable: false), - Hash = table.Column(type: "TEXT", nullable: true), - MetadataID = table.Column(type: "INTEGER", nullable: true), - Protected = table.Column(type: "INTEGER", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_BeatmapSetInfo", x => x.ID); - table.ForeignKey( - name: "FK_BeatmapSetInfo_BeatmapMetadata_MetadataID", - column: x => x.MetadataID, - principalTable: "BeatmapMetadata", - principalColumn: "ID", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "BeatmapInfo", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - AudioLeadIn = table.Column(type: "INTEGER", nullable: false), - BaseDifficultyID = table.Column(type: "INTEGER", nullable: false), - BeatDivisor = table.Column(type: "INTEGER", nullable: false), - BeatmapSetInfoID = table.Column(type: "INTEGER", nullable: false), - Countdown = table.Column(type: "INTEGER", nullable: false), - DifficultyID = table.Column(type: "INTEGER", nullable: false), - DistanceSpacing = table.Column(type: "REAL", nullable: false), - GridSize = table.Column(type: "INTEGER", nullable: false), - Hash = table.Column(type: "TEXT", nullable: true), - Hidden = table.Column(type: "INTEGER", nullable: false), - LetterboxInBreaks = table.Column(type: "INTEGER", nullable: false), - MD5Hash = table.Column(type: "TEXT", nullable: true), - MetadataID = table.Column(type: "INTEGER", nullable: true), - Path = table.Column(type: "TEXT", nullable: true), - RulesetID = table.Column(type: "INTEGER", nullable: false), - SpecialStyle = table.Column(type: "INTEGER", nullable: false), - StackLeniency = table.Column(type: "REAL", nullable: false), - StarDifficulty = table.Column(type: "REAL", nullable: false), - StoredBookmarks = table.Column(type: "TEXT", nullable: true), - TimelineZoom = table.Column(type: "REAL", nullable: false), - Version = table.Column(type: "TEXT", nullable: true), - WidescreenStoryboard = table.Column(type: "INTEGER", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_BeatmapInfo", x => x.ID); - table.ForeignKey( - name: "FK_BeatmapInfo_BeatmapSetInfo_BeatmapSetInfoID", - column: x => x.BeatmapSetInfoID, - principalTable: "BeatmapSetInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_BeatmapInfo_BeatmapDifficulty_DifficultyID", - column: x => x.DifficultyID, - principalTable: "BeatmapDifficulty", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_BeatmapInfo_BeatmapMetadata_MetadataID", - column: x => x.MetadataID, - principalTable: "BeatmapMetadata", - principalColumn: "ID", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_BeatmapInfo_RulesetInfo_RulesetID", - column: x => x.RulesetID, - principalTable: "RulesetInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - }); - migrationBuilder.CreateTable( name: "BeatmapSetFileInfo", columns: table => new @@ -201,31 +95,144 @@ namespace osu.Game.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "BeatmapInfo", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + AudioLeadIn = table.Column(type: "INTEGER", nullable: false), + BaseDifficultyID = table.Column(type: "INTEGER", nullable: false), + BeatDivisor = table.Column(type: "INTEGER", nullable: false), + BeatmapSetInfoID = table.Column(type: "INTEGER", nullable: false), + Countdown = table.Column(type: "INTEGER", nullable: false), + DistanceSpacing = table.Column(type: "REAL", nullable: false), + GridSize = table.Column(type: "INTEGER", nullable: false), + Hash = table.Column(type: "TEXT", nullable: true), + Hidden = table.Column(type: "INTEGER", nullable: false), + LetterboxInBreaks = table.Column(type: "INTEGER", nullable: false), + MD5Hash = table.Column(type: "TEXT", nullable: true), + Path = table.Column(type: "TEXT", nullable: true), + RulesetID = table.Column(type: "INTEGER", nullable: false), + SpecialStyle = table.Column(type: "INTEGER", nullable: false), + StackLeniency = table.Column(type: "REAL", nullable: false), + StarDifficulty = table.Column(type: "REAL", nullable: false), + StoredBookmarks = table.Column(type: "TEXT", nullable: true), + TimelineZoom = table.Column(type: "REAL", nullable: false), + Version = table.Column(type: "TEXT", nullable: true), + WidescreenStoryboard = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_BeatmapInfo", x => x.ID); + table.ForeignKey( + name: "FK_BeatmapInfo_BeatmapSetInfo_BeatmapSetInfoID", + column: x => x.BeatmapSetInfoID, + principalTable: "BeatmapSetInfo", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_BeatmapInfo_RulesetInfo_RulesetID", + column: x => x.RulesetID, + principalTable: "RulesetInfo", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "BeatmapDifficulty", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + ApproachRate = table.Column(type: "REAL", nullable: false), + BeatmapInfoID = table.Column(type: "INTEGER", nullable: false), + CircleSize = table.Column(type: "REAL", nullable: false), + DrainRate = table.Column(type: "REAL", nullable: false), + OverallDifficulty = table.Column(type: "REAL", nullable: false), + SliderMultiplier = table.Column(type: "REAL", nullable: false), + SliderTickRate = table.Column(type: "REAL", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_BeatmapDifficulty", x => x.ID); + table.ForeignKey( + name: "FK_BeatmapDifficulty_BeatmapInfo_BeatmapInfoID", + column: x => x.BeatmapInfoID, + principalTable: "BeatmapInfo", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "BeatmapMetadata", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Artist = table.Column(type: "TEXT", nullable: true), + ArtistUnicode = table.Column(type: "TEXT", nullable: true), + AudioFile = table.Column(type: "TEXT", nullable: true), + Author = table.Column(type: "TEXT", nullable: true), + BackgroundFile = table.Column(type: "TEXT", nullable: true), + BeatmapInfoID = table.Column(type: "INTEGER", nullable: true), + BeatmapSetInfoID = table.Column(type: "INTEGER", nullable: true), + PreviewTime = table.Column(type: "INTEGER", nullable: false), + Source = table.Column(type: "TEXT", nullable: true), + Tags = table.Column(type: "TEXT", nullable: true), + Title = table.Column(type: "TEXT", nullable: true), + TitleUnicode = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_BeatmapMetadata", x => x.ID); + table.ForeignKey( + name: "FK_BeatmapMetadata_BeatmapInfo_BeatmapInfoID", + column: x => x.BeatmapInfoID, + principalTable: "BeatmapInfo", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_BeatmapMetadata_BeatmapSetInfo_BeatmapSetInfoID", + column: x => x.BeatmapSetInfoID, + principalTable: "BeatmapSetInfo", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_BeatmapDifficulty_BeatmapInfoID", + table: "BeatmapDifficulty", + column: "BeatmapInfoID", + unique: true); + migrationBuilder.CreateIndex( name: "IX_BeatmapInfo_BeatmapSetInfoID", table: "BeatmapInfo", column: "BeatmapSetInfoID"); - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_DifficultyID", - table: "BeatmapInfo", - column: "DifficultyID"); - migrationBuilder.CreateIndex( name: "IX_BeatmapInfo_MD5Hash", table: "BeatmapInfo", column: "MD5Hash"); - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_MetadataID", - table: "BeatmapInfo", - column: "MetadataID"); - migrationBuilder.CreateIndex( name: "IX_BeatmapInfo_RulesetID", table: "BeatmapInfo", column: "RulesetID"); + migrationBuilder.CreateIndex( + name: "IX_BeatmapMetadata_BeatmapInfoID", + table: "BeatmapMetadata", + column: "BeatmapInfoID", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_BeatmapMetadata_BeatmapSetInfoID", + table: "BeatmapMetadata", + column: "BeatmapSetInfoID", + unique: true); + migrationBuilder.CreateIndex( name: "IX_BeatmapSetFileInfo_BeatmapSetInfoID", table: "BeatmapSetFileInfo", @@ -241,11 +248,6 @@ namespace osu.Game.Migrations table: "BeatmapSetInfo", column: "DeletePending"); - migrationBuilder.CreateIndex( - name: "IX_BeatmapSetInfo_MetadataID", - table: "BeatmapSetInfo", - column: "MetadataID"); - migrationBuilder.CreateIndex( name: "IX_FileInfo_Hash", table: "FileInfo", @@ -288,7 +290,10 @@ namespace osu.Game.Migrations protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( - name: "BeatmapInfo"); + name: "BeatmapDifficulty"); + + migrationBuilder.DropTable( + name: "BeatmapMetadata"); migrationBuilder.DropTable( name: "BeatmapSetFileInfo"); @@ -297,19 +302,16 @@ namespace osu.Game.Migrations name: "KeyBinding"); migrationBuilder.DropTable( - name: "BeatmapDifficulty"); - - migrationBuilder.DropTable( - name: "RulesetInfo"); - - migrationBuilder.DropTable( - name: "BeatmapSetInfo"); + name: "BeatmapInfo"); migrationBuilder.DropTable( name: "FileInfo"); migrationBuilder.DropTable( - name: "BeatmapMetadata"); + name: "BeatmapSetInfo"); + + migrationBuilder.DropTable( + name: "RulesetInfo"); } } } diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index f3a6c5a520..69cc206b7e 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -1,10 +1,11 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -// +// using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; using osu.Game.Database; +using System; namespace osu.Game.Migrations { @@ -24,6 +25,8 @@ namespace osu.Game.Migrations b.Property("ApproachRate"); + b.Property("BeatmapInfoID"); + b.Property("CircleSize"); b.Property("DrainRate"); @@ -36,6 +39,9 @@ namespace osu.Game.Migrations b.HasKey("ID"); + b.HasIndex("BeatmapInfoID") + .IsUnique(); + b.ToTable("BeatmapDifficulty"); }); @@ -54,8 +60,6 @@ namespace osu.Game.Migrations b.Property("Countdown"); - b.Property("DifficultyID"); - b.Property("DistanceSpacing"); b.Property("GridSize"); @@ -68,8 +72,6 @@ namespace osu.Game.Migrations b.Property("MD5Hash"); - b.Property("MetadataID"); - b.Property("Path"); b.Property("RulesetID"); @@ -92,12 +94,8 @@ namespace osu.Game.Migrations b.HasIndex("BeatmapSetInfoID"); - b.HasIndex("DifficultyID"); - b.HasIndex("MD5Hash"); - b.HasIndex("MetadataID"); - b.HasIndex("RulesetID"); b.ToTable("BeatmapInfo"); @@ -114,10 +112,15 @@ namespace osu.Game.Migrations b.Property("AudioFile"); - b.Property("Author"); + b.Property("AuthorString") + .HasColumnName("Author"); b.Property("BackgroundFile"); + b.Property("BeatmapInfoID"); + + b.Property("BeatmapSetInfoID"); + b.Property("PreviewTime"); b.Property("Source"); @@ -130,6 +133,12 @@ namespace osu.Game.Migrations b.HasKey("ID"); + b.HasIndex("BeatmapInfoID") + .IsUnique(); + + b.HasIndex("BeatmapSetInfoID") + .IsUnique(); + b.ToTable("BeatmapMetadata"); }); @@ -163,16 +172,12 @@ namespace osu.Game.Migrations b.Property("Hash"); - b.Property("MetadataID"); - b.Property("Protected"); b.HasKey("ID"); b.HasIndex("DeletePending"); - b.HasIndex("MetadataID"); - b.ToTable("BeatmapSetInfo"); }); @@ -243,6 +248,14 @@ namespace osu.Game.Migrations b.ToTable("RulesetInfo"); }); + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapInfo") + .WithOne("Difficulty") + .HasForeignKey("osu.Game.Beatmaps.BeatmapDifficulty", "BeatmapInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => { b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") @@ -250,21 +263,25 @@ namespace osu.Game.Migrations .HasForeignKey("BeatmapSetInfoID") .OnDelete(DeleteBehavior.Cascade); - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "Difficulty") - .WithMany() - .HasForeignKey("DifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany() - .HasForeignKey("MetadataID"); - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") .WithMany() .HasForeignKey("RulesetID") .OnDelete(DeleteBehavior.Cascade); }); + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "BeatmapInfo") + .WithOne("Metadata") + .HasForeignKey("osu.Game.Beatmaps.BeatmapMetadata", "BeatmapInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSetInfo") + .WithOne("Metadata") + .HasForeignKey("osu.Game.Beatmaps.BeatmapMetadata", "BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => { b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") @@ -277,13 +294,6 @@ namespace osu.Game.Migrations .HasForeignKey("FileInfoID") .OnDelete(DeleteBehavior.Cascade); }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany() - .HasForeignKey("MetadataID"); - }); #pragma warning restore 612, 618 } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index d3ac2d6189..9c6e03249e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -286,9 +286,9 @@ - - - 20171014052545_Init.cs + + + 20171017092037_InitialCreate.cs From e4a066dc5fcb76926a34e75c2c333961d187becc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Oct 2017 19:58:33 +0900 Subject: [PATCH 67/90] Run cleanup tasks only on startup via manual calls --- osu.Game/Beatmaps/BeatmapManager.cs | 2 ++ osu.Game/Beatmaps/BeatmapStore.cs | 8 +------- osu.Game/Database/DatabaseBackedStore.cs | 6 ++---- osu.Game/IO/FileStore.cs | 8 +------- osu.Game/OsuGameBase.cs | 2 ++ 5 files changed, 8 insertions(+), 18 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index bf71033f36..a25f454218 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -104,6 +104,8 @@ namespace osu.Game.Beatmaps if (importHost != null) ipc = new BeatmapIPCChannel(importHost, this); + + beatmaps.Cleanup(); } /// diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 69aadb470e..4892d4f3db 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -40,12 +40,6 @@ namespace osu.Game.Beatmaps } } - protected override void StartupTasks() - { - base.StartupTasks(); - cleanupPendingDeletions(); - } - /// /// Add a to the database. /// @@ -136,7 +130,7 @@ namespace osu.Game.Beatmaps return true; } - private void cleanupPendingDeletions() + public override void Cleanup() { var context = GetContext(); diff --git a/osu.Game/Database/DatabaseBackedStore.cs b/osu.Game/Database/DatabaseBackedStore.cs index 90a43a47c5..be86d35335 100644 --- a/osu.Game/Database/DatabaseBackedStore.cs +++ b/osu.Game/Database/DatabaseBackedStore.cs @@ -27,14 +27,12 @@ namespace osu.Game.Database Logger.Error(e, $@"Failed to initialise the {GetType()}! Trying again with a clean database..."); Prepare(true); } - - StartupTasks(); } /// - /// Perform any common startup tasks. Runs after . + /// Perform any common clean-up tasks. Should be run when idle, or whenever necessary. /// - protected virtual void StartupTasks() + public virtual void Cleanup() { } diff --git a/osu.Game/IO/FileStore.cs b/osu.Game/IO/FileStore.cs index db4f5fd2a2..5f1b21ddb6 100644 --- a/osu.Game/IO/FileStore.cs +++ b/osu.Game/IO/FileStore.cs @@ -38,12 +38,6 @@ namespace osu.Game.IO } } - protected override void StartupTasks() - { - base.StartupTasks(); - deletePending(); - } - public FileInfo Add(Stream data, bool reference = true) { var context = GetContext(); @@ -101,7 +95,7 @@ namespace osu.Game.IO context.SaveChanges(); } - private void deletePending() + public override void Cleanup() { var context = GetContext(); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 5ecc7279da..22eb75fcea 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -160,6 +160,8 @@ namespace osu.Game }; API.Register(this); + + FileStore.Cleanup(); } private WorkingBeatmap lastBeatmap; From 4e8019b31373860c4dab4921f75b19d986f60a5d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Oct 2017 19:59:22 +0900 Subject: [PATCH 68/90] Add some more missing indices --- osu.Game/Database/OsuDbContext.cs | 2 ++ ...ner.cs => 20171017103309_InitialCreate.Designer.cs} | 6 +++++- ...nitialCreate.cs => 20171017103309_InitialCreate.cs} | 10 ++++++++++ osu.Game/Migrations/OsuDbContextModelSnapshot.cs | 4 ++++ osu.Game/osu.Game.csproj | 6 +++--- 5 files changed, 24 insertions(+), 4 deletions(-) rename osu.Game/Migrations/{20171017092037_InitialCreate.Designer.cs => 20171017103309_InitialCreate.Designer.cs} (95%) rename osu.Game/Migrations/{20171017092037_InitialCreate.cs => 20171017103309_InitialCreate.cs} (95%) diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index e360f11ba3..cfc5f056ad 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -66,7 +66,9 @@ namespace osu.Game.Database { base.OnModelCreating(modelBuilder); modelBuilder.Entity().HasIndex(b => b.MD5Hash); + modelBuilder.Entity().HasIndex(b => b.Hash); modelBuilder.Entity().HasIndex(b => b.DeletePending); + modelBuilder.Entity().HasIndex(b => b.Hash); modelBuilder.Entity().HasIndex(b => b.Variant); modelBuilder.Entity().HasIndex(b => b.IntAction); modelBuilder.Entity().HasIndex(b => b.Hash).IsUnique(); diff --git a/osu.Game/Migrations/20171017092037_InitialCreate.Designer.cs b/osu.Game/Migrations/20171017103309_InitialCreate.Designer.cs similarity index 95% rename from osu.Game/Migrations/20171017092037_InitialCreate.Designer.cs rename to osu.Game/Migrations/20171017103309_InitialCreate.Designer.cs index 422c1612d2..4d394fcd90 100644 --- a/osu.Game/Migrations/20171017092037_InitialCreate.Designer.cs +++ b/osu.Game/Migrations/20171017103309_InitialCreate.Designer.cs @@ -10,7 +10,7 @@ using System; namespace osu.Game.Migrations { [DbContext(typeof(OsuDbContext))] - [Migration("20171017092037_InitialCreate")] + [Migration("20171017103309_InitialCreate")] partial class InitialCreate { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -95,6 +95,8 @@ namespace osu.Game.Migrations b.HasIndex("BeatmapSetInfoID"); + b.HasIndex("Hash"); + b.HasIndex("MD5Hash"); b.HasIndex("RulesetID"); @@ -179,6 +181,8 @@ namespace osu.Game.Migrations b.HasIndex("DeletePending"); + b.HasIndex("Hash"); + b.ToTable("BeatmapSetInfo"); }); diff --git a/osu.Game/Migrations/20171017092037_InitialCreate.cs b/osu.Game/Migrations/20171017103309_InitialCreate.cs similarity index 95% rename from osu.Game/Migrations/20171017092037_InitialCreate.cs rename to osu.Game/Migrations/20171017103309_InitialCreate.cs index 2626e2ea74..759e8bb664 100644 --- a/osu.Game/Migrations/20171017092037_InitialCreate.cs +++ b/osu.Game/Migrations/20171017103309_InitialCreate.cs @@ -211,6 +211,11 @@ namespace osu.Game.Migrations table: "BeatmapInfo", column: "BeatmapSetInfoID"); + migrationBuilder.CreateIndex( + name: "IX_BeatmapInfo_Hash", + table: "BeatmapInfo", + column: "Hash"); + migrationBuilder.CreateIndex( name: "IX_BeatmapInfo_MD5Hash", table: "BeatmapInfo", @@ -248,6 +253,11 @@ namespace osu.Game.Migrations table: "BeatmapSetInfo", column: "DeletePending"); + migrationBuilder.CreateIndex( + name: "IX_BeatmapSetInfo_Hash", + table: "BeatmapSetInfo", + column: "Hash"); + migrationBuilder.CreateIndex( name: "IX_FileInfo_Hash", table: "FileInfo", diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index 69cc206b7e..ef2c801a6c 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -94,6 +94,8 @@ namespace osu.Game.Migrations b.HasIndex("BeatmapSetInfoID"); + b.HasIndex("Hash"); + b.HasIndex("MD5Hash"); b.HasIndex("RulesetID"); @@ -178,6 +180,8 @@ namespace osu.Game.Migrations b.HasIndex("DeletePending"); + b.HasIndex("Hash"); + b.ToTable("BeatmapSetInfo"); }); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9c6e03249e..9941792ec5 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -286,9 +286,9 @@ - - - 20171017092037_InitialCreate.cs + + + 20171017103309_InitialCreate.cs From 4193004fbfe35805b0306b168055d539d05c86cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Oct 2017 20:29:47 +0900 Subject: [PATCH 69/90] Improve performance of imports by keeping a context hot --- osu.Game/Beatmaps/BeatmapManager.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index a25f454218..e7929ff882 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -165,6 +165,8 @@ namespace osu.Game.Beatmaps private readonly object importLock = new object(); + private OsuDbContext importContext; + /// /// Import a beatmap from an . /// @@ -174,7 +176,7 @@ namespace osu.Game.Beatmaps // let's only allow one concurrent import at a time for now. lock (importLock) { - var context = createContext(); + var context = importContext ?? (importContext = createContext()); context.Database.AutoTransactionsEnabled = false; @@ -190,9 +192,9 @@ namespace osu.Game.Beatmaps { iBeatmaps.Add(set); context.SaveChanges(); - transaction.Commit(); } + transaction.Commit(); return set; } } From 9e3d54e80bd21b69225ed9ea7f62eef72961a6f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Oct 2017 11:42:55 +0900 Subject: [PATCH 70/90] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 2a53abda4b..dbcfa5c244 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 2a53abda4b2ac5b559f95d934572fb99eba42a10 +Subproject commit dbcfa5c244555e7901dac7d94eab53b3b04d17e6 From cf5290fead50c908485f3572b69fd0cdda40aaa5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Oct 2017 13:48:15 +0900 Subject: [PATCH 71/90] Remove unnecessary locking; operations are now thread-safe --- osu.Game/Beatmaps/BeatmapManager.cs | 49 ++++++----------------------- 1 file changed, 10 insertions(+), 39 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index e7929ff882..5f182c1dca 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -287,8 +287,7 @@ namespace osu.Game.Beatmaps /// The beatmap set to delete. public void Delete(BeatmapSetInfo beatmapSet) { - lock (beatmaps) - if (!beatmaps.Delete(beatmapSet)) return; + if (!beatmaps.Delete(beatmapSet)) return; if (!beatmapSet.Protected) files.Dereference(beatmapSet.Files.Select(f => f.FileInfo).ToArray()); @@ -298,21 +297,13 @@ namespace osu.Game.Beatmaps /// Delete a beatmap difficulty. /// /// The beatmap difficulty to hide. - public void Hide(BeatmapInfo beatmap) - { - lock (beatmaps) - beatmaps.Hide(beatmap); - } + public void Hide(BeatmapInfo beatmap) => beatmaps.Hide(beatmap); /// /// Restore a beatmap difficulty. /// /// The beatmap difficulty to restore. - public void Restore(BeatmapInfo beatmap) - { - lock (beatmaps) - beatmaps.Restore(beatmap); - } + public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap); /// /// Returns a to a usable state if it has previously been deleted but not yet purged. @@ -321,8 +312,7 @@ namespace osu.Game.Beatmaps /// The beatmap to restore. private void undelete(BeatmapStore beatmaps, FileStore files, BeatmapSetInfo beatmapSet) { - lock (beatmaps) - if (!beatmaps.Undelete(beatmapSet)) return; + if (!beatmaps.Undelete(beatmapSet)) return; if (!beatmapSet.Protected) files.Reference(beatmapSet.Files.Select(f => f.FileInfo).ToArray()); @@ -357,11 +347,7 @@ namespace osu.Game.Beatmaps /// /// The query. /// The first result for the provided query, or null if no results were found. - public BeatmapSetInfo QueryBeatmapSet(Func query) - { - lock (beatmaps) - return beatmaps.BeatmapSets.FirstOrDefault(query); - } + public BeatmapSetInfo QueryBeatmapSet(Func query) => beatmaps.BeatmapSets.FirstOrDefault(query); /// /// Refresh an existing instance of a from the store. @@ -375,33 +361,21 @@ namespace osu.Game.Beatmaps /// /// The query. /// Results from the provided query. - public List QueryBeatmapSets(Func query) - { - lock (beatmaps) - return beatmaps.BeatmapSets.Where(query).ToList(); - } + public List QueryBeatmapSets(Func query) => beatmaps.BeatmapSets.Where(query).ToList(); /// /// 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) - { - lock (beatmaps) - return beatmaps.Beatmaps.FirstOrDefault(query); - } + public BeatmapInfo QueryBeatmap(Func query) => beatmaps.Beatmaps.FirstOrDefault(query); /// /// Perform a lookup query on available s. /// /// The query. /// Results from the provided query. - public List QueryBeatmaps(Func query) - { - lock (beatmaps) - return beatmaps.Beatmaps.Where(query).ToList(); - } + public List QueryBeatmaps(Func query) => beatmaps.Beatmaps.Where(query).ToList(); /// /// Creates an from a valid storage path. @@ -437,9 +411,7 @@ namespace osu.Game.Beatmaps var hash = hashable.ComputeSHA2Hash(); // check if this beatmap has already been imported and exit early if so. - BeatmapSetInfo beatmapSet; - lock (beatmaps) - beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.Hash == hash); + var beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.Hash == hash); if (beatmapSet != null) { @@ -523,8 +495,7 @@ namespace osu.Game.Beatmaps /// A list of available . public List GetAllUsableBeatmapSets() { - lock (beatmaps) - return beatmaps.BeatmapSets.Where(s => !s.DeletePending).ToList(); + return beatmaps.BeatmapSets.Where(s => !s.DeletePending).ToList(); } protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap From 4841d4a937bb42a212fdced63568220750d42b9a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Oct 2017 18:27:17 +0900 Subject: [PATCH 72/90] Fix deletion and use single context for imports and deletions for now --- osu.Game/Beatmaps/BeatmapManager.cs | 53 ++++++++++++++++++++++------- osu.Game/IO/FileStore.cs | 4 +-- 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 5f182c1dca..6b6b5cf0bc 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Threading.Tasks; using Ionic.Zip; +using Microsoft.EntityFrameworkCore; using osu.Framework.Audio.Track; using osu.Framework.Extensions; using osu.Framework.Graphics.Textures; @@ -95,6 +96,13 @@ namespace osu.Game.Beatmaps public BeatmapManager(Storage storage, Func context, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null) { createContext = context; + importContext = new Lazy(() => + { + var c = createContext(); + c.Database.AutoTransactionsEnabled = false; + return c; + }); + beatmaps = createBeatmapStore(context); files = new FileStore(context, storage); @@ -163,9 +171,7 @@ namespace osu.Game.Beatmaps notification.State = ProgressNotificationState.Completed; } - private readonly object importLock = new object(); - - private OsuDbContext importContext; + private readonly Lazy importContext; /// /// Import a beatmap from an . @@ -174,9 +180,9 @@ namespace osu.Game.Beatmaps public BeatmapSetInfo Import(ArchiveReader archiveReader) { // let's only allow one concurrent import at a time for now. - lock (importLock) + lock (importContext) { - var context = importContext ?? (importContext = createContext()); + var context = importContext.Value; context.Database.AutoTransactionsEnabled = false; @@ -287,10 +293,33 @@ namespace osu.Game.Beatmaps /// The beatmap set to delete. public void Delete(BeatmapSetInfo beatmapSet) { - if (!beatmaps.Delete(beatmapSet)) return; + lock (importContext) + { + var context = importContext.Value; - if (!beatmapSet.Protected) - files.Dereference(beatmapSet.Files.Select(f => f.FileInfo).ToArray()); + using (var transaction = context.Database.BeginTransaction()) + { + context.ChangeTracker.AutoDetectChangesEnabled = false; + + // re-fetch the beatmap set on the import context. + beatmapSet = context.BeatmapSetInfo.Include(s => s.Files).ThenInclude(f => f.FileInfo).First(s => s.ID == beatmapSet.ID); + + // create local stores so we can isolate and thread safely, and share a context/transaction. + var iFiles = new FileStore(() => context, storage); + var iBeatmaps = createBeatmapStore(() => context); + + if (iBeatmaps.Delete(beatmapSet)) + { + if (!beatmapSet.Protected) + iFiles.Dereference(beatmapSet.Files.Select(f => f.FileInfo).ToArray()); + } + + context.ChangeTracker.AutoDetectChangesEnabled = true; + context.SaveChanges(); + + transaction.Commit(); + } + } } /// @@ -587,9 +616,9 @@ namespace osu.Game.Beatmaps public void DeleteAll() { - var maps = GetAllUsableBeatmapSets().ToArray(); + var maps = GetAllUsableBeatmapSets(); - if (maps.Length == 0) return; + if (maps.Count == 0) return; var notification = new ProgressNotification { @@ -607,8 +636,8 @@ namespace osu.Game.Beatmaps // user requested abort return; - notification.Text = $"Deleting ({i} of {maps.Length})"; - notification.Progress = (float)++i / maps.Length; + notification.Text = $"Deleting ({i} of {maps.Count})"; + notification.Progress = (float)++i / maps.Count; Delete(b); } diff --git a/osu.Game/IO/FileStore.cs b/osu.Game/IO/FileStore.cs index 5f1b21ddb6..6654fa7cb1 100644 --- a/osu.Game/IO/FileStore.cs +++ b/osu.Game/IO/FileStore.cs @@ -87,9 +87,9 @@ namespace osu.Game.IO { foreach (var f in files.GroupBy(f => f.ID)) { - var refetch = context.Find(f.First().ID); + var refetch = context.FileInfo.Find(f.Key); refetch.ReferenceCount -= f.Count(); - context.Update(refetch); + context.FileInfo.Update(refetch); } context.SaveChanges(); From 668f68dd63ddd270c8ab8e6acfe4b19dc70c4e22 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Oct 2017 18:27:57 +0900 Subject: [PATCH 73/90] Remove some unnecessary update calls --- osu.Game/Beatmaps/BeatmapStore.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 4892d4f3db..21a0b3ef6c 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -66,7 +66,6 @@ namespace osu.Game.Beatmaps if (beatmapSet.DeletePending) return false; beatmapSet.DeletePending = true; - context.BeatmapSetInfo.Update(beatmapSet); context.SaveChanges(); BeatmapSetRemoved?.Invoke(beatmapSet); @@ -85,7 +84,6 @@ namespace osu.Game.Beatmaps if (!beatmapSet.DeletePending) return false; beatmapSet.DeletePending = false; - context.BeatmapSetInfo.Update(beatmapSet); context.SaveChanges(); BeatmapSetAdded?.Invoke(beatmapSet); @@ -104,7 +102,6 @@ namespace osu.Game.Beatmaps if (beatmap.Hidden) return false; beatmap.Hidden = true; - context.BeatmapInfo.Update(beatmap); context.SaveChanges(); BeatmapHidden?.Invoke(beatmap); @@ -123,7 +120,6 @@ namespace osu.Game.Beatmaps if (!beatmap.Hidden) return false; beatmap.Hidden = false; - context.BeatmapInfo.Update(beatmap); context.SaveChanges(); BeatmapRestored?.Invoke(beatmap); From a85de09c0f8b76ff5a715e36c47051ab684b2e8c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Oct 2017 18:36:35 +0900 Subject: [PATCH 74/90] Fix beatmap carousel interactions with deletion when not yet displayed --- osu.Game/Screens/Select/BeatmapCarousel.cs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index c72f599955..b8cc9782ca 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -33,10 +33,7 @@ namespace osu.Game.Screens.Select public IEnumerable Beatmaps { - get - { - return groups.Select(g => g.BeatmapSet); - } + get { return groups.Select(g => g.BeatmapSet); } set { @@ -100,12 +97,10 @@ namespace osu.Game.Screens.Select public void AddBeatmap(BeatmapSetInfo beatmapSet) { - var group = createGroup(beatmapSet); - - //for the time being, let's completely load the difficulty panels in the background. - //this likely won't scale so well, but allows us to completely async the loading flow. - Schedule(delegate + Schedule(() => { + var group = createGroup(beatmapSet); + addGroup(group); computeYPositions(); if (selectedGroup == null) @@ -113,7 +108,10 @@ namespace osu.Game.Screens.Select }); } - public void RemoveBeatmap(BeatmapSetInfo beatmapSet) => removeGroup(groups.Find(b => b.BeatmapSet.ID == beatmapSet.ID)); + public void RemoveBeatmap(BeatmapSetInfo beatmapSet) + { + Schedule(() => removeGroup(groups.Find(b => b.BeatmapSet.ID == beatmapSet.ID))); + } internal void UpdateBeatmap(BeatmapInfo beatmap) { @@ -519,7 +517,7 @@ namespace osu.Game.Screens.Select float drawHeight = DrawHeight; // Remove all panels that should no longer be on-screen - scrollableContent.RemoveAll(delegate (Panel p) + scrollableContent.RemoveAll(delegate(Panel p) { float panelPosY = p.Position.Y; bool remove = panelPosY < Current - p.DrawHeight || panelPosY > Current + drawHeight || !p.IsPresent; From b73b4755ebdbe53100086dab741c2c8bcc6cd0d3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Oct 2017 18:41:04 +0900 Subject: [PATCH 75/90] Ignore r# locked context inspection here --- osu.Game/Beatmaps/BeatmapManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 6b6b5cf0bc..8a2997987b 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -414,6 +414,7 @@ namespace osu.Game.Beatmaps private ArchiveReader getReaderFrom(string path) { if (ZipFile.IsZipFile(path)) + // ReSharper disable once InconsistentlySynchronizedField return new OszArchiveReader(storage.GetStream(path)); return new LegacyFilesystemReader(path); } From 0dac770e38d899303147e6195a2fd9030a17c782 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Oct 2017 19:39:48 +0900 Subject: [PATCH 76/90] Remove TestCase cleanup temporarily until context disposal is sorted --- osu.Game/Tests/Visual/OsuTestCase.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs index 90c6e427c4..ca0aaebb5e 100644 --- a/osu.Game/Tests/Visual/OsuTestCase.cs +++ b/osu.Game/Tests/Visual/OsuTestCase.cs @@ -11,15 +11,13 @@ namespace osu.Game.Tests.Visual { public override void RunTest() { - Storage storage; using (var host = new HeadlessGameHost($"test-{Guid.NewGuid()}", realtime: false)) { - storage = host.Storage; host.Run(new OsuTestCaseTestRunner(this)); } // clean up after each run - storage.DeleteDirectory(string.Empty); + //storage.DeleteDirectory(string.Empty); } public class OsuTestCaseTestRunner : OsuGameBase From ac4b2797dc245ac34434f8c6e6b7ad0a0faaa618 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Oct 2017 20:07:25 +0900 Subject: [PATCH 77/90] valuetuple nightmare --- osu.Game.Tests/app.config | 4 ++++ osu.Game/osu.Game.csproj | 5 ----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/app.config b/osu.Game.Tests/app.config index faeaf001de..2f5b13a89d 100644 --- a/osu.Game.Tests/app.config +++ b/osu.Game.Tests/app.config @@ -6,6 +6,10 @@ + + + + \ No newline at end of file diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9941792ec5..4488ea8333 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -103,7 +103,6 @@ $(SolutionDir)\packages\DotNetZip.1.10.1\lib\net20\DotNetZip.dll True - $(SolutionDir)\packages\Microsoft.Data.Sqlite.Core.2.0.0\lib\netstandard2.0\Microsoft.Data.Sqlite.dll @@ -201,10 +200,6 @@ $(SolutionDir)\packages\System.Runtime.CompilerServices.Unsafe.4.4.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll - - $(SolutionDir)\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll - True - $(SolutionDir)\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll True From 00be98dba732a642665042a909d38b0904d7352e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Oct 2017 20:37:53 +0900 Subject: [PATCH 78/90] Query test assert conditions less often --- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 087fb54b5f..337800cd30 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -155,7 +155,7 @@ namespace osu.Game.Tests.Beatmaps.IO private void waitForOrAssert(Func result, string failureMessage, int timeout = 60000) { - Action waitAction = () => { while (!result()) Thread.Sleep(20); }; + Action waitAction = () => { while (!result()) Thread.Sleep(200); }; Assert.IsTrue(waitAction.BeginInvoke(null, null).AsyncWaitHandle.WaitOne(timeout), failureMessage); } } From 71d614b813abee9720a6f5c2276e3684ad463abb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Oct 2017 21:57:54 +0900 Subject: [PATCH 79/90] FIx missing columns yet again --- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 2 +- osu.Game/Beatmaps/BeatmapSetInfo.cs | 1 - ...Designer.cs => 20171018125509_InitialCreate.Designer.cs} | 4 +++- ...309_InitialCreate.cs => 20171018125509_InitialCreate.cs} | 1 + osu.Game/Migrations/OsuDbContextModelSnapshot.cs | 2 ++ osu.Game/osu.Game.csproj | 6 +++--- 6 files changed, 10 insertions(+), 6 deletions(-) rename osu.Game/Migrations/{20171017103309_InitialCreate.Designer.cs => 20171018125509_InitialCreate.Designer.cs} (95%) rename osu.Game/Migrations/{20171017103309_InitialCreate.cs => 20171018125509_InitialCreate.cs} (97%) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 337800cd30..e5b8c7fe57 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -115,7 +115,7 @@ namespace osu.Game.Tests.Beatmaps.IO //ensure we were stored to beatmap database backing... Assert.IsTrue(resultSets.Count() == 1, $@"Incorrect result count found ({resultSets.Count()} but should be 1)."); - Func> queryBeatmaps = () => store.QueryBeatmaps(s => s.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0); + Func> queryBeatmaps = () => store.QueryBeatmaps(s => s.BeatmapSet.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0); Func> queryBeatmapSets = () => store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526); //if we don't re-check here, the set will be inserted but the beatmaps won't be present yet. diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 404add2fa5..2dfc4d0fe0 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -12,7 +12,6 @@ namespace osu.Game.Beatmaps [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { get; set; } - [NotMapped] public int? OnlineBeatmapSetID { get; set; } public BeatmapMetadata Metadata { get; set; } diff --git a/osu.Game/Migrations/20171017103309_InitialCreate.Designer.cs b/osu.Game/Migrations/20171018125509_InitialCreate.Designer.cs similarity index 95% rename from osu.Game/Migrations/20171017103309_InitialCreate.Designer.cs rename to osu.Game/Migrations/20171018125509_InitialCreate.Designer.cs index 4d394fcd90..28e7e20743 100644 --- a/osu.Game/Migrations/20171017103309_InitialCreate.Designer.cs +++ b/osu.Game/Migrations/20171018125509_InitialCreate.Designer.cs @@ -10,7 +10,7 @@ using System; namespace osu.Game.Migrations { [DbContext(typeof(OsuDbContext))] - [Migration("20171017103309_InitialCreate")] + [Migration("20171018125509_InitialCreate")] partial class InitialCreate { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -175,6 +175,8 @@ namespace osu.Game.Migrations b.Property("Hash"); + b.Property("OnlineBeatmapSetID"); + b.Property("Protected"); b.HasKey("ID"); diff --git a/osu.Game/Migrations/20171017103309_InitialCreate.cs b/osu.Game/Migrations/20171018125509_InitialCreate.cs similarity index 97% rename from osu.Game/Migrations/20171017103309_InitialCreate.cs rename to osu.Game/Migrations/20171018125509_InitialCreate.cs index 759e8bb664..512ffa2790 100644 --- a/osu.Game/Migrations/20171017103309_InitialCreate.cs +++ b/osu.Game/Migrations/20171018125509_InitialCreate.cs @@ -16,6 +16,7 @@ namespace osu.Game.Migrations .Annotation("Sqlite:Autoincrement", true), DeletePending = table.Column(type: "INTEGER", nullable: false), Hash = table.Column(type: "TEXT", nullable: true), + OnlineBeatmapSetID = table.Column(type: "INTEGER", nullable: true), Protected = table.Column(type: "INTEGER", nullable: false) }, constraints: table => diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index ef2c801a6c..8d0d8f20fe 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -174,6 +174,8 @@ namespace osu.Game.Migrations b.Property("Hash"); + b.Property("OnlineBeatmapSetID"); + b.Property("Protected"); b.HasKey("ID"); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 4488ea8333..0e16c70277 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -281,9 +281,9 @@ - - - 20171017103309_InitialCreate.cs + + + 20171018125509_InitialCreate.cs From 5d5ea5fb2ef803260b1cf05bf426384c49c88471 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Oct 2017 14:05:11 +0900 Subject: [PATCH 80/90] Fix all remaining db structure issues --- .../Beatmaps/ManiaBeatmapConverter.cs | 2 +- .../Legacy/DistanceObjectPatternGenerator.cs | 2 +- .../Patterns/Legacy/PatternGenerator.cs | 2 +- .../ManiaDifficultyCalculator.cs | 2 +- .../Scoring/ManiaScoreProcessor.cs | 2 +- .../UI/ManiaRulesetContainer.cs | 10 +- .../Scoring/OsuScoreProcessor.cs | 2 +- .../UI/Cursor/GameplayCursor.cs | 2 +- .../Beatmaps/TaikoBeatmapConverter.cs | 12 +- .../Scoring/TaikoScoreProcessor.cs | 4 +- .../Tests/TestCaseTaikoPlayfield.cs | 2 +- .../UI/TaikoRulesetContainer.cs | 2 +- .../Beatmaps/Formats/OsuLegacyDecoderTest.cs | 2 +- osu.Game/Beatmaps/Beatmap.cs | 2 +- osu.Game/Beatmaps/BeatmapDifficulty.cs | 2 - osu.Game/Beatmaps/BeatmapInfo.cs | 5 +- osu.Game/Beatmaps/BeatmapMetadata.cs | 12 +- osu.Game/Beatmaps/BeatmapStore.cs | 4 +- osu.Game/Beatmaps/DifficultyCalculator.cs | 2 +- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 2 +- osu.Game/Beatmaps/Formats/BeatmapDecoder.cs | 2 +- osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs | 4 +- osu.Game/Database/OsuDbContext.cs | 10 +- ... 20171019041408_InitialCreate.Designer.cs} | 72 ++--- ...ate.cs => 20171019041408_InitialCreate.cs} | 277 +++++++++--------- .../Migrations/OsuDbContextModelSnapshot.cs | 70 ++--- osu.Game/Rulesets/UI/RulesetContainer.cs | 4 +- osu.Game/Screens/Select/BeatmapDetails.cs | 2 +- .../Screens/Select/Details/AdvancedStats.cs | 10 +- .../Tests/Visual/TestCaseBeatmapDetails.cs | 8 +- .../Tests/Visual/TestCaseBeatmapSetOverlay.cs | 20 +- .../Tests/Visual/TestCasePlaySongSelect.cs | 6 +- .../Visual/TestCaseScrollingPlayfield.cs | 2 +- osu.Game/osu.Game.csproj | 6 +- 34 files changed, 259 insertions(+), 309 deletions(-) rename osu.Game/Migrations/{20171018125509_InitialCreate.Designer.cs => 20171019041408_InitialCreate.Designer.cs} (80%) rename osu.Game/Migrations/{20171018125509_InitialCreate.cs => 20171019041408_InitialCreate.cs} (85%) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index d7c86e1f89..f6d30ad3fa 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps { beatmap = original; - BeatmapDifficulty difficulty = original.BeatmapInfo.Difficulty; + BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty; int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate); random = new FastRandom(seed); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 20966a75f7..270c264e0c 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy // The true distance, accounting for any repeats double distance = (distanceData?.Distance ?? 0) * repeatCount; // The velocity of the osu! hit object - calculated as the velocity of a slider - double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / timingPoint.BeatLength; + double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / timingPoint.BeatLength; // The duration of the osu! hit object double osuDuration = distance / osuVelocity; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs index a3173f9784..c38680c3a5 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy if (drainTime == 0) drainTime = 10000; - BeatmapDifficulty difficulty = Beatmap.BeatmapInfo.Difficulty; + BeatmapDifficulty difficulty = Beatmap.BeatmapInfo.BaseDifficulty; conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + Beatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15; conversionDifficulty = Math.Min(conversionDifficulty.Value, 12); diff --git a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs index b98802db69..784df1f293 100644 --- a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs @@ -21,6 +21,6 @@ namespace osu.Game.Rulesets.Mania return 0; } - protected override BeatmapConverter CreateBeatmapConverter() => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize))); + protected override BeatmapConverter CreateBeatmapConverter() => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(Beatmap.BeatmapInfo.BaseDifficulty.CircleSize))); } } diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index a200ba31e2..9b8ebe0070 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Mania.Scoring protected override void SimulateAutoplay(Beatmap beatmap) { - BeatmapDifficulty difficulty = beatmap.BeatmapInfo.Difficulty; + BeatmapDifficulty difficulty = beatmap.BeatmapInfo.BaseDifficulty; hpMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max); hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max); diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs index 3b49d81674..08acd46c57 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs @@ -88,18 +88,18 @@ namespace osu.Game.Rulesets.Mania.UI protected override BeatmapConverter CreateBeatmapConverter() { if (IsForCurrentRuleset) - AvailableColumns = (int)Math.Max(1, Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.CircleSize)); + AvailableColumns = (int)Math.Max(1, Math.Round(WorkingBeatmap.BeatmapInfo.BaseDifficulty.CircleSize)); else { float percentSliderOrSpinner = (float)WorkingBeatmap.Beatmap.HitObjects.Count(h => h is IHasEndTime) / WorkingBeatmap.Beatmap.HitObjects.Count; if (percentSliderOrSpinner < 0.2) AvailableColumns = 7; - else if (percentSliderOrSpinner < 0.3 || Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.CircleSize) >= 5) - AvailableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) > 5 ? 7 : 6; + else if (percentSliderOrSpinner < 0.3 || Math.Round(WorkingBeatmap.BeatmapInfo.BaseDifficulty.CircleSize) >= 5) + AvailableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty) > 5 ? 7 : 6; else if (percentSliderOrSpinner > 0.6) - AvailableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) > 4 ? 5 : 4; + AvailableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty) > 4 ? 5 : 4; else - AvailableColumns = Math.Max(4, Math.Min((int)Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) + 1, 7)); + AvailableColumns = Math.Max(4, Math.Min((int)Math.Round(WorkingBeatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty) + 1, 7)); } return new ManiaBeatmapConverter(IsForCurrentRuleset, AvailableColumns); diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index 50239bf16c..e4960c5ca2 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Scoring protected override void SimulateAutoplay(Beatmap beatmap) { - hpDrainRate = beatmap.BeatmapInfo.Difficulty.DrainRate; + hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate; foreach (var obj in beatmap.HitObjects) { diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs index adfc946f86..d8dd6c7323 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs @@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor if (autoCursorScale && beatmap.Value != null) { // if we have a beatmap available, let's get its circle size to figure out an automatic cursor scale modifier. - scale *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.Difficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY); + scale *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY); } cursorContainer.Scale = new Vector2(scale); diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index ceaecbb555..9b4a6c47a9 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps { // Rewrite the beatmap info to add the slider velocity multiplier BeatmapInfo info = original.BeatmapInfo.DeepClone(); - info.Difficulty.SliderMultiplier *= legacy_velocity_multiplier; + info.BaseDifficulty.SliderMultiplier *= legacy_velocity_multiplier; Beatmap converted = base.ConvertBeatmap(original); @@ -96,7 +96,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps double distance = distanceData.Distance * repeats * legacy_velocity_multiplier; // The velocity of the taiko hit object - calculated as the velocity of a drum roll - double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength; + double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength; // The duration of the taiko hit object double taikoDuration = distance / taikoVelocity; @@ -106,12 +106,12 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps speedAdjustedBeatLength *= speedAdjustment; // The velocity of the osu! hit object - calculated as the velocity of a slider - double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength; + double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength; // The duration of the osu! hit object double osuDuration = distance / osuVelocity; // If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat - double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.Difficulty.SliderTickRate, taikoDuration / repeats); + double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, taikoDuration / repeats); if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength) { @@ -154,13 +154,13 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps Samples = obj.Samples, IsStrong = strong, Duration = taikoDuration, - TickRate = beatmap.BeatmapInfo.Difficulty.SliderTickRate == 3 ? 3 : 4, + TickRate = beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate == 3 ? 3 : 4, }; } } else if (endTimeData != null) { - double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier; + double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier; yield return new Swell { diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs index abdda9676f..752e3bee26 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs @@ -71,12 +71,12 @@ namespace osu.Game.Rulesets.Taiko.Scoring protected override void SimulateAutoplay(Beatmap beatmap) { - double hpMultiplierNormal = 1 / (hp_hit_great * beatmap.HitObjects.FindAll(o => o is Hit).Count * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.DrainRate, 0.5, 0.75, 0.98)); + double hpMultiplierNormal = 1 / (hp_hit_great * beatmap.HitObjects.FindAll(o => o is Hit).Count * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98)); hpIncreaseTick = hp_hit_tick; hpIncreaseGreat = hpMultiplierNormal * hp_hit_great; hpIncreaseGood = hpMultiplierNormal * hp_hit_good; - hpIncreaseMiss = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.DrainRate, hp_miss_min, hp_miss_mid, hp_miss_max); + hpIncreaseMiss = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, hp_miss_min, hp_miss_mid, hp_miss_max); foreach (var obj in beatmap.HitObjects) { diff --git a/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs index 2136d0d86a..14c27bc332 100644 --- a/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs @@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Taiko.Tests HitObjects = new List { new CentreHit() }, BeatmapInfo = new BeatmapInfo { - Difficulty = new BeatmapDifficulty(), + BaseDifficulty = new BeatmapDifficulty(), Metadata = new BeatmapMetadata { Artist = @"Unknown", diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs index f0853aef0e..48ee0a5b42 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Taiko.UI StartTime = time, }; - barLine.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.Difficulty); + barLine.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty); bool isMajor = currentBeat % (int)currentPoint.TimeSignature == 0; Playfield.Add(isMajor ? new DrawableBarLineMajor(barLine) : new DrawableBarLine(barLine)); diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs index 6bccd47b5c..95b691e07f 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs @@ -85,7 +85,7 @@ namespace osu.Game.Tests.Beatmaps.Formats using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { var beatmap = decoder.Decode(new StreamReader(stream)); - var difficulty = beatmap.BeatmapInfo.Difficulty; + var difficulty = beatmap.BeatmapInfo.BaseDifficulty; Assert.AreEqual(6.5f, difficulty.DrainRate); Assert.AreEqual(4, difficulty.CircleSize); Assert.AreEqual(8, difficulty.OverallDifficulty); diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 56bb48965f..35b6cc2b02 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -72,7 +72,7 @@ namespace osu.Game.Beatmaps AuthorString = @"Unknown Creator", }, Version = @"Normal", - Difficulty = new BeatmapDifficulty() + BaseDifficulty = new BeatmapDifficulty() }; } } diff --git a/osu.Game/Beatmaps/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs index e310c4a646..0b0fca8292 100644 --- a/osu.Game/Beatmaps/BeatmapDifficulty.cs +++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs @@ -15,8 +15,6 @@ namespace osu.Game.Beatmaps [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { get; set; } - public int BeatmapInfoID { get; set; } - public float DrainRate { get; set; } = DEFAULT_DIFFICULTY; public float CircleSize { get; set; } = DEFAULT_DIFFICULTY; public float OverallDifficulty { get; set; } = DEFAULT_DIFFICULTY; diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index d2ed5d692c..08cef3f934 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -20,7 +20,6 @@ namespace osu.Game.Beatmaps public int BeatmapVersion; [JsonProperty("id")] - [NotMapped] public int? OnlineBeatmapID { get; set; } [JsonProperty("beatmapset_id")] @@ -36,8 +35,7 @@ namespace osu.Game.Beatmaps public int BaseDifficultyID { get; set; } - [Required] - public BeatmapDifficulty Difficulty { get; set; } + public BeatmapDifficulty BaseDifficulty { get; set; } [NotMapped] public BeatmapMetrics Metrics { get; set; } @@ -55,7 +53,6 @@ namespace osu.Game.Beatmaps /// /// MD5 is kept for legacy support (matching against replays, osu-web-10 etc.). /// - [NotMapped] [JsonProperty("file_md5")] public string MD5Hash { get; set; } diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index ca8702d222..85bcfecfb8 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -1,6 +1,7 @@ // 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.ComponentModel.DataAnnotations.Schema; using System.Linq; using Newtonsoft.Json; @@ -16,19 +17,14 @@ namespace osu.Game.Beatmaps [NotMapped] public int? OnlineBeatmapSetID { get; set; } - public int? BeatmapSetInfoID { get; set; } - - public BeatmapSetInfo BeatmapSetInfo { get; set; } - - public int? BeatmapInfoID { get; set; } - - public BeatmapInfo BeatmapInfo { get; set; } - public string Title { get; set; } public string TitleUnicode { get; set; } public string Artist { get; set; } public string ArtistUnicode { get; set; } + public List Beatmaps { get; set; } + public List BeatmapSets { get; set; } + /// /// Helper property to deserialize a username to . /// diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 21a0b3ef6c..c2ec8c7b05 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -137,7 +137,7 @@ namespace osu.Game.Beatmaps public IEnumerable BeatmapSets => GetContext().BeatmapSetInfo .Include(s => s.Metadata) .Include(s => s.Beatmaps).ThenInclude(s => s.Ruleset) - .Include(s => s.Beatmaps).ThenInclude(b => b.Difficulty) + .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty) .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata) .Include(s => s.Files).ThenInclude(f => f.FileInfo); @@ -145,6 +145,6 @@ namespace osu.Game.Beatmaps .Include(b => b.BeatmapSet).ThenInclude(s => s.Metadata) .Include(b => b.Metadata) .Include(b => b.Ruleset) - .Include(b => b.Difficulty); + .Include(b => b.BaseDifficulty); } } diff --git a/osu.Game/Beatmaps/DifficultyCalculator.cs b/osu.Game/Beatmaps/DifficultyCalculator.cs index 60cbf0ac61..bb6a292d9d 100644 --- a/osu.Game/Beatmaps/DifficultyCalculator.cs +++ b/osu.Game/Beatmaps/DifficultyCalculator.cs @@ -40,7 +40,7 @@ namespace osu.Game.Beatmaps Objects = CreateBeatmapConverter().Convert(beatmap).HitObjects; foreach (var h in Objects) - h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.Difficulty); + h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); PreprocessHitObjects(); } diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 9a61762fa6..b9376849c1 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -25,7 +25,7 @@ namespace osu.Game.Beatmaps AuthorString = "no one", }, BeatmapSet = new BeatmapSetInfo(), - Difficulty = new BeatmapDifficulty + BaseDifficulty = new BeatmapDifficulty { DrainRate = 0, CircleSize = 0, diff --git a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs index 81695c3b5a..962c6ad49a 100644 --- a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs @@ -49,7 +49,7 @@ namespace osu.Game.Beatmaps.Formats BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata(), - Difficulty = new BeatmapDifficulty(), + BaseDifficulty = new BeatmapDifficulty(), }, }; diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs index 3c06180532..d775ab409b 100644 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs @@ -196,7 +196,7 @@ namespace osu.Game.Beatmaps.Formats { var pair = splitKeyVal(line, ':'); - var difficulty = beatmap.BeatmapInfo.Difficulty; + var difficulty = beatmap.BeatmapInfo.BaseDifficulty; switch (pair.Key) { case @"HPDrainRate": @@ -674,7 +674,7 @@ namespace osu.Game.Beatmaps.Formats } foreach (var hitObject in beatmap.HitObjects) - hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.Difficulty); + hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); } private KeyValuePair splitKeyVal(string line, char separator) diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index cfc5f056ad..37d5bb21c8 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -65,20 +65,22 @@ namespace osu.Game.Database protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); + modelBuilder.Entity().HasIndex(b => b.MD5Hash); modelBuilder.Entity().HasIndex(b => b.Hash); + modelBuilder.Entity().HasIndex(b => b.DeletePending); modelBuilder.Entity().HasIndex(b => b.Hash); + modelBuilder.Entity().HasIndex(b => b.Variant); modelBuilder.Entity().HasIndex(b => b.IntAction); + modelBuilder.Entity().HasIndex(b => b.Hash).IsUnique(); modelBuilder.Entity().HasIndex(b => b.ReferenceCount); - modelBuilder.Entity().HasIndex(b => b.Name).IsUnique(); - modelBuilder.Entity().HasIndex(b => b.InstantiationInfo).IsUnique(); + modelBuilder.Entity().HasIndex(b => b.Available); - modelBuilder.Entity().HasOne(m => m.BeatmapSetInfo).WithOne(s => s.Metadata).OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity().HasOne(m => m.BeatmapInfo).WithOne(b => b.Metadata).OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity().HasOne(b => b.BaseDifficulty); } private class OsuDbLoggerFactory : ILoggerFactory diff --git a/osu.Game/Migrations/20171018125509_InitialCreate.Designer.cs b/osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs similarity index 80% rename from osu.Game/Migrations/20171018125509_InitialCreate.Designer.cs rename to osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs index 28e7e20743..0820041643 100644 --- a/osu.Game/Migrations/20171018125509_InitialCreate.Designer.cs +++ b/osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs @@ -10,7 +10,7 @@ using System; namespace osu.Game.Migrations { [DbContext(typeof(OsuDbContext))] - [Migration("20171018125509_InitialCreate")] + [Migration("20171019041408_InitialCreate")] partial class InitialCreate { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -26,8 +26,6 @@ namespace osu.Game.Migrations b.Property("ApproachRate"); - b.Property("BeatmapInfoID"); - b.Property("CircleSize"); b.Property("DrainRate"); @@ -40,9 +38,6 @@ namespace osu.Game.Migrations b.HasKey("ID"); - b.HasIndex("BeatmapInfoID") - .IsUnique(); - b.ToTable("BeatmapDifficulty"); }); @@ -73,6 +68,10 @@ namespace osu.Game.Migrations b.Property("MD5Hash"); + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + b.Property("Path"); b.Property("RulesetID"); @@ -93,12 +92,16 @@ namespace osu.Game.Migrations b.HasKey("ID"); + b.HasIndex("BaseDifficultyID"); + b.HasIndex("BeatmapSetInfoID"); b.HasIndex("Hash"); b.HasIndex("MD5Hash"); + b.HasIndex("MetadataID"); + b.HasIndex("RulesetID"); b.ToTable("BeatmapInfo"); @@ -120,10 +123,6 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); - b.Property("BeatmapInfoID"); - - b.Property("BeatmapSetInfoID"); - b.Property("PreviewTime"); b.Property("Source"); @@ -136,12 +135,6 @@ namespace osu.Game.Migrations b.HasKey("ID"); - b.HasIndex("BeatmapInfoID") - .IsUnique(); - - b.HasIndex("BeatmapSetInfoID") - .IsUnique(); - b.ToTable("BeatmapMetadata"); }); @@ -175,6 +168,8 @@ namespace osu.Game.Migrations b.Property("Hash"); + b.Property("MetadataID"); + b.Property("OnlineBeatmapSetID"); b.Property("Protected"); @@ -185,6 +180,8 @@ namespace osu.Game.Migrations b.HasIndex("Hash"); + b.HasIndex("MetadataID"); + b.ToTable("BeatmapSetInfo"); }); @@ -246,49 +243,31 @@ namespace osu.Game.Migrations b.HasIndex("Available"); - b.HasIndex("InstantiationInfo") - .IsUnique(); - - b.HasIndex("Name") - .IsUnique(); - b.ToTable("RulesetInfo"); }); - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapInfo") - .WithOne("Difficulty") - .HasForeignKey("osu.Game.Beatmaps.BeatmapDifficulty", "BeatmapInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") .WithMany("Beatmaps") .HasForeignKey("BeatmapSetInfoID") .OnDelete(DeleteBehavior.Cascade); + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") .WithMany() .HasForeignKey("RulesetID") .OnDelete(DeleteBehavior.Cascade); }); - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "BeatmapInfo") - .WithOne("Metadata") - .HasForeignKey("osu.Game.Beatmaps.BeatmapMetadata", "BeatmapInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSetInfo") - .WithOne("Metadata") - .HasForeignKey("osu.Game.Beatmaps.BeatmapMetadata", "BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => { b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") @@ -301,6 +280,13 @@ namespace osu.Game.Migrations .HasForeignKey("FileInfoID") .OnDelete(DeleteBehavior.Cascade); }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); #pragma warning restore 612, 618 } } diff --git a/osu.Game/Migrations/20171018125509_InitialCreate.cs b/osu.Game/Migrations/20171019041408_InitialCreate.cs similarity index 85% rename from osu.Game/Migrations/20171018125509_InitialCreate.cs rename to osu.Game/Migrations/20171019041408_InitialCreate.cs index 512ffa2790..23e5b6f8bb 100644 --- a/osu.Game/Migrations/20171018125509_InitialCreate.cs +++ b/osu.Game/Migrations/20171019041408_InitialCreate.cs @@ -9,19 +9,43 @@ namespace osu.Game.Migrations protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( - name: "BeatmapSetInfo", + name: "BeatmapDifficulty", columns: table => new { ID = table.Column(type: "INTEGER", nullable: false) .Annotation("Sqlite:Autoincrement", true), - DeletePending = table.Column(type: "INTEGER", nullable: false), - Hash = table.Column(type: "TEXT", nullable: true), - OnlineBeatmapSetID = table.Column(type: "INTEGER", nullable: true), - Protected = table.Column(type: "INTEGER", nullable: false) + ApproachRate = table.Column(type: "REAL", nullable: false), + CircleSize = table.Column(type: "REAL", nullable: false), + DrainRate = table.Column(type: "REAL", nullable: false), + OverallDifficulty = table.Column(type: "REAL", nullable: false), + SliderMultiplier = table.Column(type: "REAL", nullable: false), + SliderTickRate = table.Column(type: "REAL", nullable: false) }, constraints: table => { - table.PrimaryKey("PK_BeatmapSetInfo", x => x.ID); + table.PrimaryKey("PK_BeatmapDifficulty", x => x.ID); + }); + + migrationBuilder.CreateTable( + name: "BeatmapMetadata", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Artist = table.Column(type: "TEXT", nullable: true), + ArtistUnicode = table.Column(type: "TEXT", nullable: true), + AudioFile = table.Column(type: "TEXT", nullable: true), + Author = table.Column(type: "TEXT", nullable: true), + BackgroundFile = table.Column(type: "TEXT", nullable: true), + PreviewTime = table.Column(type: "INTEGER", nullable: false), + Source = table.Column(type: "TEXT", nullable: true), + Tags = table.Column(type: "TEXT", nullable: true), + Title = table.Column(type: "TEXT", nullable: true), + TitleUnicode = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_BeatmapMetadata", x => x.ID); }); migrationBuilder.CreateTable( @@ -69,6 +93,87 @@ namespace osu.Game.Migrations table.PrimaryKey("PK_RulesetInfo", x => x.ID); }); + migrationBuilder.CreateTable( + name: "BeatmapSetInfo", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + DeletePending = table.Column(type: "INTEGER", nullable: false), + Hash = table.Column(type: "TEXT", nullable: true), + MetadataID = table.Column(type: "INTEGER", nullable: true), + OnlineBeatmapSetID = table.Column(type: "INTEGER", nullable: true), + Protected = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_BeatmapSetInfo", x => x.ID); + table.ForeignKey( + name: "FK_BeatmapSetInfo_BeatmapMetadata_MetadataID", + column: x => x.MetadataID, + principalTable: "BeatmapMetadata", + principalColumn: "ID", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "BeatmapInfo", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + AudioLeadIn = table.Column(type: "INTEGER", nullable: false), + BaseDifficultyID = table.Column(type: "INTEGER", nullable: false), + BeatDivisor = table.Column(type: "INTEGER", nullable: false), + BeatmapSetInfoID = table.Column(type: "INTEGER", nullable: false), + Countdown = table.Column(type: "INTEGER", nullable: false), + DistanceSpacing = table.Column(type: "REAL", nullable: false), + GridSize = table.Column(type: "INTEGER", nullable: false), + Hash = table.Column(type: "TEXT", nullable: true), + Hidden = table.Column(type: "INTEGER", nullable: false), + LetterboxInBreaks = table.Column(type: "INTEGER", nullable: false), + MD5Hash = table.Column(type: "TEXT", nullable: true), + MetadataID = table.Column(type: "INTEGER", nullable: true), + OnlineBeatmapID = table.Column(type: "INTEGER", nullable: true), + Path = table.Column(type: "TEXT", nullable: true), + RulesetID = table.Column(type: "INTEGER", nullable: false), + SpecialStyle = table.Column(type: "INTEGER", nullable: false), + StackLeniency = table.Column(type: "REAL", nullable: false), + StarDifficulty = table.Column(type: "REAL", nullable: false), + StoredBookmarks = table.Column(type: "TEXT", nullable: true), + TimelineZoom = table.Column(type: "REAL", nullable: false), + Version = table.Column(type: "TEXT", nullable: true), + WidescreenStoryboard = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_BeatmapInfo", x => x.ID); + table.ForeignKey( + name: "FK_BeatmapInfo_BeatmapDifficulty_BaseDifficultyID", + column: x => x.BaseDifficultyID, + principalTable: "BeatmapDifficulty", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_BeatmapInfo_BeatmapSetInfo_BeatmapSetInfoID", + column: x => x.BeatmapSetInfoID, + principalTable: "BeatmapSetInfo", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_BeatmapInfo_BeatmapMetadata_MetadataID", + column: x => x.MetadataID, + principalTable: "BeatmapMetadata", + principalColumn: "ID", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_BeatmapInfo_RulesetInfo_RulesetID", + column: x => x.RulesetID, + principalTable: "RulesetInfo", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "BeatmapSetFileInfo", columns: table => new @@ -96,116 +201,10 @@ namespace osu.Game.Migrations onDelete: ReferentialAction.Cascade); }); - migrationBuilder.CreateTable( - name: "BeatmapInfo", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - AudioLeadIn = table.Column(type: "INTEGER", nullable: false), - BaseDifficultyID = table.Column(type: "INTEGER", nullable: false), - BeatDivisor = table.Column(type: "INTEGER", nullable: false), - BeatmapSetInfoID = table.Column(type: "INTEGER", nullable: false), - Countdown = table.Column(type: "INTEGER", nullable: false), - DistanceSpacing = table.Column(type: "REAL", nullable: false), - GridSize = table.Column(type: "INTEGER", nullable: false), - Hash = table.Column(type: "TEXT", nullable: true), - Hidden = table.Column(type: "INTEGER", nullable: false), - LetterboxInBreaks = table.Column(type: "INTEGER", nullable: false), - MD5Hash = table.Column(type: "TEXT", nullable: true), - Path = table.Column(type: "TEXT", nullable: true), - RulesetID = table.Column(type: "INTEGER", nullable: false), - SpecialStyle = table.Column(type: "INTEGER", nullable: false), - StackLeniency = table.Column(type: "REAL", nullable: false), - StarDifficulty = table.Column(type: "REAL", nullable: false), - StoredBookmarks = table.Column(type: "TEXT", nullable: true), - TimelineZoom = table.Column(type: "REAL", nullable: false), - Version = table.Column(type: "TEXT", nullable: true), - WidescreenStoryboard = table.Column(type: "INTEGER", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_BeatmapInfo", x => x.ID); - table.ForeignKey( - name: "FK_BeatmapInfo_BeatmapSetInfo_BeatmapSetInfoID", - column: x => x.BeatmapSetInfoID, - principalTable: "BeatmapSetInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_BeatmapInfo_RulesetInfo_RulesetID", - column: x => x.RulesetID, - principalTable: "RulesetInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "BeatmapDifficulty", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - ApproachRate = table.Column(type: "REAL", nullable: false), - BeatmapInfoID = table.Column(type: "INTEGER", nullable: false), - CircleSize = table.Column(type: "REAL", nullable: false), - DrainRate = table.Column(type: "REAL", nullable: false), - OverallDifficulty = table.Column(type: "REAL", nullable: false), - SliderMultiplier = table.Column(type: "REAL", nullable: false), - SliderTickRate = table.Column(type: "REAL", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_BeatmapDifficulty", x => x.ID); - table.ForeignKey( - name: "FK_BeatmapDifficulty_BeatmapInfo_BeatmapInfoID", - column: x => x.BeatmapInfoID, - principalTable: "BeatmapInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "BeatmapMetadata", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Artist = table.Column(type: "TEXT", nullable: true), - ArtistUnicode = table.Column(type: "TEXT", nullable: true), - AudioFile = table.Column(type: "TEXT", nullable: true), - Author = table.Column(type: "TEXT", nullable: true), - BackgroundFile = table.Column(type: "TEXT", nullable: true), - BeatmapInfoID = table.Column(type: "INTEGER", nullable: true), - BeatmapSetInfoID = table.Column(type: "INTEGER", nullable: true), - PreviewTime = table.Column(type: "INTEGER", nullable: false), - Source = table.Column(type: "TEXT", nullable: true), - Tags = table.Column(type: "TEXT", nullable: true), - Title = table.Column(type: "TEXT", nullable: true), - TitleUnicode = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_BeatmapMetadata", x => x.ID); - table.ForeignKey( - name: "FK_BeatmapMetadata_BeatmapInfo_BeatmapInfoID", - column: x => x.BeatmapInfoID, - principalTable: "BeatmapInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_BeatmapMetadata_BeatmapSetInfo_BeatmapSetInfoID", - column: x => x.BeatmapSetInfoID, - principalTable: "BeatmapSetInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - }); - migrationBuilder.CreateIndex( - name: "IX_BeatmapDifficulty_BeatmapInfoID", - table: "BeatmapDifficulty", - column: "BeatmapInfoID", - unique: true); + name: "IX_BeatmapInfo_BaseDifficultyID", + table: "BeatmapInfo", + column: "BaseDifficultyID"); migrationBuilder.CreateIndex( name: "IX_BeatmapInfo_BeatmapSetInfoID", @@ -222,23 +221,16 @@ namespace osu.Game.Migrations table: "BeatmapInfo", column: "MD5Hash"); + migrationBuilder.CreateIndex( + name: "IX_BeatmapInfo_MetadataID", + table: "BeatmapInfo", + column: "MetadataID"); + migrationBuilder.CreateIndex( name: "IX_BeatmapInfo_RulesetID", table: "BeatmapInfo", column: "RulesetID"); - migrationBuilder.CreateIndex( - name: "IX_BeatmapMetadata_BeatmapInfoID", - table: "BeatmapMetadata", - column: "BeatmapInfoID", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapMetadata_BeatmapSetInfoID", - table: "BeatmapMetadata", - column: "BeatmapSetInfoID", - unique: true); - migrationBuilder.CreateIndex( name: "IX_BeatmapSetFileInfo_BeatmapSetInfoID", table: "BeatmapSetFileInfo", @@ -259,6 +251,11 @@ namespace osu.Game.Migrations table: "BeatmapSetInfo", column: "Hash"); + migrationBuilder.CreateIndex( + name: "IX_BeatmapSetInfo_MetadataID", + table: "BeatmapSetInfo", + column: "MetadataID"); + migrationBuilder.CreateIndex( name: "IX_FileInfo_Hash", table: "FileInfo", @@ -284,27 +281,12 @@ namespace osu.Game.Migrations name: "IX_RulesetInfo_Available", table: "RulesetInfo", column: "Available"); - - migrationBuilder.CreateIndex( - name: "IX_RulesetInfo_InstantiationInfo", - table: "RulesetInfo", - column: "InstantiationInfo", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_RulesetInfo_Name", - table: "RulesetInfo", - column: "Name", - unique: true); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( - name: "BeatmapDifficulty"); - - migrationBuilder.DropTable( - name: "BeatmapMetadata"); + name: "BeatmapInfo"); migrationBuilder.DropTable( name: "BeatmapSetFileInfo"); @@ -313,16 +295,19 @@ namespace osu.Game.Migrations name: "KeyBinding"); migrationBuilder.DropTable( - name: "BeatmapInfo"); + name: "BeatmapDifficulty"); migrationBuilder.DropTable( - name: "FileInfo"); + name: "RulesetInfo"); migrationBuilder.DropTable( name: "BeatmapSetInfo"); migrationBuilder.DropTable( - name: "RulesetInfo"); + name: "FileInfo"); + + migrationBuilder.DropTable( + name: "BeatmapMetadata"); } } } diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index 8d0d8f20fe..0576242648 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -25,8 +25,6 @@ namespace osu.Game.Migrations b.Property("ApproachRate"); - b.Property("BeatmapInfoID"); - b.Property("CircleSize"); b.Property("DrainRate"); @@ -39,9 +37,6 @@ namespace osu.Game.Migrations b.HasKey("ID"); - b.HasIndex("BeatmapInfoID") - .IsUnique(); - b.ToTable("BeatmapDifficulty"); }); @@ -72,6 +67,10 @@ namespace osu.Game.Migrations b.Property("MD5Hash"); + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + b.Property("Path"); b.Property("RulesetID"); @@ -92,12 +91,16 @@ namespace osu.Game.Migrations b.HasKey("ID"); + b.HasIndex("BaseDifficultyID"); + b.HasIndex("BeatmapSetInfoID"); b.HasIndex("Hash"); b.HasIndex("MD5Hash"); + b.HasIndex("MetadataID"); + b.HasIndex("RulesetID"); b.ToTable("BeatmapInfo"); @@ -119,10 +122,6 @@ namespace osu.Game.Migrations b.Property("BackgroundFile"); - b.Property("BeatmapInfoID"); - - b.Property("BeatmapSetInfoID"); - b.Property("PreviewTime"); b.Property("Source"); @@ -135,12 +134,6 @@ namespace osu.Game.Migrations b.HasKey("ID"); - b.HasIndex("BeatmapInfoID") - .IsUnique(); - - b.HasIndex("BeatmapSetInfoID") - .IsUnique(); - b.ToTable("BeatmapMetadata"); }); @@ -174,6 +167,8 @@ namespace osu.Game.Migrations b.Property("Hash"); + b.Property("MetadataID"); + b.Property("OnlineBeatmapSetID"); b.Property("Protected"); @@ -184,6 +179,8 @@ namespace osu.Game.Migrations b.HasIndex("Hash"); + b.HasIndex("MetadataID"); + b.ToTable("BeatmapSetInfo"); }); @@ -245,49 +242,31 @@ namespace osu.Game.Migrations b.HasIndex("Available"); - b.HasIndex("InstantiationInfo") - .IsUnique(); - - b.HasIndex("Name") - .IsUnique(); - b.ToTable("RulesetInfo"); }); - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapInfo") - .WithOne("Difficulty") - .HasForeignKey("osu.Game.Beatmaps.BeatmapDifficulty", "BeatmapInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") .WithMany("Beatmaps") .HasForeignKey("BeatmapSetInfoID") .OnDelete(DeleteBehavior.Cascade); + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") .WithMany() .HasForeignKey("RulesetID") .OnDelete(DeleteBehavior.Cascade); }); - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "BeatmapInfo") - .WithOne("Metadata") - .HasForeignKey("osu.Game.Beatmaps.BeatmapMetadata", "BeatmapInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSetInfo") - .WithOne("Metadata") - .HasForeignKey("osu.Game.Beatmaps.BeatmapMetadata", "BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => { b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") @@ -300,6 +279,13 @@ namespace osu.Game.Migrations .HasForeignKey("FileInfoID") .OnDelete(DeleteBehavior.Cascade); }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); #pragma warning restore 612, 618 } } diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 729df02ffd..6f53b76031 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -172,11 +172,11 @@ namespace osu.Game.Rulesets.UI // Apply difficulty adjustments from mods before using Difficulty. foreach (var mod in Mods.OfType()) - mod.ApplyToDifficulty(Beatmap.BeatmapInfo.Difficulty); + mod.ApplyToDifficulty(Beatmap.BeatmapInfo.BaseDifficulty); // Apply defaults foreach (var h in Beatmap.HitObjects) - h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.Difficulty); + h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty); // Post-process the beatmap processor.PostProcess(Beatmap); diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index a98362e89c..d7c509d979 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -264,7 +264,7 @@ namespace osu.Game.Screens.Select advanced.Beatmap = new BeatmapInfo { StarDifficulty = 0, - Difficulty = new BeatmapDifficulty + BaseDifficulty = new BeatmapDifficulty { CircleSize = 0, DrainRate = 0, diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index f1215ab33d..3c9cffadfb 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -32,17 +32,17 @@ namespace osu.Game.Screens.Select.Details if ((Beatmap?.Ruleset?.ID ?? 0) == 3) { firstValue.Title = "Key Amount"; - firstValue.Value = (int)Math.Round(Beatmap?.Difficulty?.CircleSize ?? 0); + firstValue.Value = (int)Math.Round(Beatmap?.BaseDifficulty?.CircleSize ?? 0); } else { firstValue.Title = "Circle Size"; - firstValue.Value = Beatmap?.Difficulty?.CircleSize ?? 0; + firstValue.Value = Beatmap?.BaseDifficulty?.CircleSize ?? 0; } - hpDrain.Value = beatmap.Difficulty?.DrainRate ?? 0; - accuracy.Value = beatmap.Difficulty?.OverallDifficulty ?? 0; - approachRate.Value = beatmap.Difficulty?.ApproachRate ?? 0; + hpDrain.Value = beatmap.BaseDifficulty?.DrainRate ?? 0; + accuracy.Value = beatmap.BaseDifficulty?.OverallDifficulty ?? 0; + approachRate.Value = beatmap.BaseDifficulty?.ApproachRate ?? 0; starDifficulty.Value = (float)beatmap.StarDifficulty; } } diff --git a/osu.Game/Tests/Visual/TestCaseBeatmapDetails.cs b/osu.Game/Tests/Visual/TestCaseBeatmapDetails.cs index cd4d97425b..5306121a92 100644 --- a/osu.Game/Tests/Visual/TestCaseBeatmapDetails.cs +++ b/osu.Game/Tests/Visual/TestCaseBeatmapDetails.cs @@ -29,7 +29,7 @@ namespace osu.Game.Tests.Visual Source = "osu!lazer", Tags = "this beatmap has all the metrics", }, - Difficulty = new BeatmapDifficulty + BaseDifficulty = new BeatmapDifficulty { CircleSize = 7, DrainRate = 1, @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual Source = "osu!lazer", Tags = "this beatmap has ratings metrics but not retries or fails", }, - Difficulty = new BeatmapDifficulty + BaseDifficulty = new BeatmapDifficulty { CircleSize = 6, DrainRate = 9, @@ -75,7 +75,7 @@ namespace osu.Game.Tests.Visual Source = "osu!lazer", Tags = "this beatmap has retries and fails but no ratings", }, - Difficulty = new BeatmapDifficulty + BaseDifficulty = new BeatmapDifficulty { CircleSize = 3.7f, DrainRate = 6, @@ -98,7 +98,7 @@ namespace osu.Game.Tests.Visual Source = "osu!lazer", Tags = "this beatmap has no metrics", }, - Difficulty = new BeatmapDifficulty + BaseDifficulty = new BeatmapDifficulty { CircleSize = 5, DrainRate = 5, diff --git a/osu.Game/Tests/Visual/TestCaseBeatmapSetOverlay.cs b/osu.Game/Tests/Visual/TestCaseBeatmapSetOverlay.cs index 72d97f905c..1ade0be626 100644 --- a/osu.Game/Tests/Visual/TestCaseBeatmapSetOverlay.cs +++ b/osu.Game/Tests/Visual/TestCaseBeatmapSetOverlay.cs @@ -65,7 +65,7 @@ namespace osu.Game.Tests.Visual StarDifficulty = 1.36, Version = @"BASIC", Ruleset = mania, - Difficulty = new BeatmapDifficulty + BaseDifficulty = new BeatmapDifficulty { CircleSize = 4, DrainRate = 6.5f, @@ -93,7 +93,7 @@ namespace osu.Game.Tests.Visual StarDifficulty = 2.22, Version = @"NOVICE", Ruleset = mania, - Difficulty = new BeatmapDifficulty + BaseDifficulty = new BeatmapDifficulty { CircleSize = 4, DrainRate = 7, @@ -121,7 +121,7 @@ namespace osu.Game.Tests.Visual StarDifficulty = 3.49, Version = @"ADVANCED", Ruleset = mania, - Difficulty = new BeatmapDifficulty + BaseDifficulty = new BeatmapDifficulty { CircleSize = 4, DrainRate = 7.5f, @@ -149,7 +149,7 @@ namespace osu.Game.Tests.Visual StarDifficulty = 4.24, Version = @"EXHAUST", Ruleset = mania, - Difficulty = new BeatmapDifficulty + BaseDifficulty = new BeatmapDifficulty { CircleSize = 4, DrainRate = 8, @@ -177,7 +177,7 @@ namespace osu.Game.Tests.Visual StarDifficulty = 5.26, Version = @"GRAVITY", Ruleset = mania, - Difficulty = new BeatmapDifficulty + BaseDifficulty = new BeatmapDifficulty { CircleSize = 4, DrainRate = 8.5f, @@ -239,7 +239,7 @@ namespace osu.Game.Tests.Visual StarDifficulty = 1.40, Version = @"yzrin's Kantan", Ruleset = taiko, - Difficulty = new BeatmapDifficulty + BaseDifficulty = new BeatmapDifficulty { CircleSize = 2, DrainRate = 7, @@ -267,7 +267,7 @@ namespace osu.Game.Tests.Visual StarDifficulty = 2.23, Version = @"Futsuu", Ruleset = taiko, - Difficulty = new BeatmapDifficulty + BaseDifficulty = new BeatmapDifficulty { CircleSize = 2, DrainRate = 6, @@ -295,7 +295,7 @@ namespace osu.Game.Tests.Visual StarDifficulty = 3.19, Version = @"Muzukashii", Ruleset = taiko, - Difficulty = new BeatmapDifficulty + BaseDifficulty = new BeatmapDifficulty { CircleSize = 2, DrainRate = 6, @@ -323,7 +323,7 @@ namespace osu.Game.Tests.Visual StarDifficulty = 3.97, Version = @"Charlotte's Oni", Ruleset = taiko, - Difficulty = new BeatmapDifficulty + BaseDifficulty = new BeatmapDifficulty { CircleSize = 5, DrainRate = 6, @@ -351,7 +351,7 @@ namespace osu.Game.Tests.Visual StarDifficulty = 5.08, Version = @"Labyrinth Oni", Ruleset = taiko, - Difficulty = new BeatmapDifficulty + BaseDifficulty = new BeatmapDifficulty { CircleSize = 5, DrainRate = 5, diff --git a/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs index a46542760b..965308c32c 100644 --- a/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs +++ b/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs @@ -80,7 +80,7 @@ namespace osu.Game.Tests.Visual Ruleset = rulesets.AvailableRulesets.First(), Path = "normal.osu", Version = "Normal", - Difficulty = new BeatmapDifficulty + BaseDifficulty = new BeatmapDifficulty { OverallDifficulty = 3.5f, } @@ -91,7 +91,7 @@ namespace osu.Game.Tests.Visual Ruleset = rulesets.AvailableRulesets.First(), Path = "hard.osu", Version = "Hard", - Difficulty = new BeatmapDifficulty + BaseDifficulty = new BeatmapDifficulty { OverallDifficulty = 5, } @@ -102,7 +102,7 @@ namespace osu.Game.Tests.Visual Ruleset = rulesets.AvailableRulesets.First(), Path = "insane.osu", Version = "Insane", - Difficulty = new BeatmapDifficulty + BaseDifficulty = new BeatmapDifficulty { OverallDifficulty = 7, } diff --git a/osu.Game/Tests/Visual/TestCaseScrollingPlayfield.cs b/osu.Game/Tests/Visual/TestCaseScrollingPlayfield.cs index d0761e5841..40fb22af1e 100644 --- a/osu.Game/Tests/Visual/TestCaseScrollingPlayfield.cs +++ b/osu.Game/Tests/Visual/TestCaseScrollingPlayfield.cs @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual HitObjects = objects, BeatmapInfo = new BeatmapInfo { - Difficulty = new BeatmapDifficulty(), + BaseDifficulty = new BeatmapDifficulty(), Metadata = new BeatmapMetadata() } }; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 0e16c70277..85b08afecd 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -281,9 +281,9 @@ - - - 20171018125509_InitialCreate.cs + + + 20171019041408_InitialCreate.cs From 908c6d827fc466e719101037d94bca27ef00b97f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Oct 2017 14:49:23 +0900 Subject: [PATCH 81/90] ValueTuple please --- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game/osu.Game.csproj | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 7a3bbab176..51c1b03373 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -40,7 +40,7 @@ - ../packages/System.ValueTuple.4.4.0/lib/net461/System.ValueTuple.dll + $(SolutionDir)\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll True diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 85b08afecd..601c99e19f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -861,10 +861,6 @@ - - true - true - From 36af0dc809418ac61b1d1a72f547736ec68af9ef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Oct 2017 14:59:11 +0900 Subject: [PATCH 82/90] Update app config for rulesets --- osu.Game.Rulesets.Catch/app.config | 4 ++++ osu.Game.Rulesets.Mania/app.config | 4 ++++ osu.Game.Rulesets.Osu/app.config | 4 ++++ osu.Game.Rulesets.Taiko/app.config | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/osu.Game.Rulesets.Catch/app.config b/osu.Game.Rulesets.Catch/app.config index 11af32e2cf..c9d4e44b1a 100644 --- a/osu.Game.Rulesets.Catch/app.config +++ b/osu.Game.Rulesets.Catch/app.config @@ -10,6 +10,10 @@ + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/app.config b/osu.Game.Rulesets.Mania/app.config index 11af32e2cf..c9d4e44b1a 100644 --- a/osu.Game.Rulesets.Mania/app.config +++ b/osu.Game.Rulesets.Mania/app.config @@ -10,6 +10,10 @@ + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/app.config b/osu.Game.Rulesets.Osu/app.config index 11af32e2cf..c9d4e44b1a 100644 --- a/osu.Game.Rulesets.Osu/app.config +++ b/osu.Game.Rulesets.Osu/app.config @@ -10,6 +10,10 @@ + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/app.config b/osu.Game.Rulesets.Taiko/app.config index 11af32e2cf..c9d4e44b1a 100644 --- a/osu.Game.Rulesets.Taiko/app.config +++ b/osu.Game.Rulesets.Taiko/app.config @@ -10,6 +10,10 @@ + + + + \ No newline at end of file From 12900a8b1503869a2a5a10a7ce29ac20b6383790 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Oct 2017 15:53:11 +0900 Subject: [PATCH 83/90] Remove unnecessary second call to AutoTransactionsEnabled --- osu.Game/Beatmaps/BeatmapManager.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 8a2997987b..47dbc72837 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -184,8 +184,6 @@ namespace osu.Game.Beatmaps { var context = importContext.Value; - context.Database.AutoTransactionsEnabled = false; - using (var transaction = context.Database.BeginTransaction()) { // create local stores so we can isolate and thread safely, and share a context/transaction. From 36c00577af65638ed56f56dedaca01d0210005ce Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Oct 2017 16:35:06 +0900 Subject: [PATCH 84/90] Reduce database log output Also hard-disables it for uninteresting log levels, providing a further performance boost. --- osu.Game/Database/OsuDbContext.cs | 35 ++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index 37d5bb21c8..e3605181a4 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -31,7 +31,8 @@ namespace osu.Game.Database /// /// Create a new in-memory OsuDbContext instance. /// - public OsuDbContext() : this("DataSource=:memory:") + public OsuDbContext() + : this("DataSource=:memory:") { // required for tooling (see https://wildermuth.com/2017/07/06/Program-cs-in-ASP-NET-Core-2-0). } @@ -116,9 +117,37 @@ namespace osu.Game.Database private class OsuDbLogger : ILogger { public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) - => Logger.Log(formatter(state, exception), LoggingTarget.Database, Framework.Logging.LogLevel.Debug); + { + if (logLevel < LogLevel.Information) + return; - public bool IsEnabled(LogLevel logLevel) => true; + Framework.Logging.LogLevel frameworkLogLevel; + + switch (logLevel) + { + default: + frameworkLogLevel = Framework.Logging.LogLevel.Debug; + break; + case LogLevel.Warning: + frameworkLogLevel = Framework.Logging.LogLevel.Important; + break; + case LogLevel.Error: + case LogLevel.Critical: + frameworkLogLevel = Framework.Logging.LogLevel.Error; + break; + } + + Logger.Log(formatter(state, exception), LoggingTarget.Database, frameworkLogLevel); + } + + public bool IsEnabled(LogLevel logLevel) + { +#if DEBUG + return logLevel > LogLevel.Debug; +#else + return logLevel > LogLevel.Information; +#endif + } public IDisposable BeginScope(TState state) => null; } From b02dd196f678d67f95fa80dee534a903bc2d6abb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Oct 2017 17:29:39 +0900 Subject: [PATCH 85/90] Don't make one factory each context A factory is supposed to be re-used. --- osu.Game/Database/OsuDbContext.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index e3605181a4..98a5cc8b67 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -22,6 +22,8 @@ namespace osu.Game.Database public DbSet RulesetInfo { get; set; } private readonly string connectionString; + private static readonly Lazy logger = new Lazy(() => new OsuDbLoggerFactory()); + static OsuDbContext() { // required to initialise native SQLite libraries on some platforms. @@ -60,7 +62,7 @@ namespace osu.Game.Database { base.OnConfiguring(optionsBuilder); optionsBuilder.UseSqlite(connectionString); - optionsBuilder.UseLoggerFactory(new OsuDbLoggerFactory()); + optionsBuilder.UseLoggerFactory(logger.Value); } protected override void OnModelCreating(ModelBuilder modelBuilder) From f7d0df174327b0edf4ead40825a793dfad4300f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Oct 2017 17:50:46 +0900 Subject: [PATCH 86/90] Fix beatmap difficulty and metadata deletion --- osu.Game/Beatmaps/BeatmapStore.cs | 15 ++++++++++++++- osu.Game/Database/OsuDbContext.cs | 2 ++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index c2ec8c7b05..8eac35a667 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -130,7 +130,20 @@ namespace osu.Game.Beatmaps { var context = GetContext(); - context.BeatmapSetInfo.RemoveRange(context.BeatmapSetInfo.Where(b => b.DeletePending && !b.Protected)); + var purgeable = context.BeatmapSetInfo.Where(s => s.DeletePending && !s.Protected) + .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata) + .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty) + .Include(s => s.Metadata); + + // metadata is M-N so we can't rely on cascades + context.BeatmapMetadata.RemoveRange(purgeable.Select(s => s.Metadata)); + context.BeatmapMetadata.RemoveRange(purgeable.SelectMany(s => s.Beatmaps.Select(b => b.Metadata))); + + // todo: we can probably make cascades work here with a FK in BeatmapDifficulty. just make to make it work correctly. + context.BeatmapDifficulty.RemoveRange(purgeable.SelectMany(s => s.Beatmaps.Select(b => b.BaseDifficulty))); + + // cascades down to beatmaps. + context.BeatmapSetInfo.RemoveRange(purgeable); context.SaveChanges(); } diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index 98a5cc8b67..2938a8e7be 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -16,6 +16,8 @@ namespace osu.Game.Database public class OsuDbContext : DbContext { public DbSet BeatmapInfo { get; set; } + public DbSet BeatmapDifficulty { get; set; } + public DbSet BeatmapMetadata { get; set; } public DbSet BeatmapSetInfo { get; set; } public DbSet DatabasedKeyBinding { get; set; } public DbSet FileInfo { get; set; } From 1fbbee14e4c000931a8b404bac13b4ce376ab5a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Oct 2017 19:41:16 +0900 Subject: [PATCH 87/90] Allow migration from sqlite-net to EF Bonus stage --- osu.Game/Database/DatabaseContextFactory.cs | 2 +- osu.Game/Database/OsuDbContext.cs | 69 +++++++++++++++++++++ osu.Game/OsuGameBase.cs | 3 +- 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/DatabaseContextFactory.cs b/osu.Game/Database/DatabaseContextFactory.cs index e22301adfe..359188b4e2 100644 --- a/osu.Game/Database/DatabaseContextFactory.cs +++ b/osu.Game/Database/DatabaseContextFactory.cs @@ -14,6 +14,6 @@ namespace osu.Game.Database this.host = host; } - public OsuDbContext GetContext() => new OsuDbContext(host.Storage.GetDatabaseConnectionString(@"client-ef")); + public OsuDbContext GetContext() => new OsuDbContext(host.Storage.GetDatabaseConnectionString(@"client")); } } diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index 2938a8e7be..3f51ac1910 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -156,5 +156,74 @@ namespace osu.Game.Database public IDisposable BeginScope(TState state) => null; } } + + public void Migrate() + { + migrateFromSqliteNet(); + Database.Migrate(); + } + + private void migrateFromSqliteNet() + { + try + { + // will fail if EF hasn't touched the database yet. + Database.ExecuteSqlCommand("SELECT * FROM __EFMigrationsHistory LIMIT 1"); + } + catch + { + try + { + // will fail (intentionally) if we don't have sqlite-net data present. + Database.ExecuteSqlCommand("SELECT OnlineBeatmapSetId FROM BeatmapMetadata LIMIT 1"); + + // we are good to perform messy migration of data!. + Database.ExecuteSqlCommand("ALTER TABLE BeatmapDifficulty RENAME TO BeatmapDifficulty_Old"); + Database.ExecuteSqlCommand("ALTER TABLE BeatmapMetadata RENAME TO BeatmapMetadata_Old"); + Database.ExecuteSqlCommand("ALTER TABLE FileInfo RENAME TO FileInfo_Old"); + Database.ExecuteSqlCommand("ALTER TABLE KeyBinding RENAME TO KeyBinding_Old"); + Database.ExecuteSqlCommand("ALTER TABLE BeatmapSetInfo RENAME TO BeatmapSetInfo_Old"); + Database.ExecuteSqlCommand("ALTER TABLE BeatmapInfo RENAME TO BeatmapInfo_Old"); + Database.ExecuteSqlCommand("ALTER TABLE BeatmapSetFileInfo RENAME TO BeatmapSetFileInfo_Old"); + Database.ExecuteSqlCommand("ALTER TABLE RulesetInfo RENAME TO RulesetInfo_Old"); + + Database.ExecuteSqlCommand("DROP TABLE StoreVersion"); + + // perform EF migrations to create sane table structure. + Database.Migrate(); + + // copy data table by table to new structure, dropping old tables as we go. + Database.ExecuteSqlCommand("INSERT INTO FileInfo SELECT * FROM FileInfo_Old"); + Database.ExecuteSqlCommand("DROP TABLE FileInfo_Old"); + + Database.ExecuteSqlCommand("INSERT INTO KeyBinding SELECT ID, [Action], Keys, RulesetID, Variant FROM KeyBinding_Old"); + Database.ExecuteSqlCommand("DROP TABLE KeyBinding_Old"); + + Database.ExecuteSqlCommand( + "INSERT INTO BeatmapMetadata SELECT ID, Artist, ArtistUnicode, AudioFile, Author, BackgroundFile, PreviewTime, Source, Tags, Title, TitleUnicode FROM BeatmapMetadata_Old"); + Database.ExecuteSqlCommand("DROP TABLE BeatmapMetadata_Old"); + + Database.ExecuteSqlCommand( + "INSERT INTO BeatmapDifficulty SELECT `ID`, `ApproachRate`, `CircleSize`, `DrainRate`, `OverallDifficulty`, `SliderMultiplier`, `SliderTickRate` FROM BeatmapDifficulty_Old"); + Database.ExecuteSqlCommand("DROP TABLE BeatmapDifficulty_Old"); + + Database.ExecuteSqlCommand("INSERT INTO BeatmapSetInfo SELECT ID, DeletePending, Hash, BeatmapMetadataID, OnlineBeatmapSetID, Protected FROM BeatmapSetInfo_Old"); + Database.ExecuteSqlCommand("DROP TABLE BeatmapSetInfo_Old"); + + Database.ExecuteSqlCommand("INSERT INTO BeatmapSetFileInfo SELECT ID, BeatmapSetInfoID, FileInfoID, Filename FROM BeatmapSetFileInfo_Old"); + Database.ExecuteSqlCommand("DROP TABLE BeatmapSetFileInfo_Old"); + + Database.ExecuteSqlCommand("INSERT INTO RulesetInfo SELECT ID, Available, InstantiationInfo, Name FROM RulesetInfo_Old"); + Database.ExecuteSqlCommand("DROP TABLE RulesetInfo_Old"); + + Database.ExecuteSqlCommand( + "INSERT INTO BeatmapInfo SELECT ID, AudioLeadIn, BaseDifficultyID, BeatDivisor, BeatmapSetInfoID, Countdown, DistanceSpacing, GridSize, Hash, Hidden, LetterboxInBreaks, MD5Hash, NULLIF(BeatmapMetadataID, 0), OnlineBeatmapID, Path, RulesetID, SpecialStyle, StackLeniency, StarDifficulty, StoredBookmarks, TimelineZoom, Version, WidescreenStoryboard FROM BeatmapInfo_Old"); + Database.ExecuteSqlCommand("DROP TABLE BeatmapInfo_Old"); + } + catch + { + } + } + } } } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 22eb75fcea..be12f3ddbc 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics; using System.Reflection; -using Microsoft.EntityFrameworkCore; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Development; @@ -92,7 +91,7 @@ namespace osu.Game dependencies.Cache(LocalConfig); using (var context = contextFactory.GetContext()) - context.Database.Migrate(); + context.Migrate(); dependencies.Cache(API = new APIAccess { From 365e2343a1af18031795136e20041692bcfa06cc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Oct 2017 21:09:29 +0900 Subject: [PATCH 88/90] Remove AllRuleset references --- osu.Desktop/osu.Desktop.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 7b2ec3b24c..fb1ca7eb10 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -57,7 +57,6 @@ false AnyCPU true - AllRules.ruleset false false false @@ -76,7 +75,6 @@ false AnyCPU true - AllRules.ruleset false false @@ -102,7 +100,6 @@ false 6 prompt - AllRules.ruleset --tests From 1672e0d6b660c7379d4da163f3b92c3e7a70933b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Oct 2017 21:11:43 +0900 Subject: [PATCH 89/90] Add fallback logic in case migration fails Nuke it all. --- osu.Game/Database/OsuDbContext.cs | 74 ++++++++++++++++++------------- 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index 3f51ac1910..0ebea94683 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -177,48 +177,58 @@ namespace osu.Game.Database // will fail (intentionally) if we don't have sqlite-net data present. Database.ExecuteSqlCommand("SELECT OnlineBeatmapSetId FROM BeatmapMetadata LIMIT 1"); - // we are good to perform messy migration of data!. - Database.ExecuteSqlCommand("ALTER TABLE BeatmapDifficulty RENAME TO BeatmapDifficulty_Old"); - Database.ExecuteSqlCommand("ALTER TABLE BeatmapMetadata RENAME TO BeatmapMetadata_Old"); - Database.ExecuteSqlCommand("ALTER TABLE FileInfo RENAME TO FileInfo_Old"); - Database.ExecuteSqlCommand("ALTER TABLE KeyBinding RENAME TO KeyBinding_Old"); - Database.ExecuteSqlCommand("ALTER TABLE BeatmapSetInfo RENAME TO BeatmapSetInfo_Old"); - Database.ExecuteSqlCommand("ALTER TABLE BeatmapInfo RENAME TO BeatmapInfo_Old"); - Database.ExecuteSqlCommand("ALTER TABLE BeatmapSetFileInfo RENAME TO BeatmapSetFileInfo_Old"); - Database.ExecuteSqlCommand("ALTER TABLE RulesetInfo RENAME TO RulesetInfo_Old"); + try + { + // we are good to perform messy migration of data!. + Database.ExecuteSqlCommand("ALTER TABLE BeatmapDifficulty RENAME TO BeatmapDifficulty_Old"); + Database.ExecuteSqlCommand("ALTER TABLE BeatmapMetadata RENAME TO BeatmapMetadata_Old"); + Database.ExecuteSqlCommand("ALTER TABLE FileInfo RENAME TO FileInfo_Old"); + Database.ExecuteSqlCommand("ALTER TABLE KeyBinding RENAME TO KeyBinding_Old"); + Database.ExecuteSqlCommand("ALTER TABLE BeatmapSetInfo RENAME TO BeatmapSetInfo_Old"); + Database.ExecuteSqlCommand("ALTER TABLE BeatmapInfo RENAME TO BeatmapInfo_Old"); + Database.ExecuteSqlCommand("ALTER TABLE BeatmapSetFileInfo RENAME TO BeatmapSetFileInfo_Old"); + Database.ExecuteSqlCommand("ALTER TABLE RulesetInfo RENAME TO RulesetInfo_Old"); - Database.ExecuteSqlCommand("DROP TABLE StoreVersion"); + Database.ExecuteSqlCommand("DROP TABLE StoreVersion"); - // perform EF migrations to create sane table structure. - Database.Migrate(); + // perform EF migrations to create sane table structure. + Database.Migrate(); - // copy data table by table to new structure, dropping old tables as we go. - Database.ExecuteSqlCommand("INSERT INTO FileInfo SELECT * FROM FileInfo_Old"); - Database.ExecuteSqlCommand("DROP TABLE FileInfo_Old"); + // copy data table by table to new structure, dropping old tables as we go. + Database.ExecuteSqlCommand("INSERT INTO FileInfo SELECT * FROM FileInfo_Old"); + Database.ExecuteSqlCommand("DROP TABLE FileInfo_Old"); - Database.ExecuteSqlCommand("INSERT INTO KeyBinding SELECT ID, [Action], Keys, RulesetID, Variant FROM KeyBinding_Old"); - Database.ExecuteSqlCommand("DROP TABLE KeyBinding_Old"); + Database.ExecuteSqlCommand("INSERT INTO KeyBinding SELECT ID, [Action], Keys, RulesetID, Variant FROM KeyBinding_Old"); + Database.ExecuteSqlCommand("DROP TABLE KeyBinding_Old"); - Database.ExecuteSqlCommand( - "INSERT INTO BeatmapMetadata SELECT ID, Artist, ArtistUnicode, AudioFile, Author, BackgroundFile, PreviewTime, Source, Tags, Title, TitleUnicode FROM BeatmapMetadata_Old"); - Database.ExecuteSqlCommand("DROP TABLE BeatmapMetadata_Old"); + Database.ExecuteSqlCommand( + "INSERT INTO BeatmapMetadata SELECT ID, Artist, ArtistUnicode, AudioFile, Author, BackgroundFile, PreviewTime, Source, Tags, Title, TitleUnicode FROM BeatmapMetadata_Old"); + Database.ExecuteSqlCommand("DROP TABLE BeatmapMetadata_Old"); - Database.ExecuteSqlCommand( - "INSERT INTO BeatmapDifficulty SELECT `ID`, `ApproachRate`, `CircleSize`, `DrainRate`, `OverallDifficulty`, `SliderMultiplier`, `SliderTickRate` FROM BeatmapDifficulty_Old"); - Database.ExecuteSqlCommand("DROP TABLE BeatmapDifficulty_Old"); + Database.ExecuteSqlCommand( + "INSERT INTO BeatmapDifficulty SELECT `ID`, `ApproachRate`, `CircleSize`, `DrainRate`, `OverallDifficulty`, `SliderMultiplier`, `SliderTickRate` FROM BeatmapDifficulty_Old"); + Database.ExecuteSqlCommand("DROP TABLE BeatmapDifficulty_Old"); - Database.ExecuteSqlCommand("INSERT INTO BeatmapSetInfo SELECT ID, DeletePending, Hash, BeatmapMetadataID, OnlineBeatmapSetID, Protected FROM BeatmapSetInfo_Old"); - Database.ExecuteSqlCommand("DROP TABLE BeatmapSetInfo_Old"); + Database.ExecuteSqlCommand("INSERT INTO BeatmapSetInfo SELECT ID, DeletePending, Hash, BeatmapMetadataID, OnlineBeatmapSetID, Protected FROM BeatmapSetInfo_Old"); + Database.ExecuteSqlCommand("DROP TABLE BeatmapSetInfo_Old"); - Database.ExecuteSqlCommand("INSERT INTO BeatmapSetFileInfo SELECT ID, BeatmapSetInfoID, FileInfoID, Filename FROM BeatmapSetFileInfo_Old"); - Database.ExecuteSqlCommand("DROP TABLE BeatmapSetFileInfo_Old"); + Database.ExecuteSqlCommand("INSERT INTO BeatmapSetFileInfo SELECT ID, BeatmapSetInfoID, FileInfoID, Filename FROM BeatmapSetFileInfo_Old"); + Database.ExecuteSqlCommand("DROP TABLE BeatmapSetFileInfo_Old"); - Database.ExecuteSqlCommand("INSERT INTO RulesetInfo SELECT ID, Available, InstantiationInfo, Name FROM RulesetInfo_Old"); - Database.ExecuteSqlCommand("DROP TABLE RulesetInfo_Old"); + Database.ExecuteSqlCommand("INSERT INTO RulesetInfo SELECT ID, Available, InstantiationInfo, Name FROM RulesetInfo_Old"); + Database.ExecuteSqlCommand("DROP TABLE RulesetInfo_Old"); - Database.ExecuteSqlCommand( - "INSERT INTO BeatmapInfo SELECT ID, AudioLeadIn, BaseDifficultyID, BeatDivisor, BeatmapSetInfoID, Countdown, DistanceSpacing, GridSize, Hash, Hidden, LetterboxInBreaks, MD5Hash, NULLIF(BeatmapMetadataID, 0), OnlineBeatmapID, Path, RulesetID, SpecialStyle, StackLeniency, StarDifficulty, StoredBookmarks, TimelineZoom, Version, WidescreenStoryboard FROM BeatmapInfo_Old"); - Database.ExecuteSqlCommand("DROP TABLE BeatmapInfo_Old"); + Database.ExecuteSqlCommand( + "INSERT INTO BeatmapInfo SELECT ID, AudioLeadIn, BaseDifficultyID, BeatDivisor, BeatmapSetInfoID, Countdown, DistanceSpacing, GridSize, Hash, Hidden, LetterboxInBreaks, MD5Hash, NULLIF(BeatmapMetadataID, 0), OnlineBeatmapID, Path, RulesetID, SpecialStyle, StackLeniency, StarDifficulty, StoredBookmarks, TimelineZoom, Version, WidescreenStoryboard FROM BeatmapInfo_Old"); + Database.ExecuteSqlCommand("DROP TABLE BeatmapInfo_Old"); + + throw new Exception(); + } + catch + { + // if anything went wrong during migration just nuke the database. + Database.EnsureDeleted(); + } } catch { From 8aea6068ba4d2a3f0f923070b983ee7ebf0c9941 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Oct 2017 21:37:09 +0900 Subject: [PATCH 90/90] Add fallback logic for the case where previous database can't be migrated --- osu.Game/Database/OsuDbContext.cs | 12 +++++++++--- osu.Game/OsuGameBase.cs | 19 +++++++++++++------ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index 0ebea94683..93d23cc1bc 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -221,19 +221,25 @@ namespace osu.Game.Database Database.ExecuteSqlCommand( "INSERT INTO BeatmapInfo SELECT ID, AudioLeadIn, BaseDifficultyID, BeatDivisor, BeatmapSetInfoID, Countdown, DistanceSpacing, GridSize, Hash, Hidden, LetterboxInBreaks, MD5Hash, NULLIF(BeatmapMetadataID, 0), OnlineBeatmapID, Path, RulesetID, SpecialStyle, StackLeniency, StarDifficulty, StoredBookmarks, TimelineZoom, Version, WidescreenStoryboard FROM BeatmapInfo_Old"); Database.ExecuteSqlCommand("DROP TABLE BeatmapInfo_Old"); - - throw new Exception(); } catch { // if anything went wrong during migration just nuke the database. - Database.EnsureDeleted(); + throw new MigrationFailedException(); } } + catch (MigrationFailedException e) + { + throw; + } catch { } } } } + + public class MigrationFailedException : Exception + { + } } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index be12f3ddbc..e7ad77d099 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -90,8 +90,18 @@ namespace osu.Game dependencies.Cache(this); dependencies.Cache(LocalConfig); - using (var context = contextFactory.GetContext()) - context.Migrate(); + try + { + using (var context = contextFactory.GetContext()) + context.Migrate(); + } + catch (MigrationFailedException) + { + using (var context = contextFactory.GetContext()) + context.Database.EnsureDeleted(); + using (var context = contextFactory.GetContext()) + context.Migrate(); + } dependencies.Cache(API = new APIAccess { @@ -203,10 +213,7 @@ namespace osu.Game // TODO: This is temporary until we reimplement the local FPS display. // It's just to allow end-users to access the framework FPS display without knowing the shortcut key. fpsDisplayVisible = LocalConfig.GetBindable(OsuSetting.ShowFpsDisplay); - fpsDisplayVisible.ValueChanged += val => - { - FrameStatisticsMode = val ? FrameStatisticsMode.Minimal : FrameStatisticsMode.None; - }; + fpsDisplayVisible.ValueChanged += val => { FrameStatisticsMode = val ? FrameStatisticsMode.Minimal : FrameStatisticsMode.None; }; fpsDisplayVisible.TriggerChange(); }