diff --git a/osu.Android.props b/osu.Android.props index 5688fd8cc4..c28085557e 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs index a36f07ff7b..496d495b43 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs @@ -17,11 +17,11 @@ using osu.Framework.Testing.Input; using osu.Framework.Utils; using osu.Game.Audio; using osu.Game.Configuration; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Osu.UI.Cursor; using osu.Game.Screens.Play; using osu.Game.Skinning; +using osu.Game.Tests.Gameplay; using osuTK; namespace osu.Game.Rulesets.Osu.Tests @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Tests public TestSceneGameplayCursor() { var ruleset = new OsuRuleset(); - gameplayState = new GameplayState(CreateBeatmap(ruleset.RulesetInfo), ruleset, Array.Empty()); + gameplayState = TestGameplayState.Create(ruleset); AddStep("change background colour", () => { diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index 53364b6d89..e9aa85f4ce 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -6,7 +6,6 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; -using osu.Framework.Graphics.Containers; using osu.Framework.Lists; using osu.Framework.Testing; using osu.Framework.Timing; @@ -22,7 +21,6 @@ using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; using osu.Game.Skinning; using osu.Game.Storyboards; -using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual.Gameplay { @@ -33,18 +31,6 @@ namespace osu.Game.Tests.Visual.Gameplay [Resolved] private SkinManager skinManager { get; set; } - [Cached] - private ScoreProcessor scoreProcessor = new ScoreProcessor(new OsuRuleset()); - - [Cached(typeof(HealthProcessor))] - private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); - - [Cached] - private GameplayState gameplayState = new GameplayState(new TestBeatmap(new OsuRuleset().RulesetInfo), new OsuRuleset()); - - [Cached] - private readonly GameplayClock gameplayClock = new GameplayClock(new FramedClock()); - protected override bool HasCustomSteps => true; [Test] @@ -81,11 +67,19 @@ namespace osu.Game.Tests.Visual.Gameplay if (expectedComponentsContainer == null) return false; - var expectedComponentsAdjustmentContainer = new Container + var expectedComponentsAdjustmentContainer = new DependencyProvidingContainer { Position = actualComponentsContainer.Parent.ToSpaceOfOtherDrawable(actualComponentsContainer.DrawPosition, Content), Size = actualComponentsContainer.DrawSize, Child = expectedComponentsContainer, + // proxy the same required dependencies that `actualComponentsContainer` is using. + CachedDependencies = new (Type, object)[] + { + (typeof(ScoreProcessor), actualComponentsContainer.Dependencies.Get()), + (typeof(HealthProcessor), actualComponentsContainer.Dependencies.Get()), + (typeof(GameplayState), actualComponentsContainer.Dependencies.Get()), + (typeof(GameplayClock), actualComponentsContainer.Dependencies.Get()) + }, }; Add(expectedComponentsAdjustmentContainer); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index 2d12645811..83c557ee51 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -15,7 +15,7 @@ using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play; using osu.Game.Skinning; -using osu.Game.Tests.Beatmaps; +using osu.Game.Tests.Gameplay; using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay @@ -33,7 +33,7 @@ namespace osu.Game.Tests.Visual.Gameplay private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); [Cached] - private GameplayState gameplayState = new GameplayState(new TestBeatmap(new OsuRuleset().RulesetInfo), new OsuRuleset()); + private GameplayState gameplayState = TestGameplayState.Create(new OsuRuleset()); [Cached] private readonly GameplayClock gameplayClock = new GameplayClock(new FramedClock()); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs index 81763564fa..8362739d3b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; @@ -14,16 +13,15 @@ using osu.Framework.Input.Events; using osu.Framework.Input.StateChanges; using osu.Framework.Testing; using osu.Framework.Threading; -using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Replays; using osu.Game.Rulesets; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.UI; using osu.Game.Scoring; using osu.Game.Screens.Play; +using osu.Game.Tests.Gameplay; using osu.Game.Tests.Mods; using osuTK; using osuTK.Graphics; @@ -41,7 +39,7 @@ namespace osu.Game.Tests.Visual.Gameplay private TestReplayRecorder recorder; [Cached] - private GameplayState gameplayState = new GameplayState(new Beatmap(), new OsuRuleset(), Array.Empty()); + private GameplayState gameplayState = TestGameplayState.Create(new OsuRuleset()); [SetUpSteps] public void SetUpSteps() diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs index 8150252d45..5f838b8813 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs @@ -11,7 +11,7 @@ using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play; using osu.Game.Skinning.Editor; -using osu.Game.Tests.Beatmaps; +using osu.Game.Tests.Gameplay; using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.Gameplay private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); [Cached] - private GameplayState gameplayState = new GameplayState(new TestBeatmap(new OsuRuleset().RulesetInfo), new OsuRuleset()); + private GameplayState gameplayState = TestGameplayState.Create(new OsuRuleset()); [Cached] private readonly GameplayClock gameplayClock = new GameplayClock(new FramedClock()); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs index ac5e408d90..5f2d9ee9e8 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs @@ -16,7 +16,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play; -using osu.Game.Tests.Beatmaps; +using osu.Game.Tests.Gameplay; using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Gameplay private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); [Cached] - private GameplayState gameplayState = new GameplayState(new TestBeatmap(new OsuRuleset().RulesetInfo), new OsuRuleset()); + private GameplayState gameplayState = TestGameplayState.Create(new OsuRuleset()); [Cached] private readonly GameplayClock gameplayClock = new GameplayClock(new FramedClock()); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index 8b420cebc8..b5cdd61ee5 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -18,8 +18,8 @@ using osu.Game.Rulesets.UI; using osu.Game.Scoring; using osu.Game.Screens; using osu.Game.Screens.Play; -using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Beatmaps.IO; +using osu.Game.Tests.Gameplay; using osu.Game.Tests.Visual.Multiplayer; using osu.Game.Tests.Visual.Spectator; using osuTK; @@ -259,12 +259,15 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestFinalFramesPurgedBeforeEndingPlay() { - AddStep("begin playing", () => spectatorClient.BeginPlaying(new GameplayState(new TestBeatmap(new OsuRuleset().RulesetInfo), new OsuRuleset()), new Score())); + AddStep("begin playing", () => spectatorClient.BeginPlaying(TestGameplayState.Create(new OsuRuleset()), new Score())); AddStep("send frames and finish play", () => { spectatorClient.HandleFrame(new OsuReplayFrame(1000, Vector2.Zero)); - spectatorClient.EndPlaying(new GameplayState(new TestBeatmap(new OsuRuleset().RulesetInfo), new OsuRuleset()) { HasPassed = true }); + + var completedGameplayState = TestGameplayState.Create(new OsuRuleset()); + completedGameplayState.HasPassed = true; + spectatorClient.EndPlaying(completedGameplayState); }); // We can't access API because we're an "online" test. diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs index f8748922cf..2d2e05c4c9 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs @@ -20,13 +20,13 @@ using osu.Game.Online.Spectator; using osu.Game.Replays; using osu.Game.Replays.Legacy; using osu.Game.Rulesets; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.UI; using osu.Game.Scoring; using osu.Game.Screens.Play; +using osu.Game.Tests.Gameplay; using osu.Game.Tests.Mods; using osu.Game.Tests.Visual.Spectator; using osuTK; @@ -65,7 +65,7 @@ namespace osu.Game.Tests.Visual.Gameplay CachedDependencies = new[] { (typeof(SpectatorClient), (object)(spectatorClient = new TestSpectatorClient())), - (typeof(GameplayState), new GameplayState(new Beatmap(), new OsuRuleset(), Array.Empty())) + (typeof(GameplayState), TestGameplayState.Create(new OsuRuleset())) }, Children = new Drawable[] { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index 7d010592ae..3172a68b81 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -124,13 +124,19 @@ namespace osu.Game.Tests.Visual.Multiplayer Status = { Value = new RoomStatusOpen() }, Category = { Value = RoomCategory.Spotlight }, }), + createLoungeRoom(new Room + { + Name = { Value = "Featured artist room" }, + Status = { Value = new RoomStatusOpen() }, + Category = { Value = RoomCategory.FeaturedArtist }, + }), } }; }); - AddUntilStep("wait for panel load", () => rooms.Count == 5); + AddUntilStep("wait for panel load", () => rooms.Count == 6); AddUntilStep("correct status text", () => rooms.ChildrenOfType().Count(s => s.Text.ToString().StartsWith("Currently playing", StringComparison.Ordinal)) == 2); - AddUntilStep("correct status text", () => rooms.ChildrenOfType().Count(s => s.Text.ToString().StartsWith("Ready to play", StringComparison.Ordinal)) == 3); + AddUntilStep("correct status text", () => rooms.ChildrenOfType().Count(s => s.Text.ToString().StartsWith("Ready to play", StringComparison.Ordinal)) == 4); } [Test] diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs index ed6e2bb2c0..3151279b7c 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs @@ -134,7 +134,7 @@ namespace osu.Game.Tests.Visual.Online channelManager.CurrentChannel.Value = joinedChannel; }); AddAssert("Overlay is visible", () => chatOverlay.State.Value == Visibility.Visible); - AddUntilStep("Channel is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); + waitForChannel1Visible(); } [Test] @@ -150,10 +150,14 @@ namespace osu.Game.Tests.Visual.Online public void TestChatHeight() { BindableFloat configChatHeight = new BindableFloat(); - config.BindWith(OsuSetting.ChatDisplayHeight, configChatHeight); + float newHeight = 0; - AddStep("Reset config chat height", () => configChatHeight.SetDefault()); + AddStep("Reset config chat height", () => + { + config.BindWith(OsuSetting.ChatDisplayHeight, configChatHeight); + configChatHeight.SetDefault(); + }); AddStep("Show overlay", () => chatOverlay.Show()); AddAssert("Overlay uses config height", () => chatOverlay.Height == configChatHeight.Default); AddStep("Click top bar", () => @@ -177,7 +181,7 @@ namespace osu.Game.Tests.Visual.Online AddAssert("Listing is visible", () => listingIsVisible); AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); - AddUntilStep("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); + waitForChannel1Visible(); } [Test] @@ -186,7 +190,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("Show overlay", () => chatOverlay.Show()); AddAssert("Listing is visible", () => listingIsVisible); AddStep("Search for 'number 2'", () => chatOverlayTextBox.Text = "number 2"); - AddUntilStep("Only channel 2 visibile", () => + AddUntilStep("Only channel 2 visible", () => { IEnumerable listingItems = chatOverlay.ChildrenOfType() .Where(item => item.IsPresent); @@ -276,7 +280,7 @@ namespace osu.Game.Tests.Visual.Online }); }); AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel1)); - AddUntilStep("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); + waitForChannel1Visible(); } [Test] @@ -299,7 +303,7 @@ namespace osu.Game.Tests.Visual.Online }); }); AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel2)); - AddUntilStep("Channel 2 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel2); + waitForChannel2Visible(); } [Test] @@ -323,7 +327,7 @@ namespace osu.Game.Tests.Visual.Online }); AddStep("Leave channel 2", () => channelManager.LeaveChannel(testChannel2)); AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel2)); - AddUntilStep("Channel 2 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel2); + waitForChannel2Visible(); } [Test] @@ -343,7 +347,7 @@ namespace osu.Game.Tests.Visual.Online }); }); AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel1)); - AddUntilStep("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); + waitForChannel1Visible(); } [Test] @@ -364,7 +368,7 @@ namespace osu.Game.Tests.Visual.Online }); AddStep("Set null channel", () => channelManager.CurrentChannel.Value = null); AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel1)); - AddUntilStep("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); + waitForChannel1Visible(); } [Test] @@ -374,6 +378,7 @@ namespace osu.Game.Tests.Visual.Online AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); + waitForChannel1Visible(); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); AddStep("Click drawable channel", () => clickDrawable(currentDrawableChannel)); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); @@ -411,11 +416,11 @@ namespace osu.Game.Tests.Visual.Online AddStep("Finish channel 2 load", () => chatOverlay.GetSlowLoadingChannel(testChannel2).LoadEvent.Set()); AddAssert("Channel 2 loaded", () => chatOverlay.GetSlowLoadingChannel(testChannel2).IsLoaded); - AddAssert("Channel 2 displayed", () => channelIsVisible && currentDrawableChannel.Channel == testChannel2); + waitForChannel2Visible(); AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); AddAssert("Channel 1 loaded", () => chatOverlay.GetSlowLoadingChannel(testChannel1).IsLoaded); - AddAssert("Channel 1 displayed", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); + waitForChannel1Visible(); } [Test] @@ -426,13 +431,12 @@ namespace osu.Game.Tests.Visual.Online channelManager.JoinChannel(testChannel1); chatOverlay.Show(); }); - AddAssert("Channel 1 displayed", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); - + waitForChannel1Visible(); AddStep("Press document close keys", () => InputManager.Keys(PlatformAction.DocumentClose)); AddAssert("Listing is visible", () => listingIsVisible); AddStep("Press tab restore keys", () => InputManager.Keys(PlatformAction.TabRestore)); - AddAssert("Channel 1 displayed", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); + waitForChannel1Visible(); } [Test] @@ -443,8 +447,7 @@ namespace osu.Game.Tests.Visual.Online channelManager.JoinChannel(testChannel1); chatOverlay.Show(); }); - AddAssert("Channel 1 displayed", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); - + waitForChannel1Visible(); AddStep("Press tab new keys", () => InputManager.Keys(PlatformAction.TabNew)); AddAssert("Listing is visible", () => listingIsVisible); } @@ -466,24 +469,29 @@ namespace osu.Game.Tests.Visual.Online chatOverlay.Show(); }); - AddAssert("Channel 1 displayed", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); + waitForChannel1Visible(); + AddStep("Press document next keys", () => InputManager.Keys(PlatformAction.DocumentNext)); + waitForChannel2Visible(); AddStep("Press document next keys", () => InputManager.Keys(PlatformAction.DocumentNext)); - AddAssert("Channel 2 displayed", () => channelIsVisible && currentDrawableChannel.Channel == testChannel2); + AddUntilStep("PM Channel 1 displayed", () => channelIsVisible && currentDrawableChannel?.Channel == pmChannel1); AddStep("Press document next keys", () => InputManager.Keys(PlatformAction.DocumentNext)); - AddAssert("PM Channel 1 displayed", () => channelIsVisible && currentDrawableChannel.Channel == pmChannel1); + AddUntilStep("PM Channel 2 displayed", () => channelIsVisible && currentDrawableChannel?.Channel == pmChannel2); AddStep("Press document next keys", () => InputManager.Keys(PlatformAction.DocumentNext)); - AddAssert("PM Channel 2 displayed", () => channelIsVisible && currentDrawableChannel.Channel == pmChannel2); + AddUntilStep("Announce channel displayed", () => channelIsVisible && currentDrawableChannel?.Channel == announceChannel); AddStep("Press document next keys", () => InputManager.Keys(PlatformAction.DocumentNext)); - AddAssert("Announce channel displayed", () => channelIsVisible && currentDrawableChannel.Channel == announceChannel); - - AddStep("Press document next keys", () => InputManager.Keys(PlatformAction.DocumentNext)); - AddAssert("Channel 1 displayed", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); + waitForChannel1Visible(); } + private void waitForChannel1Visible() => + AddUntilStep("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel?.Channel == testChannel1); + + private void waitForChannel2Visible() => + AddUntilStep("Channel 2 is visible", () => channelIsVisible && currentDrawableChannel?.Channel == testChannel2); + private bool listingIsVisible => chatOverlay.ChildrenOfType().Single().State.Value == Visibility.Visible; @@ -493,8 +501,9 @@ namespace osu.Game.Tests.Visual.Online private bool channelIsVisible => !listingIsVisible && !loadingIsVisible; + [CanBeNull] private DrawableChannel currentDrawableChannel => - chatOverlay.ChildrenOfType().Single(); + chatOverlay.ChildrenOfType().SingleOrDefault(); private ChannelListItem getChannelListItem(Channel channel) => chatOverlay.ChildrenOfType().Single(item => item.Channel == channel); diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneStatisticsPanel.cs b/osu.Game.Tests/Visual/Ranking/TestSceneStatisticsPanel.cs index 35281a85eb..1efe6d7380 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneStatisticsPanel.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneStatisticsPanel.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Threading; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -13,6 +14,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Scoring; using osu.Game.Screens.Ranking.Statistics; using osu.Game.Rulesets.Osu.Objects; @@ -114,10 +116,7 @@ namespace osu.Game.Tests.Visual.Ranking throw new NotImplementedException(); } - public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) - { - throw new NotImplementedException(); - } + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TestBeatmapConverter(beatmap); public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) { @@ -151,6 +150,24 @@ namespace osu.Game.Tests.Visual.Ranking } } }; + + private class TestBeatmapConverter : IBeatmapConverter + { +#pragma warning disable CS0067 // The event is never used + public event Action> ObjectConverted; +#pragma warning restore CS0067 + + public IBeatmap Beatmap { get; } + + public TestBeatmapConverter(IBeatmap beatmap) + { + Beatmap = beatmap; + } + + public bool CanConvert() => true; + + public IBeatmap Convert(CancellationToken cancellationToken = default) => Beatmap.Clone(); + } } private class TestRulesetAllStatsRequireHitEvents : TestRuleset diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index afedf36cad..7fd94b57f3 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Extensions.Color4Extensions; using osu.Game.Beatmaps; +using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; @@ -188,6 +189,24 @@ namespace osu.Game.Graphics } } + /// + /// Retrieves the main accent colour for a . + /// + public Color4? ForRoomCategory(RoomCategory roomCategory) + { + switch (roomCategory) + { + case RoomCategory.Spotlight: + return SpotlightColour; + + case RoomCategory.FeaturedArtist: + return FeaturedArtistColour; + + default: + return null; + } + } + /// /// Returns a foreground text colour that is supposed to contrast well with /// the supplied . @@ -360,5 +379,8 @@ namespace osu.Game.Graphics public readonly Color4 ChatBlue = Color4Extensions.FromHex(@"17292e"); public readonly Color4 ContextMenuGray = Color4Extensions.FromHex(@"223034"); + + public Color4 SpotlightColour => Green2; + public Color4 FeaturedArtistColour => Blue2; } } diff --git a/osu.Game/Online/HubClientConnector.cs b/osu.Game/Online/HubClientConnector.cs index ca9bf00b23..261724e315 100644 --- a/osu.Game/Online/HubClientConnector.cs +++ b/osu.Game/Online/HubClientConnector.cs @@ -20,6 +20,8 @@ namespace osu.Game.Online { public class HubClientConnector : IHubClientConnector { + public const string SERVER_SHUTDOWN_MESSAGE = "Server is shutting down."; + /// /// Invoked whenever a new hub connection is built, to configure it before it's started. /// @@ -64,20 +66,28 @@ namespace osu.Game.Online this.preferMessagePack = preferMessagePack; apiState.BindTo(api.State); - apiState.BindValueChanged(state => - { - switch (state.NewValue) - { - case APIState.Failing: - case APIState.Offline: - Task.Run(() => disconnect(true)); - break; + apiState.BindValueChanged(state => connectIfPossible(), true); + } - case APIState.Online: - Task.Run(connect); - break; - } - }, true); + public void Reconnect() + { + Logger.Log($"{clientName} reconnecting...", LoggingTarget.Network); + Task.Run(connectIfPossible); + } + + private void connectIfPossible() + { + switch (apiState.Value) + { + case APIState.Failing: + case APIState.Offline: + Task.Run(() => disconnect(true)); + break; + + case APIState.Online: + Task.Run(connect); + break; + } } private async Task connect() diff --git a/osu.Game/Online/IHubClientConnector.cs b/osu.Game/Online/IHubClientConnector.cs index d2ceb1f030..b168e4669f 100644 --- a/osu.Game/Online/IHubClientConnector.cs +++ b/osu.Game/Online/IHubClientConnector.cs @@ -30,5 +30,10 @@ namespace osu.Game.Online /// Invoked whenever a new hub connection is built, to configure it before it's started. /// public Action? ConfigureConnection { get; set; } + + /// + /// Reconnect if already connected. + /// + void Reconnect(); } } diff --git a/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs b/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs index 4729765084..cda313bd0a 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs @@ -25,12 +25,7 @@ namespace osu.Game.Online.Multiplayer Debug.Assert(exception != null); - string message = exception is HubException - // HubExceptions arrive with additional message context added, but we want to display the human readable message: - // "An unexpected error occurred invoking 'AddPlaylistItem' on the server.InvalidStateException: Can't enqueue more than 3 items at once." - // We generally use the message field for a user-parseable error (eventually to be replaced), so drop the first part for now. - ? exception.Message.Substring(exception.Message.IndexOf(':') + 1).Trim() - : exception.Message; + string message = exception.GetHubExceptionMessage() ?? exception.Message; Logger.Log(message, level: LogLevel.Important); onError?.Invoke(exception); @@ -40,5 +35,16 @@ namespace osu.Game.Online.Multiplayer onSuccess?.Invoke(); } }); + + public static string? GetHubExceptionMessage(this Exception exception) + { + if (exception is HubException hubException) + // HubExceptions arrive with additional message context added, but we want to display the human readable message: + // "An unexpected error occurred invoking 'AddPlaylistItem' on the server.InvalidStateException: Can't enqueue more than 3 items at once." + // We generally use the message field for a user-parseable error (eventually to be replaced), so drop the first part for now. + return hubException.Message.Substring(exception.Message.IndexOf(':') + 1).Trim(); + + return null; + } } } diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs index 4dc23d8b85..a3423d4189 100644 --- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs @@ -3,10 +3,12 @@ #nullable enable +using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR.Client; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -71,14 +73,23 @@ namespace osu.Game.Online.Multiplayer } } - protected override Task JoinRoom(long roomId, string? password = null) + protected override async Task JoinRoom(long roomId, string? password = null) { if (!IsConnected.Value) - return Task.FromCanceled(new CancellationToken(true)); + throw new OperationCanceledException(); Debug.Assert(connection != null); - return connection.InvokeAsync(nameof(IMultiplayerServer.JoinRoomWithPassword), roomId, password ?? string.Empty); + try + { + return await connection.InvokeAsync(nameof(IMultiplayerServer.JoinRoomWithPassword), roomId, password ?? string.Empty); + } + catch (HubException exception) + { + if (exception.GetHubExceptionMessage() == HubClientConnector.SERVER_SHUTDOWN_MESSAGE) + connector?.Reconnect(); + throw; + } } protected override Task LeaveRoomInternal() diff --git a/osu.Game/Online/Rooms/RoomCategory.cs b/osu.Game/Online/Rooms/RoomCategory.cs index a1dcfa5fd9..bca4d78359 100644 --- a/osu.Game/Online/Rooms/RoomCategory.cs +++ b/osu.Game/Online/Rooms/RoomCategory.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.ComponentModel; + namespace osu.Game.Online.Rooms { public enum RoomCategory @@ -8,5 +10,8 @@ namespace osu.Game.Online.Rooms // used for osu-web deserialization so names shouldn't be changed. Normal, Spotlight, + + [Description("Featured Artist")] + FeaturedArtist, } } diff --git a/osu.Game/Online/Spectator/OnlineSpectatorClient.cs b/osu.Game/Online/Spectator/OnlineSpectatorClient.cs index 4d6ca0b311..0f77f723db 100644 --- a/osu.Game/Online/Spectator/OnlineSpectatorClient.cs +++ b/osu.Game/Online/Spectator/OnlineSpectatorClient.cs @@ -5,10 +5,12 @@ using System.Diagnostics; using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR.Client; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Online.API; +using osu.Game.Online.Multiplayer; namespace osu.Game.Online.Spectator { @@ -47,14 +49,23 @@ namespace osu.Game.Online.Spectator } } - protected override Task BeginPlayingInternal(SpectatorState state) + protected override async Task BeginPlayingInternal(SpectatorState state) { if (!IsConnected.Value) - return Task.CompletedTask; + return; Debug.Assert(connection != null); - return connection.SendAsync(nameof(ISpectatorServer.BeginPlaySession), state); + try + { + await connection.SendAsync(nameof(ISpectatorServer.BeginPlaySession), state); + } + catch (HubException exception) + { + if (exception.GetHubExceptionMessage() == HubClientConnector.SERVER_SHUTDOWN_MESSAGE) + connector?.Reconnect(); + throw; + } } protected override Task SendFramesInternal(FrameDataBundle bundle) diff --git a/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapBadge.cs b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapBadge.cs index 4f336d85fc..20ee11c7f6 100644 --- a/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapBadge.cs +++ b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapBadge.cs @@ -15,7 +15,7 @@ namespace osu.Game.Overlays.BeatmapSet private void load(OsuColour colours) { BadgeText = BeatmapsetsStrings.FeaturedArtistBadgeLabel; - BadgeColour = colours.Blue1; + BadgeColour = colours.FeaturedArtistColour; // todo: add linking support to allow redirecting featured artist badge to corresponding track. } } diff --git a/osu.Game/Overlays/BeatmapSet/SpotlightBeatmapBadge.cs b/osu.Game/Overlays/BeatmapSet/SpotlightBeatmapBadge.cs index 3204f79b21..9c5378a967 100644 --- a/osu.Game/Overlays/BeatmapSet/SpotlightBeatmapBadge.cs +++ b/osu.Game/Overlays/BeatmapSet/SpotlightBeatmapBadge.cs @@ -15,7 +15,7 @@ namespace osu.Game.Overlays.BeatmapSet private void load(OsuColour colours) { BadgeText = BeatmapsetsStrings.SpotlightBadgeLabel; - BadgeColour = colours.Pink1; + BadgeColour = colours.SpotlightColour; // todo: add linking support to allow redirecting spotlight badge to https://osu.ppy.sh/wiki/en/Beatmap_Spotlights. } } diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index a34776ddf0..a87e65b735 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -127,9 +127,12 @@ namespace osu.Game.Overlays.Settings.Sections dropdownItems.Add(skin.ToLive(realm)); dropdownItems.Insert(protectedCount, random_skin_info); - skinDropdown.Items = dropdownItems; + Schedule(() => + { + skinDropdown.Items = dropdownItems; - updateSelectedSkinFromConfig(); + updateSelectedSkinFromConfig(); + }); } private void updateSelectedSkinFromConfig() diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index 46b8b35da2..929c362bd8 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -329,7 +329,7 @@ namespace osu.Game.Overlays.Volume if (isPrecise) { - scrollAccumulation += delta * adjust_step * 0.1; + scrollAccumulation += delta * adjust_step; while (Precision.AlmostBigger(Math.Abs(scrollAccumulation), precision)) { diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 7019dad803..aaee15eae8 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -26,6 +26,8 @@ namespace osu.Game.Rulesets.Edit public abstract class DistancedHitObjectComposer : HitObjectComposer, IDistanceSnapProvider, IScrollBindingHandler where TObject : HitObject { + private const float adjust_step = 0.1f; + public Bindable DistanceSpacingMultiplier { get; } = new BindableDouble(1.0) { MinValue = 0.1, @@ -61,7 +63,7 @@ namespace osu.Game.Rulesets.Edit Child = distanceSpacingSlider = new ExpandableSlider> { Current = { BindTarget = DistanceSpacingMultiplier }, - KeyboardStep = 0.1f, + KeyboardStep = adjust_step, } } }); @@ -93,7 +95,7 @@ namespace osu.Game.Rulesets.Edit { case GlobalAction.EditorIncreaseDistanceSpacing: case GlobalAction.EditorDecreaseDistanceSpacing: - return adjustDistanceSpacing(e.Action, 0.1f); + return adjustDistanceSpacing(e.Action, adjust_step); } return false; @@ -109,7 +111,7 @@ namespace osu.Game.Rulesets.Edit { case GlobalAction.EditorIncreaseDistanceSpacing: case GlobalAction.EditorDecreaseDistanceSpacing: - return adjustDistanceSpacing(e.Action, e.ScrollAmount * (e.IsPrecise ? 0.01f : 0.1f)); + return adjustDistanceSpacing(e.Action, e.ScrollAmount * adjust_step); } return false; diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index d66c85f9ad..1414644a54 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -493,7 +493,7 @@ namespace osu.Game.Screens.Edit if (scrollAccumulation != 0 && Math.Sign(scrollAccumulation) != scrollDirection) scrollAccumulation = scrollDirection * (precision - Math.Abs(scrollAccumulation)); - scrollAccumulation += scrollComponent * (e.IsPrecise ? 0.1 : 1); + scrollAccumulation += scrollComponent; // because we are doing snapped seeking, we need to add up precise scrolls until they accumulate to an arbitrary cut-off. while (Math.Abs(scrollAccumulation) >= precision) diff --git a/osu.Game/Screens/Edit/Setup/SetupScreen.cs b/osu.Game/Screens/Edit/Setup/SetupScreen.cs index b95aabc1c4..e0fc5f1aff 100644 --- a/osu.Game/Screens/Edit/Setup/SetupScreen.cs +++ b/osu.Game/Screens/Edit/Setup/SetupScreen.cs @@ -41,7 +41,7 @@ namespace osu.Game.Screens.Edit.Setup Add(new Box { - Colour = colourProvider.Background2, + Colour = colourProvider.Background3, RelativeSizeAxes = Axes.Both, }); diff --git a/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs b/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs index 4dd7a75d4a..57fcff6a4c 100644 --- a/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs +++ b/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Timing; using osu.Framework.Utils; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.Containers; @@ -28,6 +29,8 @@ namespace osu.Game.Screens.Edit.Timing private Drawable weight; private Drawable stick; + private IAdjustableClock metronomeClock; + [Resolved] private OverlayColourProvider overlayColourProvider { get; set; } @@ -192,6 +195,8 @@ namespace osu.Game.Screens.Edit.Timing Y = -3, }, }; + + Clock = new FramedClock(metronomeClock = new StopwatchClock(true)); } private double beatLength; @@ -216,6 +221,8 @@ namespace osu.Game.Screens.Edit.Timing if (BeatSyncSource.ControlPoints == null || BeatSyncSource.Clock == null) return; + metronomeClock.Rate = IsBeatSyncedWithTrack ? BeatSyncSource.Clock.Rate : 1; + timingPoint = BeatSyncSource.ControlPoints.TimingPointAt(BeatSyncSource.Clock.CurrentTime); if (beatLength != timingPoint.BeatLength) diff --git a/osu.Game/Screens/Edit/Timing/RowAttribute.cs b/osu.Game/Screens/Edit/Timing/RowAttribute.cs index 74d43628e1..46bb62c9e0 100644 --- a/osu.Game/Screens/Edit/Timing/RowAttribute.cs +++ b/osu.Game/Screens/Edit/Timing/RowAttribute.cs @@ -19,6 +19,8 @@ namespace osu.Game.Screens.Edit.Timing private readonly string label; + protected Drawable Background { get; private set; } + protected FillFlowContainer Content { get; private set; } public RowAttribute(ControlPoint point, string label) @@ -41,11 +43,11 @@ namespace osu.Game.Screens.Edit.Timing Masking = true; CornerRadius = 3; - InternalChildren = new Drawable[] + InternalChildren = new[] { - new Box + Background = new Box { - Colour = overlayColours.Background4, + Colour = overlayColours.Background5, RelativeSizeAxes = Axes.Both, }, Content = new FillFlowContainer diff --git a/osu.Game/Screens/Edit/Timing/RowAttributes/TimingRowAttribute.cs b/osu.Game/Screens/Edit/Timing/RowAttributes/TimingRowAttribute.cs index f8ec4aef25..8a07088545 100644 --- a/osu.Game/Screens/Edit/Timing/RowAttributes/TimingRowAttribute.cs +++ b/osu.Game/Screens/Edit/Timing/RowAttributes/TimingRowAttribute.cs @@ -7,6 +7,7 @@ using osu.Framework.Extensions; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; namespace osu.Game.Screens.Edit.Timing.RowAttributes { @@ -24,10 +25,12 @@ namespace osu.Game.Screens.Edit.Timing.RowAttributes } [BackgroundDependencyLoader] - private void load() + private void load(OverlayColourProvider colourProvider) { Content.Add(text = new AttributeText(Point)); + Background.Colour = colourProvider.Background4; + timeSignature.BindValueChanged(_ => updateText()); beatLength.BindValueChanged(_ => updateText(), true); } diff --git a/osu.Game/Screens/Edit/Verify/IssueList.cs b/osu.Game/Screens/Edit/Verify/IssueList.cs index 415acc0e22..84b2609a61 100644 --- a/osu.Game/Screens/Edit/Verify/IssueList.cs +++ b/osu.Game/Screens/Edit/Verify/IssueList.cs @@ -51,7 +51,7 @@ namespace osu.Game.Screens.Edit.Verify { new Box { - Colour = colours.Background2, + Colour = colours.Background3, RelativeSizeAxes = Axes.Both, }, new OsuScrollContainer diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index 9dc5a53907..56e16bb746 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -41,7 +41,7 @@ namespace osu.Game.Screens.Edit.Verify ColumnDimensions = new[] { new Dimension(), - new Dimension(GridSizeMode.Absolute, 200), + new Dimension(GridSizeMode.Absolute, 250), }, Content = new[] { diff --git a/osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.cs b/osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.cs index 760de354dc..a7ea32ee7c 100644 --- a/osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.cs @@ -30,8 +30,7 @@ namespace osu.Game.Screens.OnlinePlay.Components { status.BindValueChanged(s => { - this.FadeColour(category.Value == RoomCategory.Spotlight ? colours.Pink : s.NewValue.GetAppropriateColour(colours) - , transitionDuration); + this.FadeColour(colours.ForRoomCategory(category.Value) ?? s.NewValue.GetAppropriateColour(colours), transitionDuration); }, true); } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 8e3aa77e7b..772232f6b4 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -237,7 +237,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components roomCategory.BindTo(Room.Category); roomCategory.BindValueChanged(c => { - if (c.NewValue == RoomCategory.Spotlight) + if (c.NewValue > RoomCategory.Normal) specialCategoryPill.Show(); else specialCategoryPill.Hide(); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs index 6cdbeb2af4..539af2ebaf 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; @@ -13,6 +14,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components public class RoomSpecialCategoryPill : OnlinePlayComposite { private SpriteText text; + private PillContainer pill; + + [Resolved] + private OsuColour colours { get; set; } public RoomSpecialCategoryPill() { @@ -20,9 +25,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load() { - InternalChild = new PillContainer + InternalChild = pill = new PillContainer { Background = { @@ -43,7 +48,14 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { base.LoadComplete(); - Category.BindValueChanged(c => text.Text = c.NewValue.ToString(), true); + Category.BindValueChanged(c => + { + text.Text = c.NewValue.GetLocalisableDescription(); + + var backgroundColour = colours.ForRoomCategory(Category.Value); + if (backgroundColour != null) + pill.Background.Colour = backgroundColour.Value; + }, true); } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index d61fbea387..0fd9290880 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -128,7 +128,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { foreach (var room in roomFlow) { - roomFlow.SetLayoutPosition(room, room.Room.Category.Value == RoomCategory.Spotlight + roomFlow.SetLayoutPosition(room, room.Room.Category.Value > RoomCategory.Normal // Always show spotlight playlists at the top of the listing. ? float.MinValue : -(room.Room.RoomID.Value ?? 0)); diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs index dced9b8691..b36e162e3d 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -49,6 +50,10 @@ namespace osu.Game.Screens.OnlinePlay.Playlists case PlaylistsCategory.Spotlight: criteria.Category = @"spotlight"; break; + + case PlaylistsCategory.FeaturedArtist: + criteria.Category = @"featured_artist"; + break; } return criteria; @@ -73,7 +78,10 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { Any, Normal, - Spotlight + Spotlight, + + [Description("Featured Artist")] + FeaturedArtist, } } } diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 019a9f9730..bdc98e53f9 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; +using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio.Track; @@ -80,16 +81,13 @@ namespace osu.Game.Screens.Play.HUD difficultyCache.GetTimedDifficultyAttributesAsync(gameplayWorkingBeatmap, gameplayState.Ruleset, clonedMods, loadCancellationSource.Token) .ContinueWith(task => Schedule(() => { - if (task.Exception != null) - return; - timedAttributes = task.GetResultSafely(); IsValid = true; if (lastJudgement != null) onJudgementChanged(lastJudgement); - })); + }), TaskContinuationOptions.OnlyOnRanToCompletion); } } diff --git a/osu.Game/Tests/Gameplay/TestGameplayState.cs b/osu.Game/Tests/Gameplay/TestGameplayState.cs new file mode 100644 index 0000000000..0d00f52d15 --- /dev/null +++ b/osu.Game/Tests/Gameplay/TestGameplayState.cs @@ -0,0 +1,32 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using System.Collections.Generic; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Scoring; +using osu.Game.Screens.Play; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Tests.Gameplay +{ + /// + /// Static class providing a convenience method to retrieve a correctly-initialised instance in testing scenarios. + /// + public static class TestGameplayState + { + /// + /// Creates a correctly-initialised instance for use in testing. + /// + public static GameplayState Create(Ruleset ruleset, IReadOnlyList? mods = null, Score? score = null) + { + var beatmap = new TestBeatmap(ruleset.RulesetInfo); + var workingBeatmap = new TestWorkingBeatmap(beatmap); + var playableBeatmap = workingBeatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods); + + return new GameplayState(playableBeatmap, ruleset, mods, score); + } + } +} diff --git a/osu.Game/Tests/Visual/EditorClockTestScene.cs b/osu.Game/Tests/Visual/EditorClockTestScene.cs index 2d3e1960d9..15e4fc4d8f 100644 --- a/osu.Game/Tests/Visual/EditorClockTestScene.cs +++ b/osu.Game/Tests/Visual/EditorClockTestScene.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Input.Events; using osu.Game.Beatmaps; @@ -24,6 +25,8 @@ namespace osu.Game.Tests.Visual [Cached] protected new readonly EditorClock Clock; + private readonly Bindable frequencyAdjustment = new BindableDouble(1); + protected virtual bool ScrollUsingMouseWheel => true; protected EditorClockTestScene() @@ -44,14 +47,21 @@ namespace osu.Game.Tests.Visual protected override void LoadComplete() { base.LoadComplete(); + Beatmap.BindValueChanged(beatmapChanged, true); + + AddSliderStep("editor clock rate", 0.0, 2.0, 1.0, v => frequencyAdjustment.Value = v); } private void beatmapChanged(ValueChangedEvent e) { + e.OldValue?.Track.RemoveAdjustment(AdjustableProperty.Frequency, frequencyAdjustment); + Clock.Beatmap = e.NewValue.Beatmap; Clock.ChangeSource(e.NewValue.Track); Clock.ProcessFrame(); + + e.NewValue.Track.AddAdjustment(AdjustableProperty.Frequency, frequencyAdjustment); } protected override void Update() diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 5de0f4346c..79cfd7c917 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index c1a473ff75..b1ba64beba 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - +