diff --git a/osu-framework b/osu-framework index f1751c27ff..61e676094d 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit f1751c27ffe2c5febece74129368596b5ad3a4e2 +Subproject commit 61e676094d25436bb9e8858946f65c43d15d8e01 diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs index 790c4cedc3..51a8bacd2e 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs @@ -110,11 +110,13 @@ namespace osu.Game.Tests.Visual private void selectBeatmap(string name) { - var infoBefore = infoWedge.Info; + BeatmapInfoWedge.BufferedWedgeInfo infoBefore = null; AddStep($"select {name} beatmap", () => { - beatmap.Value = new TestWorkingBeatmap(beatmaps.First(b => b.BeatmapInfo.Ruleset.ShortName == name)); + infoBefore = infoWedge.Info; + WorkingBeatmap bm = new TestWorkingBeatmap(beatmaps.First(b => b.BeatmapInfo.Ruleset.ShortName == name)); + beatmap.Value = bm; infoWedge.UpdateBeatmap(beatmap); }); diff --git a/osu.Game.Tests/Visual/TestCasePlayerLoader.cs b/osu.Game.Tests/Visual/TestCasePlayerLoader.cs new file mode 100644 index 0000000000..1e7618232d --- /dev/null +++ b/osu.Game.Tests/Visual/TestCasePlayerLoader.cs @@ -0,0 +1,24 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Screens.Play; + +namespace osu.Game.Tests.Visual +{ + public class TestCasePlayerLoader : OsuTestCase + { + [BackgroundDependencyLoader] + private void load(OsuGameBase game) + { + AddStep("load dummy beatmap", () => Add(new PlayerLoader(new Player + { + InitialBeatmap = new DummyWorkingBeatmap(game), + AllowPause = false, + AllowLeadIn = false, + AllowResults = false, + }))); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/TestCaseUserProfile.cs index 0fdc01a974..aca832110a 100644 --- a/osu.Game.Tests/Visual/TestCaseUserProfile.cs +++ b/osu.Game.Tests/Visual/TestCaseUserProfile.cs @@ -5,7 +5,9 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Overlays.Profile; using osu.Game.Overlays.Profile.Header; @@ -17,6 +19,7 @@ namespace osu.Game.Tests.Visual public class TestCaseUserProfile : OsuTestCase { private readonly TestUserProfileOverlay profile; + private APIAccess api; public override IReadOnlyList<Type> RequiredTypes => new[] { @@ -32,6 +35,12 @@ namespace osu.Game.Tests.Visual Add(profile = new TestUserProfileOverlay()); } + [BackgroundDependencyLoader] + private void load(APIAccess api) + { + this.api = api; + } + protected override void LoadComplete() { base.LoadComplete(); @@ -79,9 +88,10 @@ namespace osu.Game.Tests.Visual { Username = @"peppy", Id = 2, + IsSupporter = true, Country = new Country { FullName = @"Australia", FlagName = @"AU" }, CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg" - })); + }, api.IsLoggedIn)); checkSupporterTag(true); @@ -91,7 +101,7 @@ namespace osu.Game.Tests.Visual Id = 3103765, Country = new Country { FullName = @"Japan", FlagName = @"JP" }, CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" - })); + }, api.IsLoggedIn)); AddStep("Hide", profile.Hide); AddStep("Show without reload", profile.Show); diff --git a/osu.Game/IO/Archives/ArchiveReader.cs b/osu.Game/IO/Archives/ArchiveReader.cs index d14080de5b..808ce159bb 100644 --- a/osu.Game/IO/Archives/ArchiveReader.cs +++ b/osu.Game/IO/Archives/ArchiveReader.cs @@ -1,14 +1,13 @@ // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using System.Collections.Generic; using System.IO; using osu.Framework.IO.Stores; namespace osu.Game.IO.Archives { - public abstract class ArchiveReader : IDisposable, IResourceStore<byte[]> + public abstract class ArchiveReader : IResourceStore<byte[]> { /// <summary> /// Opens a stream for reading a specific file from this archive. diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index b76510ea1e..487cb50c9a 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -188,6 +188,20 @@ namespace osu.Game FileStore.Cleanup(); AddInternal(api); + + GlobalActionContainer globalBinding; + + CursorOverrideContainer = new CursorOverrideContainer { RelativeSizeAxes = Axes.Both }; + CursorOverrideContainer.Child = globalBinding = new GlobalActionContainer(this) + { + RelativeSizeAxes = Axes.Both, + Child = content = new OsuTooltipContainer(CursorOverrideContainer.Cursor) { RelativeSizeAxes = Axes.Both } + }; + + base.Content.Add(new DrawSizePreservingFillContainer { Child = CursorOverrideContainer }); + + KeyBindingStore.Register(globalBinding); + dependencies.Cache(globalBinding); } private void runMigrations() @@ -217,20 +231,6 @@ namespace osu.Game { base.LoadComplete(); - GlobalActionContainer globalBinding; - - CursorOverrideContainer = new CursorOverrideContainer { RelativeSizeAxes = Axes.Both }; - CursorOverrideContainer.Child = globalBinding = new GlobalActionContainer(this) - { - RelativeSizeAxes = Axes.Both, - Child = content = new OsuTooltipContainer(CursorOverrideContainer.Cursor) { RelativeSizeAxes = Axes.Both } - }; - - base.Content.Add(new DrawSizePreservingFillContainer { Child = CursorOverrideContainer }); - - KeyBindingStore.Register(globalBinding); - dependencies.Cache(globalBinding); - // TODO: This is temporary until we reimplement the local FPS display. // It's just to allow end-users to access the framework FPS display without knowing the shortcut key. fpsDisplayVisible = LocalConfig.GetBindable<bool>(OsuSetting.ShowFpsDisplay); diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs index e7d3690f59..5e9863f642 100644 --- a/osu.Game/Screens/BackgroundScreen.cs +++ b/osu.Game/Screens/BackgroundScreen.cs @@ -26,14 +26,14 @@ namespace osu.Game.Screens return false; } - public override bool Push(Screen screen) + public override void Push(Screen screen) { // When trying to push a non-loaded screen, load it asynchronously and re-invoke Push // once it's done. if (screen.LoadState == LoadState.NotLoaded) { LoadComponentAsync(screen, d => Push((BackgroundScreen)d)); - return true; + return; } // Make sure the in-progress loading is complete before pushing the screen. @@ -41,8 +41,6 @@ namespace osu.Game.Screens Thread.Sleep(1); base.Push(screen); - - return true; } protected override void Update() diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index ea1d85bb5b..adb749b492 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -178,7 +178,7 @@ namespace osu.Game.Screens.Edit } currentScreen.Beatmap.BindTo(Beatmap); - screenContainer.Add(currentScreen); + LoadComponentAsync(currentScreen, screenContainer.Add); } protected override bool OnWheel(InputState state) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ec7c1a1009..83958b2912 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Play private DrawableStoryboard storyboard; private Container storyboardContainer; - private bool loadedSuccessfully => RulesetContainer?.Objects.Any() == true; + public bool LoadedBeatmapSuccessfully => RulesetContainer?.Objects.Any() == true; [BackgroundDependencyLoader] private void load(AudioManager audio, APIAccess api, OsuConfigManager config) @@ -86,10 +86,7 @@ namespace osu.Game.Screens.Play WorkingBeatmap working = Beatmap.Value; if (working is DummyWorkingBeatmap) - { - Exit(); return; - } sampleRestart = audio.Sample.Get(@"Gameplay/restart"); @@ -122,14 +119,15 @@ namespace osu.Game.Screens.Play } if (!RulesetContainer.Objects.Any()) - throw new InvalidOperationException("Beatmap contains no hit objects!"); + { + Logger.Error(new InvalidOperationException("Beatmap contains no hit objects!"), "Beatmap contains no hit objects!"); + return; + } } catch (Exception e) { Logger.Error(e, "Could not load beatmap sucessfully!"); - //couldn't load, hard abort! - Exit(); return; } @@ -293,7 +291,7 @@ namespace osu.Game.Screens.Play { base.OnEntering(last); - if (!loadedSuccessfully) + if (!LoadedBeatmapSuccessfully) return; Content.Alpha = 0; @@ -343,7 +341,7 @@ namespace osu.Game.Screens.Play return base.OnExiting(next); } - if (loadedSuccessfully) + if (LoadedBeatmapSuccessfully) pauseContainer?.Pause(); return true; diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index ca22b274a4..56fbd7b6e7 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -159,14 +159,14 @@ namespace osu.Game.Screens.Play loadTask = null; - if (!Push(player)) - Exit(); + //By default, we want to load the player and never be returned to. + //Note that this may change if the player we load requested a re-run. + ValidForResume = false; + + if (player.LoadedBeatmapSuccessfully) + Push(player); else - { - //By default, we want to load the player and never be returned to. - //Note that this may change if the player we load requested a re-run. - ValidForResume = false; - } + Exit(); }); }, 500); } diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs index 7ea6e7c82b..a1f44763d6 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs @@ -16,6 +16,12 @@ namespace osu.Game.Screens.Select.Carousel protected List<CarouselItem> InternalChildren = new List<CarouselItem>(); + /// <summary> + /// Used to assign a monotonically increasing ID to children as they are added. This member is + /// incremented whenever a child is added. + /// </summary> + private ulong currentChildID; + public override List<DrawableCarouselItem> Drawables { get @@ -39,6 +45,7 @@ namespace osu.Game.Screens.Select.Carousel public virtual void AddChild(CarouselItem i) { i.State.ValueChanged += v => ChildItemStateChanged(i, v); + i.ChildID = ++currentChildID; InternalChildren.Add(i); } diff --git a/osu.Game/Screens/Select/Carousel/CarouselItem.cs b/osu.Game/Screens/Select/Carousel/CarouselItem.cs index 2efc928984..0de32c12f1 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselItem.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselItem.cs @@ -31,6 +31,8 @@ namespace osu.Game.Screens.Select.Carousel } } + private int creationOrder; + protected CarouselItem() { drawableRepresentation = new Lazy<DrawableCarouselItem>(CreateDrawableRepresentation); @@ -44,13 +46,18 @@ namespace osu.Game.Screens.Select.Carousel private readonly Lazy<DrawableCarouselItem> drawableRepresentation; + /// <summary> + /// Used as a default sort method for <see cref="CarouselItem"/>s of differing types. + /// </summary> + internal ulong ChildID; + protected abstract DrawableCarouselItem CreateDrawableRepresentation(); public virtual void Filter(FilterCriteria criteria) { } - public virtual int CompareTo(FilterCriteria criteria, CarouselItem other) => GetHashCode().CompareTo(other.GetHashCode()); + public virtual int CompareTo(FilterCriteria criteria, CarouselItem other) => ChildID.CompareTo(other.ChildID); } public enum CarouselItemState diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 0a05137a87..45c6ec80aa 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -113,6 +113,31 @@ namespace osu.Game.Skinning string path = getPathForFile(name); return path == null ? null : underlyingStore.Get(path); } + + #region IDisposable Support + + private bool isDisposed; + + protected virtual void Dispose(bool disposing) + { + if (!isDisposed) + { + isDisposed = true; + } + } + + ~LegacySkinResourceStore() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion } } } diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs index 02f425c296..2b677f1f42 100644 --- a/osu.Game/Tests/Visual/OsuTestCase.cs +++ b/osu.Game/Tests/Visual/OsuTestCase.cs @@ -15,18 +15,14 @@ namespace osu.Game.Tests.Visual { protected override string MainResourceFile => File.Exists(base.MainResourceFile) ? base.MainResourceFile : Assembly.GetExecutingAssembly().Location; - private readonly TestCaseTestRunner.TestRunner runner; + private TestCaseTestRunner.TestRunner runner; - public OsuTestCaseTestRunner() + protected override void LoadAsyncComplete() { - runner = new TestCaseTestRunner.TestRunner(); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Add(runner); + // this has to be run here rather than LoadComplete because + // TestCase.cs is checking the IsLoaded state (on another thread) and expects + // the runner to be loaded at that point. + Add(runner = new TestCaseTestRunner.TestRunner()); } public void RunTestBlocking(TestCase test) => runner.RunTestBlocking(test);