From 3a6f3cdd8afbe7de2195dcbac2041ed443b8d849 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 Jul 2017 20:38:35 +0900 Subject: [PATCH 1/7] Add the ability to create migrations on a per-store level Now stores store versions to the database itself. --- osu.Game/Beatmaps/BeatmapStore.cs | 33 ++++++++++++++++++++++++ osu.Game/Database/DatabaseBackedStore.cs | 24 +++++++++++++++++ osu.Game/Database/StoreVersion.cs | 15 +++++++++++ osu.Game/OsuGameBase.cs | 3 +++ osu.Game/osu.Game.csproj | 1 + 5 files changed, 76 insertions(+) create mode 100644 osu.Game/Database/StoreVersion.cs diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 4a7336535e..f9019537a0 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -17,6 +17,12 @@ namespace osu.Game.Beatmaps public event Action BeatmapSetAdded; public event Action BeatmapSetRemoved; + /// + /// The current version of this store. Used for migrations (see ). + /// The initial version is 1. + /// + protected override int StoreVersion => 1; + public BeatmapStore(SQLiteConnection connection) : base(connection) { @@ -50,6 +56,33 @@ 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 newVersion) + { + base.PerformMigration(currentVersion, newVersion); + + while (currentVersion++ < newVersion) + { + switch (currentVersion) + { + case 1: + // initialising from a version before we had versioning (or a fresh install). + + // force adding of Protected column (not automatically migrated). + Connection.MigrateTable(); + + // remove all existing beatmaps. + foreach (var b in Connection.GetAllWithChildren(null, true)) + Connection.Delete(b, true); + break; + } + } + } + /// /// Add a to the database. /// diff --git a/osu.Game/Database/DatabaseBackedStore.cs b/osu.Game/Database/DatabaseBackedStore.cs index 8366775483..bb61fc1870 100644 --- a/osu.Game/Database/DatabaseBackedStore.cs +++ b/osu.Game/Database/DatabaseBackedStore.cs @@ -17,6 +17,8 @@ namespace osu.Game.Database protected readonly Storage Storage; protected readonly SQLiteConnection Connection; + protected virtual int StoreVersion => 1; + protected DatabaseBackedStore(SQLiteConnection connection, Storage storage = null) { Storage = storage; @@ -31,6 +33,28 @@ 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().FirstOrDefault(s => s.StoreName == storeName) ?? new StoreVersion + { + StoreName = storeName, + Version = 0 + }; + + if (reportedVersion.Version != StoreVersion) + PerformMigration(reportedVersion.Version, reportedVersion.Version = StoreVersion); + + Connection.InsertOrReplace(reportedVersion); + } + + protected virtual void PerformMigration(int currentVersion, int newVersion) + { } /// diff --git a/osu.Game/Database/StoreVersion.cs b/osu.Game/Database/StoreVersion.cs new file mode 100644 index 0000000000..00314875a6 --- /dev/null +++ b/osu.Game/Database/StoreVersion.cs @@ -0,0 +1,15 @@ +// 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/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index b507aa2315..0dec4228de 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -18,6 +18,7 @@ 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.IO; using osu.Game.Rulesets; using osu.Game.Rulesets.Scoring; @@ -97,6 +98,8 @@ namespace osu.Game SQLiteConnection connection = Host.Storage.GetDatabase(@"client"); + connection.CreateTable(); + dependencies.Cache(RulesetStore = new RulesetStore(connection)); dependencies.Cache(FileStore = new FileStore(connection, Host.Storage)); dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, FileStore, connection, RulesetStore, Host)); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index f8509314be..5ac76ed00e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -80,6 +80,7 @@ + From cd22ff11e9c75cafa9f7f9d79dd0b1a16458b314 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 Jul 2017 20:50:26 +0900 Subject: [PATCH 2/7] Fix VisualTests --- osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs index 61e87a6621..51c78ff442 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs @@ -6,6 +6,7 @@ using osu.Desktop.VisualTests.Platform; using osu.Framework.Testing; using osu.Framework.MathUtils; using osu.Game.Beatmaps; +using osu.Game.Database; using osu.Game.Rulesets; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Filter; @@ -29,6 +30,7 @@ namespace osu.Desktop.VisualTests.Tests var storage = new TestStorage(@"TestCasePlaySongSelect"); var backingDatabase = storage.GetDatabase(@"client"); + backingDatabase.CreateTable(); rulesets = new RulesetStore(backingDatabase); manager = new BeatmapManager(storage, null, backingDatabase, rulesets); From 6a36fa78097be3794956f5a8218e52563aa5c7aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Jul 2017 12:50:04 +0900 Subject: [PATCH 3/7] Make OsuButtons visually disable when not enabled --- osu.Game/Graphics/UserInterface/OsuButton.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index ecbf51f8b9..3c454f2af2 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -68,6 +68,14 @@ namespace osu.Game.Graphics.UserInterface sampleClick = audio.Sample.Get(@"UI/generic-click"); sampleHover = audio.Sample.Get(@"UI/generic-hover"); + + Enabled.ValueChanged += enabled_ValueChanged; + Enabled.TriggerChange(); + } + + private void enabled_ValueChanged(bool enabled) + { + this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint); } protected override bool OnClick(InputState state) From f705589bf2b9664b87ed31b6992c9d832d6f025d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Jul 2017 14:51:49 +0900 Subject: [PATCH 4/7] Rename NotificationManager to NotificationOverlay --- ...cationManager.cs => TestCaseNotificationOverlay.cs} | 8 ++++---- osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj | 2 +- osu.Desktop/Overlays/VersionManager.cs | 8 ++++---- osu.Game/Online/API/APIAccess.cs | 4 ++-- osu.Game/OsuGame.cs | 10 +++++----- .../{NotificationManager.cs => NotificationOverlay.cs} | 4 ++-- osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs | 4 ++-- osu.Game/Screens/Play/HUDOverlay.cs | 4 ++-- osu.Game/osu.Game.csproj | 2 +- 9 files changed, 23 insertions(+), 23 deletions(-) rename osu.Desktop.VisualTests/Tests/{TestCaseNotificationManager.cs => TestCaseNotificationOverlay.cs} (91%) rename osu.Game/Overlays/{NotificationManager.cs => NotificationOverlay.cs} (95%) diff --git a/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs b/osu.Desktop.VisualTests/Tests/TestCaseNotificationOverlay.cs similarity index 91% rename from osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs rename to osu.Desktop.VisualTests/Tests/TestCaseNotificationOverlay.cs index 849df1263e..3b9c251670 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseNotificationOverlay.cs @@ -12,17 +12,17 @@ using osu.Framework.Graphics.Containers; namespace osu.Desktop.VisualTests.Tests { - internal class TestCaseNotificationManager : TestCase + internal class TestCaseNotificationOverlay : TestCase { public override string Description => @"I handle notifications"; - private readonly NotificationManager manager; + private readonly NotificationOverlay manager; - public TestCaseNotificationManager() + public TestCaseNotificationOverlay() { progressingNotifications.Clear(); - Content.Add(manager = new NotificationManager + Content.Add(manager = new NotificationOverlay { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj index 4974f0c0d1..1f4fd80ca6 100644 --- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj +++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj @@ -198,7 +198,7 @@ - + diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 71ae5a6697..6983c51c7d 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -25,16 +25,16 @@ namespace osu.Desktop.Overlays public class VersionManager : OverlayContainer { private UpdateManager updateManager; - private NotificationManager notificationManager; + private NotificationOverlay notificationOverlay; protected override bool HideOnEscape => false; public override bool HandleInput => false; [BackgroundDependencyLoader] - private void load(NotificationManager notification, OsuColour colours, TextureStore textures, OsuGameBase game) + private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game) { - notificationManager = notification; + notificationOverlay = notification; AutoSizeAxes = Axes.Both; Anchor = Anchor.BottomCentre; @@ -116,7 +116,7 @@ namespace osu.Desktop.Overlays if (notification == null) { notification = new UpdateProgressNotification { State = ProgressNotificationState.Active }; - Schedule(() => notificationManager.Post(notification)); + Schedule(() => notificationOverlay.Post(notification)); } Schedule(() => diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 7e3bb44465..57f5c54a18 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -117,7 +117,7 @@ namespace osu.Game.Online.API if (!authentication.HasValidAccessToken && !authentication.AuthenticateWithLogin(Username, Password)) { //todo: this fails even on network-related issues. we should probably handle those differently. - //NotificationManager.ShowMessage("Login failed!"); + //NotificationOverlay.ShowMessage("Login failed!"); log.Add(@"Login failed!"); Password = null; continue; @@ -254,7 +254,7 @@ namespace osu.Game.Online.API { //OsuGame.Scheduler.Add(delegate { - //NotificationManager.ShowMessage($@"We just went {newState}!", newState == APIState.Online ? Color4.YellowGreen : Color4.OrangeRed, 5000); + //NotificationOverlay.ShowMessage($@"We just went {newState}!", newState == APIState.Online ? Color4.YellowGreen : Color4.OrangeRed, 5000); log.Add($@"We just went {newState}!"); Scheduler.Add(delegate { diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 4f4c2e2883..fe6d2dbb41 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -37,7 +37,7 @@ namespace osu.Game private MusicController musicController; - private NotificationManager notificationManager; + private NotificationOverlay notificationOverlay; private DialogOverlay dialogOverlay; @@ -132,7 +132,7 @@ namespace osu.Game if (s.Beatmap == null) { - notificationManager.Post(new SimpleNotification + notificationOverlay.Post(new SimpleNotification { Text = @"Tried to load a score for a beatmap we don't have!", Icon = FontAwesome.fa_life_saver, @@ -189,7 +189,7 @@ namespace osu.Game Origin = Anchor.TopRight, }, overlayContent.Add); - LoadComponentAsync(notificationManager = new NotificationManager + LoadComponentAsync(notificationOverlay = new NotificationOverlay { Depth = -3, Anchor = Anchor.TopRight, @@ -205,7 +205,7 @@ namespace osu.Game { if (entry.Level < LogLevel.Important) return; - notificationManager.Post(new SimpleNotification + notificationOverlay.Post(new SimpleNotification { Text = $@"{entry.Level}: {entry.Message}" }); @@ -216,7 +216,7 @@ namespace osu.Game dependencies.Cache(chat); dependencies.Cache(userProfile); dependencies.Cache(musicController); - dependencies.Cache(notificationManager); + dependencies.Cache(notificationOverlay); dependencies.Cache(dialogOverlay); // ensure both overlays aren't presented at the same time diff --git a/osu.Game/Overlays/NotificationManager.cs b/osu.Game/Overlays/NotificationOverlay.cs similarity index 95% rename from osu.Game/Overlays/NotificationManager.cs rename to osu.Game/Overlays/NotificationOverlay.cs index ad0236ae1f..4c381fae0e 100644 --- a/osu.Game/Overlays/NotificationManager.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -13,7 +13,7 @@ using osu.Game.Graphics.Containers; namespace osu.Game.Overlays { - public class NotificationManager : OsuFocusedOverlayContainer + public class NotificationOverlay : OsuFocusedOverlayContainer { private const float width = 320; @@ -109,4 +109,4 @@ namespace osu.Game.Overlays this.FadeTo(0, TRANSITION_LENGTH / 2); } } -} \ No newline at end of file +} diff --git a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs index 5126f6a2a4..dcadc4bf56 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs @@ -19,9 +19,9 @@ namespace osu.Game.Overlays.Toolbar } [BackgroundDependencyLoader] - private void load(NotificationManager notificationManager) + private void load(NotificationOverlay notificationOverlay) { - StateContainer = notificationManager; + StateContainer = notificationOverlay; } } } \ No newline at end of file diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 70093a1407..ea75c140db 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -61,7 +61,7 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader(true)] - private void load(OsuConfigManager config, NotificationManager notificationManager, OsuColour colours) + private void load(OsuConfigManager config, NotificationOverlay notificationOverlay, OsuColour colours) { showHud = config.GetBindable(OsuSetting.ShowInterface); showHud.ValueChanged += hudVisibility => content.FadeTo(hudVisibility ? 1 : 0, duration); @@ -71,7 +71,7 @@ namespace osu.Game.Screens.Play { hasShownNotificationOnce = true; - notificationManager?.Post(new SimpleNotification + notificationOverlay?.Post(new SimpleNotification { Text = @"The score overlay is currently disabled. You can toggle this by pressing Shift+Tab." }); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index f8509314be..a81c5e2fc4 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -249,7 +249,7 @@ - + From 02a04afb294545bd44552fa1883fb9f4f7a7fe81 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Jul 2017 15:03:08 +0900 Subject: [PATCH 5/7] Ensure notification posts are always scheduled to local thread Posts can be triggered by Logger.Log events which are not guaranteed to be on the update thread. --- osu.Game/Overlays/NotificationOverlay.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 4c381fae0e..6f5d4bc6fc 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -72,17 +72,20 @@ namespace osu.Game.Overlays public void Post(Notification notification) { - State = Visibility.Visible; + Schedule(() => + { + State = Visibility.Visible; - ++runningDepth; - notification.Depth = notification.DisplayOnTop ? runningDepth : -runningDepth; + ++runningDepth; + notification.Depth = notification.DisplayOnTop ? runningDepth : -runningDepth; - var hasCompletionTarget = notification as IHasCompletionTarget; - if (hasCompletionTarget != null) - hasCompletionTarget.CompletionTarget = Post; + var hasCompletionTarget = notification as IHasCompletionTarget; + if (hasCompletionTarget != null) + hasCompletionTarget.CompletionTarget = Post; - var ourType = notification.GetType(); - sections.Children.FirstOrDefault(s => s.AcceptTypes.Any(accept => accept.IsAssignableFrom(ourType)))?.Add(notification); + var ourType = notification.GetType(); + sections.Children.FirstOrDefault(s => s.AcceptTypes.Any(accept => accept.IsAssignableFrom(ourType)))?.Add(notification); + }); } protected override void PopIn() From 0d332d4754d867ade6b7f8aa1c5d636bd403c09a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Jul 2017 16:54:29 +0900 Subject: [PATCH 6/7] Fix NotificationOverlay no longer automatically displaying --- osu.Game/Overlays/NotificationOverlay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 6f5d4bc6fc..7eabb592c6 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -28,6 +28,8 @@ namespace osu.Game.Overlays Width = width; RelativeSizeAxes = Axes.Y; + AlwaysPresent = true; + Children = new Drawable[] { new Box From 6807caa752b1410f3ceda67e0ea5f685363ffab3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Jul 2017 17:31:13 +0900 Subject: [PATCH 7/7] Use a transaction when adding a beatmap Fixes flakey unit test. --- osu.Game/Beatmaps/BeatmapStore.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 4a7336535e..2c3ffe25b4 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -56,7 +56,11 @@ namespace osu.Game.Beatmaps /// The beatmap to add. public void Add(BeatmapSetInfo beatmapSet) { - Connection.InsertOrReplaceWithChildren(beatmapSet, true); + Connection.RunInTransaction(() => + { + Connection.InsertOrReplaceWithChildren(beatmapSet, true); + }); + BeatmapSetAdded?.Invoke(beatmapSet); }