From e98f9f1323342f3643da07d2fbb3347b0a3b1b25 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Sat, 2 Jun 2018 21:02:45 +0300 Subject: [PATCH 01/60] Add dropdown for selecting fullscreen resolution --- .../Sections/Graphics/LayoutSettings.cs | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 1f87a635de..b3a2243ca3 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -1,10 +1,14 @@ // Copyright (c) 2007-2018 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.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Platform; namespace osu.Game.Overlays.Settings.Sections.Graphics { @@ -16,20 +20,32 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private Bindable letterboxing; + private OsuGame game; + private SettingsDropdown resolutionDropdown; + private SettingsEnumDropdown windowModeDropdown; + private const int transition_duration = 400; [BackgroundDependencyLoader] - private void load(FrameworkConfigManager config) + private void load(FrameworkConfigManager config, OsuGame game) { + this.game = game; + letterboxing = config.GetBindable(FrameworkSetting.Letterboxing); Children = new Drawable[] { - new SettingsEnumDropdown + windowModeDropdown = new SettingsEnumDropdown { LabelText = "Screen mode", Bindable = config.GetBindable(FrameworkSetting.WindowMode), }, + resolutionDropdown = new SettingsDropdown + { + LabelText = "Resolution", + Items = getResolutions(), + Bindable = config.GetBindable(FrameworkSetting.FullscreenResolution) + }, new SettingsCheckbox { LabelText = "Letterboxing", @@ -62,6 +78,15 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics }, }; + windowModeDropdown.Bindable.ValueChanged += (s) => + { + if (windowModeDropdown.Bindable.Value == WindowMode.Fullscreen) + resolutionDropdown.Show(); + else + resolutionDropdown.Hide(); + }; + windowModeDropdown.Bindable.TriggerChange(); + letterboxing.ValueChanged += isVisible => { letterboxSettings.ClearTransforms(); @@ -72,5 +97,12 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics }; letterboxing.TriggerChange(); } + + private IEnumerable> getResolutions() + { + var availableDisplayResolutions = (game.Window as DesktopGameWindow)?.AvailableDisplayResolutions; + + return (availableDisplayResolutions ?? throw new InvalidOperationException()).Select((t, i) => new KeyValuePair($"{t.Width}x{t.Height}@{t.RefreshRate}Hz", i)); + } } } From de7e4328c5c316df687ab91c5901d99961fe2d69 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Sun, 10 Jun 2018 16:17:57 +0300 Subject: [PATCH 02/60] Use bindable size --- .../Sections/Graphics/LayoutSettings.cs | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index b3a2243ca3..2d970e41de 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -1,8 +1,8 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using System.Collections.Generic; +using System.Drawing; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; @@ -19,11 +19,13 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private FillFlowContainer letterboxSettings; private Bindable letterboxing; + private Bindable sizeFullscreen; private OsuGame game; private SettingsDropdown resolutionDropdown; private SettingsEnumDropdown windowModeDropdown; + private const int transition_duration = 400; [BackgroundDependencyLoader] @@ -32,6 +34,17 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics this.game = game; letterboxing = config.GetBindable(FrameworkSetting.Letterboxing); + sizeFullscreen = config.GetBindable(FrameworkSetting.SizeFullscreen); + + var resolutions = getResolutions(); + var resolutionDropdownBindable = new BindableInt(resolutions.FirstOrDefault(r => r.Key.StartsWith($"{sizeFullscreen.Value.Width}x{sizeFullscreen.Value.Height}")).Value); + + resolutionDropdownBindable.ValueChanged += _ => + { + var newResolution = resolutions.First(r => r.Value == _); + var newResolutionparts = newResolution.Key.Split('x'); + sizeFullscreen.Value = new Size(int.Parse(newResolutionparts.First()), int.Parse(newResolutionparts.Last())); + }; Children = new Drawable[] { @@ -43,8 +56,8 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics resolutionDropdown = new SettingsDropdown { LabelText = "Resolution", - Items = getResolutions(), - Bindable = config.GetBindable(FrameworkSetting.FullscreenResolution) + Items = resolutions, + Bindable = resolutionDropdownBindable }, new SettingsCheckbox { @@ -98,11 +111,14 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics letterboxing.TriggerChange(); } - private IEnumerable> getResolutions() + private List> getResolutions() { var availableDisplayResolutions = (game.Window as DesktopGameWindow)?.AvailableDisplayResolutions; + if (availableDisplayResolutions == null) + return new List>(); + var availableDisplayResolutionsStr = availableDisplayResolutions.Select(r => $"{r.Width}x{r.Height}").Distinct().ToList(); - return (availableDisplayResolutions ?? throw new InvalidOperationException()).Select((t, i) => new KeyValuePair($"{t.Width}x{t.Height}@{t.RefreshRate}Hz", i)); + return availableDisplayResolutionsStr.Select((t, i) => new KeyValuePair(t, i)).ToList(); } } } From 71371dc4b86090e493c73ee6ea5b325424a34e91 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Tue, 12 Jun 2018 19:27:55 +0300 Subject: [PATCH 03/60] ValueChanged parameters quickfix --- .../Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 2d970e41de..588be967e5 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -39,9 +39,9 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics var resolutions = getResolutions(); var resolutionDropdownBindable = new BindableInt(resolutions.FirstOrDefault(r => r.Key.StartsWith($"{sizeFullscreen.Value.Width}x{sizeFullscreen.Value.Height}")).Value); - resolutionDropdownBindable.ValueChanged += _ => + resolutionDropdownBindable.ValueChanged += resolution => { - var newResolution = resolutions.First(r => r.Value == _); + var newResolution = resolutions.First(r => r.Value == resolution); var newResolutionparts = newResolution.Key.Split('x'); sizeFullscreen.Value = new Size(int.Parse(newResolutionparts.First()), int.Parse(newResolutionparts.Last())); }; @@ -91,9 +91,9 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics }, }; - windowModeDropdown.Bindable.ValueChanged += (s) => + windowModeDropdown.Bindable.ValueChanged += windowMode => { - if (windowModeDropdown.Bindable.Value == WindowMode.Fullscreen) + if (windowMode == WindowMode.Fullscreen) resolutionDropdown.Show(); else resolutionDropdown.Hide(); From 5d26d5d4ed81d97dc0ce257d85665ce9b8495d62 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Thu, 21 Jun 2018 20:42:22 +0300 Subject: [PATCH 04/60] Fix resolutionDropdown --- .../Sections/Graphics/LayoutSettings.cs | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 588be967e5..36de8d4771 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -20,6 +20,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private Bindable letterboxing; private Bindable sizeFullscreen; + private readonly BindableInt resolutionDropdownBindable = new BindableInt(); private OsuGame game; private SettingsDropdown resolutionDropdown; @@ -36,14 +37,23 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics letterboxing = config.GetBindable(FrameworkSetting.Letterboxing); sizeFullscreen = config.GetBindable(FrameworkSetting.SizeFullscreen); - var resolutions = getResolutions(); - var resolutionDropdownBindable = new BindableInt(resolutions.FirstOrDefault(r => r.Key.StartsWith($"{sizeFullscreen.Value.Width}x{sizeFullscreen.Value.Height}")).Value); + sizeFullscreen.ValueChanged += size => + { + KeyValuePair valuePair = getResolutions().FirstOrDefault(r => r.Key.StartsWith($"{size.Width}x{size.Height}")); + + resolutionDropdownBindable.Value = valuePair.Value; + }; resolutionDropdownBindable.ValueChanged += resolution => { - var newResolution = resolutions.First(r => r.Value == resolution); - var newResolutionparts = newResolution.Key.Split('x'); - sizeFullscreen.Value = new Size(int.Parse(newResolutionparts.First()), int.Parse(newResolutionparts.Last())); + var newSelection = getResolutions().First(r => r.Value == resolution); + var newSelectionParts = newSelection.Key.Split('x'); + + var newSelectionWidth = int.Parse(newSelectionParts.First()); + var newSelectionHeight = int.Parse(newSelectionParts.Last()); + + if (sizeFullscreen.Value.Width != newSelectionWidth || sizeFullscreen.Value.Height != newSelectionHeight) + sizeFullscreen.Value = new Size(newSelectionWidth, newSelectionHeight); }; Children = new Drawable[] @@ -56,7 +66,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics resolutionDropdown = new SettingsDropdown { LabelText = "Resolution", - Items = resolutions, + Items = getResolutions(), Bindable = resolutionDropdownBindable }, new SettingsCheckbox @@ -94,7 +104,10 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics windowModeDropdown.Bindable.ValueChanged += windowMode => { if (windowMode == WindowMode.Fullscreen) + { resolutionDropdown.Show(); + sizeFullscreen.TriggerChange(); + } else resolutionDropdown.Hide(); }; @@ -113,7 +126,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private List> getResolutions() { - var availableDisplayResolutions = (game.Window as DesktopGameWindow)?.AvailableDisplayResolutions; + var availableDisplayResolutions = (game.Window as DesktopGameWindow)?.AvailableDisplayResolutions.OrderByDescending(r => r.Width).ThenByDescending(r => r.Height); if (availableDisplayResolutions == null) return new List>(); var availableDisplayResolutionsStr = availableDisplayResolutions.Select(r => $"{r.Width}x{r.Height}").Distinct().ToList(); From 5076fe4c2000747b34c826fd3d3925a1ce7d8f85 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Sat, 23 Jun 2018 12:45:13 +0300 Subject: [PATCH 05/60] LayoutSettings: inject OsuGameBase instead of OsuGame --- .../Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 36de8d4771..27ce8bad13 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private Bindable sizeFullscreen; private readonly BindableInt resolutionDropdownBindable = new BindableInt(); - private OsuGame game; + private OsuGameBase game; private SettingsDropdown resolutionDropdown; private SettingsEnumDropdown windowModeDropdown; @@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private const int transition_duration = 400; [BackgroundDependencyLoader] - private void load(FrameworkConfigManager config, OsuGame game) + private void load(FrameworkConfigManager config, OsuGameBase game) { this.game = game; From 4af45b751817f8a95624b6d7a93e98468c56a412 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Sat, 30 Jun 2018 20:06:11 +0300 Subject: [PATCH 06/60] Do not display resolutions lower than 800x600 --- .../Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 27ce8bad13..996b6a2a24 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -126,11 +126,14 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private List> getResolutions() { - var availableDisplayResolutions = (game.Window as DesktopGameWindow)?.AvailableDisplayResolutions.OrderByDescending(r => r.Width).ThenByDescending(r => r.Height); + var availableDisplayResolutions = (game.Window as DesktopGameWindow)?.AvailableDisplayResolutions + .Where(r => r.Width >= 800 && r.Height >= 600) + .OrderByDescending(r => r.Width).ThenByDescending(r => r.Height); + if (availableDisplayResolutions == null) return new List>(); - var availableDisplayResolutionsStr = availableDisplayResolutions.Select(r => $"{r.Width}x{r.Height}").Distinct().ToList(); + var availableDisplayResolutionsStr = availableDisplayResolutions.Select(r => $"{r.Width}x{r.Height}").Distinct(); return availableDisplayResolutionsStr.Select((t, i) => new KeyValuePair(t, i)).ToList(); } } From 699702f8d027eebba3e9a676539246f184deb690 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Mon, 9 Jul 2018 21:57:31 +0300 Subject: [PATCH 07/60] Use DesktopGameWindow.GetCurrentDisplay method --- osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 996b6a2a24..a67548d959 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -126,7 +126,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private List> getResolutions() { - var availableDisplayResolutions = (game.Window as DesktopGameWindow)?.AvailableDisplayResolutions + var availableDisplayResolutions = (game.Window as DesktopGameWindow)?.GetCurrentDisplay().AvailableResolutions .Where(r => r.Width >= 800 && r.Height >= 600) .OrderByDescending(r => r.Width).ThenByDescending(r => r.Height); From 8f9bf4bd3e4a4e4b5da5b3cc219d38036379d87a Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Tue, 10 Jul 2018 22:07:32 +0300 Subject: [PATCH 08/60] Remove redundant DesktopGameWindow cast --- .../Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index a67548d959..7fe628016c 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -8,7 +8,6 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Platform; namespace osu.Game.Overlays.Settings.Sections.Graphics { @@ -126,9 +125,9 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private List> getResolutions() { - var availableDisplayResolutions = (game.Window as DesktopGameWindow)?.GetCurrentDisplay().AvailableResolutions - .Where(r => r.Width >= 800 && r.Height >= 600) - .OrderByDescending(r => r.Width).ThenByDescending(r => r.Height); + var availableDisplayResolutions = game.Window?.GetCurrentDisplay().AvailableResolutions + .Where(r => r.Width >= 800 && r.Height >= 600) + .OrderByDescending(r => r.Width).ThenByDescending(r => r.Height); if (availableDisplayResolutions == null) return new List>(); From bf6fb1c38048f8a7f44cfc3bc525cab22b56591b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 1 Sep 2018 12:55:11 +0900 Subject: [PATCH 09/60] Don't use ConcurrentQueue for API This queue type can hold several references to already dequeued requests. In our usage, this can cause old api calls to hold references to already-disposed screens (and in turn, very large memory portions). --- osu.Game/Online/API/APIAccess.cs | 56 +++++++++++++++++--------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 12935a5ffe..ff5247e8f6 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Net; @@ -26,7 +25,7 @@ namespace osu.Game.Online.API private const string client_id = @"5"; private const string client_secret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk"; - private ConcurrentQueue queue = new ConcurrentQueue(); + private readonly Queue queue = new Queue(); /// /// The username/email provided by the user when initiating a login. @@ -75,10 +74,7 @@ namespace osu.Game.Online.API public void Unregister(IOnlineComponent component) { - Scheduler.Add(delegate - { - components.Remove(component); - }); + Scheduler.Add(delegate { components.Remove(component); }); } public string AccessToken => authentication.RequestAccessToken(); @@ -103,6 +99,7 @@ namespace osu.Game.Online.API log.Add(@"Queueing a ping request"); Queue(new ListChannelsRequest { Timeout = 5000 }); } + break; case APIState.Offline: case APIState.Connecting: @@ -161,18 +158,19 @@ namespace osu.Game.Online.API continue; } - //process the request queue. - APIRequest req; - while (queue.TryPeek(out req)) + APIRequest req = null; + + lock (queue) + if (queue.Count > 0) + req = queue.Dequeue(); + + if (req != null) { - if (handleRequest(req)) - { - //we have succeeded, so let's unqueue. - queue.TryDequeue(out req); - } + // TODO: handle failures better + handleRequest(req); } - Thread.Sleep(1); + Thread.Sleep(50); } } @@ -205,7 +203,8 @@ namespace osu.Game.Online.API } catch (WebException we) { - HttpStatusCode statusCode = (we.Response as HttpWebResponse)?.StatusCode ?? (we.Status == WebExceptionStatus.UnknownError ? HttpStatusCode.NotAcceptable : HttpStatusCode.RequestTimeout); + HttpStatusCode statusCode = (we.Response as HttpWebResponse)?.StatusCode + ?? (we.Status == WebExceptionStatus.UnknownError ? HttpStatusCode.NotAcceptable : HttpStatusCode.RequestTimeout); // special cases for un-typed but useful message responses. switch (we.Message) @@ -247,6 +246,7 @@ namespace osu.Game.Online.API } private APIState state; + public APIState State { get { return state; } @@ -271,7 +271,10 @@ namespace osu.Game.Online.API public bool IsLoggedIn => LocalUser.Value.Id > 1; - public void Queue(APIRequest request) => queue.Enqueue(request); + public void Queue(APIRequest request) + { + lock (queue) queue.Enqueue(request); + } public event StateChangeDelegate OnStateChange; @@ -279,16 +282,17 @@ namespace osu.Game.Online.API private void flushQueue(bool failOldRequests = true) { - var oldQueue = queue; - - //flush the queue. - queue = new ConcurrentQueue(); - - if (failOldRequests) + lock (queue) { - APIRequest req; - while (oldQueue.TryDequeue(out req)) - req.Fail(new WebException(@"Disconnected from server")); + var oldQueueRequests = queue.ToArray(); + + queue.Clear(); + + if (failOldRequests) + { + foreach (var req in oldQueueRequests) + req.Fail(new WebException(@"Disconnected from server")); + } } } From 562a792a9957e92ba82c78f0e3fff1b25428f20b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 1 Sep 2018 13:00:55 +0900 Subject: [PATCH 10/60] Use thread instead of LongRunning for API --- osu.Game/Online/API/APIAccess.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index ff5247e8f6..eb9a60115f 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Net; using System.Threading; -using System.Threading.Tasks; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Logging; @@ -54,7 +53,13 @@ namespace osu.Game.Online.API authentication.TokenString = config.Get(OsuSetting.Token); authentication.Token.ValueChanged += onTokenChanged; - Task.Factory.StartNew(run, cancellationToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); + var thread = new Thread(run) + { + Name = "APIAccess", + IsBackground = true + }; + + thread.Start(); } private void onTokenChanged(OAuthToken token) => config.Set(OsuSetting.Token, config.Get(OsuSetting.SavePassword) ? authentication.TokenString : string.Empty); From d9e9c61731d1deac746c831b94724ba52172dc7d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 1 Sep 2018 17:39:54 +0900 Subject: [PATCH 11/60] Begin loading fonts earlier in startup Should result in a considerably faster font load, as they can be concurrently loaded alongside EF. --- osu.Game/OsuGameBase.cs | 48 ++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index bada2a794d..6987b6eea7 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -114,31 +114,8 @@ namespace osu.Game dependencies.CacheAs(this); dependencies.Cache(LocalConfig); - runMigrations(); - - dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio)); - dependencies.CacheAs(SkinManager); - - var api = new APIAccess(LocalConfig); - - dependencies.Cache(api); - dependencies.CacheAs(api); - - dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); - dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage)); - dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Audio, Host)); - dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, contextFactory, Host, BeatmapManager, RulesetStore)); - dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore)); - dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); - dependencies.Cache(RulesetConfigCache = new RulesetConfigCache(SettingsStore)); - dependencies.Cache(new OsuColour()); - - fileImporters.Add(BeatmapManager); - fileImporters.Add(ScoreStore); - fileImporters.Add(SkinManager); - //this completely overrides the framework default. will need to change once we make a proper FontStore. - dependencies.Cache(Fonts = new FontStore { ScaleAdjust = 100 }); + dependencies.Cache(Fonts = new FontStore()); Fonts.AddStore(new GlyphStore(Resources, @"Fonts/FontAwesome")); Fonts.AddStore(new GlyphStore(Resources, @"Fonts/osuFont")); @@ -164,6 +141,29 @@ namespace osu.Game Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera")); Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera-Light")); + runMigrations(); + + dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio)); + dependencies.CacheAs(SkinManager); + + var api = new APIAccess(LocalConfig); + + dependencies.Cache(api); + dependencies.CacheAs(api); + + dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); + dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage)); + dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Audio, Host)); + dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, contextFactory, Host, BeatmapManager, RulesetStore)); + dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore)); + dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); + dependencies.Cache(RulesetConfigCache = new RulesetConfigCache(SettingsStore)); + dependencies.Cache(new OsuColour()); + + fileImporters.Add(BeatmapManager); + fileImporters.Add(ScoreStore); + fileImporters.Add(SkinManager); + var defaultBeatmap = new DummyWorkingBeatmap(this); beatmap = new OsuBindableBeatmap(defaultBeatmap, Audio); BeatmapManager.DefaultBeatmap = defaultBeatmap; From 144e80dff6806271052918a6ba3d677b4a206400 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Aug 2018 18:28:53 +0900 Subject: [PATCH 12/60] Add "import all skins from stable" option (and mass delete) --- osu.Game/Beatmaps/BeatmapManager.cs | 28 +------------- osu.Game/Database/ArchiveModelManager.cs | 38 ++++++++++++++++++- osu.Game/OsuGame.cs | 6 ++- .../Sections/Maintenance/GeneralSettings.cs | 26 ++++++++++++- osu.Game/Screens/Select/PlaySongSelect.cs | 4 +- osu.Game/Skinning/SkinManager.cs | 12 +++++- 6 files changed, 78 insertions(+), 36 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 1adbb4a389..46c48bcd0f 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -62,6 +62,8 @@ namespace osu.Game.Beatmaps public override string[] HandledExtensions => new[] { ".osz" }; + protected override string ImportFromStablePath => "Songs"; + private readonly RulesetStore rulesets; private readonly BeatmapStore beatmaps; @@ -72,11 +74,6 @@ namespace osu.Game.Beatmaps private readonly List currentDownloads = new List(); - /// - /// Set a storage with access to an osu-stable install for import purposes. - /// - public Func GetStableStorage { private get; set; } - public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, APIAccess api, AudioManager audioManager, IIpcHost importHost = null) : base(storage, contextFactory, new BeatmapStore(contextFactory), importHost) { @@ -311,27 +308,6 @@ namespace osu.Game.Beatmaps /// Results from the provided query. public IQueryable QueryBeatmaps(Expression> query) => beatmaps.Beatmaps.AsNoTracking().Where(query); - /// - /// Denotes whether an osu-stable installation is present to perform automated imports from. - /// - public bool StableInstallationAvailable => GetStableStorage?.Invoke() != null; - - /// - /// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future. - /// - public Task ImportFromStable() - { - var stable = GetStableStorage?.Invoke(); - - if (stable == null) - { - Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error); - return Task.CompletedTask; - } - - return Task.Factory.StartNew(() => Import(stable.GetDirectories("Songs").Select(f => stable.GetFullPath(f)).ToArray()), TaskCreationOptions.LongRunning); - } - /// /// Create a SHA-2 hash from the provided archive based on contained beatmap (.osu) file content. /// diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 326d042c39..a3ae19f97d 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; using osu.Framework.IO.File; @@ -283,7 +284,7 @@ namespace osu.Game.Database var notification = new ProgressNotification { Progress = 0, - CompletionText = "Deleted all beatmaps!", + CompletionText = $"Deleted all {typeof(TModel)}s!", State = ProgressNotificationState.Active, }; @@ -385,6 +386,41 @@ namespace osu.Game.Database return fileInfos; } + #region osu-stable import + + /// + /// Set a storage with access to an osu-stable install for import purposes. + /// + public Func GetStableStorage { private get; set; } + + /// + /// Denotes whether an osu-stable installation is present to perform automated imports from. + /// + public bool StableInstallationAvailable => GetStableStorage?.Invoke() != null; + + /// + /// The relative path from osu-stable's data directory to import items from. + /// + protected virtual string ImportFromStablePath => null; + + /// + /// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future. + /// + public Task ImportFromStableAsync() + { + var stable = GetStableStorage?.Invoke(); + + if (stable == null) + { + Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error); + return Task.CompletedTask; + } + + return Task.Factory.StartNew(() => Import(stable.GetDirectories(ImportFromStablePath).Select(f => stable.GetFullPath(f)).ToArray()), TaskCreationOptions.LongRunning); + } + + #endregion + /// /// Create a barebones model from the provided archive. /// Actual expensive population should be done in ; this should just prepare for duplicate checking. diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 7f0576608d..6fcb948298 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -299,11 +299,13 @@ namespace osu.Game // This prevents the cursor from showing until we have a screen with CursorVisible = true MenuCursorContainer.CanShowCursor = currentScreen?.CursorVisible ?? false; - // hook up notifications to components. + // todo: all archive managers should be able to be looped here. SkinManager.PostNotification = n => notifications?.Post(n); - BeatmapManager.PostNotification = n => notifications?.Post(n); + SkinManager.GetStableStorage = GetStorageForStableInstall; + BeatmapManager.PostNotification = n => notifications?.Post(n); BeatmapManager.GetStableStorage = GetStorageForStableInstall; + BeatmapManager.PresentBeatmap = PresentBeatmap; AddRange(new Drawable[] diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index 93d1986e3a..57e9a528d2 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; +using osu.Game.Skinning; namespace osu.Game.Overlays.Settings.Sections.Maintenance { @@ -20,7 +21,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance protected override string Header => "General"; [BackgroundDependencyLoader] - private void load(BeatmapManager beatmaps, DialogOverlay dialogOverlay) + private void load(BeatmapManager beatmaps, SkinManager skins, DialogOverlay dialogOverlay) { Children = new Drawable[] { @@ -30,7 +31,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Action = () => { importButton.Enabled.Value = false; - beatmaps.ImportFromStable().ContinueWith(t => Schedule(() => importButton.Enabled.Value = true)); + beatmaps.ImportFromStableAsync().ContinueWith(t => Schedule(() => importButton.Enabled.Value = true)); } }, deleteButton = new DangerousSettingsButton @@ -45,6 +46,27 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance })); } }, + importButton = new SettingsButton + { + Text = "Import skins from stable", + Action = () => + { + importButton.Enabled.Value = false; + skins.ImportFromStableAsync().ContinueWith(t => Schedule(() => importButton.Enabled.Value = true)); + } + }, + deleteButton = new DangerousSettingsButton + { + Text = "Delete ALL skins", + Action = () => + { + dialogOverlay?.Push(new DeleteAllBeatmapsDialog(() => + { + deleteButton.Enabled.Value = false; + Task.Run(() => skins.Delete(skins.GetAllUserSkins())).ContinueWith(t => Schedule(() => deleteButton.Enabled.Value = true)); + })); + } + }, restoreButton = new SettingsButton { Text = "Restore all hidden difficulties", diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index e914eb365e..0cba6e645e 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using OpenTK.Input; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -77,8 +76,7 @@ namespace osu.Game.Screens.Select { // if we have no beatmaps but osu-stable is found, let's prompt the user to import. if (!beatmaps.GetAllUsableBeatmapSets().Any() && beatmaps.StableInstallationAvailable) - dialogOverlay.Push(new ImportFromStablePopup(() => - Task.Factory.StartNew(beatmaps.ImportFromStable, TaskCreationOptions.LongRunning))); + dialogOverlay.Push(new ImportFromStablePopup(() => beatmaps.ImportFromStableAsync())); }); } } diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 2387924cfa..05cee8261f 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -26,17 +26,25 @@ namespace osu.Game.Skinning public override string[] HandledExtensions => new[] { ".osk" }; + protected override string ImportFromStablePath => "Skins"; + /// - /// Returns a list of all usable s. + /// Returns a list of all usable s. Includes the special default skin plus all skins from . /// /// A list of available . public List GetAllUsableSkins() { - var userSkins = ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList(); + var userSkins = GetAllUserSkins(); userSkins.Insert(0, SkinInfo.Default); return userSkins; } + /// + /// Returns a list of all usable s that have been loaded by the user. + /// + /// A list of available . + public List GetAllUserSkins() => ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList(); + protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo { Name = archive.Name From e67f63eab651dc0be85da08b66db6a9a08061c46 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Aug 2018 18:32:21 +0900 Subject: [PATCH 13/60] Improve import progress messaging --- osu.Game/Database/ArchiveModelManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index a3ae19f97d..234e1a9bd6 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -168,6 +168,7 @@ namespace osu.Game.Database } notification.Text = errors > 0 ? $"Import complete with {errors} errors" : "Import successful!"; + notification.CompletionText = $"Imported {current} {typeof(TModel).Name.Replace("Info", "").ToLower()}s!"; notification.State = ProgressNotificationState.Completed; } @@ -284,7 +285,7 @@ namespace osu.Game.Database var notification = new ProgressNotification { Progress = 0, - CompletionText = $"Deleted all {typeof(TModel)}s!", + CompletionText = $"Deleted all {typeof(TModel).Name.Replace("Info", "").ToLower()}s!", State = ProgressNotificationState.Active, }; From fa569e5ae4edcb86a2bf44ab44d39942443ef6f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Aug 2018 18:36:35 +0900 Subject: [PATCH 14/60] Offer to import skins on first startup --- osu.Game/Screens/Select/ImportFromStablePopup.cs | 2 +- osu.Game/Screens/Select/PlaySongSelect.cs | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/ImportFromStablePopup.cs b/osu.Game/Screens/Select/ImportFromStablePopup.cs index 7c3b98cc2e..a52b5bab2a 100644 --- a/osu.Game/Screens/Select/ImportFromStablePopup.cs +++ b/osu.Game/Screens/Select/ImportFromStablePopup.cs @@ -12,7 +12,7 @@ namespace osu.Game.Screens.Select public ImportFromStablePopup(Action importFromStable) { HeaderText = @"You have no beatmaps!"; - BodyText = "An existing copy of osu! was found, though.\nWould you like to import your beatmaps?"; + BodyText = "An existing copy of osu! was found, though.\nWould you like to import your beatmaps (and skins)?"; Icon = FontAwesome.fa_plane; diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 0cba6e645e..2c43b333aa 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -19,6 +19,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Screens.Edit; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; +using osu.Game.Skinning; namespace osu.Game.Screens.Select { @@ -54,7 +55,7 @@ namespace osu.Game.Screens.Select private readonly Bindable> selectedMods = new Bindable>(new Mod[] { }); [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, AudioManager audio, BeatmapManager beatmaps, DialogOverlay dialogOverlay, Bindable> selectedMods) + private void load(OsuColour colours, AudioManager audio, BeatmapManager beatmaps, SkinManager skins, DialogOverlay dialogOverlay, Bindable> selectedMods) { if (selectedMods != null) this.selectedMods.BindTo(selectedMods); @@ -76,7 +77,11 @@ namespace osu.Game.Screens.Select { // if we have no beatmaps but osu-stable is found, let's prompt the user to import. if (!beatmaps.GetAllUsableBeatmapSets().Any() && beatmaps.StableInstallationAvailable) - dialogOverlay.Push(new ImportFromStablePopup(() => beatmaps.ImportFromStableAsync())); + dialogOverlay.Push(new ImportFromStablePopup(() => + { + beatmaps.ImportFromStableAsync(); + skins.ImportFromStableAsync(); + })); }); } } From fb1e8fbdcf8577165253a96874d0a4982ea698b0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Sep 2018 10:23:56 +0900 Subject: [PATCH 15/60] Remove migration code --- osu.Game/Skinning/SkinManager.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 05cee8261f..91c78f80b8 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -101,16 +101,6 @@ namespace osu.Game.Skinning SourceChanged?.Invoke(); }; - - // migrate older imports which didn't have access to skin.ini - using (ContextFactory.GetForWrite()) - { - foreach (var skinInfo in ModelStore.ConsumableItems.Where(s => s.Name.EndsWith(".osk"))) - { - populate(skinInfo); - Update(skinInfo); - } - } } /// From 43824c2a94d9e108c7d05282b0c54af189692f4a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Sep 2018 11:50:50 +0900 Subject: [PATCH 16/60] Switch back to default skin when the user selected skin is deleted --- osu.Game/Skinning/SkinManager.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 91c78f80b8..74222da4a2 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -93,6 +93,12 @@ namespace osu.Game.Skinning { this.audio = audio; + ItemRemoved += removedInfo => { + // check the removed skin is not the current user choice. if it is, switch back to default. + if (removedInfo.ID == CurrentSkinInfo.Value.ID) + CurrentSkinInfo.Value = SkinInfo.Default; + }; + CurrentSkinInfo.ValueChanged += info => CurrentSkin.Value = GetSkin(info); CurrentSkin.ValueChanged += skin => { From 5efa9c765248d9b82779a601ca9a630debd28c84 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Sep 2018 11:51:03 +0900 Subject: [PATCH 17/60] Fix items in skin settings not always being removed corrrectly --- .../Overlays/Settings/Sections/SkinSection.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index df8ebaf4aa..15787d29f7 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -51,10 +51,10 @@ namespace osu.Game.Overlays.Settings.Sections }, }; - skins.ItemAdded += onItemsChanged; - skins.ItemRemoved += onItemsChanged; + skins.ItemAdded += itemAdded; + skins.ItemRemoved += itemRemoved; - reloadSkins(); + skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair(s.ToString(), s.ID)); var skinBindable = config.GetBindable(OsuSetting.Skin); @@ -65,9 +65,8 @@ namespace osu.Game.Overlays.Settings.Sections skinDropdown.Bindable = skinBindable; } - private void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair(s.ToString(), s.ID)); - - private void onItemsChanged(SkinInfo _) => Schedule(reloadSkins); + private void itemRemoved(SkinInfo s) => skinDropdown.Items = skinDropdown.Items.Where(i => i.Value != s.ID); + private void itemAdded(SkinInfo s) => skinDropdown.Items = skinDropdown.Items.Append(new KeyValuePair(s.ToString(), s.ID)); protected override void Dispose(bool isDisposing) { @@ -75,8 +74,8 @@ namespace osu.Game.Overlays.Settings.Sections if (skins != null) { - skins.ItemAdded -= onItemsChanged; - skins.ItemRemoved -= onItemsChanged; + skins.ItemAdded -= itemAdded; + skins.ItemRemoved -= itemRemoved; } } From 923acfbeaf5d49a643c047a90a1d0be1bf112316 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Sep 2018 10:23:23 +0900 Subject: [PATCH 18/60] Fix some web requests being run after disposal of their owner --- .../BeatmapSet/Scores/ScoresContainer.cs | 2 +- .../Beatmaps/PaginatedBeatmapContainer.cs | 17 +++++++++++------ .../PaginatedMostPlayedBeatmapContainer.cs | 18 +++++++++++++----- .../Profile/Sections/PaginatedContainer.cs | 1 + .../Sections/Ranks/PaginatedScoreContainer.cs | 16 +++++++++++----- .../Recent/PaginatedRecentActivityContainer.cs | 17 ++++++++++++----- osu.Game/Overlays/UserProfileOverlay.cs | 15 +++++++-------- 7 files changed, 56 insertions(+), 30 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 626de14c98..6cb85c778b 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -62,7 +62,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores loading = true; getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset); - getScoresRequest.Success += r => Scores = r.Scores; + getScoresRequest.Success += r => Schedule(() => Scores = r.Scores); api.Queue(getScoresRequest); } } diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index 0b06acd426..621f752b9c 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -14,8 +14,8 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps public class PaginatedBeatmapContainer : PaginatedContainer { private const float panel_padding = 10f; - private readonly BeatmapSetType type; + private GetUserBeatmapsRequest request; public PaginatedBeatmapContainer(BeatmapSetType type, Bindable user, string header, string missing = "None... yet.") : base(user, header, missing) @@ -31,9 +31,8 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps { base.ShowMore(); - var req = new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage); - - req.Success += sets => + request = new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage); + request.Success += sets => Schedule(() => { ShowMoreButton.FadeTo(sets.Count == ItemsPerPage ? 1 : 0); ShowMoreLoading.Hide(); @@ -52,9 +51,15 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps var panel = new DirectGridPanel(s.ToBeatmapSet(Rulesets)); ItemsContainer.Add(panel); } - }; + }); - Api.Queue(req); + Api.Queue(request); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + request?.Cancel(); } } } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index 42784682be..ad886c363b 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -12,6 +12,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { public class PaginatedMostPlayedBeatmapContainer : PaginatedContainer { + private GetUserMostPlayedBeatmapsRequest request; + public PaginatedMostPlayedBeatmapContainer(Bindable user) :base(user, "Most Played Beatmaps", "No records. :(") { @@ -24,9 +26,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { base.ShowMore(); - var req = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++ * ItemsPerPage); - - req.Success += beatmaps => + request = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++ * ItemsPerPage); + request.Success += beatmaps => Schedule(() => { ShowMoreButton.FadeTo(beatmaps.Count == ItemsPerPage ? 1 : 0); ShowMoreLoading.Hide(); @@ -43,9 +44,16 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { ItemsContainer.Add(new DrawableMostPlayedRow(beatmap.GetBeatmapInfo(Rulesets), beatmap.PlayCount)); } - }; + }); - Api.Queue(req); + Api.Queue(request); + } + + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + request?.Cancel(); } } } diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index 6dbb9b9ba3..db93fcbc1b 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -28,6 +28,7 @@ namespace osu.Game.Overlays.Profile.Sections protected readonly Bindable User = new Bindable(); protected APIAccess Api; + protected APIRequest RetrievalRequest; protected RulesetStore Rulesets; public PaginatedContainer(Bindable user, string header, string missing) diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index 707de0a10f..ed82c62e5c 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -16,6 +16,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks { private readonly bool includeWeight; private readonly ScoreType type; + private GetUserScoresRequest request; public PaginatedScoreContainer(ScoreType type, Bindable user, string header, string missing, bool includeWeight = false) : base(user, header, missing) @@ -32,9 +33,8 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks { base.ShowMore(); - var req = new GetUserScoresRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage); - - req.Success += scores => + request = new GetUserScoresRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage); + request.Success += scores => Schedule(() => { foreach (var s in scores) s.ApplyRuleset(Rulesets.GetRuleset(s.OnlineRulesetID)); @@ -66,9 +66,15 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks ItemsContainer.Add(drawableScore); } - }; + }); - Api.Queue(req); + Api.Queue(request); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + request?.Cancel(); } } } diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs index ee2f2f5973..fd5eda4e44 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -12,6 +12,8 @@ namespace osu.Game.Overlays.Profile.Sections.Recent { public class PaginatedRecentActivityContainer : PaginatedContainer { + private GetUserRecentActivitiesRequest request; + public PaginatedRecentActivityContainer(Bindable user, string header, string missing) : base(user, header, missing) { @@ -22,9 +24,8 @@ namespace osu.Game.Overlays.Profile.Sections.Recent { base.ShowMore(); - var req = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++ * ItemsPerPage); - - req.Success += activities => + request = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++ * ItemsPerPage); + request.Success += activities => Schedule(() => { ShowMoreButton.FadeTo(activities.Count == ItemsPerPage ? 1 : 0); ShowMoreLoading.Hide(); @@ -41,9 +42,15 @@ namespace osu.Game.Overlays.Profile.Sections.Recent { ItemsContainer.Add(new DrawableRecentActivity(activity)); } - }; + }); - Api.Queue(req); + Api.Queue(request); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + request?.Cancel(); } } } diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 11b68b0e09..ea077ff645 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -73,16 +73,15 @@ namespace osu.Game.Overlays FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out); } - public void ShowUser(long userId) - { - if (userId == Header.User.Id) - return; - - ShowUser(new User { Id = userId }); - } + public void ShowUser(long userId) => ShowUser(new User { Id = userId }); public void ShowUser(User user, bool fetchOnline = true) { + Show(); + + if (user.Id == Header?.User.Id) + return; + userReq?.Cancel(); Clear(); lastSection = null; @@ -97,6 +96,7 @@ namespace osu.Game.Overlays new BeatmapsSection(), new KudosuSection() }; + tabs = new ProfileTabControl { RelativeSizeAxes = Axes.X, @@ -161,7 +161,6 @@ namespace osu.Game.Overlays userLoadComplete(user); } - Show(); sectionsContainer.ScrollToTop(); } From da3eb18784fcdcd4380ebc6cde38a83b4ddca51a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Sep 2018 10:38:05 +0900 Subject: [PATCH 19/60] Fix the toolbar being visible during startup causing a weird offset --- osu.Game/Overlays/Toolbar/Toolbar.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 2eabf1eb74..cdc3bc2b51 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -83,14 +83,14 @@ namespace osu.Game.Overlays.Toolbar [BackgroundDependencyLoader(true)] private void load(OsuGame osuGame) { - if (osuGame != null) - overlayActivationMode.BindTo(osuGame.OverlayActivationMode); - StateChanged += visibility => { if (overlayActivationMode == OverlayActivation.Disabled) State = Visibility.Hidden; }; + + if (osuGame != null) + overlayActivationMode.BindTo(osuGame.OverlayActivationMode); } public class ToolbarBackground : Container From 581da108fc2c348dae73486e18d828f340d1fe41 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Sep 2018 11:32:38 +0900 Subject: [PATCH 20/60] Rewrite to not suck --- .../Sections/Graphics/LayoutSettings.cs | 57 ++++++------------- 1 file changed, 18 insertions(+), 39 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 14680bc7cb..4d3cdacb6a 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -19,13 +19,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private Bindable letterboxing; private Bindable sizeFullscreen; - private readonly BindableInt resolutionDropdownBindable = new BindableInt(); private OsuGameBase game; - private SettingsDropdown resolutionDropdown; + private SettingsDropdown resolutionDropdown; private SettingsEnumDropdown windowModeDropdown; - private const int transition_duration = 400; [BackgroundDependencyLoader] @@ -36,25 +34,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics letterboxing = config.GetBindable(FrameworkSetting.Letterboxing); sizeFullscreen = config.GetBindable(FrameworkSetting.SizeFullscreen); - sizeFullscreen.ValueChanged += size => - { - KeyValuePair valuePair = getResolutions().FirstOrDefault(r => r.Key.StartsWith($"{size.Width}x{size.Height}")); - - resolutionDropdownBindable.Value = valuePair.Value; - }; - - resolutionDropdownBindable.ValueChanged += resolution => - { - var newSelection = getResolutions().First(r => r.Value == resolution); - var newSelectionParts = newSelection.Key.Split('x'); - - var newSelectionWidth = int.Parse(newSelectionParts.First()); - var newSelectionHeight = int.Parse(newSelectionParts.Last()); - - if (sizeFullscreen.Value.Width != newSelectionWidth || sizeFullscreen.Value.Height != newSelectionHeight) - sizeFullscreen.Value = new Size(newSelectionWidth, newSelectionHeight); - }; - Children = new Drawable[] { windowModeDropdown = new SettingsEnumDropdown @@ -62,11 +41,12 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics LabelText = "Screen mode", Bindable = config.GetBindable(FrameworkSetting.WindowMode), }, - resolutionDropdown = new SettingsDropdown + resolutionDropdown = new SettingsDropdown { LabelText = "Resolution", + ShowsDefaultIndicator = false, Items = getResolutions(), - Bindable = resolutionDropdownBindable + Bindable = sizeFullscreen }, new SettingsCheckbox { @@ -100,7 +80,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics }, }; - windowModeDropdown.Bindable.ValueChanged += windowMode => + windowModeDropdown.Bindable.BindValueChanged(windowMode => { if (windowMode == WindowMode.Fullscreen) { @@ -109,31 +89,30 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics } else resolutionDropdown.Hide(); - }; - windowModeDropdown.Bindable.TriggerChange(); + }, true); - letterboxing.ValueChanged += isVisible => + letterboxing.BindValueChanged(isVisible => { letterboxSettings.ClearTransforms(); letterboxSettings.AutoSizeAxes = isVisible ? Axes.Y : Axes.None; if (!isVisible) letterboxSettings.ResizeHeightTo(0, transition_duration, Easing.OutQuint); - }; - letterboxing.TriggerChange(); + }, true); } - private List> getResolutions() + private List> getResolutions() { - var availableDisplayResolutions = game.Window?.GetCurrentDisplay().AvailableResolutions - .Where(r => r.Width >= 800 && r.Height >= 600) - .OrderByDescending(r => r.Width).ThenByDescending(r => r.Height); + if (game.Window == null) + return new List>(); - if (availableDisplayResolutions == null) - return new List>(); - - var availableDisplayResolutionsStr = availableDisplayResolutions.Select(r => $"{r.Width}x{r.Height}").Distinct(); - return availableDisplayResolutionsStr.Select((t, i) => new KeyValuePair(t, i)).ToList(); + return game.Window?.AvailableResolutions + .Where(r => r.Width >= 800 && r.Height >= 600) + .OrderByDescending(r => r.Width) + .ThenByDescending(r => r.Height) + .Select(res => new KeyValuePair($"{res.Width}x{res.Height}", new Size(res.Width, res.Height))) + .Distinct() + .ToList(); } } } From d5acc96efaefcf8fca81fd67bcf49c1a67bec7e2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 5 Sep 2018 14:59:37 +0900 Subject: [PATCH 21/60] Apply changes in line with osu!framework changes --- osu.Game/Graphics/DrawableDate.cs | 2 -- osu.Game/Graphics/Sprites/OsuSpriteText.cs | 24 ------------------- osu.Game/OsuGameBase.cs | 3 +-- osu.Game/Overlays/Settings/SettingsSection.cs | 2 +- .../Drawables/DrawableStoryboard.cs | 2 +- 5 files changed, 3 insertions(+), 30 deletions(-) diff --git a/osu.Game/Graphics/DrawableDate.cs b/osu.Game/Graphics/DrawableDate.cs index 406fc2ffd2..be794d93a6 100644 --- a/osu.Game/Graphics/DrawableDate.cs +++ b/osu.Game/Graphics/DrawableDate.cs @@ -4,7 +4,6 @@ using System; using Humanizer; using osu.Framework.Allocation; -using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Game.Graphics.Sprites; @@ -16,7 +15,6 @@ namespace osu.Game.Graphics public DrawableDate(DateTimeOffset date) { - AutoSizeAxes = Axes.Both; Font = "Exo2.0-RegularItalic"; Date = date.ToLocalTime(); diff --git a/osu.Game/Graphics/Sprites/OsuSpriteText.cs b/osu.Game/Graphics/Sprites/OsuSpriteText.cs index c9389bb9e2..8607d51e12 100644 --- a/osu.Game/Graphics/Sprites/OsuSpriteText.cs +++ b/osu.Game/Graphics/Sprites/OsuSpriteText.cs @@ -3,9 +3,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Framework.MathUtils; -using OpenTK; -using OpenTK.Graphics; using osu.Framework.Graphics.Transforms; namespace osu.Game.Graphics.Sprites @@ -19,27 +16,6 @@ namespace osu.Game.Graphics.Sprites Shadow = true; TextSize = FONT_SIZE; } - - protected override Drawable CreateFallbackCharacterDrawable() - { - var tex = GetTextureForCharacter('?'); - - if (tex != null) - { - float adjust = (RNG.NextSingle() - 0.5f) * 2; - return new Sprite - { - Texture = tex, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Scale = new Vector2(1 + adjust * 0.2f), - Rotation = adjust * 15, - Colour = Color4.White, - }; - } - - return base.CreateFallbackCharacterDrawable(); - } } public static class OsuSpriteTextTransformExtensions diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index bada2a794d..29072523f3 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -138,9 +138,8 @@ namespace osu.Game fileImporters.Add(SkinManager); //this completely overrides the framework default. will need to change once we make a proper FontStore. - dependencies.Cache(Fonts = new FontStore { ScaleAdjust = 100 }); + dependencies.Cache(Fonts = new FontStore(new GlyphStore(Resources, @"Fonts/FontAwesome"))); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/FontAwesome")); Fonts.AddStore(new GlyphStore(Resources, @"Fonts/osuFont")); Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Medium")); Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-MediumItalic")); diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 9555f0fbc5..58714208c0 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -73,7 +73,7 @@ namespace osu.Game.Overlays.Settings }, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Children = new[] + Children = new Drawable[] { new OsuSpriteText { diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index d746bb90c4..c5a9514095 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -57,7 +57,7 @@ namespace osu.Game.Storyboards.Drawables [BackgroundDependencyLoader] private void load(FileStore fileStore) { - dependencies.Cache(new TextureStore(new RawTextureLoaderStore(fileStore.Store), false) { ScaleAdjust = 1, }); + dependencies.Cache(new TextureStore(new RawTextureLoaderStore(fileStore.Store), false, scaleAdjust: 1)); foreach (var layer in Storyboard.Layers) Add(layer.CreateDrawable()); From c03d1d9566cdb95507358af2c89ec84ea415b4b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Sep 2018 16:45:30 +0900 Subject: [PATCH 22/60] Attempt to fix CI failures on CatcherArea.Explode --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 4327abb96f..5802f64b96 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Catch.UI // this is required to make this run after the last caught fruit runs UpdateState at least once. // TODO: find a better alternative - if (lastPlateableFruit.IsLoaded) + if (lastPlateableFruit.LoadState > LoadState.Ready) action(); else lastPlateableFruit.OnLoadComplete = _ => action(); @@ -407,9 +407,7 @@ namespace osu.Game.Rulesets.Catch.UI /// public void Explode() { - var fruit = caughtFruit.ToArray(); - - foreach (var f in fruit) + foreach (var f in caughtFruit.ToArray()) Explode(f); } @@ -422,15 +420,15 @@ namespace osu.Game.Rulesets.Catch.UI fruit.Anchor = Anchor.TopLeft; fruit.Position = caughtFruit.ToSpaceOfOtherDrawable(fruit.DrawPosition, ExplodingFruitTarget); - caughtFruit.Remove(fruit); + if (!caughtFruit.Remove(fruit)) + // we may have already been removed by a previous operation (due to the weird OnLoadComplete scheduling). + // this avoids a crash on potentially attempting to Add a fruit to ExplodingFruitTarget twice. + return; ExplodingFruitTarget.Add(fruit); } - fruit.MoveToY(fruit.Y - 50, 250, Easing.OutSine) - .Then() - .MoveToY(fruit.Y + 50, 500, Easing.InSine); - + fruit.MoveToY(fruit.Y - 50, 250, Easing.OutSine).Then().MoveToY(fruit.Y + 50, 500, Easing.InSine); fruit.MoveToX(fruit.X + originalX * 6, 1000); fruit.FadeOut(750); From 8d9e0ff1c34ce62d7660db1fd2ea68e4deb60ab0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Sep 2018 12:51:23 +0900 Subject: [PATCH 23/60] Remove async WorkingBeatmap logic --- osu.Game/Beatmaps/WorkingBeatmap.cs | 146 ++++++++++++---------------- 1 file changed, 62 insertions(+), 84 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 085c591fce..85b98413e5 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -8,10 +8,10 @@ using osu.Game.Rulesets.Mods; using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using osu.Game.Storyboards; using osu.Framework.IO.File; using System.IO; +using System.Threading; using osu.Game.IO.Serialization; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; @@ -38,12 +38,26 @@ namespace osu.Game.Beatmaps Mods.ValueChanged += mods => applyRateAdjustments(); - beatmap = new AsyncLazy(populateBeatmap); - background = new AsyncLazy(populateBackground, b => b == null || !b.IsDisposed); - track = new AsyncLazy(populateTrack); - waveform = new AsyncLazy(populateWaveform); - storyboard = new AsyncLazy(populateStoryboard); - skin = new AsyncLazy(populateSkin); + beatmap = new RecyclableLazy(() => + { + var b = GetBeatmap() ?? new Beatmap(); + // use the database-backed info. + b.BeatmapInfo = BeatmapInfo; + return b; + }); + + track = new RecyclableLazy(() => + { + // we want to ensure that we always have a track, even if it's a fake one. + var t = GetTrack() ?? new VirtualBeatmapTrack(Beatmap); + applyRateAdjustments(t); + return t; + }); + + background = new RecyclableLazy(GetBackground, BackgroundStillValid); + waveform = new RecyclableLazy(GetWaveform); + storyboard = new RecyclableLazy(GetStoryboard); + skin = new RecyclableLazy(GetSkin); } /// @@ -58,28 +72,6 @@ namespace osu.Game.Beatmaps return path; } - protected abstract IBeatmap GetBeatmap(); - protected abstract Texture GetBackground(); - protected abstract Track GetTrack(); - protected virtual Skin GetSkin() => new DefaultSkin(); - protected virtual Waveform GetWaveform() => new Waveform(); - protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo }; - - public bool BeatmapLoaded => beatmap.IsResultAvailable; - public IBeatmap Beatmap => beatmap.Value.Result; - public Task GetBeatmapAsync() => beatmap.Value; - private readonly AsyncLazy beatmap; - - private IBeatmap populateBeatmap() - { - var b = GetBeatmap() ?? new Beatmap(); - - // use the database-backed info. - b.BeatmapInfo = BeatmapInfo; - - return b; - } - /// /// Constructs a playable from using the applicable converters for a specific . /// @@ -136,62 +128,53 @@ namespace osu.Game.Beatmaps public override string ToString() => BeatmapInfo.ToString(); - public bool BackgroundLoaded => background.IsResultAvailable; - public Texture Background => background.Value.Result; - public Task GetBackgroundAsync() => background.Value; - private AsyncLazy background; + public bool BeatmapLoaded => beatmap.IsResultAvailable; + public IBeatmap Beatmap => beatmap.Value; + protected abstract IBeatmap GetBeatmap(); + private readonly RecyclableLazy beatmap; - private Texture populateBackground() => GetBackground(); + public bool BackgroundLoaded => background.IsResultAvailable; + public Texture Background => background.Value; + protected virtual bool BackgroundStillValid(Texture b) => b == null || !b.IsDisposed; + protected abstract Texture GetBackground(); + private readonly RecyclableLazy background; public bool TrackLoaded => track.IsResultAvailable; - public Track Track => track.Value.Result; - public Task GetTrackAsync() => track.Value; - private AsyncLazy track; - - private Track populateTrack() - { - // we want to ensure that we always have a track, even if it's a fake one. - var t = GetTrack() ?? new VirtualBeatmapTrack(Beatmap); - applyRateAdjustments(t); - return t; - } + public Track Track => track.Value; + protected abstract Track GetTrack(); + private RecyclableLazy track; public bool WaveformLoaded => waveform.IsResultAvailable; - public Waveform Waveform => waveform.Value.Result; - public Task GetWaveformAsync() => waveform.Value; - private readonly AsyncLazy waveform; - - private Waveform populateWaveform() => GetWaveform(); + public Waveform Waveform => waveform.Value; + protected virtual Waveform GetWaveform() => new Waveform(); + private readonly RecyclableLazy waveform; public bool StoryboardLoaded => storyboard.IsResultAvailable; - public Storyboard Storyboard => storyboard.Value.Result; - public Task GetStoryboardAsync() => storyboard.Value; - private readonly AsyncLazy storyboard; - - private Storyboard populateStoryboard() => GetStoryboard(); + public Storyboard Storyboard => storyboard.Value; + protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo }; + private readonly RecyclableLazy storyboard; public bool SkinLoaded => skin.IsResultAvailable; - public Skin Skin => skin.Value.Result; - public Task GetSkinAsync() => skin.Value; - private readonly AsyncLazy skin; + public Skin Skin => skin.Value; + protected virtual Skin GetSkin() => new DefaultSkin(); + private readonly RecyclableLazy skin; - private Skin populateSkin() => GetSkin(); - - public void TransferTo(WorkingBeatmap other) + /// + /// Transfer pieces of a beatmap to a new one, where possible, to save on loading. + /// + /// The new beatmap which is being switched to. + public virtual void TransferTo(WorkingBeatmap other) { if (track.IsResultAvailable && Track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo)) other.track = track; - - if (background.IsResultAvailable && Background != null && BeatmapInfo.BackgroundEquals(other.BeatmapInfo)) - other.background = background; } public virtual void Dispose() { - if (BackgroundLoaded) Background?.Dispose(); - if (WaveformLoaded) Waveform?.Dispose(); - if (StoryboardLoaded) Storyboard?.Dispose(); - if (SkinLoaded) Skin?.Dispose(); + background.Recycle(); + waveform.Recycle(); + storyboard.Recycle(); + skin.Recycle(); } /// @@ -210,15 +193,15 @@ namespace osu.Game.Beatmaps mod.ApplyToClock(t); } - public class AsyncLazy + public class RecyclableLazy { - private Lazy> lazy; + private Lazy lazy; private readonly Func valueFactory; private readonly Func stillValidFunction; private readonly object initLock = new object(); - public AsyncLazy(Func valueFactory, Func stillValidFunction = null) + public RecyclableLazy(Func valueFactory, Func stillValidFunction = null) { this.valueFactory = valueFactory; this.stillValidFunction = stillValidFunction; @@ -230,20 +213,13 @@ namespace osu.Game.Beatmaps { if (!IsResultAvailable) return; - (lazy.Value.Result as IDisposable)?.Dispose(); + (lazy.Value as IDisposable)?.Dispose(); recreate(); } - public bool IsResultAvailable - { - get - { - recreateIfInvalid(); - return lazy.Value.IsCompleted; - } - } + public bool IsResultAvailable => stillValid; - public Task Value + public T Value { get { @@ -252,15 +228,17 @@ namespace osu.Game.Beatmaps } } + private bool stillValid => lazy.IsValueCreated && (stillValidFunction?.Invoke(lazy.Value) ?? true); + private void recreateIfInvalid() { lock (initLock) { - if (!lazy.IsValueCreated || !lazy.Value.IsCompleted) + if (!lazy.IsValueCreated) // we have not yet been initialised or haven't run the task. return; - if (stillValidFunction?.Invoke(lazy.Value.Result) ?? true) + if (stillValid) // we are still in a valid state. return; @@ -268,7 +246,7 @@ namespace osu.Game.Beatmaps } } - private void recreate() => lazy = new Lazy>(() => Task.Run(valueFactory)); + private void recreate() => lazy = new Lazy(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication); } } } From 65018705f43bc99465572c1e983794365e784ff5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Sep 2018 13:09:57 +0900 Subject: [PATCH 24/60] Restore IsLoaded check --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 5802f64b96..9460512a8d 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Catch.UI // this is required to make this run after the last caught fruit runs UpdateState at least once. // TODO: find a better alternative - if (lastPlateableFruit.LoadState > LoadState.Ready) + if (lastPlateableFruit.IsLoaded) action(); else lastPlateableFruit.OnLoadComplete = _ => action(); From 89a92ab7eb15c86130a7127aaa7e79b75c48d0dd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Sep 2018 13:27:53 +0900 Subject: [PATCH 25/60] Fix potential thread-safety issue --- osu.Game/Beatmaps/WorkingBeatmap.cs | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 85b98413e5..a2b44aab52 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -199,7 +199,7 @@ namespace osu.Game.Beatmaps private readonly Func valueFactory; private readonly Func stillValidFunction; - private readonly object initLock = new object(); + private readonly object fetchLock = new object(); public RecyclableLazy(Func valueFactory, Func stillValidFunction = null) { @@ -223,29 +223,17 @@ namespace osu.Game.Beatmaps { get { - recreateIfInvalid(); - return lazy.Value; + lock (fetchLock) + { + if (!stillValid) + recreate(); + return lazy.Value; + } } } private bool stillValid => lazy.IsValueCreated && (stillValidFunction?.Invoke(lazy.Value) ?? true); - private void recreateIfInvalid() - { - lock (initLock) - { - if (!lazy.IsValueCreated) - // we have not yet been initialised or haven't run the task. - return; - - if (stillValid) - // we are still in a valid state. - return; - - recreate(); - } - } - private void recreate() => lazy = new Lazy(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication); } } From 4ff66bf531fc32983880ae7a7f12c3efb8affbd4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Sep 2018 13:14:29 +0900 Subject: [PATCH 26/60] Update in line with framework Image changes --- .../Objects/Drawables/Pieces/SliderBody.cs | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index 283d6b91f6..6f0197e711 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -7,14 +7,15 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Lines; using osu.Framework.Graphics.Textures; -using OpenTK; using OpenTK.Graphics.ES30; using OpenTK.Graphics; using osu.Framework.Graphics.Primitives; using osu.Game.Rulesets.Objects.Types; +using OpenTK; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { @@ -43,6 +44,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces public double? SnakedEnd { get; private set; } private Color4 accentColour = Color4.White; + /// /// Used to colour the path. /// @@ -61,6 +63,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } private Color4 borderColour = Color4.White; + /// /// Used to colour the path border. /// @@ -85,6 +88,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private Vector2 topLeftOffset; private readonly Slider slider; + public SliderBody(Slider s) { slider = s; @@ -139,8 +143,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces var texture = new Texture(textureWidth, 1); //initialise background - var raw = new RawTexture(textureWidth, 1); - var bytes = raw.Data; + var raw = new Image(textureWidth, 1); const float aa_portion = 0.02f; const float border_portion = 0.128f; @@ -155,19 +158,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces if (progress <= border_portion) { - bytes[i * 4] = (byte)(BorderColour.R * 255); - bytes[i * 4 + 1] = (byte)(BorderColour.G * 255); - bytes[i * 4 + 2] = (byte)(BorderColour.B * 255); - bytes[i * 4 + 3] = (byte)(Math.Min(progress / aa_portion, 1) * (BorderColour.A * 255)); + raw[i, 0] = new Rgba32(BorderColour.R, BorderColour.G, BorderColour.B, Math.Min(progress / aa_portion, 1) * BorderColour.A); } else { progress -= border_portion; - - bytes[i * 4] = (byte)(AccentColour.R * 255); - bytes[i * 4 + 1] = (byte)(AccentColour.G * 255); - bytes[i * 4 + 2] = (byte)(AccentColour.B * 255); - bytes[i * 4 + 3] = (byte)((opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * (AccentColour.A * 255)); + raw[i, 0] = new Rgba32(AccentColour.R, AccentColour.G, AccentColour.B, + (opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * AccentColour.A); } } From 29b0d62f213441c668572f56eee01bed9d663af5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Sep 2018 13:15:43 +0900 Subject: [PATCH 27/60] Changes in line with framework refcount changes --- .../Beatmaps/BeatmapManager_WorkingBeatmap.cs | 15 +++++++++++++-- osu.Game/Graphics/Backgrounds/Background.cs | 2 +- .../Graphics/Textures/LargeTextureStore.cs | 18 ------------------ osu.Game/OsuGameBase.cs | 3 +-- osu.Game/Screens/Tournament/Drawings.cs | 2 +- osu.Game/Skinning/LegacySkin.cs | 2 +- .../Drawables/DrawableStoryboard.cs | 4 +++- 7 files changed, 20 insertions(+), 26 deletions(-) delete mode 100644 osu.Game/Graphics/Textures/LargeTextureStore.cs diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index 43ae30f780..77ff53b893 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Framework.Logging; using osu.Game.Beatmaps.Formats; -using osu.Game.Graphics.Textures; using osu.Game.Skinning; using osu.Game.Storyboards; @@ -45,6 +44,10 @@ namespace osu.Game.Beatmaps private string getPathForFile(string filename) => BeatmapSetInfo.Files.First(f => string.Equals(f.Filename, filename, StringComparison.InvariantCultureIgnoreCase)).FileInfo.StoragePath; + private LargeTextureStore textureStore; + + protected override bool BackgroundStillValid(Texture b) => false; // bypass lazy logic. we want to return a new background each time for refcounting purposes. + protected override Texture GetBackground() { if (Metadata?.BackgroundFile == null) @@ -52,7 +55,7 @@ namespace osu.Game.Beatmaps try { - return new LargeTextureStore(new RawTextureLoaderStore(store)).Get(getPathForFile(Metadata.BackgroundFile)); + return (textureStore ?? (textureStore = new LargeTextureStore(new TextureLoaderStore(store)))).Get(getPathForFile(Metadata.BackgroundFile)); } catch { @@ -73,6 +76,14 @@ namespace osu.Game.Beatmaps } } + public override void TransferTo(WorkingBeatmap other) + { + base.TransferTo(other); + + if (other is BeatmapManagerWorkingBeatmap owb && textureStore != null && BeatmapInfo.BackgroundEquals(other.BeatmapInfo)) + owb.textureStore = textureStore; + } + protected override Waveform GetWaveform() { try diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs index d5825a8c42..6fc8b0e070 100644 --- a/osu.Game/Graphics/Backgrounds/Background.cs +++ b/osu.Game/Graphics/Backgrounds/Background.cs @@ -5,8 +5,8 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; using OpenTK.Graphics; -using osu.Game.Graphics.Textures; namespace osu.Game.Graphics.Backgrounds { diff --git a/osu.Game/Graphics/Textures/LargeTextureStore.cs b/osu.Game/Graphics/Textures/LargeTextureStore.cs deleted file mode 100644 index 4dcbb1220d..0000000000 --- a/osu.Game/Graphics/Textures/LargeTextureStore.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics.Textures; -using osu.Framework.IO.Stores; - -namespace osu.Game.Graphics.Textures -{ - /// - /// A texture store that bypasses atlasing. - /// - public class LargeTextureStore : TextureStore - { - public LargeTextureStore(IResourceStore store = null) : base(store, false) - { - } - } -} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index bada2a794d..e3ce7a19f1 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -24,7 +24,6 @@ using osu.Framework.Input; using osu.Framework.Logging; using osu.Game.Audio; using osu.Game.Database; -using osu.Game.Graphics.Textures; using osu.Game.Input; using osu.Game.Input.Bindings; using osu.Game.IO; @@ -109,7 +108,7 @@ namespace osu.Game dependencies.Cache(contextFactory = new DatabaseContextFactory(Host.Storage)); - dependencies.Cache(new LargeTextureStore(new RawTextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures")))); + dependencies.Cache(new LargeTextureStore(new TextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures")))); dependencies.CacheAs(this); dependencies.Cache(LocalConfig); diff --git a/osu.Game/Screens/Tournament/Drawings.cs b/osu.Game/Screens/Tournament/Drawings.cs index 63d29d5cd7..4e2109afd0 100644 --- a/osu.Game/Screens/Tournament/Drawings.cs +++ b/osu.Game/Screens/Tournament/Drawings.cs @@ -59,7 +59,7 @@ namespace osu.Game.Screens.Tournament TextureStore flagStore = new TextureStore(); // Local flag store - flagStore.AddStore(new RawTextureLoaderStore(new NamespacedResourceStore(new StorageBackedResourceStore(storage), "Drawings"))); + flagStore.AddStore(new TextureLoaderStore(new NamespacedResourceStore(new StorageBackedResourceStore(storage), "Drawings"))); // Default texture store flagStore.AddStore(textures); diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 9c881c6abb..ce7edf8683 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -37,7 +37,7 @@ namespace osu.Game.Skinning Configuration = new SkinConfiguration(); Samples = audioManager.GetSampleManager(storage); - Textures = new TextureStore(new RawTextureLoaderStore(storage)); + Textures = new TextureStore(new TextureLoaderStore(storage)); } public override Drawable GetDrawableComponent(string componentName) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index d746bb90c4..8053a7413c 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -22,6 +22,7 @@ namespace osu.Game.Storyboards.Drawables public override bool HandleMouseInput => false; private bool passing = true; + public bool Passing { get { return passing; } @@ -36,6 +37,7 @@ namespace osu.Game.Storyboards.Drawables public override bool RemoveCompletedTransforms => false; private DependencyContainer dependencies; + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); @@ -57,7 +59,7 @@ namespace osu.Game.Storyboards.Drawables [BackgroundDependencyLoader] private void load(FileStore fileStore) { - dependencies.Cache(new TextureStore(new RawTextureLoaderStore(fileStore.Store), false) { ScaleAdjust = 1, }); + dependencies.Cache(new TextureStore(new TextureLoaderStore(fileStore.Store), false, scaleAdjust: 1)); foreach (var layer in Storyboard.Layers) Add(layer.CreateDrawable()); From e63f60231a408162d0f41ddc733363cc0fbd0a11 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Sep 2018 13:27:17 +0900 Subject: [PATCH 28/60] Optimise carousel memory usage by unloading off-screen panels --- osu.Game/Screens/Select/BeatmapCarousel.cs | 11 ++++++++++- .../Select/Carousel/DrawableCarouselBeatmapSet.cs | 9 +++++++-- .../Screens/Select/Carousel/DrawableCarouselItem.cs | 13 ++++++++++++- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 7bb0b95b9a..4dbfe65ebf 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -424,7 +424,7 @@ namespace osu.Game.Screens.Select float drawHeight = DrawHeight; // Remove all items that should no longer be on-screen - scrollableContent.RemoveAll(p => p.Y < Current - p.DrawHeight || p.Y > Current + drawHeight || !p.IsPresent); + scrollableContent.RemoveAll(p => p.CanBeRemoved && (p.Y < Current - p.DrawHeight || p.Y > Current + drawHeight || !p.IsPresent)); // Find index range of all items that should be on-screen Trace.Assert(Items.Count == yPositions.Count); @@ -486,6 +486,15 @@ namespace osu.Game.Screens.Select updateItem(p, halfHeight); } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + // aggressively dispose "off-screen" items to reduce GC pressure. + foreach (var i in Items) + i.Dispose(); + } + private CarouselBeatmapSet createCarouselSet(BeatmapSetInfo beatmapSet) { if (beatmapSet.Beatmaps.All(b => b.Hidden)) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index d554a22735..3fcea908a0 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -30,6 +30,7 @@ namespace osu.Game.Screens.Select.Carousel private DialogOverlay dialogOverlay; private readonly BeatmapSetInfo beatmapSet; + private DelayedLoadUnloadWrapper delayed; public DrawableCarouselBeatmapSet(CarouselBeatmapSet set) : base(set) @@ -37,6 +38,10 @@ namespace osu.Game.Screens.Select.Carousel beatmapSet = set.BeatmapSet; } + public override bool CanBeRemoved => delayed?.DelayedLoadCompleted != true; + + protected override bool RequiresChildrenUpdate => true; + [BackgroundDependencyLoader(true)] private void load(LocalisationEngine localisation, BeatmapManager manager, BeatmapSetOverlay beatmapOverlay, DialogOverlay overlay) { @@ -50,12 +55,12 @@ namespace osu.Game.Screens.Select.Carousel Children = new Drawable[] { - new DelayedLoadWrapper( + delayed = new DelayedLoadUnloadWrapper(() => new PanelBackground(manager.GetWorkingBeatmap(beatmapSet.Beatmaps.FirstOrDefault())) { RelativeSizeAxes = Axes.Both, OnLoadComplete = d => d.FadeInFromZero(1000, Easing.OutQuint), - }, 300 + }, 300, 5000 ), new FillFlowContainer { diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs index 8a0052559e..5bcf4e6441 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs @@ -24,6 +24,11 @@ namespace osu.Game.Screens.Select.Carousel public override bool IsPresent => base.IsPresent || Item.Visible; + /// + /// Whether this item can be removed from the scroll container (usually due to being off-screen). + /// + public virtual bool CanBeRemoved => true; + public readonly CarouselItem Item; private Container nestedContainer; @@ -86,7 +91,13 @@ namespace osu.Game.Screens.Select.Carousel base.OnHoverLost(state); } - public void SetMultiplicativeAlpha(float alpha) => borderContainer.Alpha = alpha; + protected bool VisibleInCarousel; + + public void SetMultiplicativeAlpha(float alpha) + { + borderContainer.Alpha = alpha; + VisibleInCarousel = alpha > 0; + } protected override void LoadComplete() { From 91aada8be59b227938f10a4c782bc9f719209037 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Sep 2018 14:20:29 +0900 Subject: [PATCH 29/60] Fix ScaleAdjust going missing --- osu.Game/OsuGameBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 6987b6eea7..5e93c2547d 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -115,7 +115,7 @@ namespace osu.Game dependencies.Cache(LocalConfig); //this completely overrides the framework default. will need to change once we make a proper FontStore. - dependencies.Cache(Fonts = new FontStore()); + dependencies.Cache(Fonts = new FontStore { ScaleAdjust = 100 }); Fonts.AddStore(new GlyphStore(Resources, @"Fonts/FontAwesome")); Fonts.AddStore(new GlyphStore(Resources, @"Fonts/osuFont")); From ccd7c1a17d10e43d5467c99197d7f6d54ef01181 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Sep 2018 17:04:33 +0900 Subject: [PATCH 30/60] Cancel request on disposal for safety --- osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 6cb85c778b..60811d8b12 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -134,5 +134,10 @@ namespace osu.Game.Overlays.BeatmapSet.Scores this.api = api; updateDisplay(); } + + protected override void Dispose(bool isDisposing) + { + getScoresRequest?.Cancel(); + } } } From c23b9b61a814e570635da4d08eb9fde16efb799b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Sep 2018 17:27:21 +0900 Subject: [PATCH 31/60] Simplify implementation in line with framework improvements --- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 +- .../Select/Carousel/DrawableCarouselBeatmapSet.cs | 7 +------ .../Screens/Select/Carousel/DrawableCarouselItem.cs | 13 +------------ 3 files changed, 3 insertions(+), 19 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 4dbfe65ebf..b6cbaf45e9 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -424,7 +424,7 @@ namespace osu.Game.Screens.Select float drawHeight = DrawHeight; // Remove all items that should no longer be on-screen - scrollableContent.RemoveAll(p => p.CanBeRemoved && (p.Y < Current - p.DrawHeight || p.Y > Current + drawHeight || !p.IsPresent)); + scrollableContent.RemoveAll(p => p.Y < Current - p.DrawHeight || p.Y > Current + drawHeight || !p.IsPresent); // Find index range of all items that should be on-screen Trace.Assert(Items.Count == yPositions.Count); diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 3fcea908a0..23f338b530 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -30,7 +30,6 @@ namespace osu.Game.Screens.Select.Carousel private DialogOverlay dialogOverlay; private readonly BeatmapSetInfo beatmapSet; - private DelayedLoadUnloadWrapper delayed; public DrawableCarouselBeatmapSet(CarouselBeatmapSet set) : base(set) @@ -38,10 +37,6 @@ namespace osu.Game.Screens.Select.Carousel beatmapSet = set.BeatmapSet; } - public override bool CanBeRemoved => delayed?.DelayedLoadCompleted != true; - - protected override bool RequiresChildrenUpdate => true; - [BackgroundDependencyLoader(true)] private void load(LocalisationEngine localisation, BeatmapManager manager, BeatmapSetOverlay beatmapOverlay, DialogOverlay overlay) { @@ -55,7 +50,7 @@ namespace osu.Game.Screens.Select.Carousel Children = new Drawable[] { - delayed = new DelayedLoadUnloadWrapper(() => + new DelayedLoadUnloadWrapper(() => new PanelBackground(manager.GetWorkingBeatmap(beatmapSet.Beatmaps.FirstOrDefault())) { RelativeSizeAxes = Axes.Both, diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs index 5bcf4e6441..8a0052559e 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs @@ -24,11 +24,6 @@ namespace osu.Game.Screens.Select.Carousel public override bool IsPresent => base.IsPresent || Item.Visible; - /// - /// Whether this item can be removed from the scroll container (usually due to being off-screen). - /// - public virtual bool CanBeRemoved => true; - public readonly CarouselItem Item; private Container nestedContainer; @@ -91,13 +86,7 @@ namespace osu.Game.Screens.Select.Carousel base.OnHoverLost(state); } - protected bool VisibleInCarousel; - - public void SetMultiplicativeAlpha(float alpha) - { - borderContainer.Alpha = alpha; - VisibleInCarousel = alpha > 0; - } + public void SetMultiplicativeAlpha(float alpha) => borderContainer.Alpha = alpha; protected override void LoadComplete() { From d05cd52d9aecc3e2cc3bf888291e413167bda555 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Sep 2018 17:38:15 +0900 Subject: [PATCH 32/60] Run the queue faster if multiple requests are pending --- osu.Game/Online/API/APIAccess.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index eb9a60115f..1dda257c95 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -163,14 +163,16 @@ namespace osu.Game.Online.API continue; } - APIRequest req = null; - - lock (queue) - if (queue.Count > 0) - req = queue.Dequeue(); - - if (req != null) + while (true) { + APIRequest req; + + lock (queue) + { + if (queue.Count == 0) break; + req = queue.Dequeue(); + } + // TODO: handle failures better handleRequest(req); } From 4e012042ab55d85af59f4c5cccd9a2abf3570956 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Sep 2018 17:57:09 +0900 Subject: [PATCH 33/60] Fix renaming variables too eagerly --- osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs | 2 +- osu.Game/OsuGameBase.cs | 2 +- osu.Game/Screens/Tournament/Drawings.cs | 2 +- osu.Game/Skinning/LegacySkin.cs | 2 +- osu.Game/Storyboards/Drawables/DrawableStoryboard.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index 77ff53b893..cc70aa783f 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -55,7 +55,7 @@ namespace osu.Game.Beatmaps try { - return (textureStore ?? (textureStore = new LargeTextureStore(new TextureLoaderStore(store)))).Get(getPathForFile(Metadata.BackgroundFile)); + return (textureStore ?? (textureStore = new LargeTextureStore(new RawTextureLoaderStore(store)))).Get(getPathForFile(Metadata.BackgroundFile)); } catch { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index e3ce7a19f1..a72abb8d4e 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -108,7 +108,7 @@ namespace osu.Game dependencies.Cache(contextFactory = new DatabaseContextFactory(Host.Storage)); - dependencies.Cache(new LargeTextureStore(new TextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures")))); + dependencies.Cache(new LargeTextureStore(new RawTextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures")))); dependencies.CacheAs(this); dependencies.Cache(LocalConfig); diff --git a/osu.Game/Screens/Tournament/Drawings.cs b/osu.Game/Screens/Tournament/Drawings.cs index 4e2109afd0..63d29d5cd7 100644 --- a/osu.Game/Screens/Tournament/Drawings.cs +++ b/osu.Game/Screens/Tournament/Drawings.cs @@ -59,7 +59,7 @@ namespace osu.Game.Screens.Tournament TextureStore flagStore = new TextureStore(); // Local flag store - flagStore.AddStore(new TextureLoaderStore(new NamespacedResourceStore(new StorageBackedResourceStore(storage), "Drawings"))); + flagStore.AddStore(new RawTextureLoaderStore(new NamespacedResourceStore(new StorageBackedResourceStore(storage), "Drawings"))); // Default texture store flagStore.AddStore(textures); diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index ce7edf8683..9c881c6abb 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -37,7 +37,7 @@ namespace osu.Game.Skinning Configuration = new SkinConfiguration(); Samples = audioManager.GetSampleManager(storage); - Textures = new TextureStore(new TextureLoaderStore(storage)); + Textures = new TextureStore(new RawTextureLoaderStore(storage)); } public override Drawable GetDrawableComponent(string componentName) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index 8053a7413c..d0d4c678bf 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -59,7 +59,7 @@ namespace osu.Game.Storyboards.Drawables [BackgroundDependencyLoader] private void load(FileStore fileStore) { - dependencies.Cache(new TextureStore(new TextureLoaderStore(fileStore.Store), false, scaleAdjust: 1)); + dependencies.Cache(new TextureStore(new RawTextureLoaderStore(fileStore.Store), false, scaleAdjust: 1)); foreach (var layer in Storyboard.Layers) Add(layer.CreateDrawable()); From 9f67119ba90244dbdc5fb5b2dd43c2c7d74f6db7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Sep 2018 18:01:32 +0900 Subject: [PATCH 34/60] Fix potential nullref in IsPresent override --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index e69f340184..10cd246172 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class DrawableOsuHitObject : DrawableHitObject { - public override bool IsPresent => base.IsPresent || State.Value == ArmedState.Idle && Time.Current >= HitObject.StartTime - HitObject.TimePreempt; + public override bool IsPresent => base.IsPresent || State.Value == ArmedState.Idle && Clock?.CurrentTime >= HitObject.StartTime - HitObject.TimePreempt; private readonly ShakeContainer shakeContainer; From a1780fddc98fec5c353bf52cb23fe70606739655 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Sep 2018 18:02:04 +0900 Subject: [PATCH 35/60] DrawInfo -> DrawColourInfo --- osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs | 2 +- osu.Game/Graphics/Backgrounds/Triangles.cs | 4 ++-- osu.Game/Graphics/SpriteIcon.cs | 2 +- osu.Game/Screens/Menu/LogoVisualisation.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index abcd1ddbda..4a6b12d41a 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -216,7 +216,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Texture.DrawQuad( new Quad(pos.X - Size.X / 2, pos.Y - Size.Y / 2, Size.X, Size.Y), - DrawInfo.Colour, + DrawColourInfo.Colour, null, v => Shared.VertexBuffer.Vertices[end++] = new TexturedTrailVertex { diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 01b09c0a40..0f382900ce 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -116,7 +116,7 @@ namespace osu.Game.Graphics.Backgrounds float adjustedAlpha = HideAlphaDiscrepancies ? // Cubically scale alpha to make it drop off more sharply. - (float)Math.Pow(DrawInfo.Colour.AverageColour.Linear.A, 3) : + (float)Math.Pow(DrawColourInfo.Colour.AverageColour.Linear.A, 3) : 1; float elapsedSeconds = (float)Time.Elapsed / 1000; @@ -235,7 +235,7 @@ namespace osu.Game.Graphics.Backgrounds Vector2Extensions.Transform(particle.Position * Size + new Vector2(-offset.X, offset.Y), DrawInfo.Matrix) ); - ColourInfo colourInfo = DrawInfo.Colour; + ColourInfo colourInfo = DrawColourInfo.Colour; colourInfo.ApplyChild(particle.Colour); Texture.DrawTriangle( diff --git a/osu.Game/Graphics/SpriteIcon.cs b/osu.Game/Graphics/SpriteIcon.cs index b72ba7e02f..1b1df45c77 100644 --- a/osu.Game/Graphics/SpriteIcon.cs +++ b/osu.Game/Graphics/SpriteIcon.cs @@ -95,7 +95,7 @@ namespace osu.Game.Graphics { //adjust shadow alpha based on highest component intensity to avoid muddy display of darker text. //squared result for quadratic fall-off seems to give the best result. - var avgColour = (Color4)DrawInfo.Colour.AverageColour; + var avgColour = (Color4)DrawColourInfo.Colour.AverageColour; spriteShadow.Alpha = (float)Math.Pow(Math.Max(Math.Max(avgColour.R, avgColour.G), avgColour.B), 2); diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index fb6130fa36..a3cb2f13d0 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -176,7 +176,7 @@ namespace osu.Game.Screens.Menu Vector2 inflation = DrawInfo.MatrixInverse.ExtractScale().Xy; - ColourInfo colourInfo = DrawInfo.Colour; + ColourInfo colourInfo = DrawColourInfo.Colour; colourInfo.ApplyChild(Colour); if (AudioData != null) From da906c0ddf43f6c896f6e27cfe93d82d9f598e9e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Sep 2018 18:03:27 +0900 Subject: [PATCH 36/60] Update framework --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 669b775674..b3df528043 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,7 +18,7 @@ - + From 1327aed16411a5030a49a9eef4cda420ffc033c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Sep 2018 19:23:19 +0900 Subject: [PATCH 37/60] Update framework again --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b3df528043..cb553731c3 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,7 +18,7 @@ - + From 1e39b84089c8d040f92a29327a1619e773dae01b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Sep 2018 23:49:54 +0900 Subject: [PATCH 38/60] Increase visibility of osu!mania long notes --- .../Objects/Drawables/DrawableHoldNote.cs | 19 +++++++++++--- .../Objects/Drawables/Pieces/BodyPiece.cs | 25 +++++++++++++++++-- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index af2a889f03..6a0457efc6 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -111,6 +111,18 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables bodyPiece.Height = DrawHeight - Head.Height / 2 + Tail.Height / 2; } + protected void BeginHold() + { + holdStartTime = Time.Current; + bodyPiece.Hitting = true; + } + + protected void EndHold() + { + holdStartTime = null; + bodyPiece.Hitting = false; + } + public bool OnPressed(ManiaAction action) { // Make sure the action happened within the body of the hold note @@ -123,8 +135,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables // The user has pressed during the body of the hold note, after the head note and its hit windows have passed // and within the limited range of the above if-statement. This state will be managed by the head note if the // user has pressed during the hit windows of the head note. - holdStartTime = Time.Current; - + BeginHold(); return true; } @@ -137,7 +148,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (action != Action.Value) return false; - holdStartTime = null; + EndHold(); // If the key has been released too early, the user should not receive full score for the release if (!Tail.IsHit) @@ -170,7 +181,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables // The head note also handles early hits before the body, but we want accurate early hits to count as the body being held // The body doesn't handle these early early hits, so we have to explicitly set the holding state here - holdNote.holdStartTime = Time.Current; + holdNote.BeginHold(); return true; } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs index 4ab2da208a..01bc02b15a 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs @@ -32,6 +32,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces background = new Box { RelativeSizeAxes = Axes.Both }, foreground = new BufferedContainer { + Blending = BlendingMode.Additive, RelativeSizeAxes = Axes.Both, CacheDrawnFrameBuffer = true, Children = new Drawable[] @@ -73,6 +74,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces } private Color4 accentColour; + public Color4 AccentColour { get { return accentColour; } @@ -86,6 +88,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces } } + public bool Hitting + { + get { return hitting; } + set + { + hitting = value; + updateAccentColour(); + } + } + private Cached subtractionCache = new Cached(); public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) @@ -118,13 +130,22 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces } } + private bool hitting; + private void updateAccentColour() { if (!IsLoaded) return; - foreground.Colour = AccentColour.Opacity(0.9f); - background.Colour = AccentColour.Opacity(0.6f); + foreground.Colour = AccentColour.Opacity(0.5f); + background.Colour = AccentColour.Opacity(0.7f); + + if (hitting) + foreground.FadeColour(AccentColour.Lighten(0.3f), 50).Then().FadeColour(foreground.Colour, 50).Loop(); + else + { + foreground.ClearTransforms(false, nameof(foreground.Colour)); + } subtractionCache.Invalidate(); } From a2ba68d147e99b736df230b0dfa48dc0bf4cd103 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Sep 2018 03:18:39 +0900 Subject: [PATCH 39/60] Mention LD_LIBRARY_PATH requirement for linux building in README Closes #3257. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a1f478e39a..dc36145337 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,9 @@ Clone the repository including submodules Build and run - Using Visual Studio 2017, Rider or Visual Studio Code (configurations are included) -- From command line using `dotnet run --project osu.Desktop` +- From command line using `dotnet run --project osu.Desktop`. When building for non-development purposes, add `-c Release` to gain higher performance. + +Note: If you run from command line under linux, you will need to prefix the output folder to your `LD_LIBRARY_PATH`. See `.vscode/launch.json` for an example If you run into issues building you may need to restore nuget packages (commonly via `dotnet restore`). Visual Studio Code users must run `Restore` task from debug tab before attempt to build. From f76d00e8aee43920d978e6551d3c0b52e7312ee7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Sep 2018 14:31:38 +0900 Subject: [PATCH 40/60] Simplify null checks --- .../Sections/Graphics/LayoutSettings.cs | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 4d3cdacb6a..0c463ce142 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -101,18 +101,13 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics }, true); } - private List> getResolutions() - { - if (game.Window == null) - return new List>(); - - return game.Window?.AvailableResolutions - .Where(r => r.Width >= 800 && r.Height >= 600) - .OrderByDescending(r => r.Width) - .ThenByDescending(r => r.Height) - .Select(res => new KeyValuePair($"{res.Width}x{res.Height}", new Size(res.Width, res.Height))) - .Distinct() - .ToList(); - } + private IEnumerable> getResolutions() => + game.Window?.AvailableResolutions? + .Where(r => r.Width >= 800 && r.Height >= 600) + .OrderByDescending(r => r.Width) + .ThenByDescending(r => r.Height) + .Select(res => new KeyValuePair($"{res.Width}x{res.Height}", new Size(res.Width, res.Height))) + .Distinct() + .ToList() ?? Enumerable.Empty>(); } } From bf0ad723f594f567124db69804e4c7b743b92733 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Sep 2018 14:35:38 +0900 Subject: [PATCH 41/60] Add default fallback for headless operation --- osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 0c463ce142..548d49bd36 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -6,6 +6,7 @@ using System.Drawing; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -108,6 +109,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics .ThenByDescending(r => r.Height) .Select(res => new KeyValuePair($"{res.Width}x{res.Height}", new Size(res.Width, res.Height))) .Distinct() - .ToList() ?? Enumerable.Empty>(); + .ToList() ?? new KeyValuePair("Default", new Size(9999, 9999)).Yield(); } } From 2c0ba401d529ad4de98e3f5df56f736a0280ac43 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Sep 2018 16:30:11 +0900 Subject: [PATCH 42/60] Add ability to click on imported complete notification to present last import --- osu.Game/Beatmaps/BeatmapManager.cs | 9 ++++++++- osu.Game/Database/ArchiveModelManager.cs | 12 +++++++++++- .../Overlays/Notifications/ProgressNotification.cs | 2 +- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f676927404..eb30525e80 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -11,6 +11,7 @@ using Microsoft.EntityFrameworkCore; using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Extensions; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics.Textures; using osu.Framework.Logging; using osu.Framework.Platform; @@ -195,7 +196,7 @@ namespace osu.Game.Beatmaps downloadNotification.CompletionClickAction = () => { - PresentBeatmap?.Invoke(importedBeatmap); + PresentCompletedImport(importedBeatmap.Yield()); return true; }; downloadNotification.State = ProgressNotificationState.Completed; @@ -231,6 +232,12 @@ namespace osu.Game.Beatmaps BeatmapDownloadBegan?.Invoke(request); } + protected override void PresentCompletedImport(IEnumerable imported) + { + base.PresentCompletedImport(imported); + PresentBeatmap?.Invoke(imported.LastOrDefault()); + } + /// /// Get an existing download request if it exists. /// diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 326d042c39..e85d6f29aa 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -166,10 +166,20 @@ namespace osu.Game.Database } } - notification.Text = errors > 0 ? $"Import complete with {errors} errors" : "Import successful!"; + notification.CompletionText = errors > 0 ? $"Import complete with {errors} errors" : "Import successful!"; + notification.CompletionClickAction += () => + { + if (imported.Count > 0) + PresentCompletedImport(imported); + return true; + }; notification.State = ProgressNotificationState.Completed; } + protected virtual void PresentCompletedImport(IEnumerable imported) + { + } + /// /// Import an item from an . /// diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index 7a07fb970c..254258d098 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -169,7 +169,7 @@ namespace osu.Game.Overlays.Notifications public Action CompletionTarget { get; set; } /// - /// An action to complete when the completion notification is clicked. + /// An action to complete when the completion notification is clicked. Return true to close. /// public Func CompletionClickAction; From ced6e5efd09788de3133e14d043a2f337c68f85c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Sep 2018 16:29:29 +0900 Subject: [PATCH 43/60] Synchronise animation; reduce flashiness --- .../Objects/Drawables/Pieces/BodyPiece.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs index 01bc02b15a..619fe06c73 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs @@ -140,11 +140,15 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces foreground.Colour = AccentColour.Opacity(0.5f); background.Colour = AccentColour.Opacity(0.7f); + const float animation_length = 50; + + foreground.ClearTransforms(false, nameof(foreground.Colour)); if (hitting) - foreground.FadeColour(AccentColour.Lighten(0.3f), 50).Then().FadeColour(foreground.Colour, 50).Loop(); - else { - foreground.ClearTransforms(false, nameof(foreground.Colour)); + // wait for the next sync point + double synchronisedOffset = animation_length * 2 - Time.Current % (animation_length * 2); + using (foreground.BeginDelayedSequence(synchronisedOffset)) + foreground.FadeColour(AccentColour.Lighten(0.2f), animation_length).Then().FadeColour(foreground.Colour, animation_length).Loop(); } subtractionCache.Invalidate(); From 168dbe9329c3f390c19a9f3976b04e3595ccc165 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Sep 2018 18:14:23 +0900 Subject: [PATCH 44/60] Fix error notification --- osu.Game/Database/ArchiveModelManager.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 234e1a9bd6..0564f1eb76 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -130,7 +130,6 @@ namespace osu.Game.Database List imported = new List(); int current = 0; - int errors = 0; foreach (string path in paths) { if (notification.State == ProgressNotificationState.Cancelled) @@ -163,13 +162,19 @@ namespace osu.Game.Database { e = e.InnerException ?? e; Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})"); - errors++; } } - notification.Text = errors > 0 ? $"Import complete with {errors} errors" : "Import successful!"; - notification.CompletionText = $"Imported {current} {typeof(TModel).Name.Replace("Info", "").ToLower()}s!"; - notification.State = ProgressNotificationState.Completed; + if (imported.Count == 0) + { + notification.Text = "Import failed!"; + notification.State = ProgressNotificationState.Cancelled; + } + else + { + notification.CompletionText = $"Imported {current} {typeof(TModel).Name.Replace("Info", "").ToLower()}s!"; + notification.State = ProgressNotificationState.Completed; + } } /// From 75d2cb199cb858a68fa01eac2370d02e598c40d3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Sep 2018 18:14:27 +0900 Subject: [PATCH 45/60] Fix formatting --- osu.Game/Skinning/SkinManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 74222da4a2..bd694e443a 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -93,7 +93,8 @@ namespace osu.Game.Skinning { this.audio = audio; - ItemRemoved += removedInfo => { + ItemRemoved += removedInfo => + { // check the removed skin is not the current user choice. if it is, switch back to default. if (removedInfo.ID == CurrentSkinInfo.Value.ID) CurrentSkinInfo.Value = SkinInfo.Default; From cc533a05c57916facbd1b5a260cfa7794180b666 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Sep 2018 18:56:08 +0900 Subject: [PATCH 46/60] Update framework --- osu.Game/OsuGameBase.cs | 2 +- osu.Game/Screens/Tournament/Drawings.cs | 2 +- osu.Game/Skinning/LegacySkin.cs | 2 +- osu.Game/osu.Game.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 0037ecf335..80520a1c31 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -108,7 +108,7 @@ namespace osu.Game dependencies.Cache(contextFactory = new DatabaseContextFactory(Host.Storage)); - dependencies.Cache(new LargeTextureStore(new RawTextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures")))); + dependencies.Cache(new LargeTextureStore(new TextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures")))); dependencies.CacheAs(this); dependencies.Cache(LocalConfig); diff --git a/osu.Game/Screens/Tournament/Drawings.cs b/osu.Game/Screens/Tournament/Drawings.cs index 63d29d5cd7..4e2109afd0 100644 --- a/osu.Game/Screens/Tournament/Drawings.cs +++ b/osu.Game/Screens/Tournament/Drawings.cs @@ -59,7 +59,7 @@ namespace osu.Game.Screens.Tournament TextureStore flagStore = new TextureStore(); // Local flag store - flagStore.AddStore(new RawTextureLoaderStore(new NamespacedResourceStore(new StorageBackedResourceStore(storage), "Drawings"))); + flagStore.AddStore(new TextureLoaderStore(new NamespacedResourceStore(new StorageBackedResourceStore(storage), "Drawings"))); // Default texture store flagStore.AddStore(textures); diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 9c881c6abb..ce7edf8683 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -37,7 +37,7 @@ namespace osu.Game.Skinning Configuration = new SkinConfiguration(); Samples = audioManager.GetSampleManager(storage); - Textures = new TextureStore(new RawTextureLoaderStore(storage)); + Textures = new TextureStore(new TextureLoaderStore(storage)); } public override Drawable GetDrawableComponent(string componentName) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index cb553731c3..6401a0eb1b 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,7 +18,7 @@ - + From c1cdff8505dd03a1f4c6465b1529a1d7abdeb7e6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Sep 2018 19:29:51 +0900 Subject: [PATCH 47/60] Add default resolution to avoid crashing --- .../Sections/Graphics/LayoutSettings.cs | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 548d49bd36..4a8164c6df 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -102,13 +102,18 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics }, true); } - private IEnumerable> getResolutions() => - game.Window?.AvailableResolutions? - .Where(r => r.Width >= 800 && r.Height >= 600) - .OrderByDescending(r => r.Width) - .ThenByDescending(r => r.Height) - .Select(res => new KeyValuePair($"{res.Width}x{res.Height}", new Size(res.Width, res.Height))) - .Distinct() - .ToList() ?? new KeyValuePair("Default", new Size(9999, 9999)).Yield(); + private IEnumerable> getResolutions() + { + var resolutions = new KeyValuePair("Default", new Size(9999, 9999)).Yield(); + + if (game.Window != null) + resolutions = resolutions.Concat(game.Window.AvailableResolutions + .Where(r => r.Width >= 800 && r.Height >= 600) + .OrderByDescending(r => r.Width) + .ThenByDescending(r => r.Height) + .Select(res => new KeyValuePair($"{res.Width}x{res.Height}", new Size(res.Width, res.Height))) + .Distinct()).ToList(); + return resolutions; + } } } From 6c150c9ed793799fd6672cc2107c97c2e3844a09 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 8 Sep 2018 03:21:42 +0900 Subject: [PATCH 48/60] Remove unnecessary override --- osu.Game/Overlays/Direct/DirectPanel.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 8a22ff7587..2ee1857ca2 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -104,8 +104,6 @@ namespace osu.Game.Overlays.Direct beatmaps.ItemAdded += setAdded; } - public override bool DisposeOnDeathRemoval => true; - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From f149a66a4dff892d23c84580a62c3bf718a8c3a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 9 Sep 2018 02:41:47 +0900 Subject: [PATCH 49/60] Use LargeTextureStore for all online texture retrieval Until now, many online textures were retrieved via the default texture store, which causes them to never be removed from GPU memory. It also has a performance overhead due to mipmap generation (which will be avoided via ppy/osu-framework#1885. --- osu.Game/OsuGameBase.cs | 4 +++- osu.Game/Overlays/MedalSplash/DrawableMedal.cs | 4 ++-- osu.Game/Overlays/Profile/Header/BadgeContainer.cs | 2 +- osu.Game/Overlays/Profile/Sections/Recent/MedalIcon.cs | 2 +- osu.Game/Screens/Ranking/ResultsPageScore.cs | 2 +- osu.Game/Users/Avatar.cs | 2 +- osu.Game/Users/UserCoverBackground.cs | 2 +- 7 files changed, 10 insertions(+), 8 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 80520a1c31..9a5dac35b9 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -108,7 +108,9 @@ namespace osu.Game dependencies.Cache(contextFactory = new DatabaseContextFactory(Host.Storage)); - dependencies.Cache(new LargeTextureStore(new TextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures")))); + var largeStore = new LargeTextureStore(new TextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures"))); + largeStore.AddStore(new TextureLoaderStore(new OnlineStore())); + dependencies.Cache(largeStore); dependencies.CacheAs(this); dependencies.Cache(LocalConfig); diff --git a/osu.Game/Overlays/MedalSplash/DrawableMedal.cs b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs index a27278e002..629b6d6fa4 100644 --- a/osu.Game/Overlays/MedalSplash/DrawableMedal.cs +++ b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs @@ -118,9 +118,9 @@ namespace osu.Game.Overlays.MedalSplash } [BackgroundDependencyLoader] - private void load(OsuColour colours, TextureStore textures) + private void load(OsuColour colours, TextureStore textures, LargeTextureStore largeTextures) { - medalSprite.Texture = textures.Get(medal.ImageUrl); + medalSprite.Texture = largeTextures.Get(medal.ImageUrl); medalGlow.Texture = textures.Get(@"MedalSplash/medal-glow"); description.Colour = colours.BlueLight; } diff --git a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs index bfade5e45c..33baaf1fff 100644 --- a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs @@ -176,7 +176,7 @@ namespace osu.Game.Overlays.Profile.Header } [BackgroundDependencyLoader] - private void load(TextureStore textures) + private void load(LargeTextureStore textures) { Child = new Sprite { diff --git a/osu.Game/Overlays/Profile/Sections/Recent/MedalIcon.cs b/osu.Game/Overlays/Profile/Sections/Recent/MedalIcon.cs index 0d354c728f..45569271df 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/MedalIcon.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/MedalIcon.cs @@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Profile.Sections.Recent } [BackgroundDependencyLoader] - private void load(TextureStore textures) + private void load(LargeTextureStore textures) { sprite.Texture = textures.Get(url); } diff --git a/osu.Game/Screens/Ranking/ResultsPageScore.cs b/osu.Game/Screens/Ranking/ResultsPageScore.cs index 42d8af07b9..6fa41202a4 100644 --- a/osu.Game/Screens/Ranking/ResultsPageScore.cs +++ b/osu.Game/Screens/Ranking/ResultsPageScore.cs @@ -368,7 +368,7 @@ namespace osu.Game.Screens.Ranking } [BackgroundDependencyLoader] - private void load(TextureStore textures) + private void load(LargeTextureStore textures) { if (!string.IsNullOrEmpty(user.CoverUrl)) cover.Texture = textures.Get(user.CoverUrl); diff --git a/osu.Game/Users/Avatar.cs b/osu.Game/Users/Avatar.cs index 9ba9549164..e6e1ba3c53 100644 --- a/osu.Game/Users/Avatar.cs +++ b/osu.Game/Users/Avatar.cs @@ -24,7 +24,7 @@ namespace osu.Game.Users } [BackgroundDependencyLoader] - private void load(TextureStore textures) + private void load(LargeTextureStore textures) { if (textures == null) throw new ArgumentNullException(nameof(textures)); diff --git a/osu.Game/Users/UserCoverBackground.cs b/osu.Game/Users/UserCoverBackground.cs index 58b92b2750..fddbd57f9c 100644 --- a/osu.Game/Users/UserCoverBackground.cs +++ b/osu.Game/Users/UserCoverBackground.cs @@ -18,7 +18,7 @@ namespace osu.Game.Users } [BackgroundDependencyLoader] - private void load(TextureStore textures) + private void load(LargeTextureStore textures) { if (textures == null) throw new ArgumentNullException(nameof(textures)); From 55372496d117463c11e8e79043e0f1f1c6580956 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 9 Sep 2018 22:37:15 +0900 Subject: [PATCH 50/60] Fix thread-safety of queued events list in ArchiveModelManager --- osu.Game/Database/ArchiveModelManager.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index e9fe943f15..f4f169f27c 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -59,7 +59,7 @@ namespace osu.Game.Database // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) private ArchiveImportIPCChannel ipc; - private readonly List cachedEvents = new List(); + private readonly List queuedEvents = new List(); /// /// Allows delaying of outwards events until an operation is confirmed (at a database level). @@ -77,20 +77,26 @@ namespace osu.Game.Database /// Whether the flushed events should be performed. private void flushEvents(bool perform) { + Action[] events; + lock (queuedEvents) + { + events = queuedEvents.ToArray(); + queuedEvents.Clear(); + } + if (perform) { - foreach (var a in cachedEvents) + foreach (var a in events) a.Invoke(); } - cachedEvents.Clear(); delayingEvents = false; } private void handleEvent(Action a) { if (delayingEvents) - cachedEvents.Add(a); + lock (queuedEvents) queuedEvents.Add(a); else a.Invoke(); } From 92386edb32e54a65774ebcf52f53505170ccaef8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 10 Sep 2018 18:13:44 +0900 Subject: [PATCH 51/60] Scale the results screen score to keep it in view --- osu.Game/Screens/Ranking/ResultsPageScore.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Ranking/ResultsPageScore.cs b/osu.Game/Screens/Ranking/ResultsPageScore.cs index 6fa41202a4..040458e0eb 100644 --- a/osu.Game/Screens/Ranking/ResultsPageScore.cs +++ b/osu.Game/Screens/Ranking/ResultsPageScore.cs @@ -29,6 +29,7 @@ namespace osu.Game.Screens.Ranking { public class ResultsPageScore : ResultsPage { + private Container scoreContainer; private ScoreCounter scoreCounter; public ResultsPageScore(Score score, WorkingBeatmap beatmap) : base(score, beatmap) { } @@ -76,7 +77,7 @@ namespace osu.Game.Screens.Ranking Size = new Vector2(150, 60), Margin = new MarginPadding(20), }, - new Container + scoreContainer = new Container { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, @@ -92,8 +93,8 @@ namespace osu.Game.Screens.Ranking }, scoreCounter = new SlowScoreCounter(6) { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Colour = colours.PinkDarker, Y = 10, TextSize = 56, @@ -185,6 +186,13 @@ namespace osu.Game.Screens.Ranking }); } + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + scoreCounter.Scale = new Vector2(Math.Min(1f, (scoreContainer.DrawWidth - 20) / scoreCounter.DrawWidth)); + } + private class DrawableScoreStatistic : Container { private readonly KeyValuePair statistic; From 144779c698e5372409b2691cfbcb2f123850d39e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Sep 2018 00:30:13 +0900 Subject: [PATCH 52/60] Update framework --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 6401a0eb1b..3e16e90d06 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,7 +18,7 @@ - + From 9b0954ab81a162971103867b0d967539a53d7215 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 11 Sep 2018 11:28:02 +0900 Subject: [PATCH 53/60] Reference whether texture is available rather than disposed --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index a2b44aab52..e0a22460ef 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -135,7 +135,7 @@ namespace osu.Game.Beatmaps public bool BackgroundLoaded => background.IsResultAvailable; public Texture Background => background.Value; - protected virtual bool BackgroundStillValid(Texture b) => b == null || !b.IsDisposed; + protected virtual bool BackgroundStillValid(Texture b) => b == null || b.Available; protected abstract Texture GetBackground(); private readonly RecyclableLazy background; From 02313c6d6edeab0350cc22785bcb54220f6bdefa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Sep 2018 13:10:34 +0900 Subject: [PATCH 54/60] Wait longer before confirming test players are cleaned up --- osu.Game/Tests/Visual/TestCasePlayer.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index 9afb1dd6cd..9e499cb961 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -54,7 +54,7 @@ namespace osu.Game.Tests.Visual AddStep(r.Name, () => p = loadPlayerFor(r)); AddUntilStep(() => ContinueCondition(p)); - AddAssert("no leaked beatmaps", () => + AddUntilStep(() => { p = null; @@ -64,9 +64,9 @@ namespace osu.Game.Tests.Visual workingWeakReferences.ForEachAlive(_ => count++); return count == 1; - }); + }, "no leaked beatmaps"); - AddAssert("no leaked players", () => + AddUntilStep(() => { GC.Collect(); GC.WaitForPendingFinalizers(); @@ -74,7 +74,7 @@ namespace osu.Game.Tests.Visual playerWeakReferences.ForEachAlive(_ => count++); return count == 1; - }); + }, "no leaked players"); } } } From 356a60b56181fdb5e2c8414bf90fc620a7aed869 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 12 Sep 2018 15:09:10 +0900 Subject: [PATCH 55/60] Fix hitobjects in scrolling rulesets getting masked away --- .../Rulesets/Objects/Drawables/DrawableHitObject.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 7e3e955740..a274d9b12f 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -7,6 +7,8 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Extensions.TypeExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Primitives; using osu.Game.Audio; using osu.Game.Graphics; using osu.Game.Rulesets.Judgements; @@ -165,6 +167,14 @@ namespace osu.Game.Rulesets.Objects.Drawables } } + public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds) + { + if (!AllJudged) + return false; + + return base.UpdateSubTreeMasking(source, maskingBounds); + } + protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); From a3c3bfb1a8628e574611c4dcf3d81c8d8d8286ba Mon Sep 17 00:00:00 2001 From: Hanamuke Date: Wed, 12 Sep 2018 19:48:35 +0200 Subject: [PATCH 56/60] Fixes hyperdash computation (for nested objects) --- .../Beatmaps/CatchBeatmapProcessor.cs | 45 ++++++++++--------- .../Objects/CatchHitObject.cs | 4 +- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index ab0afb08d7..34bddf425c 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -74,42 +74,43 @@ namespace osu.Game.Rulesets.Catch.Beatmaps private void initialiseHyperDash(List objects) { - // todo: add difficulty adjust. - double halfCatcherWidth = CatcherArea.CATCHER_SIZE * (objects.FirstOrDefault()?.Scale ?? 1) / CatchPlayfield.BASE_WIDTH / 2; + if (objects.Count == 0) + return; + List objectWithDroplets = new List(); + + foreach (var currentObject in objects) + { + if (currentObject is Fruit) + objectWithDroplets.Add(currentObject); + if (currentObject is JuiceStream) + foreach (var currentJuiceElement in currentObject.NestedHitObjects) + if (!(currentJuiceElement is TinyDroplet)) + objectWithDroplets.Add((CatchHitObject)currentJuiceElement); + } + + double halfCatcherWidth = CatcherArea.CATCHER_SIZE * objectWithDroplets[0].Scale / CatchPlayfield.BASE_WIDTH / 2; int lastDirection = 0; double lastExcess = halfCatcherWidth; - int objCount = objects.Count; - - for (int i = 0; i < objCount - 1; i++) + for (int i = 0; i < objectWithDroplets.Count - 1; i++) { - CatchHitObject currentObject = objects[i]; - - // not needed? - // if (currentObject is TinyDroplet) continue; - - CatchHitObject nextObject = objects[i + 1]; - - // while (nextObject is TinyDroplet) - // { - // if (++i == objCount - 1) break; - // nextObject = objects[i + 1]; - // } + CatchHitObject currentObject = objectWithDroplets[i]; + CatchHitObject nextObject = objectWithDroplets[i + 1]; int thisDirection = nextObject.X > currentObject.X ? 1 : -1; - double timeToNext = nextObject.StartTime - ((currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime) - 4; + double timeToNext = nextObject.StartTime - currentObject.StartTime; double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth); - - if (timeToNext * CatcherArea.Catcher.BASE_SPEED < distanceToNext) + float distanceToHyper = (float)(timeToNext * CatcherArea.Catcher.BASE_SPEED - distanceToNext); + if (distanceToHyper < 0) { currentObject.HyperDashTarget = nextObject; lastExcess = halfCatcherWidth; } else { - //currentObject.DistanceToHyperDash = timeToNext - distanceToNext; - lastExcess = MathHelper.Clamp(timeToNext - distanceToNext, 0, halfCatcherWidth); + currentObject.DistanceToHyperDash = distanceToHyper; + lastExcess = MathHelper.Clamp(distanceToHyper, 0, halfCatcherWidth); } lastDirection = thisDirection; diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index 621fc100c2..5eae4de9e6 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -27,7 +27,9 @@ namespace osu.Game.Rulesets.Catch.Objects public int ComboIndex { get; set; } /// - /// The distance for a fruit to to next hyper if it's not a hyper. + /// The difference between the distance of the next object + /// and the distance that would have triggered hyper dashing. + /// A value close to 0 indicates a difficult jump (for SR calculation) /// public float DistanceToHyperDash { get; set; } From 067bc39185149ac34be800fdbefdba7190539356 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Sep 2018 12:47:26 +0900 Subject: [PATCH 57/60] Update nuget package --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3e16e90d06..83ab5534c6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,7 +18,7 @@ - + From 7d3380db665fb938c5c9c6992d76eec07a5fa6e7 Mon Sep 17 00:00:00 2001 From: Hanamuke Date: Thu, 13 Sep 2018 17:01:33 +0200 Subject: [PATCH 58/60] Fixed comment. Created static CatchArea.GetCatcheWidth method --- osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs | 5 +---- osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs | 6 +++--- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 5 +++++ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 34bddf425c..d4be6564ca 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -74,9 +74,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps private void initialiseHyperDash(List objects) { - if (objects.Count == 0) - return; - List objectWithDroplets = new List(); foreach (var currentObject in objects) @@ -89,7 +86,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps objectWithDroplets.Add((CatchHitObject)currentJuiceElement); } - double halfCatcherWidth = CatcherArea.CATCHER_SIZE * objectWithDroplets[0].Scale / CatchPlayfield.BASE_WIDTH / 2; + double halfCatcherWidth = CatcherArea.GetCatcherSize(Beatmap.BeatmapInfo.BaseDifficulty) / 2; int lastDirection = 0; double lastExcess = halfCatcherWidth; diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index 5eae4de9e6..773bf576dd 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -27,9 +27,9 @@ namespace osu.Game.Rulesets.Catch.Objects public int ComboIndex { get; set; } /// - /// The difference between the distance of the next object - /// and the distance that would have triggered hyper dashing. - /// A value close to 0 indicates a difficult jump (for SR calculation) + /// Difference between the distance to the next object + /// and the distance that would have triggered a hyper dash. + /// A value close to 0 indicates a difficult jump (for difficulty calculation). /// public float DistanceToHyperDash { get; set; } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 9460512a8d..1662246b62 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -107,6 +107,11 @@ namespace osu.Game.Rulesets.Catch.UI public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj); + public static float GetCatcherSize(BeatmapDifficulty difficulty) + { + return (CATCHER_SIZE / CatchPlayfield.BASE_WIDTH) * (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5); + } + public class Catcher : Container, IKeyBindingHandler { /// From 7e07a07c01004655b812c3a01c1614ccf4d7303c Mon Sep 17 00:00:00 2001 From: Hanamuke Date: Thu, 13 Sep 2018 17:15:46 +0200 Subject: [PATCH 59/60] Fix HitObjects being out of order because of nested objects --- osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index d4be6564ca..c7ea29f8c0 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -86,6 +86,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps objectWithDroplets.Add((CatchHitObject)currentJuiceElement); } + objectWithDroplets.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime)); + double halfCatcherWidth = CatcherArea.GetCatcherSize(Beatmap.BeatmapInfo.BaseDifficulty) / 2; int lastDirection = 0; double lastExcess = halfCatcherWidth; From 9b6f5c90976a41595814aec313d643349a7a8626 Mon Sep 17 00:00:00 2001 From: Hanamuke Date: Thu, 13 Sep 2018 17:29:10 +0200 Subject: [PATCH 60/60] Fix redundant paranthesis --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 1662246b62..be56ccf8c1 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -109,7 +109,7 @@ namespace osu.Game.Rulesets.Catch.UI public static float GetCatcherSize(BeatmapDifficulty difficulty) { - return (CATCHER_SIZE / CatchPlayfield.BASE_WIDTH) * (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5); + return CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5); } public class Catcher : Container, IKeyBindingHandler